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