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) {}
101 SQLRETURN sr = SQLEndTran(SQL_HANDLE_DBC, handle, SQL_COMMIT);
102 SQLDisconnect(handle);
103 SQLFreeHandle(SQL_HANDLE_DBC,handle);
104 if (!SQL_SUCCEEDED(sr))
105 throw IOException("Failed to commit connection.");
107 operator SQLHDBC() {return handle;}
111 class ODBCStorageService : public StorageService
114 ODBCStorageService(const DOMElement* e);
115 virtual ~ODBCStorageService();
117 bool createString(const char* context, const char* key, const char* value, time_t expiration) {
118 return createRow(STRING_TABLE, context, key, value, expiration);
120 int readString(const char* context, const char* key, string* pvalue=NULL, time_t* pexpiration=NULL, int version=0) {
121 return readRow(STRING_TABLE, context, key, pvalue, pexpiration, version, false);
123 int updateString(const char* context, const char* key, const char* value=NULL, time_t expiration=0, int version=0) {
124 return updateRow(STRING_TABLE, context, key, value, expiration, version);
126 bool deleteString(const char* context, const char* key) {
127 return deleteRow(STRING_TABLE, context, key);
130 bool createText(const char* context, const char* key, const char* value, time_t expiration) {
131 return createRow(TEXT_TABLE, context, key, value, expiration);
133 int readText(const char* context, const char* key, string* pvalue=NULL, time_t* pexpiration=NULL, int version=0) {
134 return readRow(TEXT_TABLE, context, key, pvalue, pexpiration, version, true);
136 int updateText(const char* context, const char* key, const char* value=NULL, time_t expiration=0, int version=0) {
137 return updateRow(TEXT_TABLE, context, key, value, expiration, version);
139 bool deleteText(const char* context, const char* key) {
140 return deleteRow(TEXT_TABLE, context, key);
143 void reap(const char* context) {
144 reap(STRING_TABLE, context);
145 reap(TEXT_TABLE, context);
148 void updateContext(const char* context, time_t expiration) {
149 updateContext(STRING_TABLE, context, expiration);
150 updateContext(TEXT_TABLE, context, expiration);
153 void deleteContext(const char* context) {
154 deleteContext(STRING_TABLE, context);
155 deleteContext(TEXT_TABLE, context);
160 bool createRow(const char *table, const char* context, const char* key, const char* value, time_t expiration);
161 int readRow(const char *table, const char* context, const char* key, string* pvalue, time_t* pexpiration, int version, bool text);
162 int updateRow(const char *table, const char* context, const char* key, const char* value, time_t expiration, int version);
163 bool deleteRow(const char *table, const char* context, const char* key);
165 void reap(const char* table, const char* context);
166 void updateContext(const char* table, const char* context, time_t expiration);
167 void deleteContext(const char* table, const char* context);
170 SQLHSTMT getHSTMT(SQLHDBC);
171 pair<int,int> getVersion(SQLHDBC);
172 bool log_error(SQLHANDLE handle, SQLSMALLINT htype, const char* checkfor=NULL);
174 static void* cleanup_fn(void*);
178 int m_cleanupInterval;
179 CondWait* shutdown_wait;
180 Thread* cleanup_thread;
187 StorageService* ODBCStorageServiceFactory(const DOMElement* const & e)
189 return new ODBCStorageService(e);
192 // convert SQL timestamp to time_t
193 time_t timeFromTimestamp(SQL_TIMESTAMP_STRUCT expires)
197 t.tm_sec=expires.second;
198 t.tm_min=expires.minute;
199 t.tm_hour=expires.hour;
200 t.tm_mday=expires.day;
201 t.tm_mon=expires.month-1;
202 t.tm_year=expires.year-1900;
204 #if defined(HAVE_TIMEGM)
207 ret = mktime(&t) - timezone;
212 // conver time_t to SQL string
213 void timestampFromTime(time_t t, char* ret)
217 struct tm* ptime=gmtime_r(&t,&res);
219 struct tm* ptime=gmtime(&t);
221 strftime(ret,32,"{ts '%Y-%m-%d %H:%M:%S'}",ptime);
224 // make a string safe for SQL command
225 // result to be free'd only if it isn't the input
226 static char *makeSafeSQL(const char *src)
232 // see if any conversion needed
233 for (s=(char*)src; *s; nc++,s++) if (*s=='\'') ns++;
234 if (ns==0) return ((char*)src);
236 char *safe = new char[(nc+2*ns+1)];
237 for (s=safe; *src; src++) {
238 if (*src=='\'') *s++ = '\'';
245 void freeSafeSQL(char *safe, const char *src)
252 ODBCStorageService::ODBCStorageService(const DOMElement* e) : m_log(Category::getInstance("XMLTooling.StorageService")),
253 m_cleanupInterval(900), shutdown_wait(NULL), cleanup_thread(NULL), shutdown(false), m_henv(SQL_NULL_HANDLE)
256 xmltooling::NDC ndc("ODBCStorageService");
259 const XMLCh* tag=e ? e->getAttributeNS(NULL,cleanupInterval) : NULL;
261 m_cleanupInterval = XMLString::parseInt(tag);
262 if (!m_cleanupInterval)
263 m_cleanupInterval = 900;
265 if (m_henv == SQL_NULL_HANDLE) {
266 // Enable connection pooling.
267 SQLSetEnvAttr(SQL_NULL_HANDLE, SQL_ATTR_CONNECTION_POOLING, (void*)SQL_CP_ONE_PER_HENV, 0);
269 // Allocate the environment.
270 if (!SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &m_henv)))
271 throw XMLToolingException("ODBC failed to initialize.");
274 SQLSetEnvAttr(m_henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
276 m_log.info("ODBC initialized");
279 // Grab connection string from the configuration.
280 e = e ? XMLHelper::getFirstChildElement(e,ConnectionString) : NULL;
281 if (!e || !e->hasChildNodes()) {
282 SQLFreeHandle(SQL_HANDLE_ENV, m_henv);
283 throw XMLToolingException("ODBC StorageService requires ConnectionString element in configuration.");
285 auto_ptr_char arg(e->getFirstChild()->getNodeValue());
286 m_connstring=arg.get();
288 // Connect and check version.
289 ODBCConn conn(getHDBC());
290 pair<int,int> v=getVersion(conn);
292 // Make sure we've got the right version.
293 if (v.first != PLUGIN_VER_MAJOR) {
294 SQLFreeHandle(SQL_HANDLE_ENV, m_henv);
295 m_log.crit("unknown database version: %d.%d", v.first, v.second);
296 throw XMLToolingException("Unknown database version for ODBC StorageService.");
299 // Initialize the cleanup thread
300 shutdown_wait = CondWait::create();
301 cleanup_thread = Thread::create(&cleanup_fn, (void*)this);
304 ODBCStorageService::~ODBCStorageService()
307 shutdown_wait->signal();
308 cleanup_thread->join(NULL);
309 delete shutdown_wait;
310 if (m_henv != SQL_NULL_HANDLE)
311 SQLFreeHandle(SQL_HANDLE_ENV, m_henv);
314 bool ODBCStorageService::log_error(SQLHANDLE handle, SQLSMALLINT htype, const char* checkfor)
325 ret = SQLGetDiagRec(htype, handle, ++i, state, &native, text, sizeof(text), &len);
326 if (SQL_SUCCEEDED(ret)) {
327 m_log.error("ODBC Error: %s:%ld:%ld:%s", state, i, native, text);
328 if (checkfor && !strcmp(checkfor, (const char*)state))
331 } while(SQL_SUCCEEDED(ret));
335 SQLHDBC ODBCStorageService::getHDBC()
338 xmltooling::NDC ndc("getHDBC");
343 SQLRETURN sr=SQLAllocHandle(SQL_HANDLE_DBC, m_henv, &handle);
344 if (!SQL_SUCCEEDED(sr)) {
345 m_log.error("failed to allocate connection handle");
346 log_error(m_henv, SQL_HANDLE_ENV);
347 throw IOException("ODBC StorageService failed to allocate a connection handle.");
350 sr=SQLDriverConnect(handle,NULL,(SQLCHAR*)m_connstring.c_str(),m_connstring.length(),NULL,0,NULL,SQL_DRIVER_NOPROMPT);
351 if (!SQL_SUCCEEDED(sr)) {
352 m_log.error("failed to connect to database");
353 log_error(handle, SQL_HANDLE_DBC);
354 throw IOException("ODBC StorageService failed to connect to database.");
357 sr = SQLSetConnectAttr(handle, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, NULL);
358 if (!SQL_SUCCEEDED(sr))
359 throw IOException("ODBC StorageService failed to disable auto-commit mode.");
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, 0, 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, 0, 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 sr = SQLBindParam(stmt, 3, SQL_C_CHAR, (strcmp(table, TEXT_TABLE)==0 ? SQL_LONGVARCHAR : SQL_VARCHAR), 0, 0, const_cast<char*>(value), &b_ind);
448 if (!SQL_SUCCEEDED(sr)) {
449 m_log.error("SQLBindParam failed (value = %s)", value);
450 log_error(stmt, SQL_HANDLE_STMT);
451 throw IOException("ODBC StorageService failed to insert record.");
453 m_log.debug("SQLBindParam succeded (value = %s)", value);
455 //freeSafeSQL(scontext, context);
456 //freeSafeSQL(skey, key);
457 //freeSafeSQL(svalue, value);
458 //m_log.debug("SQL: %s", q.c_str());
461 if (!SQL_SUCCEEDED(sr)) {
462 m_log.error("insert record failed (t=%s, c=%s, k=%s)", table, context, key);
463 if (log_error(stmt, SQL_HANDLE_STMT, "23000"))
464 return false; // supposedly integrity violation?
465 throw IOException("ODBC StorageService failed to insert record.");
470 int ODBCStorageService::readRow(
471 const char *table, const char* context, const char* key, string* pvalue, time_t* pexpiration, int version, bool text
475 xmltooling::NDC ndc("readRow");
478 // Get statement handle.
479 ODBCConn conn(getHDBC());
480 SQLHSTMT stmt = getHSTMT(conn);
482 // Prepare and exectute select statement.
484 timestampFromTime(time(NULL), timebuf);
485 char *scontext = makeSafeSQL(context);
486 char *skey = makeSafeSQL(key);
488 q << "SELECT version";
492 q << ",CASE version WHEN " << version << " THEN NULL ELSE value END";
493 q << " FROM " << table << " WHERE context='" << scontext << "' AND id='" << skey << "' AND expires > " << timebuf;
494 freeSafeSQL(scontext, context);
495 freeSafeSQL(skey, key);
496 if (m_log.isDebugEnabled())
497 m_log.debug("SQL: %s", q.str().c_str());
499 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)q.str().c_str(), SQL_NTS);
500 if (!SQL_SUCCEEDED(sr)) {
501 m_log.error("error searching for (t=%s, c=%s, k=%s)", table, context, key);
502 log_error(stmt, SQL_HANDLE_STMT);
503 throw IOException("ODBC StorageService search failed.");
507 SQL_TIMESTAMP_STRUCT expiration;
509 SQLBindCol(stmt,1,SQL_C_SSHORT,&ver,0,NULL);
511 SQLBindCol(stmt,2,SQL_C_TYPE_TIMESTAMP,&expiration,0,NULL);
513 if ((sr=SQLFetch(stmt)) == SQL_NO_DATA)
517 *pexpiration = timeFromTimestamp(expiration);
520 return version; // nothing's changed, so just echo back the version
524 SQLCHAR buf[LONGDATA_BUFLEN];
525 while ((sr=SQLGetData(stmt,pexpiration ? 3 : 2,SQL_C_CHAR,buf,sizeof(buf),&len)) != SQL_NO_DATA) {
526 if (!SQL_SUCCEEDED(sr)) {
527 m_log.error("error while reading text field from result set");
528 log_error(stmt, SQL_HANDLE_STMT);
529 throw IOException("ODBC StorageService search failed to read data from result set.");
531 pvalue->append((char*)buf);
538 int ODBCStorageService::updateRow(const char *table, const char* context, const char* key, const char* value, time_t expiration, int version)
541 xmltooling::NDC ndc("updateRow");
544 if (!value && !expiration)
545 throw IOException("ODBC StorageService given invalid update instructions.");
547 // Get statement handle.
548 ODBCConn conn(getHDBC());
549 SQLHSTMT stmt = getHSTMT(conn);
551 // First, fetch the current version for later, which also ensures the record still exists.
553 timestampFromTime(time(NULL), timebuf);
554 char *scontext = makeSafeSQL(context);
555 char *skey = makeSafeSQL(key);
556 string q("SELECT version FROM ");
557 q = q + table + " WHERE context='" + scontext + "' AND id='" + key + "' AND expires > " + timebuf;
559 m_log.debug("SQL: %s", q.c_str());
561 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
562 if (!SQL_SUCCEEDED(sr)) {
563 freeSafeSQL(scontext, context);
564 freeSafeSQL(skey, key);
565 m_log.error("error searching for (t=%s, c=%s, k=%s)", table, context, key);
566 log_error(stmt, SQL_HANDLE_STMT);
567 throw IOException("ODBC StorageService search failed.");
571 SQLBindCol(stmt,1,SQL_C_SSHORT,&ver,0,NULL);
572 if ((sr=SQLFetch(stmt)) == SQL_NO_DATA) {
573 freeSafeSQL(scontext, context);
574 freeSafeSQL(skey, key);
579 if (version > 0 && version != ver) {
580 freeSafeSQL(scontext, context);
581 freeSafeSQL(skey, key);
585 // Prepare and exectute update statement.
586 q = string("UPDATE ") + table + " SET ";
589 q = q + "value=?, version=version+1";
592 timestampFromTime(expiration, timebuf);
595 q = q + "expires = " + timebuf;
598 q = q + " WHERE context='" + scontext + "' AND id='" + key + "'";
599 freeSafeSQL(scontext, context);
600 freeSafeSQL(skey, key);
602 sr = SQLPrepare(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
603 if (!SQL_SUCCEEDED(sr)) {
604 m_log.error("update of record failed (t=%s, c=%s, k=%s", table, context, key);
605 log_error(stmt, SQL_HANDLE_STMT);
606 throw IOException("ODBC StorageService failed to update record.");
608 m_log.debug("SQLPrepare() succeded. SQL: %s", q.c_str());
610 SQLINTEGER b_ind = SQL_NTS;
612 sr = SQLBindParam(stmt, 1, SQL_C_CHAR, (strcmp(table, TEXT_TABLE)==0 ? SQL_LONGVARCHAR : SQL_VARCHAR), 0, 0, const_cast<char*>(value), &b_ind);
613 if (!SQL_SUCCEEDED(sr)) {
614 m_log.error("SQLBindParam failed (context = %s)", context);
615 log_error(stmt, SQL_HANDLE_STMT);
616 throw IOException("ODBC StorageService failed to update record.");
618 m_log.debug("SQLBindParam succeded (context = %s)", context);
623 return 0; // went missing?
624 else if (!SQL_SUCCEEDED(sr)) {
625 m_log.error("update of record failed (t=%s, c=%s, k=%s", table, context, key);
626 log_error(stmt, SQL_HANDLE_STMT);
627 throw IOException("ODBC StorageService failed to update record.");
633 bool ODBCStorageService::deleteRow(const char *table, const char *context, const char* key)
636 xmltooling::NDC ndc("deleteRow");
639 // Get statement handle.
640 ODBCConn conn(getHDBC());
641 SQLHSTMT stmt = getHSTMT(conn);
643 // Prepare and execute delete statement.
644 char *scontext = makeSafeSQL(context);
645 char *skey = makeSafeSQL(key);
646 string q = string("DELETE FROM ") + table + " WHERE context='" + scontext + "' AND id='" + skey + "'";
647 freeSafeSQL(scontext, context);
648 freeSafeSQL(skey, key);
649 m_log.debug("SQL: %s", q.c_str());
651 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
654 else if (!SQL_SUCCEEDED(sr)) {
655 m_log.error("error deleting record (t=%s, c=%s, k=%s)", table, context, key);
656 log_error(stmt, SQL_HANDLE_STMT);
657 throw IOException("ODBC StorageService failed to delete record.");
664 void ODBCStorageService::cleanup()
667 xmltooling::NDC ndc("cleanup");
670 Mutex* mutex = Mutex::create();
674 m_log.info("cleanup thread started... running every %d secs", m_cleanupInterval);
677 shutdown_wait->timedwait(mutex, m_cleanupInterval);
683 catch (exception& ex) {
684 m_log.error("cleanup thread swallowed exception: %s", ex.what());
688 m_log.info("cleanup thread exiting...");
695 void* ODBCStorageService::cleanup_fn(void* cache_p)
697 ODBCStorageService* cache = (ODBCStorageService*)cache_p;
700 // First, let's block all signals
701 Thread::mask_all_signals();
704 // Now run the cleanup process.
709 void ODBCStorageService::updateContext(const char *table, const char* context, time_t expiration)
712 xmltooling::NDC ndc("updateContext");
715 // Get statement handle.
716 ODBCConn conn(getHDBC());
717 SQLHSTMT stmt = getHSTMT(conn);
720 timestampFromTime(expiration, timebuf);
723 timestampFromTime(time(NULL), nowbuf);
725 char *scontext = makeSafeSQL(context);
727 q = q + table + " SET expires = " + timebuf + " WHERE context='" + scontext + "' AND expires > " + nowbuf;
728 freeSafeSQL(scontext, context);
730 m_log.debug("SQL: %s", q.c_str());
732 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
733 if ((sr!=SQL_NO_DATA) && !SQL_SUCCEEDED(sr)) {
734 m_log.error("error updating records (t=%s, c=%s)", table, context ? context : "all");
735 log_error(stmt, SQL_HANDLE_STMT);
736 throw IOException("ODBC StorageService failed to update context expiration.");
740 void ODBCStorageService::reap(const char *table, const char* context)
743 xmltooling::NDC ndc("reap");
746 // Get statement handle.
747 ODBCConn conn(getHDBC());
748 SQLHSTMT stmt = getHSTMT(conn);
750 // Prepare and execute delete statement.
752 timestampFromTime(time(NULL), nowbuf);
755 char *scontext = makeSafeSQL(context);
756 q = string("DELETE FROM ") + table + " WHERE context='" + scontext + "' AND expires <= " + nowbuf;
757 freeSafeSQL(scontext, context);
760 q = string("DELETE FROM ") + table + " WHERE expires <= " + nowbuf;
762 m_log.debug("SQL: %s", q.c_str());
764 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
765 if ((sr!=SQL_NO_DATA) && !SQL_SUCCEEDED(sr)) {
766 m_log.error("error expiring records (t=%s, c=%s)", table, context ? context : "all");
767 log_error(stmt, SQL_HANDLE_STMT);
768 throw IOException("ODBC StorageService failed to purge expired records.");
772 void ODBCStorageService::deleteContext(const char *table, const char* context)
775 xmltooling::NDC ndc("deleteContext");
778 // Get statement handle.
779 ODBCConn conn(getHDBC());
780 SQLHSTMT stmt = getHSTMT(conn);
782 // Prepare and execute delete statement.
783 char *scontext = makeSafeSQL(context);
784 string q = string("DELETE FROM ") + table + " WHERE context='" + scontext + "'";
785 freeSafeSQL(scontext, context);
786 m_log.debug("SQL: %s", q.c_str());
788 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
789 if ((sr!=SQL_NO_DATA) && !SQL_SUCCEEDED(sr)) {
790 m_log.error("error deleting context (t=%s, c=%s)", table, context);
791 log_error(stmt, SQL_HANDLE_STMT);
792 throw IOException("ODBC StorageService failed to delete context.");
796 extern "C" int ODBCSTORE_EXPORTS xmltooling_extension_init(void*)
798 // Register this SS type
799 XMLToolingConfig::getConfig().StorageServiceManager.registerFactory("ODBC", ODBCStorageServiceFactory);
803 extern "C" void ODBCSTORE_EXPORTS xmltooling_extension_term()
805 XMLToolingConfig::getConfig().StorageServiceManager.deregisterFactory("ODBC");