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 struct ODBCStatement {
112 ODBCStatement(SQLHSTMT statement) : handle(statement) {}
113 ~ODBCStatement() {SQLFreeHandle(SQL_HANDLE_STMT,handle);}
114 operator SQLHSTMT() {return handle;}
118 class ODBCStorageService : public StorageService
121 ODBCStorageService(const DOMElement* e);
122 virtual ~ODBCStorageService();
124 bool createString(const char* context, const char* key, const char* value, time_t expiration) {
125 return createRow(STRING_TABLE, context, key, value, expiration);
127 int readString(const char* context, const char* key, string* pvalue=NULL, time_t* pexpiration=NULL, int version=0) {
128 return readRow(STRING_TABLE, context, key, pvalue, pexpiration, version, false);
130 int updateString(const char* context, const char* key, const char* value=NULL, time_t expiration=0, int version=0) {
131 return updateRow(STRING_TABLE, context, key, value, expiration, version);
133 bool deleteString(const char* context, const char* key) {
134 return deleteRow(STRING_TABLE, context, key);
137 bool createText(const char* context, const char* key, const char* value, time_t expiration) {
138 return createRow(TEXT_TABLE, context, key, value, expiration);
140 int readText(const char* context, const char* key, string* pvalue=NULL, time_t* pexpiration=NULL, int version=0) {
141 return readRow(TEXT_TABLE, context, key, pvalue, pexpiration, version, true);
143 int updateText(const char* context, const char* key, const char* value=NULL, time_t expiration=0, int version=0) {
144 return updateRow(TEXT_TABLE, context, key, value, expiration, version);
146 bool deleteText(const char* context, const char* key) {
147 return deleteRow(TEXT_TABLE, context, key);
150 void reap(const char* context) {
151 reap(STRING_TABLE, context);
152 reap(TEXT_TABLE, context);
155 void updateContext(const char* context, time_t expiration) {
156 updateContext(STRING_TABLE, context, expiration);
157 updateContext(TEXT_TABLE, context, expiration);
160 void deleteContext(const char* context) {
161 deleteContext(STRING_TABLE, context);
162 deleteContext(TEXT_TABLE, context);
167 bool createRow(const char *table, const char* context, const char* key, const char* value, time_t expiration);
168 int readRow(const char *table, const char* context, const char* key, string* pvalue, time_t* pexpiration, int version, bool text);
169 int updateRow(const char *table, const char* context, const char* key, const char* value, time_t expiration, int version);
170 bool deleteRow(const char *table, const char* context, const char* key);
172 void reap(const char* table, const char* context);
173 void updateContext(const char* table, const char* context, time_t expiration);
174 void deleteContext(const char* table, const char* context);
177 SQLHSTMT getHSTMT(SQLHDBC);
178 pair<int,int> getVersion(SQLHDBC);
179 bool log_error(SQLHANDLE handle, SQLSMALLINT htype, const char* checkfor=NULL);
181 static void* cleanup_fn(void*);
185 int m_cleanupInterval;
186 CondWait* shutdown_wait;
187 Thread* cleanup_thread;
194 StorageService* ODBCStorageServiceFactory(const DOMElement* const & e)
196 return new ODBCStorageService(e);
199 // convert SQL timestamp to time_t
200 time_t timeFromTimestamp(SQL_TIMESTAMP_STRUCT expires)
204 t.tm_sec=expires.second;
205 t.tm_min=expires.minute;
206 t.tm_hour=expires.hour;
207 t.tm_mday=expires.day;
208 t.tm_mon=expires.month-1;
209 t.tm_year=expires.year-1900;
211 #if defined(HAVE_TIMEGM)
214 ret = mktime(&t) - timezone;
219 // conver time_t to SQL string
220 void timestampFromTime(time_t t, char* ret)
224 struct tm* ptime=gmtime_r(&t,&res);
226 struct tm* ptime=gmtime(&t);
228 strftime(ret,32,"{ts '%Y-%m-%d %H:%M:%S'}",ptime);
231 // make a string safe for SQL command
232 // result to be free'd only if it isn't the input
233 static char *makeSafeSQL(const char *src)
239 // see if any conversion needed
240 for (s=(char*)src; *s; nc++,s++) if (*s=='\'') ns++;
241 if (ns==0) return ((char*)src);
243 char *safe = new char[(nc+2*ns+1)];
244 for (s=safe; *src; src++) {
245 if (*src=='\'') *s++ = '\'';
252 void freeSafeSQL(char *safe, const char *src)
259 ODBCStorageService::ODBCStorageService(const DOMElement* e) : m_log(Category::getInstance("XMLTooling.StorageService")),
260 m_cleanupInterval(900), shutdown_wait(NULL), cleanup_thread(NULL), shutdown(false), m_henv(SQL_NULL_HANDLE)
263 xmltooling::NDC ndc("ODBCStorageService");
266 const XMLCh* tag=e ? e->getAttributeNS(NULL,cleanupInterval) : NULL;
268 m_cleanupInterval = XMLString::parseInt(tag);
269 if (!m_cleanupInterval)
270 m_cleanupInterval = 900;
272 if (m_henv == SQL_NULL_HANDLE) {
273 // Enable connection pooling.
274 SQLSetEnvAttr(SQL_NULL_HANDLE, SQL_ATTR_CONNECTION_POOLING, (void*)SQL_CP_ONE_PER_HENV, 0);
276 // Allocate the environment.
277 if (!SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &m_henv)))
278 throw XMLToolingException("ODBC failed to initialize.");
281 SQLSetEnvAttr(m_henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
283 m_log.info("ODBC initialized");
286 // Grab connection string from the configuration.
287 e = e ? XMLHelper::getFirstChildElement(e,ConnectionString) : NULL;
288 if (!e || !e->hasChildNodes()) {
289 SQLFreeHandle(SQL_HANDLE_ENV, m_henv);
290 throw XMLToolingException("ODBC StorageService requires ConnectionString element in configuration.");
292 auto_ptr_char arg(e->getFirstChild()->getNodeValue());
293 m_connstring=arg.get();
295 // Connect and check version.
296 ODBCConn conn(getHDBC());
297 pair<int,int> v=getVersion(conn);
299 // Make sure we've got the right version.
300 if (v.first != PLUGIN_VER_MAJOR) {
301 SQLFreeHandle(SQL_HANDLE_ENV, m_henv);
302 m_log.crit("unknown database version: %d.%d", v.first, v.second);
303 throw XMLToolingException("Unknown database version for ODBC StorageService.");
306 // Initialize the cleanup thread
307 shutdown_wait = CondWait::create();
308 cleanup_thread = Thread::create(&cleanup_fn, (void*)this);
311 ODBCStorageService::~ODBCStorageService()
314 shutdown_wait->signal();
315 cleanup_thread->join(NULL);
316 delete shutdown_wait;
317 SQLFreeHandle(SQL_HANDLE_ENV, m_henv);
320 bool ODBCStorageService::log_error(SQLHANDLE handle, SQLSMALLINT htype, const char* checkfor)
331 ret = SQLGetDiagRec(htype, handle, ++i, state, &native, text, sizeof(text), &len);
332 if (SQL_SUCCEEDED(ret)) {
333 m_log.error("ODBC Error: %s:%ld:%ld:%s", state, i, native, text);
334 if (checkfor && !strcmp(checkfor, (const char*)state))
337 } while(SQL_SUCCEEDED(ret));
341 SQLHDBC ODBCStorageService::getHDBC()
344 xmltooling::NDC ndc("getHDBC");
349 SQLRETURN sr=SQLAllocHandle(SQL_HANDLE_DBC, m_henv, &handle);
350 if (!SQL_SUCCEEDED(sr)) {
351 m_log.error("failed to allocate connection handle");
352 log_error(m_henv, SQL_HANDLE_ENV);
353 throw IOException("ODBC StorageService failed to allocate a connection handle.");
356 sr=SQLDriverConnect(handle,NULL,(SQLCHAR*)m_connstring.c_str(),m_connstring.length(),NULL,0,NULL,SQL_DRIVER_NOPROMPT);
357 if (!SQL_SUCCEEDED(sr)) {
358 m_log.error("failed to connect to database");
359 log_error(handle, SQL_HANDLE_DBC);
360 throw IOException("ODBC StorageService failed to connect to database.");
363 sr = SQLSetConnectAttr(handle, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, NULL);
364 if (!SQL_SUCCEEDED(sr))
365 throw IOException("ODBC StorageService failed to disable auto-commit mode.");
366 sr = SQLSetConnectAttr(handle, SQL_ATTR_TXN_ISOLATION, (SQLPOINTER)SQL_TXN_SERIALIZABLE, NULL);
367 if (!SQL_SUCCEEDED(sr))
368 throw IOException("ODBC StorageService failed to enable transaction isolation.");
373 SQLHSTMT ODBCStorageService::getHSTMT(SQLHDBC conn)
376 SQLRETURN sr=SQLAllocHandle(SQL_HANDLE_STMT,conn,&hstmt);
377 if (!SQL_SUCCEEDED(sr)) {
378 m_log.error("failed to allocate statement handle");
379 log_error(conn, SQL_HANDLE_DBC);
380 throw IOException("ODBC StorageService failed to allocate a statement handle.");
385 pair<int,int> ODBCStorageService::getVersion(SQLHDBC conn)
387 // Grab the version number from the database.
388 ODBCStatement stmt(getHSTMT(conn));
390 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)"SELECT major,minor FROM version", SQL_NTS);
391 if (!SQL_SUCCEEDED(sr)) {
392 m_log.error("failed to read version from database");
393 log_error(stmt, SQL_HANDLE_STMT);
394 throw IOException("ODBC StorageService failed to read version from database.");
399 SQLBindCol(stmt,1,SQL_C_SLONG,&major,0,NULL);
400 SQLBindCol(stmt,2,SQL_C_SLONG,&minor,0,NULL);
402 if ((sr=SQLFetch(stmt)) != SQL_NO_DATA)
403 return pair<int,int>(major,minor);
405 m_log.error("no rows returned in version query");
406 throw IOException("ODBC StorageService failed to read version from database.");
409 bool ODBCStorageService::createRow(const char* table, const char* context, const char* key, const char* value, time_t expiration)
412 xmltooling::NDC ndc("createRow");
416 timestampFromTime(expiration, timebuf);
418 // Get statement handle.
419 ODBCConn conn(getHDBC());
420 ODBCStatement stmt(getHSTMT(conn));
422 // Prepare and exectute insert statement.
423 //char *scontext = makeSafeSQL(context);
424 //char *skey = makeSafeSQL(key);
425 //char *svalue = makeSafeSQL(value);
426 string q = string("INSERT INTO ") + table + " VALUES (?,?," + timebuf + ",1,?)";
428 SQLRETURN sr = SQLPrepare(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
429 if (!SQL_SUCCEEDED(sr)) {
430 m_log.error("SQLPrepare failed (t=%s, c=%s, k=%s)", table, context, key);
431 log_error(stmt, SQL_HANDLE_STMT);
432 throw IOException("ODBC StorageService failed to insert record.");
434 m_log.debug("SQLPrepare() succeded. SQL: %s", q.c_str());
436 SQLINTEGER b_ind = SQL_NTS;
437 sr = SQLBindParam(stmt, 1, SQL_C_CHAR, SQL_VARCHAR, 0, 0, const_cast<char*>(context), &b_ind);
438 if (!SQL_SUCCEEDED(sr)) {
439 m_log.error("SQLBindParam failed (context = %s)", context);
440 log_error(stmt, SQL_HANDLE_STMT);
441 throw IOException("ODBC StorageService failed to insert record.");
443 m_log.debug("SQLBindParam succeded (context = %s)", context);
445 sr = SQLBindParam(stmt, 2, SQL_C_CHAR, SQL_VARCHAR, 0, 0, const_cast<char*>(key), &b_ind);
446 if (!SQL_SUCCEEDED(sr)) {
447 m_log.error("SQLBindParam failed (key = %s)", key);
448 log_error(stmt, SQL_HANDLE_STMT);
449 throw IOException("ODBC StorageService failed to insert record.");
451 m_log.debug("SQLBindParam succeded (key = %s)", key);
453 sr = SQLBindParam(stmt, 3, SQL_C_CHAR, (strcmp(table, TEXT_TABLE)==0 ? SQL_LONGVARCHAR : SQL_VARCHAR), 0, 0, const_cast<char*>(value), &b_ind);
454 if (!SQL_SUCCEEDED(sr)) {
455 m_log.error("SQLBindParam failed (value = %s)", value);
456 log_error(stmt, SQL_HANDLE_STMT);
457 throw IOException("ODBC StorageService failed to insert record.");
459 m_log.debug("SQLBindParam succeded (value = %s)", value);
461 //freeSafeSQL(scontext, context);
462 //freeSafeSQL(skey, key);
463 //freeSafeSQL(svalue, value);
464 //m_log.debug("SQL: %s", q.c_str());
467 if (!SQL_SUCCEEDED(sr)) {
468 m_log.error("insert record failed (t=%s, c=%s, k=%s)", table, context, key);
469 if (log_error(stmt, SQL_HANDLE_STMT, "23000"))
470 return false; // supposedly integrity violation?
471 throw IOException("ODBC StorageService failed to insert record.");
476 int ODBCStorageService::readRow(
477 const char *table, const char* context, const char* key, string* pvalue, time_t* pexpiration, int version, bool text
481 xmltooling::NDC ndc("readRow");
484 // Get statement handle.
485 ODBCConn conn(getHDBC());
486 ODBCStatement stmt(getHSTMT(conn));
488 // Prepare and exectute select statement.
490 timestampFromTime(time(NULL), timebuf);
491 char *scontext = makeSafeSQL(context);
492 char *skey = makeSafeSQL(key);
494 q << "SELECT version";
498 q << ",CASE version WHEN " << version << " THEN NULL ELSE value END";
499 q << " FROM " << table << " WHERE context='" << scontext << "' AND id='" << skey << "' AND expires > " << timebuf;
500 freeSafeSQL(scontext, context);
501 freeSafeSQL(skey, key);
502 if (m_log.isDebugEnabled())
503 m_log.debug("SQL: %s", q.str().c_str());
505 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)q.str().c_str(), SQL_NTS);
506 if (!SQL_SUCCEEDED(sr)) {
507 m_log.error("error searching for (t=%s, c=%s, k=%s)", table, context, key);
508 log_error(stmt, SQL_HANDLE_STMT);
509 throw IOException("ODBC StorageService search failed.");
513 SQL_TIMESTAMP_STRUCT expiration;
515 SQLBindCol(stmt,1,SQL_C_SSHORT,&ver,0,NULL);
517 SQLBindCol(stmt,2,SQL_C_TYPE_TIMESTAMP,&expiration,0,NULL);
519 if ((sr=SQLFetch(stmt)) == SQL_NO_DATA)
523 *pexpiration = timeFromTimestamp(expiration);
526 return version; // nothing's changed, so just echo back the version
530 SQLCHAR buf[LONGDATA_BUFLEN];
531 while ((sr=SQLGetData(stmt,pexpiration ? 3 : 2,SQL_C_CHAR,buf,sizeof(buf),&len)) != SQL_NO_DATA) {
532 if (!SQL_SUCCEEDED(sr)) {
533 m_log.error("error while reading text field from result set");
534 log_error(stmt, SQL_HANDLE_STMT);
535 throw IOException("ODBC StorageService search failed to read data from result set.");
537 pvalue->append((char*)buf);
544 int ODBCStorageService::updateRow(const char *table, const char* context, const char* key, const char* value, time_t expiration, int version)
547 xmltooling::NDC ndc("updateRow");
550 if (!value && !expiration)
551 throw IOException("ODBC StorageService given invalid update instructions.");
553 // Get statement handle.
554 ODBCConn conn(getHDBC());
555 ODBCStatement stmt(getHSTMT(conn));
557 // First, fetch the current version for later, which also ensures the record still exists.
559 timestampFromTime(time(NULL), timebuf);
560 char *scontext = makeSafeSQL(context);
561 char *skey = makeSafeSQL(key);
562 string q("SELECT version FROM ");
563 q = q + table + " WHERE context='" + scontext + "' AND id='" + key + "' AND expires > " + timebuf;
565 m_log.debug("SQL: %s", q.c_str());
567 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
568 if (!SQL_SUCCEEDED(sr)) {
569 freeSafeSQL(scontext, context);
570 freeSafeSQL(skey, key);
571 m_log.error("error searching for (t=%s, c=%s, k=%s)", table, context, key);
572 log_error(stmt, SQL_HANDLE_STMT);
573 throw IOException("ODBC StorageService search failed.");
577 SQLBindCol(stmt,1,SQL_C_SSHORT,&ver,0,NULL);
578 if ((sr=SQLFetch(stmt)) == SQL_NO_DATA) {
579 freeSafeSQL(scontext, context);
580 freeSafeSQL(skey, key);
585 if (version > 0 && version != ver) {
586 freeSafeSQL(scontext, context);
587 freeSafeSQL(skey, key);
591 // Prepare and exectute update statement.
592 q = string("UPDATE ") + table + " SET ";
595 q = q + "value=?, version=version+1";
598 timestampFromTime(expiration, timebuf);
601 q = q + "expires = " + timebuf;
604 q = q + " WHERE context='" + scontext + "' AND id='" + key + "'";
605 freeSafeSQL(scontext, context);
606 freeSafeSQL(skey, key);
608 sr = SQLPrepare(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
609 if (!SQL_SUCCEEDED(sr)) {
610 m_log.error("update of record failed (t=%s, c=%s, k=%s", table, context, key);
611 log_error(stmt, SQL_HANDLE_STMT);
612 throw IOException("ODBC StorageService failed to update record.");
614 m_log.debug("SQLPrepare() succeded. SQL: %s", q.c_str());
616 SQLINTEGER b_ind = SQL_NTS;
618 sr = SQLBindParam(stmt, 1, SQL_C_CHAR, (strcmp(table, TEXT_TABLE)==0 ? SQL_LONGVARCHAR : SQL_VARCHAR), 0, 0, const_cast<char*>(value), &b_ind);
619 if (!SQL_SUCCEEDED(sr)) {
620 m_log.error("SQLBindParam failed (context = %s)", context);
621 log_error(stmt, SQL_HANDLE_STMT);
622 throw IOException("ODBC StorageService failed to update record.");
624 m_log.debug("SQLBindParam succeded (context = %s)", context);
629 return 0; // went missing?
630 else if (!SQL_SUCCEEDED(sr)) {
631 m_log.error("update of record failed (t=%s, c=%s, k=%s", table, context, key);
632 log_error(stmt, SQL_HANDLE_STMT);
633 throw IOException("ODBC StorageService failed to update record.");
639 bool ODBCStorageService::deleteRow(const char *table, const char *context, const char* key)
642 xmltooling::NDC ndc("deleteRow");
645 // Get statement handle.
646 ODBCConn conn(getHDBC());
647 ODBCStatement stmt(getHSTMT(conn));
649 // Prepare and execute delete statement.
650 char *scontext = makeSafeSQL(context);
651 char *skey = makeSafeSQL(key);
652 string q = string("DELETE FROM ") + table + " WHERE context='" + scontext + "' AND id='" + skey + "'";
653 freeSafeSQL(scontext, context);
654 freeSafeSQL(skey, key);
655 m_log.debug("SQL: %s", q.c_str());
657 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
660 else if (!SQL_SUCCEEDED(sr)) {
661 m_log.error("error deleting record (t=%s, c=%s, k=%s)", table, context, key);
662 log_error(stmt, SQL_HANDLE_STMT);
663 throw IOException("ODBC StorageService failed to delete record.");
670 void ODBCStorageService::cleanup()
673 xmltooling::NDC ndc("cleanup");
676 Mutex* mutex = Mutex::create();
680 m_log.info("cleanup thread started... running every %d secs", m_cleanupInterval);
683 shutdown_wait->timedwait(mutex, m_cleanupInterval);
689 catch (exception& ex) {
690 m_log.error("cleanup thread swallowed exception: %s", ex.what());
694 m_log.info("cleanup thread exiting...");
701 void* ODBCStorageService::cleanup_fn(void* cache_p)
703 ODBCStorageService* cache = (ODBCStorageService*)cache_p;
706 // First, let's block all signals
707 Thread::mask_all_signals();
710 // Now run the cleanup process.
715 void ODBCStorageService::updateContext(const char *table, const char* context, time_t expiration)
718 xmltooling::NDC ndc("updateContext");
721 // Get statement handle.
722 ODBCConn conn(getHDBC());
723 ODBCStatement stmt(getHSTMT(conn));
726 timestampFromTime(expiration, timebuf);
729 timestampFromTime(time(NULL), nowbuf);
731 char *scontext = makeSafeSQL(context);
733 q = q + table + " SET expires = " + timebuf + " WHERE context='" + scontext + "' AND expires > " + nowbuf;
734 freeSafeSQL(scontext, context);
736 m_log.debug("SQL: %s", q.c_str());
738 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
739 if ((sr!=SQL_NO_DATA) && !SQL_SUCCEEDED(sr)) {
740 m_log.error("error updating records (t=%s, c=%s)", table, context ? context : "all");
741 log_error(stmt, SQL_HANDLE_STMT);
742 throw IOException("ODBC StorageService failed to update context expiration.");
746 void ODBCStorageService::reap(const char *table, const char* context)
749 xmltooling::NDC ndc("reap");
752 // Get statement handle.
753 ODBCConn conn(getHDBC());
754 ODBCStatement stmt(getHSTMT(conn));
756 // Prepare and execute delete statement.
758 timestampFromTime(time(NULL), nowbuf);
761 char *scontext = makeSafeSQL(context);
762 q = string("DELETE FROM ") + table + " WHERE context='" + scontext + "' AND expires <= " + nowbuf;
763 freeSafeSQL(scontext, context);
766 q = string("DELETE FROM ") + table + " WHERE expires <= " + nowbuf;
768 m_log.debug("SQL: %s", q.c_str());
770 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
771 if ((sr!=SQL_NO_DATA) && !SQL_SUCCEEDED(sr)) {
772 m_log.error("error expiring records (t=%s, c=%s)", table, context ? context : "all");
773 log_error(stmt, SQL_HANDLE_STMT);
774 throw IOException("ODBC StorageService failed to purge expired records.");
778 void ODBCStorageService::deleteContext(const char *table, const char* context)
781 xmltooling::NDC ndc("deleteContext");
784 // Get statement handle.
785 ODBCConn conn(getHDBC());
786 ODBCStatement stmt(getHSTMT(conn));
788 // Prepare and execute delete statement.
789 char *scontext = makeSafeSQL(context);
790 string q = string("DELETE FROM ") + table + " WHERE context='" + scontext + "'";
791 freeSafeSQL(scontext, context);
792 m_log.debug("SQL: %s", q.c_str());
794 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
795 if ((sr!=SQL_NO_DATA) && !SQL_SUCCEEDED(sr)) {
796 m_log.error("error deleting context (t=%s, c=%s)", table, context);
797 log_error(stmt, SQL_HANDLE_STMT);
798 throw IOException("ODBC StorageService failed to delete context.");
802 extern "C" int ODBCSTORE_EXPORTS xmltooling_extension_init(void*)
804 // Register this SS type
805 XMLToolingConfig::getConfig().StorageServiceManager.registerFactory("ODBC", ODBCStorageServiceFactory);
809 extern "C" void ODBCSTORE_EXPORTS xmltooling_extension_term()
811 XMLToolingConfig::getConfig().StorageServiceManager.deregisterFactory("ODBC");