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