|
|
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:
1.28 ! misha 8: 2007.10.25 using PgSQL 8.1.5
1.1 parser 9: */
1.28 ! misha 10: static const char *RCSId="$Id: parser3pgsql.C,v 1.27 2007/01/29 10:22:45 misha 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.28 ! misha 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");
1.18 paf 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));
1.27 misha 240: bind_parameters(placeholders_count, placeholders, paramValues, connection);
1.26 misha 241: }
1.1 parser 242:
1.18 paf 243: // transcode from $request:charset to connect-string?client_charset
1.19 paf 244: if(cstrClientCharset) {
1.18 paf 245: size_t transcoded_statement_size;
246: services.transcode(astatement, strlen(astatement),
247: astatement, transcoded_statement_size,
248: services.request_charset(),
249: cstrClientCharset);
250: }
251:
1.16 paf 252: const char *statement=preprocess_statement(connection,
1.1 parser 253: astatement, offset, limit);
254:
1.26 misha 255: PGresult *res;
256: if(placeholders_count>0){
257: res=PQexecParams(conn, statement, placeholders_count, NULL, paramValues, NULL, NULL, 0);
258: } else {
259: res=PQexec(conn, statement);
260: }
1.1 parser 261: if(!res)
262: throwPQerror;
263:
264: switch(PQresultStatus(res)) {
265: case PGRES_EMPTY_QUERY:
266: PQclear_throw("no query");
267: break;
268: case PGRES_COMMAND_OK:
269: // empty result: insert|delete|update|...
270: PQclear(res);
271: return;
272: case PGRES_TUPLES_OK:
273: break;
274: default:
275: PQclear_throwPQerror;
276: break;
277: }
278:
279: int column_count=PQnfields(res);
280: if(!column_count)
281: PQclear_throw("result contains no columns");
282:
1.9 paf 283: bool failed=false;
284: SQL_Error sql_error;
285: #define CHECK(afailed) \
286: if(afailed) { \
287: failed=true; \
288: goto cleanup; \
289: }
290:
1.1 parser 291: for(int i=0; i<column_count; i++){
292: char *name=PQfname(res, i);
1.18 paf 293: size_t length=strlen(name);
1.19 paf 294: char* strm=(char*)services.malloc(length+1);
295: memcpy(strm, name, length+1);
296: const char* str=strm;
1.18 paf 297:
298: // transcode to $request:charset from connect-string?client_charset
1.19 paf 299: if(cstrClientCharset)
1.18 paf 300: services.transcode(str, length,
301: str, length,
302: cstrClientCharset,
303: services.request_charset());
304:
305: CHECK(handlers.add_column(sql_error, str, length));
1.1 parser 306: }
307:
1.9 paf 308: CHECK(handlers.before_rows(sql_error));
1.1 parser 309:
310: if(unsigned long row_count=(unsigned long)PQntuples(res))
311: for(unsigned long r=0; r<row_count; r++) {
1.9 paf 312: CHECK(handlers.add_row(sql_error));
1.1 parser 313: for(int i=0; i<column_count; i++){
314: const char *cell=PQgetvalue(res, r, i);
1.18 paf 315: size_t length;
1.19 paf 316: const char* str;
1.1 parser 317: if(PQftype(res, i)==OIDOID) {
318: // ObjectID column, read object bytes
319:
320: char *error_pos=0;
321: Oid oid=cell?atoi(cell):0;
322: int fd=lo_open(conn, oid, INV_READ);
323: if(fd>=0) {
324: // seek to end
325: if(lo_lseek(conn, fd, 0, SEEK_END)<0)
326: PQclear_throwPQerror;
1.18 paf 327: // get length
1.1 parser 328: int size_tell=lo_tell(conn, fd);
329: if(size_tell<0)
330: PQclear_throwPQerror;
331: // seek to begin
332: if(lo_lseek(conn, fd, 0, SEEK_SET)<0)
333: PQclear_throwPQerror;
1.18 paf 334: length=(size_t)size_tell;
335: if(length) {
1.1 parser 336: // read
1.19 paf 337: char* strm=(char*)services.malloc(length+1);
338: if(!lo_read_ex(conn, fd, strm, size_tell))
1.1 parser 339: PQclear_throw("lo_read can not read all bytes of object");
1.19 paf 340: strm[length]=0;
341: str=strm;
1.1 parser 342: } else
1.13 paf 343: str=0;
1.1 parser 344: if(lo_close(conn, fd)<0)
345: PQclear_throwPQerror;
346: } else
347: PQclear_throwPQerror;
348: } else {
349: // normal column, read it normally
1.18 paf 350: length=(size_t)PQgetlength(res, r, i);
351: if(length) {
1.19 paf 352: char* strm=(char*)services.malloc(length+1);
353: memcpy(strm, cell, length+1);
354: str=strm;
1.1 parser 355: } else
1.13 paf 356: str=0;
1.1 parser 357: }
1.18 paf 358:
359: if(str && length) {
360: // transcode to $request:charset from connect-string?client_charset
1.19 paf 361: if(cstrClientCharset)
1.18 paf 362: services.transcode(str, length,
363: str, length,
364: cstrClientCharset,
365: services.request_charset());
366: }
367:
368: CHECK(handlers.add_row_cell(sql_error, str, length));
1.1 parser 369: }
370: }
1.9 paf 371: cleanup:
1.1 parser 372: PQclear(res);
1.9 paf 373: if(failed)
374: services._throw(sql_error);
1.1 parser 375: }
376:
377: private: // private funcs
378:
1.27 misha 379: void bind_parameters(
380: size_t placeholders_count,
381: Placeholder* placeholders,
382: const char** paramValues,
383: Connection& connection
384: ) {
385: for(size_t i=0; i<placeholders_count; i++) {
386: Placeholder& ph=placeholders[i];
387: size_t value_length;
388: if(connection.cstrClientCharset) {
389: size_t name_length;
390: connection.services->transcode(ph.name, strlen(ph.name),
391: ph.name, name_length,
392: connection.services->request_charset(),
393: connection.cstrClientCharset);
394:
395: if(ph.value) {
396: connection.services->transcode(ph.value, strlen(ph.value),
397: ph.value, value_length,
398: connection.services->request_charset(),
399: connection.cstrClientCharset);
400: }
401: }
402: if( atoi(ph.name) <= 0 || atoi(ph.name) > placeholders_count) {
403: connection.services->_throw("bad bind parameter key");
404: }
405: paramValues[atoi(ph.name)-1] = ph.value;
406: }
407: }
408:
409:
1.26 misha 410: void execute_transaction_cmd(void *aconnection, const char *query) {
411: if(isDefaultTransaction)
412: {
413: Connection& connection=*static_cast<Connection*>(aconnection);
414: execute_resultless(connection, query);
415: begin_transaction(connection);
416: }
417: }
418:
419: /**
420: Executes a query and throws the result.
421: */
422: void execute_resultless(const Connection& connection, const char *query) {
423: if(PGresult *res=PQexec(connection.conn, query))
424: PQclear(res); // throw out the result [don't need but must call]
425: else
426: throwPQerror;
427: }
428:
1.16 paf 429: void begin_transaction(Connection& connection) {
1.23 paf 430: if(isDefaultTransaction)
431: {
1.26 misha 432: execute_resultless(connection, "BEGIN");
1.23 paf 433: }
1.1 parser 434: }
435:
1.16 paf 436: const char *preprocess_statement(Connection& connection,
1.1 parser 437: const char *astatement, unsigned long offset, unsigned long limit) {
1.16 paf 438: PGconn *conn=connection.conn;
439:
1.1 parser 440: size_t statement_size=strlen(astatement);
441:
1.16 paf 442: char *result=(char *)connection.services->malloc(statement_size
1.1 parser 443: +MAX_NUMBER*2+15 // limit # offset #
444: +MAX_STRING // in case of short 'strings'
445: +1);
446: // offset & limit -> suffixes
447: const char *o;
448: if(offset || limit) {
449: char *cur=result;
450: memcpy(cur, astatement, statement_size); cur+=statement_size;
451: if(limit)
452: cur+=snprintf(cur, 7+MAX_NUMBER, " limit %u", limit);
453: if(offset)
454: cur+=snprintf(cur, 8+MAX_NUMBER, " offset %u", offset);
455: o=result;
456: } else
457: o=astatement;
458:
459: // /**xxx**/'literal' -> oid
460: char *n=result;
461: while(*o) {
462: if(
463: o[0]=='/' &&
464: o[1]=='*' &&
465: o[2]=='*') { // name start
1.15 paf 466: const char* saved_o=o;
1.1 parser 467: o+=3;
468: while(*o)
469: if(
470: o[0]=='*' &&
471: o[1]=='*' &&
472: o[2]=='/' &&
473: o[3]=='\'') { // name end
1.15 paf 474: saved_o=0; // found, marking that
1.1 parser 475: o+=4;
476: Oid oid=lo_creat(conn, INV_READ|INV_WRITE);
477: if(oid==InvalidOid)
478: throwPQerror;
479: int fd=lo_open(conn, oid, INV_WRITE);
480: if(fd>=0) {
481: const char *start=o;
482: bool escaped=false;
483: while(*o && !(o[0]=='\'' && o[1]!='\'' && !escaped)) {
484: escaped=*o=='\\' || (o[0]=='\'' && o[1]=='\'');
485: if(escaped) {
486: // write pending, skip "\" or "'"
487: if(!lo_write_ex(conn, fd, start, o-start))
1.16 paf 488: connection.services->_throw("lo_write could not write all bytes of object (1)");
1.1 parser 489: start=++o;
490: } else
491: o++;
492: }
493: if(!lo_write_ex(conn, fd, start, o-start))
1.16 paf 494: connection.services->_throw("lo_write can not write all bytes of object (2)");
1.1 parser 495: if(lo_close(conn, fd)<0)
496: throwPQerror;
497: } else
498: throwPQerror;
499: if(*o)
500: o++; // skip "'"
501:
502: n+=snprintf(n, MAX_NUMBER, "%u", oid);
503: break;
504: } else
505: o++; // /**skip**/'xxx'
1.15 paf 506: if(saved_o) {
507: o=saved_o;
508: *n++=*o++;
509: }
1.1 parser 510: } else
511: *n++=*o++;
512: }
513: *n=0;
514:
515: return result;
516: }
517:
518: private: // lo_read/write exchancements
519:
520: bool lo_read_ex(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len) {
1.26 misha 521: return lo_rw_method (conn, fd, buf, len, lo_read);
1.1 parser 522: }
523:
524: bool lo_write_ex(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len) {
1.26 misha 525: return lo_rw_method (conn, fd, buf, len, lo_write);
526: }
527:
528: 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)) {
529: int size_op;
530: while(len && (size_op=lo_func(conn, fd, buf, min(LO_BUFSIZE, len)))>0) {
531: buf+=size_op;
532: len-=size_op;
1.1 parser 533: }
534: return len==0;
535: }
536:
537: private: // conn client library funcs
538:
539: typedef PGconn* (*t_PQsetdbLogin)(
540: const char *pghost,
541: const char *pgport,
542: const char *pgoptions,
543: const char *pgtty,
544: const char *dbName,
545: const char *login,
546: const char *pwd); t_PQsetdbLogin PQsetdbLogin;
547: typedef void (*t_PQfinish)(PGconn *conn); t_PQfinish PQfinish;
548: typedef char *(*t_PQerrorMessage)(const PGconn* conn); t_PQerrorMessage PQerrorMessage;
549: typedef ConnStatusType (*t_PQstatus)(const PGconn *conn); t_PQstatus PQstatus;
550: typedef PGresult *(*t_PQexec)(PGconn *conn,
551: const char *query); t_PQexec PQexec;
1.26 misha 552: //PQexecParams
553: typedef PGresult *(*t_PQexecParams)(
554: PGconn *conn,
555: const char *query,
556: int nParams,
557: const Oid *paramTypes,
558: const char * const *paramValues,
559: const int *paramLengths,
560: const int *paramFormats,
561: int resultFormat); t_PQexecParams PQexecParams;
562:
1.1 parser 563: typedef ExecStatusType (*t_PQresultStatus)(const PGresult *res); t_PQresultStatus PQresultStatus;
564: typedef int (*t_PQgetlength)(const PGresult *res,
565: int tup_num,
566: int field_num); t_PQgetlength PQgetlength;
567: typedef char* (*t_PQgetvalue)(const PGresult *res,
568: int tup_num,
569: int field_num); t_PQgetvalue PQgetvalue;
570: typedef int (*t_PQntuples)(const PGresult *res); t_PQntuples PQntuples;
571: typedef char *(*t_PQfname)(const PGresult *res,
572: int field_index); t_PQfname PQfname;
573: typedef int (*t_PQnfields)(const PGresult *res); t_PQnfields PQnfields;
574: typedef void (*t_PQclear)(PGresult *res); t_PQclear PQclear;
575:
576: typedef Oid (*t_PQftype)(const PGresult *res, int field_num); t_PQftype PQftype;
577:
1.26 misha 578: typedef size_t (*t_PQescapeStringConn)(PGconn *conn,
579: char *to, const char *from, size_t length,
580: int *error); t_PQescapeStringConn PQescapeStringConn;
581:
1.1 parser 582: typedef int (*t_lo_open)(PGconn *conn, Oid lobjId, int mode); t_lo_open lo_open;
583: typedef int (*t_lo_close)(PGconn *conn, int fd); t_lo_close lo_close;
584: typedef int (*t_lo_read)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len); t_lo_read lo_read;
585: typedef int (*t_lo_write)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len); t_lo_write lo_write;
586: typedef int (*t_lo_lseek)(PGconn *conn, int fd, int offset, int whence); t_lo_lseek lo_lseek;
587: typedef Oid (*t_lo_creat)(PGconn *conn, int mode); t_lo_creat lo_creat;
588: typedef int (*t_lo_tell)(PGconn *conn, int fd); t_lo_tell lo_tell;
589: typedef int (*t_lo_unlink)(PGconn *conn, Oid lobjId); t_lo_unlink lo_unlink;
590: typedef Oid (*t_lo_import)(PGconn *conn, const char *filename); t_lo_import lo_import;
591: typedef int (*t_lo_export)(PGconn *conn, Oid lobjId, const char *filename); t_lo_export lo_export;
592:
593: private: // conn client library funcs linking
594:
595: const char *dlink(const char *dlopen_file_spec) {
1.11 paf 596: if(lt_dlinit())
597: return lt_dlerror();
1.1 parser 598: lt_dlhandle handle=lt_dlopen(dlopen_file_spec);
599: if(!handle)
600: return "can not open the dynamic link module";
601:
602: #define DSLINK(name, action) \
603: name=(t_##name)lt_dlsym(handle, #name); \
604: if(!name) \
605: action;
606:
607: #define DLINK(name) DSLINK(name, return "function " #name " was not found")
608:
609: DLINK(PQsetdbLogin);
610: DLINK(PQerrorMessage);
611: DLINK(PQstatus);
612: DLINK(PQfinish);
613: DLINK(PQgetvalue);
614: DLINK(PQgetlength);
615: DLINK(PQntuples);
616: DLINK(PQfname);
617: DLINK(PQnfields);
618: DLINK(PQclear);
619: DLINK(PQresultStatus);
620: DLINK(PQexec);
1.26 misha 621: DLINK(PQexecParams);
1.1 parser 622: DLINK(PQftype);
1.26 misha 623: DLINK(PQescapeStringConn);
1.1 parser 624: DLINK(lo_open); DLINK(lo_close);
625: DLINK(lo_read); DLINK(lo_write);
626: DLINK(lo_lseek); DLINK(lo_creat);
627: DLINK(lo_tell); DLINK(lo_unlink);
628: DLINK(lo_import); DLINK(lo_export);
629:
630: return 0;
631: }
632:
1.23 paf 633: bool isDefaultTransaction;
1.1 parser 634: };
635:
636: extern "C" SQL_Driver *SQL_DRIVER_CREATE() {
637: return new PgSQL_Driver();
1.2 paf 638: }