Annotation of sql/sqlite/parser3sqlite.C, revision 1.19
1.1 misha 1: /** @file
2: Parser SQLite driver.
3:
4: (c) Dmitry "Creator" Bobrik, 2004
5: */
6:
7: #include "config_includes.h"
8:
9: #include "pa_sql_driver.h"
1.16 moko 10:
1.19 ! moko 11: volatile const char * IDENT_PARSER3SQLITE_C="$Id: parser3sqlite.C,v 1.18 2021/02/01 18:56:45 moko Exp $" IDENT_PA_SQL_DRIVER_H;
1.1 misha 12:
13: #define NO_CLIENT_LONG_LONG
14: #include "sqlite3.h"
15: #include "ltdl.h"
16:
17: #define MAX_STRING 0x400
18: #define MAX_NUMBER 20
19:
1.4 misha 20: #define SQLITE_DEFAULT_CHARSET "UTF-8"
1.18 moko 21: #define PA_REGEXP
1.4 misha 22:
1.1 misha 23: #if _MSC_VER
24: # define snprintf _snprintf
25: # define strcasecmp _stricmp
26: #endif
27:
28: static char *lsplit(char *string, char delim) {
1.6 misha 29: if(string) {
1.1 misha 30: char *v=strchr(string, delim);
1.6 misha 31: if(v) {
1.1 misha 32: *v=0;
33: return v+1;
34: }
1.6 misha 35: }
36: return 0;
1.1 misha 37: }
38:
39: static char *lsplit(char **string_ref, char delim) {
1.6 misha 40: char *result=*string_ref;
1.1 misha 41: char *next=lsplit(*string_ref, delim);
1.6 misha 42: *string_ref=next;
43: return result;
1.1 misha 44: }
45:
46: static void toupper_str(char *out, const char *in, size_t size) {
47: while(size--)
48: *out++=(char)toupper(*in++);
49: }
50:
51: struct Connection {
52: SQL_Driver_services* services;
53:
54: sqlite3* handle;
1.6 misha 55: const char* client_charset;
56: bool multi_statements;
1.1 misha 57: bool autocommit;
1.12 moko 58: int busy_timeout;
1.1 misha 59: };
60:
1.17 moko 61: // sqlite client library funcs
1.1 misha 62:
1.17 moko 63: typedef int (*t_sqlite3_open)(const char *filename, sqlite3 **ppDb); t_sqlite3_open pa_sqlite3_open;
64:
65: typedef int (*t_sqlite3_close)(sqlite3 *); t_sqlite3_close pa_sqlite3_close;
66:
67: typedef int (*t_sqlite3_busy_timeout)(sqlite3*, int ms); t_sqlite3_busy_timeout pa_sqlite3_busy_timeout;
68:
69: typedef int (*t_sqlite3_exec)(sqlite3*, const char *sql, sqlite3_callback, void *, char **errmsg); t_sqlite3_exec pa_sqlite3_exec;
70:
71: typedef void (*t_sqlite3_free)(char *z); t_sqlite3_free pa_sqlite3_free;
72:
73: typedef const char *(* t_sqlite3_errmsg)(sqlite3*); t_sqlite3_errmsg pa_sqlite3_errmsg;
74:
75: typedef int (* t_sqlite3_prepare)(sqlite3 *db, const char *zSql, int nBytes, sqlite3_stmt **ppStmt, const char **pzTail); t_sqlite3_prepare pa_sqlite3_prepare;
76:
77: typedef int (* t_sqlite3_column_count)(sqlite3_stmt *pStmt); t_sqlite3_column_count pa_sqlite3_column_count;
78:
79: typedef int (* t_sqlite3_finalize)(sqlite3_stmt *pStmt); t_sqlite3_finalize pa_sqlite3_finalize;
80:
81: typedef const char *(* t_sqlite3_column_name)(sqlite3_stmt*,int); t_sqlite3_column_name pa_sqlite3_column_name;
82:
83: typedef int (* t_sqlite3_step)(sqlite3_stmt*); t_sqlite3_step pa_sqlite3_step;
84:
85: typedef int (* t_sqlite3_column_type)(sqlite3_stmt*, int iCol); t_sqlite3_column_type pa_sqlite3_column_type;
86:
87: typedef const unsigned char *(* t_sqlite3_column_text)(sqlite3_stmt*, int iCol); t_sqlite3_column_text pa_sqlite3_column_text;
88:
89: typedef const unsigned char *(* t_sqlite3_column_blob)(sqlite3_stmt*, int iCol); t_sqlite3_column_blob pa_sqlite3_column_blob;
90:
91: typedef int (* t_sqlite3_column_bytes)(sqlite3_stmt*, int iCol); t_sqlite3_column_bytes pa_sqlite3_column_bytes;
1.1 misha 92:
1.18 moko 93: #ifdef PA_REGEXP
94: typedef int (* t_sqlite3_create_function)(sqlite3 *, const char *, int, int, void *, void (*)(sqlite3_context*,int,sqlite3_value**), void *, void *); t_sqlite3_create_function pa_sqlite3_create_function;
95:
96: typedef const unsigned char *(* t_sqlite3_value_text)(sqlite3_value*); static t_sqlite3_value_text pa_sqlite3_value_text;
97:
98: typedef void *(* t_sqlite3_get_auxdata)(sqlite3_context*, int N); static t_sqlite3_get_auxdata pa_sqlite3_get_auxdata;
99:
100: typedef void (* t_sqlite3_set_auxdata)(sqlite3_context*, int N, void*, void (*)(void*)); static t_sqlite3_set_auxdata pa_sqlite3_set_auxdata;
101:
102: typedef void (* t_sqlite3_result_error)(sqlite3_context*, const char*, int); static t_sqlite3_result_error pa_sqlite3_result_error;
103:
104: typedef void (* t_sqlite3_result_error_nomem)(sqlite3_context*); static t_sqlite3_result_error_nomem pa_sqlite3_result_error_nomem;
105:
106: typedef void (* t_sqlite3_result_int)(sqlite3_context*, int); static t_sqlite3_result_int pa_sqlite3_result_int;
107:
108:
109: // "regexp.h" hardcoded
110: struct ReCompiled;
111: const char *pa_re_compile(ReCompiled **ppRe, const char *zIn, int noCase);
112: void pa_re_free(ReCompiled *pRe);
113: int pa_re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn);
114:
115: // regexp(pattern, string) sqlite3 function implementation
116: static void regexp_sql_func(sqlite3_context *context, int argc, sqlite3_value **argv){
117: ReCompiled *pRe = (ReCompiled *)pa_sqlite3_get_auxdata(context, 0);
118: int setAux = 0; /* True to invoke sqlite3_set_auxdata() */
119:
120: if( pRe==0 ){
121: const char *zPattern = (const char*)pa_sqlite3_value_text(argv[0]);
122: if( zPattern==0 ) return;
123: const char *zErr = pa_re_compile(&pRe, zPattern, 0);
124: if( zErr ){
125: pa_re_free(pRe);
126: pa_sqlite3_result_error(context, zErr, -1);
127: return;
128: }
129: if( pRe==0 ){
130: pa_sqlite3_result_error_nomem(context);
131: return;
132: }
133: setAux = 1;
134: }
135: const unsigned char *zStr = (const unsigned char*)pa_sqlite3_value_text(argv[1]);
136: if( zStr!=0 ){
137: pa_sqlite3_result_int(context, pa_re_match(pRe, zStr, -1));
138: }
139: if( setAux ){
140: pa_sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))pa_re_free);
141: }
142: }
143: #endif
144:
145:
1.1 misha 146: /**
147: SQLite server driver
148: */
149: class SQLite_Driver : public SQL_Driver {
150: public:
151:
152: SQLite_Driver() : SQL_Driver() {
153: }
154:
155: /// get api version
156: int api_version() { return SQL_DRIVER_API_VERSION; }
1.4 misha 157:
1.1 misha 158: /// initialize driver by loading sql dynamic link library
159: const char *initialize(char *dlopen_file_spec) {
1.18 moko 160: return dlopen_file_spec ? dlink(dlopen_file_spec) : "client library column is empty";
1.1 misha 161: }
1.4 misha 162:
1.1 misha 163: /** connect
164: @param url
1.8 misha 165: format: @b db-file|:memory:|temporary:?
166: autocommit=1& // =0 disable autocommit. in this case 1 connect == 1 transaction.
167: // or you can use begin/commit|rollback explicitly
168: multi_statements=0& // =1 allow many statements in 1 query
169: ClientCharset=UTF-8 // will transcode to/from specified charset instead of UTF-8 (default for sqlite)
1.1 misha 170: */
171: void connect(
1.6 misha 172: char *url,
173: SQL_Driver_services& services,
174: void **connection_ref ///< output: Connection*
1.4 misha 175: ){
1.1 misha 176:
1.4 misha 177: Connection& connection=*(Connection *)services.malloc(sizeof(Connection));
1.6 misha 178: *connection_ref=&connection;
1.4 misha 179: connection.services=&services;
1.6 misha 180:
1.12 moko 181: connection.client_charset=SQLITE_DEFAULT_CHARSET;
1.6 misha 182: connection.multi_statements=false;
1.4 misha 183: connection.autocommit=true;
1.12 moko 184: connection.busy_timeout=4000;
1.1 misha 185:
1.6 misha 186: char* db_path=0;
187: char* db=url;
188: char* options=lsplit(db, '?');
189:
1.8 misha 190: if(strcmp(db, ":memory:")==0){ // in-memory temporary DB
1.6 misha 191: db_path=db;
1.8 misha 192: } else if(strcmp(db, ":temporary:")==0){ // on-disk temporary DB
1.6 misha 193: // do nothing: empty path mean temporary table on disk
1.8 misha 194: } else {
195: char* document_root=(char*)services.request_document_root();
196: if(!document_root) // path to DB-file which was specified by user is path from document_root as anywhere in parser
197: services._throw("document_root is empty");
198:
199: db_path=(char*)services.malloc_atomic(strlen(document_root)+1+strlen(db)+1);
1.9 misha 200: strcpy(db_path, document_root);
201: strcat(db_path, "/");
202: strcat(db_path, db);
1.5 misha 203: }
1.4 misha 204:
1.6 misha 205: //services._throw(db_path);
206:
207: while(options){
208: if(char* key=lsplit(&options, '&')){
1.4 misha 209: if(*key) {
1.6 misha 210: if(char* value=lsplit(key, '=')){
1.8 misha 211: if(strcasecmp(key, "multi_statements")==0){
1.6 misha 212: if(atoi(value)!=0)
213: connection.multi_statements=true;
1.12 moko 214: } else if(strcasecmp(key, "busy_timeout")==0){
215: connection.busy_timeout=atoi(value);
1.6 misha 216: } else if(strcasecmp(key, "autocommit")==0){
1.4 misha 217: if(atoi(value)==0)
218: connection.autocommit=false;
1.8 misha 219: } else if(strcmp(key, "ClientCharset")==0){
1.6 misha 220: toupper_str(value, value, strlen(value));
221: connection.client_charset=value;
1.4 misha 222: } else
223: services._throw("unknown connect option" /*key*/);
224: } else
225: services._throw("connect option without =value" /*key*/);
226: }
227: }
228: }
229:
230: // transcode database_name from $request:charset to UTF-8
1.6 misha 231: if(db_path && _transcode_required(connection, SQLITE_DEFAULT_CHARSET)){
232: size_t length=strlen(db_path);
233: services.transcode((const char*)db_path, length,
234: (const char*&)db_path, length,
235: services.request_charset(),
236: SQLITE_DEFAULT_CHARSET);
237:
238: }
1.4 misha 239:
1.1 misha 240:
1.17 moko 241: int rc=pa_sqlite3_open(db_path, &connection.handle);
1.18 moko 242: #ifdef PA_REGEXP
243: if(rc==SQLITE_OK)
244: rc=pa_sqlite3_create_function(connection.handle, "regexp", 2, SQLITE_UTF8, 0, regexp_sql_func, 0, 0);
245: #endif
1.5 misha 246: if(rc!=SQLITE_OK){
1.17 moko 247: const char* error_msg=pa_sqlite3_errmsg(connection.handle);
248: pa_sqlite3_close(connection.handle);
1.6 misha 249: _throw(connection, error_msg);
1.1 misha 250: }
1.6 misha 251:
1.17 moko 252: pa_sqlite3_busy_timeout(connection.handle, connection.busy_timeout);
1.12 moko 253:
1.6 misha 254: _begin_transaction(connection);
1.1 misha 255: }
256:
1.6 misha 257: void disconnect(void *aconnection){
1.1 misha 258: Connection& connection=*static_cast<Connection*>(aconnection);
1.17 moko 259: pa_sqlite3_close(connection.handle);
1.1 misha 260: connection.handle=0;
1.4 misha 261: }
1.1 misha 262:
1.6 misha 263: void commit(void *aconnection){
1.1 misha 264: Connection& connection=*static_cast<Connection*>(aconnection);
265: if(!connection.autocommit)
1.6 misha 266: _execute_cmd(connection, "COMMIT");
267:
268: _begin_transaction(connection);
1.1 misha 269: }
1.4 misha 270:
1.6 misha 271: void rollback(void *aconnection){
1.1 misha 272: Connection& connection=*static_cast<Connection*>(aconnection);
273: if(!connection.autocommit)
1.6 misha 274: _execute_cmd(connection, "ROLLBACK");
275:
276: _begin_transaction(connection);
1.1 misha 277: }
278:
1.6 misha 279: bool ping(void *aconnection){
280: return true; // not needed
1.1 misha 281: }
282:
1.11 moko 283: // charset here is services.request_charset(), not connection.client_charset
284: // thus we can't use the sql server quoting support
285: const char* quote(void *aconnection, const char *str, unsigned int length)
286: {
287: const char* from;
288: const char* from_end=str+length;
289:
290: size_t quoted=0;
291:
292: for(from=str; from<from_end; from++){
293: if(*from=='\'')
294: quoted++;
295: }
296:
297: if(!quoted)
298: return str;
299:
1.1 misha 300: Connection& connection=*static_cast<Connection*>(aconnection);
1.11 moko 301: char *result=(char*)connection.services->malloc_atomic(length + quoted + 1);
302: char *to = result;
303:
304: for(from=str; from<from_end; from++){
305: if(*from=='\'')
306: *to++= '\''; // ' -> ''
307: *to++=*from;
1.1 misha 308: }
1.11 moko 309:
1.1 misha 310: *to=0;
311: return result;
312: }
1.4 misha 313:
1.1 misha 314: void query(void *aconnection,
1.6 misha 315: const char *astatement,
316: size_t placeholders_count, Placeholder* placeholders,
317: unsigned long offset, unsigned long limit,
318: SQL_Driver_query_event_handlers& handlers
319: ){
1.1 misha 320:
321: Connection& connection=*static_cast<Connection*>(aconnection);
322: SQL_Driver_services& services=*connection.services;
323:
324: if(placeholders_count>0)
1.5 misha 325: services._throw("bind variables not supported yet");
1.9 misha 326:
327: const char* request_charset=services.request_charset();
328: const char* client_charset=connection.client_charset;
1.6 misha 329: bool transcode_needed=_transcode_required(connection);
330:
331: // transcode query from $request:charset to ?ClientCharset
332: if(transcode_needed){
333: size_t length=strlen(astatement);
334: services.transcode(astatement, length,
335: astatement, length,
1.9 misha 336: request_charset,
337: client_charset);
1.4 misha 338: }
339:
1.1 misha 340: const char *statement;
1.6 misha 341: if(offset || limit!=SQL_NO_LIMIT){
1.1 misha 342: size_t statement_size=strlen(astatement);
343: char *statement_limited=(char *)services.malloc_atomic(
1.6 misha 344: statement_size+MAX_NUMBER*2+8/* LIMIT #,#*/+1);
1.1 misha 345: char *cur=statement_limited;
1.6 misha 346: memcpy(cur, astatement, statement_size);
347: cur+=statement_size;
348: cur+=sprintf(cur, " LIMIT ");
1.1 misha 349: if(offset)
1.12 moko 350: cur+=snprintf(cur, MAX_NUMBER+1, "%lu,", offset);
1.6 misha 351: if(limit!=SQL_NO_LIMIT)
1.12 moko 352: cur+=snprintf(cur, MAX_NUMBER, "%lu", limit);
1.1 misha 353: statement=statement_limited;
354: } else
355: statement=astatement;
356:
357:
358: const char *pzTail;
1.6 misha 359: int next_statement_length=0;
1.1 misha 360: sqlite3_stmt *SQL;
361: int rc;
362: SQL_Error sql_error;
1.5 misha 363: bool failed=false;
1.1 misha 364:
365: do{ // cycling through SQL commands
1.17 moko 366: rc=pa_sqlite3_prepare(connection.handle, statement, -1, &SQL, &pzTail);
1.6 misha 367: next_statement_length=strlen(pzTail);
1.4 misha 368: if(rc!=SQLITE_OK){
1.17 moko 369: //pa_sqlite3_free((char*)pzTail);
370: _throw(connection, pa_sqlite3_errmsg(connection.handle));
1.6 misha 371: }
372: if(!connection.multi_statements && next_statement_length>0){ // multi statements was not allowed but pzTail point to not empty one
1.17 moko 373: //pa_sqlite3_free((char*)pzTail);
1.15 moko 374: _throw(connection, "multi statements are not allowed until option ?multi_statements=1 in connect string is specified.");
1.1 misha 375: }
376:
1.6 misha 377: #define CHECK(afailed) if(afailed){ failed=true; goto cleanup; }
1.1 misha 378:
1.17 moko 379: int column_count=pa_sqlite3_column_count(SQL);
1.1 misha 380:
1.6 misha 381: if(!column_count){ // empty result: insert|delete|update|...
1.17 moko 382: rc=pa_sqlite3_step(SQL);
1.1 misha 383: } else {
1.6 misha 384: for(int i=0; i<column_count; i++){
1.17 moko 385: const char *column_name=pa_sqlite3_column_name(SQL, i);
1.6 misha 386: size_t length=strlen(column_name);
1.1 misha 387:
388: char* strm=(char*)services.malloc_atomic(length+1);
389: memcpy(strm, column_name, length+1);
1.6 misha 390: const char* str=strm;
391: // transcode column name from ?ClientCharset to $request:charset
392: if(transcode_needed){
1.4 misha 393: services.transcode(str, length,
394: str, length,
1.9 misha 395: client_charset,
396: request_charset);
1.4 misha 397: }
398:
1.6 misha 399: CHECK(handlers.add_column(sql_error, (const char*)str, length));
1.1 misha 400: }
401: CHECK(handlers.before_rows(sql_error));
402:
1.5 misha 403: const char *str;
1.6 misha 404: size_t length=0;
1.1 misha 405:
406: do{
1.17 moko 407: rc=pa_sqlite3_step(SQL);
1.6 misha 408: if(rc==SQLITE_ROW){ // new line!!
1.1 misha 409:
410: CHECK(handlers.add_row(sql_error));
411:
1.6 misha 412: for(int i=0; i<column_count; i++){
1.17 moko 413: // SQLite allow to get value of any type using pa_sqlite3_column_text function
1.14 moko 414: bool transcode_value=false;
1.17 moko 415: int column_type=pa_sqlite3_column_type(SQL, i);
1.14 moko 416: switch(column_type){
1.2 misha 417: case SQLITE_NULL:
1.6 misha 418: length=0;
1.4 misha 419: str=NULL;
1.2 misha 420: break;
1.6 misha 421: case SQLITE_BLOB:
1.17 moko 422: str=(const char*)pa_sqlite3_column_blob(SQL, i);
423: length=(size_t)pa_sqlite3_column_bytes(SQL, i);
1.6 misha 424: break;
1.14 moko 425: case SQLITE_TEXT: // for text transcoding can be required
1.9 misha 426: default: // anything else?
1.14 moko 427: transcode_value=transcode_needed;
428: case SQLITE_INTEGER:
429: case SQLITE_FLOAT:
1.17 moko 430: str=(const char*)pa_sqlite3_column_text(SQL, i);
431: length=(size_t)pa_sqlite3_column_bytes(SQL, i);
1.1 misha 432: break;
433: }
434:
1.4 misha 435: if(length){
436: char* strm=(char*)services.malloc_atomic(length+1);
1.14 moko 437: memcpy(strm, str, length);
438: strm[length]=0;
1.5 misha 439: str=strm;
1.4 misha 440:
1.14 moko 441: if(transcode_value){
1.9 misha 442: // transcode cell value from ?ClientCharset to $request:charset
1.4 misha 443: services.transcode(str, length,
444: str, length,
1.9 misha 445: client_charset,
446: request_charset);
1.4 misha 447: }
448: } else
1.6 misha 449: str=0;
1.4 misha 450:
451: CHECK(handlers.add_row_cell(sql_error, str, length));
1.1 misha 452:
453: }
454: }
1.6 misha 455: } while(rc==SQLITE_BUSY || rc==SQLITE_ROW);
1.1 misha 456:
1.6 misha 457: }
1.1 misha 458:
1.6 misha 459: if(rc==SQLITE_ERROR || rc==SQLITE_MISUSE){
1.17 moko 460: _throw(connection, pa_sqlite3_errmsg(connection.handle));
1.1 misha 461: }
462:
463: cleanup:
1.17 moko 464: pa_sqlite3_finalize(SQL);
1.6 misha 465: statement=pzTail;
466: } while (next_statement_length>0);
1.1 misha 467:
468: if(failed)
1.5 misha 469: services._throw(sql_error);
1.1 misha 470: }
471:
1.4 misha 472: private:
1.6 misha 473: void _begin_transaction(Connection& connection) {
474: if(!connection.autocommit){
475: _execute_cmd(connection, "BEGIN");
476: }
477: }
478:
479: void _execute_cmd(Connection& connection, const char* statement){
480: char* zErr;
1.17 moko 481: int rc=pa_sqlite3_exec(connection.handle, statement, 0, 0, &zErr);
1.6 misha 482: if(rc!=SQLITE_OK){
483: size_t length=strlen(zErr);
484: char* err_msg=(char *)connection.services->malloc_atomic(length+1);
485: memcpy(err_msg, zErr, length);
486:
1.17 moko 487: pa_sqlite3_free(zErr);
1.6 misha 488: _throw(connection, err_msg);
489: }
490:
491: }
492:
493: void _throw(Connection& connection, const char* aerr_msg){
494: size_t length=strlen(aerr_msg);
495: if(length && _transcode_required(connection)){
496: // transcode server error message from ?ClientCharset to $request:charset
497: connection.services->transcode(aerr_msg, length,
498: aerr_msg, length,
499: connection.client_charset,
500: connection.services->request_charset());
501: }
502: connection.services->_throw(aerr_msg);
503: }
504:
505: bool _transcode_required(Connection& connection, const char* charset=0){
506: return (strcmp(charset?charset:connection.client_charset, connection.services->request_charset())!=0);
507: }
508:
1.4 misha 509:
1.1 misha 510: private: // sqlite client library funcs linking
511:
512: const char *dlink(const char *dlopen_file_spec) {
1.13 moko 513: if(lt_dlinit()){
514: if(const char* result=lt_dlerror())
515: return result;
516: return "can not prepare to dynamic loading";
517: }
518:
519: lt_dlhandle handle=lt_dlopen(dlopen_file_spec);
1.1 misha 520:
1.13 moko 521: if(!handle){
522: if(const char* result=lt_dlerror())
523: return result;
1.1 misha 524: return "can not open the dynamic link module";
525: }
526:
527: #define DSLINK(name, action) \
1.17 moko 528: pa_##name=(t_##name)lt_dlsym(handle, #name); \
1.19 ! moko 529: if(!pa_##name) \
1.1 misha 530: action;
531:
532: #define DLINK(name) DSLINK(name, return "function " #name " was not found")
533: #define SLINK(name) DSLINK(name, name=subst_##name)
534:
535: DLINK(sqlite3_open);
536: DLINK(sqlite3_close);
1.12 moko 537: DLINK(sqlite3_busy_timeout);
1.1 misha 538: DLINK(sqlite3_exec);
539: DLINK(sqlite3_free);
540: DLINK(sqlite3_errmsg);
541: DLINK(sqlite3_prepare);
542: DLINK(sqlite3_column_count);
543: DLINK(sqlite3_finalize);
544: DLINK(sqlite3_column_name);
545: DLINK(sqlite3_step);
546: DLINK(sqlite3_column_type);
547: DLINK(sqlite3_column_text);
1.6 misha 548: DLINK(sqlite3_column_blob);
549: DLINK(sqlite3_column_bytes);
1.18 moko 550: #ifdef PA_REGEXP
551: DLINK(sqlite3_create_function);
552: DLINK(sqlite3_value_text);
553: DLINK(sqlite3_get_auxdata);
554: DLINK(sqlite3_set_auxdata);
555: DLINK(sqlite3_result_error);
556: DLINK(sqlite3_result_error_nomem);
557: DLINK(sqlite3_result_int);
558: #endif
1.1 misha 559: return 0;
560: }
561:
562: };
563:
564: extern "C" SQL_Driver *SQL_DRIVER_CREATE() {
565: return new SQLite_Driver();
566: }
E-mail: