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