|
|
1.34 misha 1: /** @file
2: Parser PgSQL driver.
3:
1.46 moko 4: Copyright (c) 2001-2019 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.47 ! moko 18: volatile const char * IDENT_PARSER3PGSQL_C="$Id: parser3pgsql.C,v 1.46 2019/11/30 22:11:11 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;
1.42 misha 102: bool standard_conforming_strings;
1.34 misha 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]
1.45 moko 136: autocommit=0& // 1 -- each statement is commited automatically, only when with_default_transaction enabled
137: standard_conforming_strings=1& // 0 -- escape \ char that could be needed for old servers
1.34 misha 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));
1.45 moko 156:
1.34 misha 157: *connection_ref=&connection;
158: connection.services=&services;
1.45 moko 159: connection.client_charset=0;
1.47 ! moko 160: connection.autocommit=true;
1.42 misha 161: connection.standard_conforming_strings=true;
1.34 misha 162:
163: connection.conn=PQsetdbLogin(
164: (host&&strcasecmp(host, "local")==0)?NULL/* local Unix domain socket */:host, port,
165: NULL, NULL, db, user, pwd);
166:
167: if(!connection.conn)
168: services._throw("PQsetdbLogin failed");
169:
170: if(PQstatus(connection.conn)!=CONNECTION_OK)
171: throwPQerror;
172:
173: while(options){
174: if(char *key=lsplit(&options, '&')){
175: if(*key){
176: if(char *value=lsplit(key, '=')){
177: if(strcmp(key, "ClientCharset")==0){
178: toupper_str(value, value, strlen(value));
179: connection.client_charset=value;
180: } else if(strcasecmp(key, "charset")==0){
181: charset=value;
182: } else if(strcasecmp(key, "datestyle")==0){
183: datestyle=value;
184: } else if(strcasecmp(key, "autocommit")==0){
1.45 moko 185: if(atoi(value)==0)
1.47 ! moko 186: connection.autocommit=false;
1.42 misha 187: } else if(strcasecmp(key, "standard_conforming_strings")==0){
188: if(atoi(value)==0)
189: connection.standard_conforming_strings=false;
1.34 misha 190: } else
191: services._throw("unknown connect option" /*key*/);
192: } else
193: services._throw("connect option without =value" /*key*/);
194: }
195: }
196: }
197:
198: if(charset){
1.39 moko 199: char statement[MAX_STRING+1]="SET CLIENT_ENCODING=";
1.34 misha 200: strncat(statement, charset, MAX_STRING);
201:
202: _execute_cmd(connection, statement);
203: }
204:
205: if(datestyle){
1.40 moko 206: char statement[MAX_STRING+1]="SET DATESTYLE=";
1.34 misha 207: strncat(statement, datestyle, MAX_STRING);
208:
209: _execute_cmd(connection, statement);
210: }
211:
1.47 ! moko 212: if(!connection.autocommit)
! 213: _execute_cmd(connection, "set AUTOCOMMIT off");
1.34 misha 214: }
215:
216: void disconnect(void *aconnection){
217: Connection& connection=*static_cast<Connection*>(aconnection);
218: PQfinish(connection.conn);
219: connection.conn=0;
220: }
221:
222: void commit(void *aconnection){
223: Connection& connection=*static_cast<Connection*>(aconnection);
1.47 ! moko 224: if(!connection.autocommit)
! 225: _execute_cmd(connection, "COMMIT");
1.34 misha 226: }
227:
228: void rollback(void *aconnection){
229: Connection& connection=*static_cast<Connection*>(aconnection);
1.47 ! moko 230: if(!connection.autocommit)
! 231: _execute_cmd(connection, "ROLLBACK");
1.34 misha 232: }
233:
234: bool ping(void *aconnection) {
235: Connection& connection=*static_cast<Connection*>(aconnection);
236: return PQstatus(connection.conn)==CONNECTION_OK;
237: }
238:
1.35 moko 239: // charset here is services.request_charset(), not connection.client_charset
240: // thus we can't use the sql server quoting support
241: const char* quote(void *aconnection, const char *str, unsigned int length)
242: {
1.42 misha 243: Connection& connection=*static_cast<Connection*>(aconnection);
244:
1.35 moko 245: const char* from;
246: const char* from_end=str+length;
247:
248: size_t quoted=0;
249:
1.42 misha 250: if(connection.standard_conforming_strings){
251: for(from=str; from<from_end; from++){
252: if(*from=='\'')
253: quoted++;
254: }
255: } else {
256: for(from=str; from<from_end; from++){
257: switch (*from) {
258: case '\'':
259: case '\\':
260: quoted++;
261: }
1.35 moko 262: }
263: }
264:
265: if(!quoted)
266: return str;
267:
268: char *result=(char*)connection.services->malloc_atomic(length + quoted + 1);
269: char *to = result;
1.34 misha 270:
1.42 misha 271: if(connection.standard_conforming_strings){
272: for(from=str; from<from_end; from++){
273: if(*from=='\'')
274: *to++= '\''; // "'" -> "''"
275: *to++=*from;
276: }
277: } else {
278: for(from=str; from<from_end; from++){
279: switch (*from) {
280: case '\'': // "'" -> "''"
281: *to++= '\'';
282: break;
283: case '\\': // "\" -> "\\"
284: *to++='\\';
285: break;
286: }
287: *to++=*from;
1.35 moko 288: }
289: }
290:
291: *to=0;
1.34 misha 292: return result;
293: }
1.35 moko 294:
1.34 misha 295: void query(void *aconnection,
296: const char *astatement,
297: size_t placeholders_count, Placeholder* placeholders,
298: unsigned long offset, unsigned long limit,
299: SQL_Driver_query_event_handlers& handlers
300: ){
301: Connection& connection=*static_cast<Connection*>(aconnection);
302: SQL_Driver_services& services=*connection.services;
303: PGconn *conn=connection.conn;
304:
305: const char* client_charset=connection.client_charset;
306: const char* request_charset=services.request_charset();
307: bool transcode_needed=client_charset && strcmp(client_charset, request_charset)!=0;
308:
309: const char** paramValues;
310: if(placeholders_count>0){
311: int binds_size=sizeof(char)*placeholders_count;
312: paramValues = static_cast<const char**>(services.malloc_atomic(binds_size));
313: _bind_parameters(placeholders_count, placeholders, paramValues, connection, transcode_needed);
314: }
315:
1.36 misha 316: size_t statement_size=0;
1.34 misha 317: if(transcode_needed){
318: // transcode query from $request:charset to ?ClientCharset
1.36 misha 319: statement_size=strlen(astatement);
320: services.transcode(astatement, statement_size,
321: astatement, statement_size,
1.34 misha 322: request_charset,
323: client_charset);
324: }
325:
1.36 misha 326: const char *statement=_preprocess_statement(connection, astatement, statement_size, offset, limit);
1.34 misha 327: // error after prepare?
328:
329: PGresult *res;
330: if(placeholders_count>0){
331: res=PQexecParams(conn, statement, placeholders_count, NULL, paramValues, NULL, NULL, 0);
332: } else {
333: res=PQexec(conn, statement);
334: }
335: if(!res)
336: throwPQerror;
337:
338: bool failed=false;
339: SQL_Error sql_error;
340:
341: switch(PQresultStatus(res)) {
342: case PGRES_EMPTY_QUERY:
343: PQclear_throw("no query");
344: break;
345: case PGRES_COMMAND_OK: // empty result: insert|delete|update|...
346: PQclear(res);
347: return;
348: case PGRES_TUPLES_OK:
1.47 ! moko 349: break;
1.34 misha 350: default:
351: PQclear_throwPQerror;
352: break;
353: }
354:
355: #define CHECK(afailed) \
356: if(afailed) { \
357: failed=true; \
358: goto cleanup; \
359: }
360:
1.36 misha 361: size_t column_count=PQnfields(res);
1.34 misha 362: if(!column_count)
363: PQclear_throw("result contains no columns");
364:
365: if(column_count>MAX_COLS)
366: column_count=MAX_COLS;
367:
368: unsigned int column_types[MAX_COLS];
369:
1.36 misha 370: for(size_t i=0; i<column_count; i++){
1.34 misha 371: column_types[i]=PQftype(res, i);
1.36 misha 372:
1.34 misha 373: char *name=PQfname(res, i);
374: size_t length=strlen(name);
1.36 misha 375: const char* str=strdup(services, name, length);
1.34 misha 376:
1.36 misha 377: if(transcode_needed)
378: // transcode column name from ?ClientCharset to $request:charset
1.34 misha 379: services.transcode(str, length,
380: str, length,
381: client_charset,
382: request_charset);
383:
384: CHECK(handlers.add_column(sql_error, str, length));
385: }
386:
387: CHECK(handlers.before_rows(sql_error));
388:
389: if(unsigned long row_count=(unsigned long)PQntuples(res))
390: for(unsigned long r=0; r<row_count; r++) {
391: CHECK(handlers.add_row(sql_error));
1.36 misha 392: for(size_t i=0; i<column_count; i++){
393: char *cell=PQgetvalue(res, r, i);
394:
395: size_t length=0;
1.34 misha 396: const char* str;
397:
398: switch(column_types[i]){
1.36 misha 399: case BOOLOID:
400: case INT8OID:
401: case INT2OID:
402: case INT4OID:
403: case FLOAT4OID:
404: case FLOAT8OID:
405: case DATEOID:
406: case TIMEOID:
407: case TIMESTAMPOID:
408: case TIMESTAMPTZOID:
409: case TIMETZOID:
410: case NUMERICOID:
411: length=(size_t)PQgetlength(res, r, i);
412: str=length ? strdup(services, cell, length) : 0;
413: // transcode is never required for these types
414: break;
1.34 misha 415: case OIDOID:
416: {
417: Oid oid=cell?atoi(cell):0;
418: int fd=lo_open(conn, oid, INV_READ);
419: if(fd>=0){
420: // seek to end
421: if(lo_lseek(conn, fd, 0, SEEK_END)<0)
422: PQclear_throwPQerror;
423: // get length
424: int size_tell=lo_tell(conn, fd);
425: if(size_tell<0)
426: PQclear_throwPQerror;
427: // seek to begin
428: if(lo_lseek(conn, fd, 0, SEEK_SET)<0)
429: PQclear_throwPQerror;
430: length=(size_t)size_tell;
431: if(length){
432: // read
433: char* strm=(char*)services.malloc(length+1);
434: if(!lo_read_ex(conn, fd, strm, size_tell))
435: PQclear_throw("lo_read can not read all bytes of object");
436: strm[length]=0;
437: str=strm;
1.36 misha 438: if(transcode_needed) {
439: // transcode cell value from ?ClientCharset to $request:charset
440: services.transcode(str, length,
441: str, length,
442: client_charset,
443: request_charset);
444: }
1.34 misha 445: } else
446: str=0;
447: if(lo_close(conn, fd)<0)
448: PQclear_throwPQerror;
449: } else
450: PQclear_throwPQerror;
1.36 misha 451: break;
1.34 misha 452: }
453: default:
454: // normal column, read it normally
455: length=(size_t)PQgetlength(res, r, i);
1.36 misha 456: str=length ? strdup(services, cell, length) : 0;
457: if(transcode_needed) {
458: // transcode cell value from ?ClientCharset to $request:charset
459: services.transcode(str, length,
460: str, length,
461: client_charset,
462: request_charset);
463: }
464: break;
1.34 misha 465: }
466: CHECK(handlers.add_row_cell(sql_error, str, length));
467: }
468: }
469: cleanup:
470: PQclear(res);
471: if(failed)
472: services._throw(sql_error);
473: }
474:
475: private:
476: void _bind_parameters(
477: size_t placeholders_count,
478: Placeholder* placeholders,
479: const char** paramValues,
480: Connection& connection,
481: bool transcode_needed
482: ){
483: for(size_t i=0; i<placeholders_count; i++){
484: Placeholder& ph=placeholders[i];
485: if(transcode_needed){
486: size_t name_length;
487: connection.services->transcode(ph.name, strlen(ph.name),
488: ph.name, name_length,
489: connection.services->request_charset(),
490: connection.client_charset);
491:
492: if(ph.value) {
493: size_t value_length;
494: connection.services->transcode(ph.value, strlen(ph.value),
495: ph.value, value_length,
496: connection.services->request_charset(),
497: connection.client_charset);
498: }
499: }
1.36 misha 500: int name_number=atoi(ph.name);
501: if(name_number <= 0 || (size_t)name_number > placeholders_count)
1.34 misha 502: connection.services->_throw("bad bind parameter key");
503:
1.36 misha 504: paramValues[name_number-1]=ph.value;
1.34 misha 505: }
506: }
507:
508: // executes a query and throw away the result.
509: void _execute_cmd(const Connection& connection, const char *query){
510: if(PGresult *res=PQexec(connection.conn, query))
511: PQclear(res); // throw away the result [don't need but must call]
512: else
513: throwPQerror;
514: }
515:
516: const char *_preprocess_statement(
517: Connection& connection,
518: const char *astatement,
1.36 misha 519: size_t statement_size,
1.34 misha 520: unsigned long offset,
521: unsigned long limit
522: ){
523: PGconn *conn=connection.conn;
524:
1.36 misha 525: if(!statement_size)
526: statement_size=strlen(astatement);
1.34 misha 527:
528: char *result=(char *)connection.services->malloc(statement_size
1.36 misha 529: +MAX_NUMBER*2+15 // " limit # offset #"
1.34 misha 530: +MAX_STRING // in case of short 'strings'
531: +1);
532: // offset & limit -> suffixes
533: const char *o;
534: if(offset || limit!=SQL_NO_LIMIT){
535: char *cur=result;
536: memcpy(cur, astatement, statement_size); cur+=statement_size;
537: if(limit!=SQL_NO_LIMIT)
1.39 moko 538: cur+=snprintf(cur, 7+MAX_NUMBER, " limit %lu", limit);
1.34 misha 539: if(offset)
1.39 moko 540: cur+=snprintf(cur, 8+MAX_NUMBER, " offset %lu", offset);
1.34 misha 541: o=result;
542: } else
543: o=astatement;
544:
545: // /**xxx**/'literal' -> oid
546: char *n=result;
547: while(*o) {
548: if(
549: o[0]=='/' &&
1.36 misha 550: o[1]=='*' &&
1.34 misha 551: o[2]=='*') { // name start
552: const char* saved_o=o;
553: o+=3;
554: while(*o)
555: if(
556: o[0]=='*' &&
557: o[1]=='*' &&
558: o[2]=='/' &&
559: o[3]=='\'') { // name end
560: saved_o=0; // found, marking that
561: o+=4;
562: Oid oid=lo_creat(conn, INV_READ|INV_WRITE);
563: if(oid==InvalidOid)
564: throwPQerror;
565: int fd=lo_open(conn, oid, INV_WRITE);
566: if(fd>=0) {
567: const char *start=o;
568: bool escaped=false;
569: while(*o && !(o[0]=='\'' && o[1]!='\'' && !escaped)) {
570: escaped=*o=='\\' || (o[0]=='\'' && o[1]=='\'');
571: if(escaped) {
572: // write pending, skip "\" or "'"
573: if(!lo_write_ex(conn, fd, start, o-start))
574: connection.services->_throw("lo_write could not write all bytes of object (1)");
575: start=++o;
576: } else
577: o++;
578: }
579: if(!lo_write_ex(conn, fd, start, o-start))
580: connection.services->_throw("lo_write can not write all bytes of object (2)");
581: if(lo_close(conn, fd)<0)
582: throwPQerror;
583: } else
584: throwPQerror;
585: if(*o)
586: o++; // skip "'"
587:
588: n+=snprintf(n, MAX_NUMBER, "%u", oid);
589: break;
590: } else
591: o++; // /**skip**/'xxx'
592: if(saved_o) {
593: o=saved_o;
594: *n++=*o++;
595: }
596: } else
597: *n++=*o++;
598: }
599: *n=0;
600:
601: return result;
602: }
603:
604: private: // lo_read/write exchancements
605:
606: bool lo_read_ex(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len) {
607: return lo_rw_method (conn, fd, buf, len, lo_read);
608: }
609:
610: bool lo_write_ex(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len) {
611: return lo_rw_method (conn, fd, buf, len, lo_write);
612: }
613:
614: 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)) {
615: int size_op;
616: while(len && (size_op=lo_func(conn, fd, buf, min(LO_BUFSIZE, len)))>0) {
617: buf+=size_op;
618: len-=size_op;
619: }
620: return len==0;
621: }
622:
623: private: // conn client library funcs
624:
625: typedef PGconn* (*t_PQsetdbLogin)(
626: const char *pghost,
627: const char *pgport,
628: const char *pgoptions,
629: const char *pgtty,
630: const char *dbName,
631: const char *login,
632: const char *pwd); t_PQsetdbLogin PQsetdbLogin;
633: typedef void (*t_PQfinish)(PGconn *conn); t_PQfinish PQfinish;
634: typedef char *(*t_PQerrorMessage)(const PGconn* conn); t_PQerrorMessage PQerrorMessage;
635: typedef ConnStatusType (*t_PQstatus)(const PGconn *conn); t_PQstatus PQstatus;
636: typedef PGresult *(*t_PQexec)(PGconn *conn,
637: const char *query); t_PQexec PQexec;
638: typedef PGresult *(*t_PQexecParams)(
639: PGconn *conn,
640: const char *query,
641: int nParams,
642: const Oid *paramTypes,
643: const char * const *paramValues,
644: const int *paramLengths,
645: const int *paramFormats,
646: int resultFormat); t_PQexecParams PQexecParams;
647:
648: typedef ExecStatusType (*t_PQresultStatus)(const PGresult *res); t_PQresultStatus PQresultStatus;
649: typedef int (*t_PQgetlength)(const PGresult *res,
650: int tup_num,
651: int field_num); t_PQgetlength PQgetlength;
652: typedef char* (*t_PQgetvalue)(const PGresult *res,
653: int tup_num,
654: int field_num); t_PQgetvalue PQgetvalue;
655: typedef int (*t_PQntuples)(const PGresult *res); t_PQntuples PQntuples;
656: typedef char *(*t_PQfname)(const PGresult *res,
657: int field_index); t_PQfname PQfname;
658: typedef int (*t_PQnfields)(const PGresult *res); t_PQnfields PQnfields;
659: typedef void (*t_PQclear)(PGresult *res); t_PQclear PQclear;
660:
661: typedef Oid (*t_PQftype)(const PGresult *res, int field_num); t_PQftype PQftype;
662:
663: typedef size_t (*t_PQescapeStringConn)(PGconn *conn,
664: char *to, const char *from, size_t length,
665: int *error); t_PQescapeStringConn PQescapeStringConn;
666:
667: typedef int (*t_lo_open)(PGconn *conn, Oid lobjId, int mode); t_lo_open lo_open;
668: typedef int (*t_lo_close)(PGconn *conn, int fd); t_lo_close lo_close;
669: typedef int (*t_lo_read)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len); t_lo_read lo_read;
670: typedef int (*t_lo_write)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len); t_lo_write lo_write;
671: typedef int (*t_lo_lseek)(PGconn *conn, int fd, int offset, int whence); t_lo_lseek lo_lseek;
672: typedef Oid (*t_lo_creat)(PGconn *conn, int mode); t_lo_creat lo_creat;
673: typedef int (*t_lo_tell)(PGconn *conn, int fd); t_lo_tell lo_tell;
674: typedef int (*t_lo_unlink)(PGconn *conn, Oid lobjId); t_lo_unlink lo_unlink;
675: typedef Oid (*t_lo_import)(PGconn *conn, const char *filename); t_lo_import lo_import;
676: typedef int (*t_lo_export)(PGconn *conn, Oid lobjId, const char *filename); t_lo_export lo_export;
677:
678: private: // conn client library funcs linking
679:
680: const char *dlink(const char *dlopen_file_spec) {
1.41 moko 681: if(lt_dlinit()){
682: if(const char* result=lt_dlerror())
683: return result;
684: return "can not prepare to dynamic loading";
685: }
686:
1.34 misha 687: lt_dlhandle handle=lt_dlopen(dlopen_file_spec);
1.41 moko 688:
689: if(!handle){
690: if(const char* result=lt_dlerror())
691: return result;
1.34 misha 692: return "can not open the dynamic link module";
1.41 moko 693: }
1.34 misha 694:
695: #define DSLINK(name, action) \
696: name=(t_##name)lt_dlsym(handle, #name); \
697: if(!name) \
698: action;
699:
700: #define DLINK(name) DSLINK(name, return "function " #name " was not found")
701:
702: DLINK(PQsetdbLogin);
703: DLINK(PQerrorMessage);
704: DLINK(PQstatus);
705: DLINK(PQfinish);
706: DLINK(PQgetvalue);
707: DLINK(PQgetlength);
708: DLINK(PQntuples);
709: DLINK(PQfname);
710: DLINK(PQnfields);
711: DLINK(PQclear);
712: DLINK(PQresultStatus);
713: DLINK(PQexec);
714: DLINK(PQexecParams);
715: DLINK(PQftype);
716: DLINK(PQescapeStringConn);
717: DLINK(lo_open); DLINK(lo_close);
718: DLINK(lo_read); DLINK(lo_write);
719: DLINK(lo_lseek); DLINK(lo_creat);
720: DLINK(lo_tell); DLINK(lo_unlink);
721: DLINK(lo_import); DLINK(lo_export);
722:
723: return 0;
724: }
725: };
726:
727: extern "C" SQL_Driver *SQL_DRIVER_CREATE() {
728: return new PgSQL_Driver();
729: }