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, 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.
553 ODBCConn conn(getHDBC());
554 SQLHSTMT stmt = getHSTMT(conn);
556 // First, fetch the current version for later, which also ensures the record still exists.
558 timestampFromTime(time(NULL), timebuf);
559 char *scontext = makeSafeSQL(context);
560 char *skey = makeSafeSQL(key);
561 string q("SELECT version FROM ");
562 q = q + table + " WHERE context='" + scontext + "' AND id='" + key + "' AND expires > " + timebuf;
564 m_log.debug("SQL: %s", q.c_str());
566 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
567 if (!SQL_SUCCEEDED(sr)) {
568 freeSafeSQL(scontext, context);
569 freeSafeSQL(skey, key);
570 m_log.error("error searching for (t=%s, c=%s, k=%s)", table, context, key);
571 log_error(stmt, SQL_HANDLE_STMT);
572 throw IOException("ODBC StorageService search failed.");
576 SQLBindCol(stmt,1,SQL_C_SSHORT,&ver,0,NULL);
577 if ((sr=SQLFetch(stmt)) == SQL_NO_DATA) {
578 freeSafeSQL(scontext, context);
579 freeSafeSQL(skey, key);
584 if (version > 0 && version != ver) {
585 freeSafeSQL(scontext, context);
586 freeSafeSQL(skey, key);
590 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
591 stmt = getHSTMT(conn);
593 // Prepare and exectute update statement.
594 q = string("UPDATE ") + table + " SET ";
597 q = q + "value=?, version=version+1";
600 timestampFromTime(expiration, timebuf);
603 q = q + "expires = " + timebuf;
606 q = q + " WHERE context='" + scontext + "' AND id='" + key + "'";
607 freeSafeSQL(scontext, context);
608 freeSafeSQL(skey, key);
610 sr = SQLPrepare(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
611 if (!SQL_SUCCEEDED(sr)) {
612 m_log.error("update of record failed (t=%s, c=%s, k=%s", table, context, key);
613 log_error(stmt, SQL_HANDLE_STMT);
614 throw IOException("ODBC StorageService failed to update record.");
616 m_log.debug("SQLPrepare succeded. SQL: %s", q.c_str());
618 SQLINTEGER b_ind = SQL_NTS;
620 if (strcmp(table, TEXT_TABLE)==0)
621 sr = SQLBindParam(stmt, 1, SQL_C_CHAR, SQL_LONGVARCHAR, strlen(value), 0, const_cast<char*>(value), &b_ind);
623 sr = SQLBindParam(stmt, 1, SQL_C_CHAR, SQL_VARCHAR, 255, 0, const_cast<char*>(value), &b_ind);
624 if (!SQL_SUCCEEDED(sr)) {
625 m_log.error("SQLBindParam failed (context = %s)", context);
626 log_error(stmt, SQL_HANDLE_STMT);
627 throw IOException("ODBC StorageService failed to update record.");
629 m_log.debug("SQLBindParam succeded (context = %s)", context);
634 return 0; // went missing?
635 else if (!SQL_SUCCEEDED(sr)) {
636 m_log.error("update of record failed (t=%s, c=%s, k=%s", table, context, key);
637 log_error(stmt, SQL_HANDLE_STMT);
638 throw IOException("ODBC StorageService failed to update record.");
641 m_log.debug("SQLExecute of update succeeded");
645 bool ODBCStorageService::deleteRow(const char *table, const char *context, const char* key)
648 xmltooling::NDC ndc("deleteRow");
651 // Get statement handle.
652 ODBCConn conn(getHDBC());
653 SQLHSTMT stmt = getHSTMT(conn);
655 // Prepare and execute delete statement.
656 char *scontext = makeSafeSQL(context);
657 char *skey = makeSafeSQL(key);
658 string q = string("DELETE FROM ") + table + " WHERE context='" + scontext + "' AND id='" + skey + "'";
659 freeSafeSQL(scontext, context);
660 freeSafeSQL(skey, key);
661 m_log.debug("SQL: %s", q.c_str());
663 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
666 else if (!SQL_SUCCEEDED(sr)) {
667 m_log.error("error deleting record (t=%s, c=%s, k=%s)", table, context, key);
668 log_error(stmt, SQL_HANDLE_STMT);
669 throw IOException("ODBC StorageService failed to delete record.");
676 void ODBCStorageService::cleanup()
679 xmltooling::NDC ndc("cleanup");
682 Mutex* mutex = Mutex::create();
686 m_log.info("cleanup thread started... running every %d secs", m_cleanupInterval);
689 shutdown_wait->timedwait(mutex, m_cleanupInterval);
695 catch (exception& ex) {
696 m_log.error("cleanup thread swallowed exception: %s", ex.what());
700 m_log.info("cleanup thread exiting...");
707 void* ODBCStorageService::cleanup_fn(void* cache_p)
709 ODBCStorageService* cache = (ODBCStorageService*)cache_p;
712 // First, let's block all signals
713 Thread::mask_all_signals();
716 // Now run the cleanup process.
721 void ODBCStorageService::updateContext(const char *table, const char* context, time_t expiration)
724 xmltooling::NDC ndc("updateContext");
727 // Get statement handle.
728 ODBCConn conn(getHDBC());
729 SQLHSTMT stmt = getHSTMT(conn);
732 timestampFromTime(expiration, timebuf);
735 timestampFromTime(time(NULL), nowbuf);
737 char *scontext = makeSafeSQL(context);
739 q = q + table + " SET expires = " + timebuf + " WHERE context='" + scontext + "' AND expires > " + nowbuf;
740 freeSafeSQL(scontext, context);
742 m_log.debug("SQL: %s", q.c_str());
744 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
745 if ((sr!=SQL_NO_DATA) && !SQL_SUCCEEDED(sr)) {
746 m_log.error("error updating records (t=%s, c=%s)", table, context ? context : "all");
747 log_error(stmt, SQL_HANDLE_STMT);
748 throw IOException("ODBC StorageService failed to update context expiration.");
752 void ODBCStorageService::reap(const char *table, const char* context)
755 xmltooling::NDC ndc("reap");
758 // Get statement handle.
759 ODBCConn conn(getHDBC());
760 SQLHSTMT stmt = getHSTMT(conn);
762 // Prepare and execute delete statement.
764 timestampFromTime(time(NULL), nowbuf);
767 char *scontext = makeSafeSQL(context);
768 q = string("DELETE FROM ") + table + " WHERE context='" + scontext + "' AND expires <= " + nowbuf;
769 freeSafeSQL(scontext, context);
772 q = string("DELETE FROM ") + table + " WHERE expires <= " + nowbuf;
774 m_log.debug("SQL: %s", q.c_str());
776 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
777 if ((sr!=SQL_NO_DATA) && !SQL_SUCCEEDED(sr)) {
778 m_log.error("error expiring records (t=%s, c=%s)", table, context ? context : "all");
779 log_error(stmt, SQL_HANDLE_STMT);
780 throw IOException("ODBC StorageService failed to purge expired records.");
784 void ODBCStorageService::deleteContext(const char *table, const char* context)
787 xmltooling::NDC ndc("deleteContext");
790 // Get statement handle.
791 ODBCConn conn(getHDBC());
792 SQLHSTMT stmt = getHSTMT(conn);
794 // Prepare and execute delete statement.
795 char *scontext = makeSafeSQL(context);
796 string q = string("DELETE FROM ") + table + " WHERE context='" + scontext + "'";
797 freeSafeSQL(scontext, context);
798 m_log.debug("SQL: %s", q.c_str());
800 SQLRETURN sr=SQLExecDirect(stmt, (SQLCHAR*)q.c_str(), SQL_NTS);
801 if ((sr!=SQL_NO_DATA) && !SQL_SUCCEEDED(sr)) {
802 m_log.error("error deleting context (t=%s, c=%s)", table, context);
803 log_error(stmt, SQL_HANDLE_STMT);
804 throw IOException("ODBC StorageService failed to delete context.");
808 extern "C" int ODBCSTORE_EXPORTS xmltooling_extension_init(void*)
810 // Register this SS type
811 XMLToolingConfig::getConfig().StorageServiceManager.registerFactory("ODBC", ODBCStorageServiceFactory);
815 extern "C" void ODBCSTORE_EXPORTS xmltooling_extension_term()
817 XMLToolingConfig::getConfig().StorageServiceManager.deregisterFactory("ODBC");