|
|
1.1 parser 1: /** @file
2: Parser PgSQL driver.
3:
1.12 paf 4: Copyright(c) 2001, 2003 ArtLebedev Group (http://www.artlebedev.com)
1.1 parser 5:
1.6 paf 6: Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
1.1 parser 7:
8: 2001.07.30 using PgSQL 7.1.2
9: */
1.26 ! misha 10: static const char *RCSId="$Id: parser3pgsql.C,v 1.25 2004/12/23 16:54:52 paf Exp $";
1.1 parser 11:
12: #include "config_includes.h"
13:
14: #include "pa_sql_driver.h"
15:
16: #include <libpq-fe.h>
17: #include <libpq/libpq-fs.h>
18:
19: // OIDOID from catalog/pg_type.h
20: #define OIDOID 26
21: // LO_BUFSIZE from interfaces\libpq\fe-lobj.c = 8192 (0x2000)
22: // actually writing chunks of that size failed, reduced it twice
23: #define LO_BUFSIZE 0x1000
24: // from postgres_ext.h
1.26 ! misha 25: //#define InvalidOid ((Oid) 0)
1.1 parser 26:
27:
28: #include "ltdl.h"
29:
30: #define MAX_STRING 0x400
31: #define MAX_NUMBER 20
32:
33: #if _MSC_VER
34: # define snprintf _snprintf
35: # define strcasecmp _stricmp
36: #endif
37:
38: #ifndef max
39: inline int max(int a,int b) { return a>b?a:b; }
40: inline int min(int a,int b){ return a<b?a:b; }
41: #endif
42:
43: static char *lsplit(char *string, char delim) {
44: if(string) {
45: char *v=strchr(string, delim);
46: if(v) {
47: *v=0;
48: return v+1;
49: }
50: }
51: return 0;
52: }
53:
1.8 paf 54: static char *lsplit(char **string_ref, char delim) {
55: char *result=*string_ref;
56: char *next=lsplit(*string_ref, delim);
57: *string_ref=next;
58: return result;
59: }
60:
1.25 paf 61: static char* rsplit(char* string, char delim) {
62: if(string) {
63: char* v=strrchr(string, delim);
64: if(v) {
65: *v=0;
66: return v+1;
67: }
68: }
69: return NULL;
70: }
71:
1.20 paf 72: static void toupper_str(char *out, const char *in, size_t size) {
1.18 paf 73: while(size--)
74: *out++=(char)toupper(*in++);
75: }
76:
1.16 paf 77: struct Connection {
78: SQL_Driver_services* services;
79:
80: PGconn *conn;
1.18 paf 81: const char* cstrClientCharset;
1.16 paf 82: };
83:
1.1 parser 84: /**
85: PgSQL server driver
86: */
87: class PgSQL_Driver : public SQL_Driver {
88: public:
89:
90: PgSQL_Driver() : SQL_Driver() {
91: }
92:
93: /// get api version
94: int api_version() { return SQL_DRIVER_API_VERSION; }
95: /// initialize driver by loading sql dynamic link library
1.3 paf 96: const char *initialize(char *dlopen_file_spec) {
1.1 parser 97: return dlopen_file_spec?
98: dlink(dlopen_file_spec):"client library column is empty";
99: }
100:
1.16 paf 101: #define throwPQerror connection.services->_throw(PQerrorMessage(connection.conn))
1.8 paf 102: #define PQclear_throw(msg) { \
103: PQclear(res); \
1.16 paf 104: connection.services->_throw(msg); \
1.8 paf 105: }
1.16 paf 106: #define PQclear_throwPQerror PQclear_throw(PQerrorMessage(connection.conn))
1.1 parser 107:
108: /** connect
1.21 paf 109: @param url
1.1 parser 110: format: @b user:pass@host[:port]|[local]/database
111: */
112: void connect(
1.21 paf 113: char *url,
1.1 parser 114: SQL_Driver_services& services,
1.16 paf 115: void **connection_ref ///< output: Connection*
1.1 parser 116: ) {
1.21 paf 117: char *user=url;
1.25 paf 118: char *host=rsplit(user, '@');
1.1 parser 119: char *db=lsplit(host, '/');
120: char *pwd=lsplit(user, ':');
121: char *port=lsplit(host, ':');
122:
1.8 paf 123: char *options=lsplit(db, '?');
124:
1.18 paf 125: char *cstrBackwardCompAskServerToTranscode=0;
126:
1.17 paf 127: Connection& connection=*(Connection *)services.malloc(sizeof(Connection));
1.16 paf 128: *connection_ref=&connection;
129: connection.services=&services;
1.18 paf 130: connection.cstrClientCharset=0;
1.16 paf 131: connection.conn=PQsetdbLogin(
1.7 paf 132: (host&&strcasecmp(host, "local")==0)?NULL/* local Unix domain socket */:host, port,
1.1 parser 133: NULL, NULL, db, user, pwd);
1.16 paf 134: if(!connection.conn)
1.1 parser 135: services._throw("PQsetdbLogin failed");
1.16 paf 136: if(PQstatus(connection.conn)!=CONNECTION_OK)
1.1 parser 137: throwPQerror;
138:
1.8 paf 139: char *charset=0;
140: char *datestyle=0;
1.23 paf 141: isDefaultTransaction = true;
1.8 paf 142:
143: while(options) {
144: if(char *key=lsplit(&options, '&')) {
145: if(*key) {
146: if(char *value=lsplit(key, '=')) {
1.18 paf 147: if(strcmp(key, "ClientCharset" ) == 0) {
1.20 paf 148: toupper_str(value, value, strlen(value));
1.18 paf 149: connection.cstrClientCharset=value;
150: } else if(strcasecmp(key, "charset")==0) { // left for backward compatibility, consider using ClientCharset
151: cstrBackwardCompAskServerToTranscode=value;
1.8 paf 152: } else if(strcasecmp(key, "datestyle")==0) {
153: datestyle=value;
1.23 paf 154: } else if(strcmp(key, "WithoutDefaultTransaction")==0) {
1.26 ! misha 155: if(strcmp(value, "1" ) == 0) {
! 156: isDefaultTransaction = false;
! 157: } else if(strcmp(value, "0" ) == 0) {
! 158: isDefaultTransaction = true;
! 159: } else {
! 160: services._throw("Bad WithoutDefaultTransaction option value. Only 0 or 1 are accepted." /*value*/);
! 161: }
1.8 paf 162: } else
163: services._throw("unknown connect option" /*key*/);
164: } else
165: services._throw("connect option without =value" /*key*/);
166: }
167: }
168: }
169:
1.18 paf 170: if(connection.cstrClientCharset && cstrBackwardCompAskServerToTranscode)
171: services._throw("use 'ClientCharset' option only, "
172: "'charset' option is obsolete and should not be used with new 'ClientCharset' option");
173:
174: if(cstrBackwardCompAskServerToTranscode) {
1.8 paf 175: // set CLIENT_ENCODING
176: char statement[MAX_STRING]="set CLIENT_ENCODING="; // win
1.18 paf 177: strncat(statement, cstrBackwardCompAskServerToTranscode, MAX_STRING);
1.26 ! misha 178:
! 179: execute_resultless(connection, statement);
1.8 paf 180: }
181:
182: if(datestyle) {
183: // set DATESTYLE
184: char statement[MAX_STRING]="set DATESTYLE="; // ISO,SQL,Postgres,European,NonEuropean=US,German,DEFAULT=ISO
185: strncat(statement, charset, MAX_STRING);
1.26 ! misha 186:
! 187: execute_resultless(connection, statement);
1.8 paf 188: }
189:
1.16 paf 190: begin_transaction(connection);
1.1 parser 191: }
1.16 paf 192: void disconnect(void *aconnection) {
193: Connection& connection=*static_cast<Connection*>(aconnection);
194:
195: PQfinish(connection.conn);
196: connection.conn=0;
1.1 parser 197: }
1.16 paf 198: void commit(void *aconnection) {
1.26 ! misha 199: execute_transaction_cmd(aconnection, "COMMIT");
1.1 parser 200: }
1.16 paf 201: void rollback(void *aconnection) {
1.26 ! misha 202: execute_transaction_cmd(aconnection, "ROLLBACK");
1.1 parser 203: }
204:
1.16 paf 205: bool ping(void *aconnection) {
206: Connection& connection=*static_cast<Connection*>(aconnection);
207:
208: return PQstatus(connection.conn)==CONNECTION_OK;
1.1 parser 209: }
210:
1.13 paf 211: const char* quote(
1.16 paf 212: void *aconnection,
1.13 paf 213: const char *from, unsigned int length) {
1.16 paf 214: Connection& connection=*static_cast<Connection*>(aconnection);
215:
216: char *result=(char*)connection.services->malloc_atomic(length*2+1);
1.26 ! misha 217: int err = 0;
! 218: PQescapeStringConn (connection.conn,
! 219: result, from, length,
! 220: &err);
1.13 paf 221: return result;
1.26 ! misha 222: }
! 223:
1.16 paf 224: void query(void *aconnection,
1.22 paf 225: const char *astatement,
226: size_t placeholders_count, Placeholder* placeholders,
227: unsigned long offset, unsigned long limit,
1.1 parser 228: SQL_Driver_query_event_handlers& handlers) {
229: // _asm int 3;
1.16 paf 230: Connection& connection=*static_cast<Connection*>(aconnection);
1.19 paf 231: const char* cstrClientCharset=connection.cstrClientCharset;
1.16 paf 232: SQL_Driver_services& services=*connection.services;
233: PGconn *conn=connection.conn;
1.22 paf 234:
1.26 ! misha 235: const char** paramValues;
! 236: if(placeholders_count>0){
! 237: //services._throw("bind variables not supported (yet)");
! 238: int binds_size=sizeof(char) * placeholders_count;
! 239: paramValues = static_cast<const char**>(services.malloc_atomic(binds_size));
! 240: for(size_t i=0; i<placeholders_count; ++i) {
! 241: Placeholder& ph=placeholders[i];
! 242: size_t value_length;
! 243: if(cstrClientCharset) {
! 244: size_t name_length;
! 245: services.transcode(ph.name, strlen(ph.name),
! 246: ph.name, name_length,
! 247: services.request_charset(),
! 248: cstrClientCharset);
! 249:
! 250: if(ph.value){
! 251: services.transcode(ph.value, strlen(ph.value),
! 252: ph.value, value_length,
! 253: services.request_charset(),
! 254: cstrClientCharset);
! 255: }
! 256: } else {
! 257: value_length=ph.value? strlen(ph.value): 0;
! 258: }
! 259: if( atoi(ph.name) <= 0 || atoi(ph.name) > placeholders_count) {
! 260: services._throw("bad bind parameter key");
! 261: }
! 262: paramValues[atoi(ph.name)-1] = ph.value;
! 263: }
! 264: }
1.1 parser 265:
1.18 paf 266: // transcode from $request:charset to connect-string?client_charset
1.19 paf 267: if(cstrClientCharset) {
1.18 paf 268: size_t transcoded_statement_size;
269: services.transcode(astatement, strlen(astatement),
270: astatement, transcoded_statement_size,
271: services.request_charset(),
272: cstrClientCharset);
273: }
274:
1.16 paf 275: const char *statement=preprocess_statement(connection,
1.1 parser 276: astatement, offset, limit);
277:
1.26 ! misha 278: PGresult *res;
! 279: if(placeholders_count>0){
! 280: res=PQexecParams(conn, statement, placeholders_count, NULL, paramValues, NULL, NULL, 0);
! 281: } else {
! 282: res=PQexec(conn, statement);
! 283: }
1.1 parser 284: if(!res)
285: throwPQerror;
286:
287: switch(PQresultStatus(res)) {
288: case PGRES_EMPTY_QUERY:
289: PQclear_throw("no query");
290: break;
291: case PGRES_COMMAND_OK:
292: // empty result: insert|delete|update|...
293: PQclear(res);
294: return;
295: case PGRES_TUPLES_OK:
296: break;
297: default:
298: PQclear_throwPQerror;
299: break;
300: }
301:
302: int column_count=PQnfields(res);
303: if(!column_count)
304: PQclear_throw("result contains no columns");
305:
1.9 paf 306: bool failed=false;
307: SQL_Error sql_error;
308: #define CHECK(afailed) \
309: if(afailed) { \
310: failed=true; \
311: goto cleanup; \
312: }
313:
1.1 parser 314: for(int i=0; i<column_count; i++){
315: char *name=PQfname(res, i);
1.18 paf 316: size_t length=strlen(name);
1.19 paf 317: char* strm=(char*)services.malloc(length+1);
318: memcpy(strm, name, length+1);
319: const char* str=strm;
1.18 paf 320:
321: // transcode to $request:charset from connect-string?client_charset
1.19 paf 322: if(cstrClientCharset)
1.18 paf 323: services.transcode(str, length,
324: str, length,
325: cstrClientCharset,
326: services.request_charset());
327:
328: CHECK(handlers.add_column(sql_error, str, length));
1.1 parser 329: }
330:
1.9 paf 331: CHECK(handlers.before_rows(sql_error));
1.1 parser 332:
333: if(unsigned long row_count=(unsigned long)PQntuples(res))
334: for(unsigned long r=0; r<row_count; r++) {
1.9 paf 335: CHECK(handlers.add_row(sql_error));
1.1 parser 336: for(int i=0; i<column_count; i++){
337: const char *cell=PQgetvalue(res, r, i);
1.18 paf 338: size_t length;
1.19 paf 339: const char* str;
1.1 parser 340: if(PQftype(res, i)==OIDOID) {
341: // ObjectID column, read object bytes
342:
343: char *error_pos=0;
344: Oid oid=cell?atoi(cell):0;
345: int fd=lo_open(conn, oid, INV_READ);
346: if(fd>=0) {
347: // seek to end
348: if(lo_lseek(conn, fd, 0, SEEK_END)<0)
349: PQclear_throwPQerror;
1.18 paf 350: // get length
1.1 parser 351: int size_tell=lo_tell(conn, fd);
352: if(size_tell<0)
353: PQclear_throwPQerror;
354: // seek to begin
355: if(lo_lseek(conn, fd, 0, SEEK_SET)<0)
356: PQclear_throwPQerror;
1.18 paf 357: length=(size_t)size_tell;
358: if(length) {
1.1 parser 359: // read
1.19 paf 360: char* strm=(char*)services.malloc(length+1);
361: if(!lo_read_ex(conn, fd, strm, size_tell))
1.1 parser 362: PQclear_throw("lo_read can not read all bytes of object");
1.19 paf 363: strm[length]=0;
364: str=strm;
1.1 parser 365: } else
1.13 paf 366: str=0;
1.1 parser 367: if(lo_close(conn, fd)<0)
368: PQclear_throwPQerror;
369: } else
370: PQclear_throwPQerror;
371: } else {
372: // normal column, read it normally
1.18 paf 373: length=(size_t)PQgetlength(res, r, i);
374: if(length) {
1.19 paf 375: char* strm=(char*)services.malloc(length+1);
376: memcpy(strm, cell, length+1);
377: str=strm;
1.1 parser 378: } else
1.13 paf 379: str=0;
1.1 parser 380: }
1.18 paf 381:
382: if(str && length) {
383: // transcode to $request:charset from connect-string?client_charset
1.19 paf 384: if(cstrClientCharset)
1.18 paf 385: services.transcode(str, length,
386: str, length,
387: cstrClientCharset,
388: services.request_charset());
389: }
390:
391: CHECK(handlers.add_row_cell(sql_error, str, length));
1.1 parser 392: }
393: }
1.9 paf 394: cleanup:
1.1 parser 395: PQclear(res);
1.9 paf 396: if(failed)
397: services._throw(sql_error);
1.1 parser 398: }
399:
400: private: // private funcs
401:
1.26 ! misha 402: void execute_transaction_cmd(void *aconnection, const char *query) {
! 403: if(isDefaultTransaction)
! 404: {
! 405: Connection& connection=*static_cast<Connection*>(aconnection);
! 406: execute_resultless(connection, query);
! 407: begin_transaction(connection);
! 408: }
! 409: }
! 410:
! 411: /**
! 412: Executes a query and throws the result.
! 413: */
! 414: void execute_resultless(const Connection& connection, const char *query) {
! 415: if(PGresult *res=PQexec(connection.conn, query))
! 416: PQclear(res); // throw out the result [don't need but must call]
! 417: else
! 418: throwPQerror;
! 419: }
! 420:
1.16 paf 421: void begin_transaction(Connection& connection) {
1.23 paf 422: if(isDefaultTransaction)
423: {
1.26 ! misha 424: execute_resultless(connection, "BEGIN");
1.23 paf 425: }
1.1 parser 426: }
427:
1.16 paf 428: const char *preprocess_statement(Connection& connection,
1.1 parser 429: const char *astatement, unsigned long offset, unsigned long limit) {
1.16 paf 430: PGconn *conn=connection.conn;
431:
1.1 parser 432: size_t statement_size=strlen(astatement);
433:
1.16 paf 434: char *result=(char *)connection.services->malloc(statement_size
1.1 parser 435: +MAX_NUMBER*2+15 // limit # offset #
436: +MAX_STRING // in case of short 'strings'
437: +1);
438: // offset & limit -> suffixes
439: const char *o;
440: if(offset || limit) {
441: char *cur=result;
442: memcpy(cur, astatement, statement_size); cur+=statement_size;
443: if(limit)
444: cur+=snprintf(cur, 7+MAX_NUMBER, " limit %u", limit);
445: if(offset)
446: cur+=snprintf(cur, 8+MAX_NUMBER, " offset %u", offset);
447: o=result;
448: } else
449: o=astatement;
450:
451: // /**xxx**/'literal' -> oid
452: char *n=result;
453: while(*o) {
454: if(
455: o[0]=='/' &&
456: o[1]=='*' &&
457: o[2]=='*') { // name start
1.15 paf 458: const char* saved_o=o;
1.1 parser 459: o+=3;
460: while(*o)
461: if(
462: o[0]=='*' &&
463: o[1]=='*' &&
464: o[2]=='/' &&
465: o[3]=='\'') { // name end
1.15 paf 466: saved_o=0; // found, marking that
1.1 parser 467: o+=4;
468: Oid oid=lo_creat(conn, INV_READ|INV_WRITE);
469: if(oid==InvalidOid)
470: throwPQerror;
471: int fd=lo_open(conn, oid, INV_WRITE);
472: if(fd>=0) {
473: const char *start=o;
474: bool escaped=false;
475: while(*o && !(o[0]=='\'' && o[1]!='\'' && !escaped)) {
476: escaped=*o=='\\' || (o[0]=='\'' && o[1]=='\'');
477: if(escaped) {
478: // write pending, skip "\" or "'"
479: if(!lo_write_ex(conn, fd, start, o-start))
1.16 paf 480: connection.services->_throw("lo_write could not write all bytes of object (1)");
1.1 parser 481: start=++o;
482: } else
483: o++;
484: }
485: if(!lo_write_ex(conn, fd, start, o-start))
1.16 paf 486: connection.services->_throw("lo_write can not write all bytes of object (2)");
1.1 parser 487: if(lo_close(conn, fd)<0)
488: throwPQerror;
489: } else
490: throwPQerror;
491: if(*o)
492: o++; // skip "'"
493:
494: n+=snprintf(n, MAX_NUMBER, "%u", oid);
495: break;
496: } else
497: o++; // /**skip**/'xxx'
1.15 paf 498: if(saved_o) {
499: o=saved_o;
500: *n++=*o++;
501: }
1.1 parser 502: } else
503: *n++=*o++;
504: }
505: *n=0;
506:
507: return result;
508: }
509:
510: private: // lo_read/write exchancements
511:
512: bool lo_read_ex(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len) {
1.26 ! misha 513: return lo_rw_method (conn, fd, buf, len, lo_read);
1.1 parser 514: }
515:
516: bool lo_write_ex(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len) {
1.26 ! misha 517: return lo_rw_method (conn, fd, buf, len, lo_write);
! 518: }
! 519:
! 520: bool lo_rw_method(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len, int (*lo_func)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len)) {
! 521: int size_op;
! 522: while(len && (size_op=lo_func(conn, fd, buf, min(LO_BUFSIZE, len)))>0) {
! 523: buf+=size_op;
! 524: len-=size_op;
1.1 parser 525: }
526: return len==0;
527: }
528:
529: private: // conn client library funcs
530:
531: typedef PGconn* (*t_PQsetdbLogin)(
532: const char *pghost,
533: const char *pgport,
534: const char *pgoptions,
535: const char *pgtty,
536: const char *dbName,
537: const char *login,
538: const char *pwd); t_PQsetdbLogin PQsetdbLogin;
539: typedef void (*t_PQfinish)(PGconn *conn); t_PQfinish PQfinish;
540: typedef char *(*t_PQerrorMessage)(const PGconn* conn); t_PQerrorMessage PQerrorMessage;
541: typedef ConnStatusType (*t_PQstatus)(const PGconn *conn); t_PQstatus PQstatus;
542: typedef PGresult *(*t_PQexec)(PGconn *conn,
543: const char *query); t_PQexec PQexec;
1.26 ! misha 544: //PQexecParams
! 545: typedef PGresult *(*t_PQexecParams)(
! 546: PGconn *conn,
! 547: const char *query,
! 548: int nParams,
! 549: const Oid *paramTypes,
! 550: const char * const *paramValues,
! 551: const int *paramLengths,
! 552: const int *paramFormats,
! 553: int resultFormat); t_PQexecParams PQexecParams;
! 554:
1.1 parser 555: typedef ExecStatusType (*t_PQresultStatus)(const PGresult *res); t_PQresultStatus PQresultStatus;
556: typedef int (*t_PQgetlength)(const PGresult *res,
557: int tup_num,
558: int field_num); t_PQgetlength PQgetlength;
559: typedef char* (*t_PQgetvalue)(const PGresult *res,
560: int tup_num,
561: int field_num); t_PQgetvalue PQgetvalue;
562: typedef int (*t_PQntuples)(const PGresult *res); t_PQntuples PQntuples;
563: typedef char *(*t_PQfname)(const PGresult *res,
564: int field_index); t_PQfname PQfname;
565: typedef int (*t_PQnfields)(const PGresult *res); t_PQnfields PQnfields;
566: typedef void (*t_PQclear)(PGresult *res); t_PQclear PQclear;
567:
568: typedef Oid (*t_PQftype)(const PGresult *res, int field_num); t_PQftype PQftype;
569:
1.26 ! misha 570: typedef size_t (*t_PQescapeStringConn)(PGconn *conn,
! 571: char *to, const char *from, size_t length,
! 572: int *error); t_PQescapeStringConn PQescapeStringConn;
! 573:
1.1 parser 574: typedef int (*t_lo_open)(PGconn *conn, Oid lobjId, int mode); t_lo_open lo_open;
575: typedef int (*t_lo_close)(PGconn *conn, int fd); t_lo_close lo_close;
576: typedef int (*t_lo_read)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len); t_lo_read lo_read;
577: typedef int (*t_lo_write)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len); t_lo_write lo_write;
578: typedef int (*t_lo_lseek)(PGconn *conn, int fd, int offset, int whence); t_lo_lseek lo_lseek;
579: typedef Oid (*t_lo_creat)(PGconn *conn, int mode); t_lo_creat lo_creat;
580: typedef int (*t_lo_tell)(PGconn *conn, int fd); t_lo_tell lo_tell;
581: typedef int (*t_lo_unlink)(PGconn *conn, Oid lobjId); t_lo_unlink lo_unlink;
582: typedef Oid (*t_lo_import)(PGconn *conn, const char *filename); t_lo_import lo_import;
583: typedef int (*t_lo_export)(PGconn *conn, Oid lobjId, const char *filename); t_lo_export lo_export;
584:
585: private: // conn client library funcs linking
586:
587: const char *dlink(const char *dlopen_file_spec) {
1.11 paf 588: if(lt_dlinit())
589: return lt_dlerror();
1.1 parser 590: lt_dlhandle handle=lt_dlopen(dlopen_file_spec);
591: if(!handle)
592: return "can not open the dynamic link module";
593:
594: #define DSLINK(name, action) \
595: name=(t_##name)lt_dlsym(handle, #name); \
596: if(!name) \
597: action;
598:
599: #define DLINK(name) DSLINK(name, return "function " #name " was not found")
600:
601: DLINK(PQsetdbLogin);
602: DLINK(PQerrorMessage);
603: DLINK(PQstatus);
604: DLINK(PQfinish);
605: DLINK(PQgetvalue);
606: DLINK(PQgetlength);
607: DLINK(PQntuples);
608: DLINK(PQfname);
609: DLINK(PQnfields);
610: DLINK(PQclear);
611: DLINK(PQresultStatus);
612: DLINK(PQexec);
1.26 ! misha 613: DLINK(PQexecParams);
1.1 parser 614: DLINK(PQftype);
1.26 ! misha 615: DLINK(PQescapeStringConn);
1.1 parser 616: DLINK(lo_open); DLINK(lo_close);
617: DLINK(lo_read); DLINK(lo_write);
618: DLINK(lo_lseek); DLINK(lo_creat);
619: DLINK(lo_tell); DLINK(lo_unlink);
620: DLINK(lo_import); DLINK(lo_export);
621:
622: return 0;
623: }
624:
1.23 paf 625: bool isDefaultTransaction;
1.1 parser 626: };
627:
628: extern "C" SQL_Driver *SQL_DRIVER_CREATE() {
629: return new PgSQL_Driver();
1.2 paf 630: }