2 * Copyright 2001-2007 Internet2
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
20 * Storage Service using ODBC
23 #if defined (_MSC_VER) || defined(__BORLANDC__)
24 # include "config_win32.h"
30 # define _CRT_NONSTDC_NO_DEPRECATE 1
31 # define _CRT_SECURE_NO_DEPRECATE 1
35 # define ODBCSTORE_EXPORTS __declspec(dllexport)
37 # define ODBCSTORE_EXPORTS
40 #include <xercesc/util/XMLUniDefs.hpp>
41 #include <xmltooling/logging.h>
42 #include <xmltooling/XMLToolingConfig.h>
43 #include <xmltooling/util/NDC.h>
44 #include <xmltooling/util/StorageService.h>
45 #include <xmltooling/util/Threads.h>
46 #include <xmltooling/util/XMLHelper.h>
51 using namespace xmltooling::logging;
52 using namespace xmltooling;
53 using namespace xercesc;
56 #define PLUGIN_VER_MAJOR 1
57 #define PLUGIN_VER_MINOR 0
59 #define LONGDATA_BUFLEN 16384
61 #define COLSIZE_CONTEXT 255
62 #define COLSIZE_ID 255
63 #define COLSIZE_STRING_VALUE 255
65 #define STRING_TABLE "strings"
66 #define TEXT_TABLE "texts"
69 CREATE TABLE version (
70 major tinyint NOT NULL,
71 minor tinyint NOT NULL
74 CREATE TABLE strings (
75 context varchar(255) not null,
76 id varchar(255) not null,
77 expires datetime not null,
78 version smallint not null,
79 value varchar(255) not null,
80 PRIMARY KEY (context, id)
84 context varchar(255) not null,
85 id varchar(255) not null,
86 expires datetime not null,
87 version smallint not null,
89 PRIMARY KEY (context, id)
94 static const XMLCh cleanupInterval[] = UNICODE_LITERAL_15(c,l,e,a,n,u,p,I,n,t,e,r,v,a,l);
95 static const XMLCh ConnectionString[] = UNICODE_LITERAL_16(C,o,n,n,e,c,t,i,o,n,S,t,r,i,n,g);
97 // RAII for ODBC handles
99 ODBCConn(SQLHDBC conn) : handle(conn), autoCommit(true) {}
101 SQLRETURN sr = SQL_SUCCESS;
103 sr = SQLSetConnectAttr(handle, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, NULL);
104 SQLDisconnect(handle);
105 SQLFreeHandle(SQL_HANDLE_DBC,handle);
106 if (!SQL_SUCCEEDED(sr))
107 throw IOException("Failed to commit connection and return to auto-commit mode.");
109 operator SQLHDBC() {return handle;}
114 class ODBCStorageService : public StorageService
117 ODBCStorageService(const DOMElement* e);
118 virtual ~ODBCStorageService();
120 bool createString(const char* context, const char* key, const char* value, time_t expiration) {
121 return createRow(STRING_TABLE, context, key, value, expiration);
123 int readString(const char* context, const char* key, string* pvalue=NULL, time_t* pexpiration=NULL, int version=0) {
124 return readRow(STRING_TABLE, context, key, pvalue, pexpiration, version, false);
126 int updateString(const char* context, const char* key, const char* value=NULL, time_t expiration=0, int version=0) {
127 return updateRow(STRING_TABLE, context, key, value, expiration, version);
129 bool deleteString(const char* context, const char* key) {
130 return deleteRow(STRING_TABLE, context, key);
133 bool createText(const char* context, const char* key, const char* value, time_t expiration) {
134 return createRow(TEXT_TABLE, context, key, value, expiration);
136 int readText(const char* context, const char* key, string* pvalue=NULL, time_t* pexpiration=NULL, int version=0) {
137 return readRow(TEXT_TABLE, context, key, pvalue, pexpiration, version, true);
139 int updateText(const char* context, const char* key, const char* value=NULL, time_t expiration=0, int version=0) {
140 return updateRow(TEXT_TABLE, context, key, value, expiration, version);
142 bool deleteText(const char* context, const char* key) {
143 return deleteRow(TEXT_TABLE, context, key);
146 void reap(const char* context) {
147 reap(STRING_TABLE, context);
148 reap(TEXT_TABLE, context);
151 void updateContext(const char* context, time_t expiration) {
152 updateContext(STRING_TABLE, context, expiration);
153 updateContext(TEXT_TABLE, context, expiration);
156 void deleteContext(const char* context) {
157 deleteContext(STRING_TABLE, context);
158 deleteContext(TEXT_TABLE, context);
163 bool createRow(const char *table, const char* context, const char* key, const char* value, time_t expiration);
164 int readRow(const char *table, const char* context, const char* key, string* pvalue, time_t* pexpiration, int version, bool text);
165 int updateRow(const char *table, const char* context, const char* key, const char* value, time_t expiration, int version);
166 bool deleteRow(const char *table, const char* context, const char* key);
168 void reap(const char* table, const char* context);
169 void updateContext(const char* table, const char* context, time_t expiration);
170 void deleteContext(const char* table, const char* context);
173 SQLHSTMT getHSTMT(SQLHDBC);
174 pair<int,int> getVersion(SQLHDBC);
175 bool log_error(SQLHANDLE handle, SQLSMALLINT htype, const char* checkfor=NULL);
177 static void* cleanup_fn(void*);
181 int m_cleanupInterval;
182 CondWait* shutdown_wait;
183 Thread* cleanup_thread;
190 StorageService* ODBCStorageServiceFactory(const DOMElement* const & e)
192 return new ODBCStorageService(e);
195 // convert SQL timestamp to time_t
196 time_t timeFromTimestamp(SQL_TIMESTAMP_STRUCT expires)
200 t.tm_sec=expires.second;
201 t.tm_min=expires.minute;
202 t.tm_hour=expires.hour;
203 t.tm_mday=expires.day;
204 t.tm_mon=expires.month-1;
205 t.tm_year=expires.year-1900;
207 #if defined(HAVE_TIMEGM)
210 ret = mktime(&t) - timezone;
215 // conver time_t to SQL string
216 void timestampFromTime(time_t t, char* ret)
220 struct tm* ptime=gmtime_r(&t,&res);
222 struct tm* ptime=gmtime(&t);
224 strftime(ret,32,"{ts '%Y-%m-%d %H:%M:%S'}",ptime);
227 // make a string safe for SQL command
228 // result to be free'd only if it isn't the input
229 static char *makeSafeSQL(const char *src)
235 // see if any conversion needed
236 for (s=(char*)src; *s; nc++,s++) if (*s=='\'') ns++;
237 if (ns==0) return ((char*)src);
239 char *safe = new char[(nc+2*ns+1)];
240 for (s=safe; *src; src++) {
241 if (*src=='\'') *s++ = '\'';
248 void freeSafeSQL(char *safe, const char *src)
255 ODBCStorageService::ODBCStorageService(const DOMElement* e) : m_log(Category::getInstance("XMLTooling.StorageService")),
256 m_cleanupInterval(900), shutdown_wait(NULL), cleanup_thread(NULL), shutdown(false), m_henv(SQL_NULL_HANDLE)
259 xmltooling::NDC ndc("ODBCStorageService");
262 const XMLCh* tag=e ? e->getAttributeNS(NULL,cleanupInterval) : NULL;
264 m_cleanupInterval = XMLString::parseInt(tag);
265 if (!m_cleanupInterval)
266 m_cleanupInterval = 900;
268 if (m_henv == SQL_NULL_HANDLE) {
269 // Enable connection pooling.
270 SQLSetEnvAttr(SQL_NULL_HANDLE, SQL_ATTR_CONNECTION_POOLING, (void*)SQL_CP_ONE_PER_HENV, 0);
272 // Allocate the environment.
273 if (!SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &m_henv)))
274 throw XMLToolingException("ODBC failed to initialize.");
277 SQLSetEnvAttr(m_henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
279 m_log.info("ODBC initialized");
282 // Grab connection string from the configuration.
283 e = e ? XMLHelper::getFirstChildElement(e,ConnectionString) : NULL;
284 if (!e || !e->hasChildNodes()) {
285 SQLFreeHandle(SQL_HANDLE_ENV, m_henv);
286 throw XMLToolingException("ODBC StorageService requires ConnectionString element in configuration.");
288 auto_ptr_char arg(e->getFirstChild()->getNodeValue());
289 m_connstring=arg.get();
291 // Connect and check version.
292 ODBCConn conn(getHDBC());
293 pair<int,int> v=getVersion(conn);
295 // Make sure we've got the right version.
296 if (v.first != PLUGIN_VER_MAJOR) {
297 SQLFreeHandle(SQL_HANDLE_ENV, m_henv);
298 m_log.crit("unknown database version: %d.%d", v.first, v.second);
299 throw XMLToolingException("Unknown database version for ODBC StorageService.");
302 // Initialize the cleanup thread
303 shutdown_wait = CondWait::create();
304 cleanup_thread = Thread::create(&cleanup_fn, (void*)this);
307 ODBCStorageService::~ODBCStorageService()
310 shutdown_wait->signal();
311 cleanup_thread->join(NULL);
312 delete shutdown_wait;
313 if (m_henv != SQL_NULL_HANDLE)
314 SQLFreeHandle(SQL_HANDLE_ENV, m_henv);
317 bool ODBCStorageService::log_error(SQLHANDLE handle, SQLSMALLINT htype, const char* checkfor)
328 ret = SQLGetDiagRec(htype, handle, ++i, state, &native, text, sizeof(text), &len);
329 if (SQL_SUCCEEDED(ret)) {
330 m_log.error("ODBC Error: %s:%ld:%ld:%s", state, i, native, text);
331 if (checkfor && !strcmp(checkfor, (const char*)state))
334 } while(SQL_SUCCEEDED(ret));
338 SQLHDBC ODBCStorageService::getHDBC()
341 xmltooling::NDC ndc("getHDBC");
346 SQLRETURN sr=SQLAllocHandle(SQL_HANDLE_DBC, m_henv, &handle);
347 if (!SQL_SUCCEEDED(sr)) {
348 m_log.error("failed to allocate connection handle");
349 log_error(m_henv, SQL_HANDLE_ENV);
350 throw IOException("ODBC StorageService failed to allocate a connection handle.");
353 sr=SQLDriverConnect(handle,NULL,(SQLCHAR*)m_connstring.c_str(),m_connstring.length(),NULL,0,NULL,SQL_DRIVER_NOPROMPT);
354 if (!SQL_SUCCEEDED(sr)) {
355 m_log.error("failed to connect to database");
356 log_error(handle, SQL_HANDLE_DBC);
357 throw IOException("ODBC StorageService failed to connect to database.");
360 sr = SQLSetConnectAttr(handle, SQL_ATTR_TXN_ISOLATION, (SQLPOINTER)SQL_TXN_SERIALIZABLE, NULL);
361 if (!SQL_SUCCEEDED(sr))
362 throw IOException("ODBC StorageService failed to enable transaction isolation.");
367 SQLHSTMT ODBCStorageService::getHSTMT(SQLHDBC conn)
370 SQLRETURN sr=SQLAllocHandle(SQL_HANDLE_STMT,conn,&hstmt);
371 if (!SQL_SUCCEEDED(sr)) {
372 m_log.error("failed to allocate statement handle");
373 log_error(conn, SQL_HANDLE_DBC);
374 throw IOException("ODBC StorageService failed to allocate a statement handle.");
379 pair<int,int> ODBCStorageService::getVersion(SQLHDBC conn)
381 // Grab the version number from the database.
382 SQLHSTMT stmt = getHSTMT(conn);
384 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)"SELECT major,minor FROM version", SQL_NTS);
385 if (!SQL_SUCCEEDED(sr)) {
386 m_log.error("failed to read version from database");
387 log_error(stmt, SQL_HANDLE_STMT);
388 throw IOException("ODBC StorageService failed to read version from database.");
393 SQLBindCol(stmt,1,SQL_C_SLONG,&major,0,NULL);
394 SQLBindCol(stmt,2,SQL_C_SLONG,&minor,0,NULL);
396 if ((sr=SQLFetch(stmt)) != SQL_NO_DATA)
397 return pair<int,int>(major,minor);
399 m_log.error("no rows returned in version query");
400 throw IOException("ODBC StorageService failed to read version from database.");
403 bool ODBCStorageService::createRow(const char* table, const char* context, const char* key, const char* value, time_t expiration)
406 xmltooling::NDC ndc("createRow");
410 timestampFromTime(expiration, timebuf);
412 // Get statement handle.
413 ODBCConn conn(getHDBC());
414 SQLHSTMT stmt = getHSTMT(conn);
416 // Prepare and exectute insert statement.
417 //char *scontext = makeSafeSQL(context);
418 //char *skey = makeSafeSQL(key);
419 //char *svalue = makeSafeSQL(value);
420 string q = string("INSERT INTO ") + table + " VALUES (?,?," + timebuf + ",1,?)";
422 SQLRETURN sr = SQLPrepare(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
423 if (!SQL_SUCCEEDED(sr)) {
424 m_log.error("SQLPrepare failed (t=%s, c=%s, k=%s)", table, context, key);
425 log_error(stmt, SQL_HANDLE_STMT);
426 throw IOException("ODBC StorageService failed to insert record.");
428 m_log.debug("SQLPrepare succeded. SQL: %s", q.c_str());
430 SQLINTEGER b_ind = SQL_NTS;
431 sr = SQLBindParam(stmt, 1, SQL_C_CHAR, SQL_VARCHAR, 255, 0, const_cast<char*>(context), &b_ind);
432 if (!SQL_SUCCEEDED(sr)) {
433 m_log.error("SQLBindParam failed (context = %s)", context);
434 log_error(stmt, SQL_HANDLE_STMT);
435 throw IOException("ODBC StorageService failed to insert record.");
437 m_log.debug("SQLBindParam succeded (context = %s)", context);
439 sr = SQLBindParam(stmt, 2, SQL_C_CHAR, SQL_VARCHAR, 255, 0, const_cast<char*>(key), &b_ind);
440 if (!SQL_SUCCEEDED(sr)) {
441 m_log.error("SQLBindParam failed (key = %s)", key);
442 log_error(stmt, SQL_HANDLE_STMT);
443 throw IOException("ODBC StorageService failed to insert record.");
445 m_log.debug("SQLBindParam succeded (key = %s)", key);
447 if (strcmp(table, TEXT_TABLE)==0)
448 sr = SQLBindParam(stmt, 3, SQL_C_CHAR, SQL_LONGVARCHAR, strlen(value), 0, const_cast<char*>(value), &b_ind);
450 sr = SQLBindParam(stmt, 3, SQL_C_CHAR, SQL_VARCHAR, 255, 0, const_cast<char*>(value), &b_ind);
451 if (!SQL_SUCCEEDED(sr)) {
452 m_log.error("SQLBindParam failed (value = %s)", value);
453 log_error(stmt, SQL_HANDLE_STMT);
454 throw IOException("ODBC StorageService failed to insert record.");
456 m_log.debug("SQLBindParam succeded (value = %s)", value);
458 //freeSafeSQL(scontext, context);
459 //freeSafeSQL(skey, key);
460 //freeSafeSQL(svalue, value);
461 //m_log.debug("SQL: %s", q.c_str());
464 if (!SQL_SUCCEEDED(sr)) {
465 m_log.error("insert record failed (t=%s, c=%s, k=%s)", table, context, key);
466 if (log_error(stmt, SQL_HANDLE_STMT, "23000"))
467 return false; // supposedly integrity violation?
468 throw IOException("ODBC StorageService failed to insert record.");
471 m_log.debug("SQLExecute of insert succeeded");
475 int ODBCStorageService::readRow(
476 const char *table, const char* context, const char* key, string* pvalue, time_t* pexpiration, int version, bool text
480 xmltooling::NDC ndc("readRow");
483 // Get statement handle.
484 ODBCConn conn(getHDBC());
485 SQLHSTMT stmt = getHSTMT(conn);
487 // Prepare and exectute select statement.
489 timestampFromTime(time(NULL), timebuf);
490 char *scontext = makeSafeSQL(context);
491 char *skey = makeSafeSQL(key);
493 q << "SELECT version";
497 q << ",CASE version WHEN " << version << " THEN NULL ELSE value END";
498 q << " FROM " << table << " WHERE context='" << scontext << "' AND id='" << skey << "' AND expires > " << timebuf;
499 freeSafeSQL(scontext, context);
500 freeSafeSQL(skey, key);
501 if (m_log.isDebugEnabled())
502 m_log.debug("SQL: %s", q.str().c_str());
504 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)q.str().c_str(), SQL_NTS);
505 if (!SQL_SUCCEEDED(sr)) {
506 m_log.error("error searching for (t=%s, c=%s, k=%s)", table, context, key);
507 log_error(stmt, SQL_HANDLE_STMT);
508 throw IOException("ODBC StorageService search failed.");
512 SQL_TIMESTAMP_STRUCT expiration;
514 SQLBindCol(stmt,1,SQL_C_SSHORT,&ver,0,NULL);
516 SQLBindCol(stmt,2,SQL_C_TYPE_TIMESTAMP,&expiration,0,NULL);
518 if ((sr=SQLFetch(stmt)) == SQL_NO_DATA)
522 *pexpiration = timeFromTimestamp(expiration);
525 return version; // nothing's changed, so just echo back the version
529 SQLCHAR buf[LONGDATA_BUFLEN];
530 while ((sr=SQLGetData(stmt,pexpiration ? 3 : 2,SQL_C_CHAR,buf,sizeof(buf),&len)) != SQL_NO_DATA) {
531 if (!SQL_SUCCEEDED(sr)) {
532 m_log.error("error while reading text field from result set");
533 log_error(stmt, SQL_HANDLE_STMT);
534 throw IOException("ODBC StorageService search failed to read data from result set.");
536 pvalue->append((char*)buf);
543 int ODBCStorageService::updateRow(const char *table, const char* context, const char* key, const char* value, time_t expiration, int version)
546 xmltooling::NDC ndc("updateRow");
549 if (!value && !expiration)
550 throw IOException("ODBC StorageService given invalid update instructions.");
552 // Get statement handle. Disable auto-commit mode to wrap select + update.
553 ODBCConn conn(getHDBC());
554 SQLRETURN sr = SQLSetConnectAttr(conn, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, NULL);
555 if (!SQL_SUCCEEDED(sr))
556 throw IOException("ODBC StorageService failed to disable auto-commit mode.");
557 conn.autoCommit = false;
558 SQLHSTMT stmt = getHSTMT(conn);
560 // First, fetch the current version for later, which also ensures the record still exists.
562 timestampFromTime(time(NULL), timebuf);
563 char *scontext = makeSafeSQL(context);
564 char *skey = makeSafeSQL(key);
565 string q("SELECT version FROM ");
566 q = q + table + " WHERE context='" + scontext + "' AND id='" + key + "' AND expires > " + timebuf;
568 m_log.debug("SQL: %s", q.c_str());
570 sr=SQLExecDirect(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
571 if (!SQL_SUCCEEDED(sr)) {
572 freeSafeSQL(scontext, context);
573 freeSafeSQL(skey, key);
574 m_log.error("error searching for (t=%s, c=%s, k=%s)", table, context, key);
575 log_error(stmt, SQL_HANDLE_STMT);
576 throw IOException("ODBC StorageService search failed.");
580 SQLBindCol(stmt,1,SQL_C_SSHORT,&ver,0,NULL);
581 if ((sr=SQLFetch(stmt)) == SQL_NO_DATA) {
582 freeSafeSQL(scontext, context);
583 freeSafeSQL(skey, key);
588 if (version > 0 && version != ver) {
589 freeSafeSQL(scontext, context);
590 freeSafeSQL(skey, key);
594 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
595 stmt = getHSTMT(conn);
597 // Prepare and exectute update statement.
598 q = string("UPDATE ") + table + " SET ";
601 q = q + "value=?, version=version+1";
604 timestampFromTime(expiration, timebuf);
607 q = q + "expires = " + timebuf;
610 q = q + " WHERE context='" + scontext + "' AND id='" + key + "'";
611 freeSafeSQL(scontext, context);
612 freeSafeSQL(skey, key);
614 sr = SQLPrepare(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
615 if (!SQL_SUCCEEDED(sr)) {
616 m_log.error("update of record failed (t=%s, c=%s, k=%s", table, context, key);
617 log_error(stmt, SQL_HANDLE_STMT);
618 throw IOException("ODBC StorageService failed to update record.");
620 m_log.debug("SQLPrepare succeded. SQL: %s", q.c_str());
622 SQLINTEGER b_ind = SQL_NTS;
624 if (strcmp(table, TEXT_TABLE)==0)
625 sr = SQLBindParam(stmt, 1, SQL_C_CHAR, SQL_LONGVARCHAR, strlen(value), 0, const_cast<char*>(value), &b_ind);
627 sr = SQLBindParam(stmt, 1, SQL_C_CHAR, SQL_VARCHAR, 255, 0, const_cast<char*>(value), &b_ind);
628 if (!SQL_SUCCEEDED(sr)) {
629 m_log.error("SQLBindParam failed (context = %s)", context);
630 log_error(stmt, SQL_HANDLE_STMT);
631 throw IOException("ODBC StorageService failed to update record.");
633 m_log.debug("SQLBindParam succeded (context = %s)", context);
638 return 0; // went missing?
639 else if (!SQL_SUCCEEDED(sr)) {
640 m_log.error("update of record failed (t=%s, c=%s, k=%s", table, context, key);
641 log_error(stmt, SQL_HANDLE_STMT);
642 throw IOException("ODBC StorageService failed to update record.");
645 m_log.debug("SQLExecute of update succeeded");
649 bool ODBCStorageService::deleteRow(const char *table, const char *context, const char* key)
652 xmltooling::NDC ndc("deleteRow");
655 // Get statement handle.
656 ODBCConn conn(getHDBC());
657 SQLHSTMT stmt = getHSTMT(conn);
659 // Prepare and execute delete statement.
660 char *scontext = makeSafeSQL(context);
661 char *skey = makeSafeSQL(key);
662 string q = string("DELETE FROM ") + table + " WHERE context='" + scontext + "' AND id='" + skey + "'";
663 freeSafeSQL(scontext, context);
664 freeSafeSQL(skey, key);
665 m_log.debug("SQL: %s", q.c_str());
667 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
670 else if (!SQL_SUCCEEDED(sr)) {
671 m_log.error("error deleting record (t=%s, c=%s, k=%s)", table, context, key);
672 log_error(stmt, SQL_HANDLE_STMT);
673 throw IOException("ODBC StorageService failed to delete record.");
680 void ODBCStorageService::cleanup()
683 xmltooling::NDC ndc("cleanup");
686 Mutex* mutex = Mutex::create();
690 m_log.info("cleanup thread started... running every %d secs", m_cleanupInterval);
693 shutdown_wait->timedwait(mutex, m_cleanupInterval);
699 catch (exception& ex) {
700 m_log.error("cleanup thread swallowed exception: %s", ex.what());
704 m_log.info("cleanup thread exiting...");
711 void* ODBCStorageService::cleanup_fn(void* cache_p)
713 ODBCStorageService* cache = (ODBCStorageService*)cache_p;
716 // First, let's block all signals
717 Thread::mask_all_signals();
720 // Now run the cleanup process.
725 void ODBCStorageService::updateContext(const char *table, const char* context, time_t expiration)
728 xmltooling::NDC ndc("updateContext");
731 // Get statement handle.
732 ODBCConn conn(getHDBC());
733 SQLHSTMT stmt = getHSTMT(conn);
736 timestampFromTime(expiration, timebuf);
739 timestampFromTime(time(NULL), nowbuf);
741 char *scontext = makeSafeSQL(context);
743 q = q + table + " SET expires = " + timebuf + " WHERE context='" + scontext + "' AND expires > " + nowbuf;
744 freeSafeSQL(scontext, context);
746 m_log.debug("SQL: %s", q.c_str());
748 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
749 if ((sr!=SQL_NO_DATA) && !SQL_SUCCEEDED(sr)) {
750 m_log.error("error updating records (t=%s, c=%s)", table, context ? context : "all");
751 log_error(stmt, SQL_HANDLE_STMT);
752 throw IOException("ODBC StorageService failed to update context expiration.");
756 void ODBCStorageService::reap(const char *table, const char* context)
759 xmltooling::NDC ndc("reap");
762 // Get statement handle.
763 ODBCConn conn(getHDBC());
764 SQLHSTMT stmt = getHSTMT(conn);
766 // Prepare and execute delete statement.
768 timestampFromTime(time(NULL), nowbuf);
771 char *scontext = makeSafeSQL(context);
772 q = string("DELETE FROM ") + table + " WHERE context='" + scontext + "' AND expires <= " + nowbuf;
773 freeSafeSQL(scontext, context);
776 q = string("DELETE FROM ") + table + " WHERE expires <= " + nowbuf;
778 m_log.debug("SQL: %s", q.c_str());
780 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
781 if ((sr!=SQL_NO_DATA) && !SQL_SUCCEEDED(sr)) {
782 m_log.error("error expiring records (t=%s, c=%s)", table, context ? context : "all");
783 log_error(stmt, SQL_HANDLE_STMT);
784 throw IOException("ODBC StorageService failed to purge expired records.");
788 void ODBCStorageService::deleteContext(const char *table, const char* context)
791 xmltooling::NDC ndc("deleteContext");
794 // Get statement handle.
795 ODBCConn conn(getHDBC());
796 SQLHSTMT stmt = getHSTMT(conn);
798 // Prepare and execute delete statement.
799 char *scontext = makeSafeSQL(context);
800 string q = string("DELETE FROM ") + table + " WHERE context='" + scontext + "'";
801 freeSafeSQL(scontext, context);
802 m_log.debug("SQL: %s", q.c_str());
804 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
805 if ((sr!=SQL_NO_DATA) && !SQL_SUCCEEDED(sr)) {
806 m_log.error("error deleting context (t=%s, c=%s)", table, context);
807 log_error(stmt, SQL_HANDLE_STMT);
808 throw IOException("ODBC StorageService failed to delete context.");
812 extern "C" int ODBCSTORE_EXPORTS xmltooling_extension_init(void*)
814 // Register this SS type
815 XMLToolingConfig::getConfig().StorageServiceManager.registerFactory("ODBC", ODBCStorageServiceFactory);
819 extern "C" void ODBCSTORE_EXPORTS xmltooling_extension_term()
821 XMLToolingConfig::getConfig().StorageServiceManager.deregisterFactory("ODBC");