|
|
1.1 parser 1: /** @file
2: Parser PgSQL driver.
3:
4: Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com)
5:
6: Author: Alexander Petrosyan <paf@design.ru>(http://design.ru/paf)
7:
8: 2001.07.30 using PgSQL 7.1.2
9: */
1.2 ! parser 10: static const char *RCSId="$Id: parser3pgsql.C,v 1.1 2001/07/30 15:32:18 parser 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 "ltdl.h"
18:
19: #define MAX_STRING 0x400
20: #define MAX_NUMBER 20
21:
22: #if _MSC_VER
23: # define snprintf _snprintf
24: # define strcasecmp _stricmp
25: #endif
26:
27: static char *lsplit(char *string, char delim) {
28: if(string) {
29: char *v=strchr(string, delim);
30: if(v) {
31: *v=0;
32: return v+1;
33: }
34: }
35: return 0;
36: }
37:
38: /**
39: PgSQL server driver
40: */
41: class PgSQL_Driver : public SQL_Driver {
42: public:
43:
44: PgSQL_Driver() : SQL_Driver() {
45: }
46:
47: /// get api version
48: int api_version() { return SQL_DRIVER_API_VERSION; }
49: /// initialize driver by loading sql dynamic link library
50: const char *initialize(const char *dlopen_file_spec) {
51: return dlopen_file_spec?
52: dlink(dlopen_file_spec):"client library column is empty";
53: }
54: /** connect
55: @param used_only_in_connect_url
56: format: @b user:pass@host[:port]|[local]/database
57: 3.23.22b
58: Currently the only option for @b character_set_name is cp1251_koi8.
59: WARNING: must be used only to connect, for buffer doesn't live long
60: */
61: void connect(
62: char *used_only_in_connect_url,
63: SQL_Driver_services& services,
64: void **connection ///< output: PGconn *
65: ) {
66: char *user=used_only_in_connect_url;
67: char *host=lsplit(user, '@');
68: char *db=lsplit(host, '/');
69: char *pwd=lsplit(user, ':');
70: char *port=lsplit(host, ':');
71:
72: // _asm int 3;
73: PGconn *pgsql=PQsetdbLogin(
74: strcasecmp(host, "local")==0?NULL/* local Unix domain socket */:host, port,
75: NULL, NULL, db, user, pwd);
76: if(!pgsql)
77: services._throw("PQsetdbLogin failed");
78: if(PQstatus(pgsql)!=CONNECTION_OK)
79: services._throw(PQerrorMessage(pgsql));
80:
81: *(PGconn **)connection=pgsql;
82: }
83: void disconnect(SQL_Driver_services&, void *connection) {
84: PQfinish((PGconn *)connection);
85: }
86: void commit(SQL_Driver_services&, void *) {}
87: void rollback(SQL_Driver_services&, void *) {}
88:
89: bool ping(SQL_Driver_services&, void *connection) {
90: return PQstatus((PGconn *)connection)==CONNECTION_OK;
91: }
92:
93: unsigned int quote(
94: SQL_Driver_services&, void *connection,
95: char *to, const char *from, unsigned int length) {
96: /*
97: 3.23.22b
98: You must allocate the to buffer to be at least length*2+1 bytes long.
99: (In the worse case, each character may need to be encoded as using two bytes,
100: and you need room for the terminating null byte.)
101:
102: it's already UNTAINT_TIMES_BIGGER
103: */
104: // ' -> ''
105: unsigned int result=length;
106: while(length--) {
107: if(*from=='\'')
108: *to++='\'';
109: *to++=*from++;
110: }
111: return result;
112: }
113: void query(
114: SQL_Driver_services& services, void *connection,
115: const char *astatement, unsigned long offset, unsigned long limit,
116: SQL_Driver_query_event_handlers& handlers) {
117:
118: PGconn *pgsql=(PGconn *)connection;
119:
120: const char *statement;
121: if(offset || limit) {
122: size_t statement_size=strlen(astatement);
123: char *statement_limited=(char *)services.malloc(
124: statement_size+MAX_NUMBER*2+15/* limit # offset #*/+1);
125: char *cur=statement_limited;
126: memcpy(cur, astatement, statement_size); cur+=statement_size;
127: if(limit)
128: cur+=snprintf(cur, 7+MAX_NUMBER, " limit %u", limit);
129: if(offset)
130: cur+=snprintf(cur, 8+MAX_NUMBER, " offset %u", offset);
131: statement=statement_limited;
132: } else
133: statement=astatement;
134:
135: PGresult *res=PQexec(pgsql, statement);
136: if(!res)
137: services._throw(PQerrorMessage(pgsql));
138:
139: switch(PQresultStatus(res)) {
140: case PGRES_EMPTY_QUERY:
141: PQclear(res);
142: services._throw("no query");
143: break;
144: case PGRES_COMMAND_OK:
145: // empty result: insert|delete|update|...
146: return;
147: case PGRES_TUPLES_OK:
148: break;
149: default:
150: PQclear(res);
151: services._throw("unknown PQexec error");
152: break;
153: }
154:
155: int column_count=PQnfields(res);
156: if(!column_count) {
157: PQclear(res);
158: services._throw("result contains no columns");
159: }
160:
161: for(int i=0; i<column_count; i++){
162: char *name=PQfname(res, i);
163: size_t size=strlen(name);
164: void *ptr=services.malloc(size);
165: memcpy(ptr, name, size);
166: handlers.add_column(ptr, size);
167: }
168:
169: handlers.before_rows();
170:
171: if(unsigned long row_count=(unsigned long)PQntuples(res))
172: for(unsigned long r=0; r<row_count; r++) {
173: handlers.add_row();
174: for(int i=0; i<column_count; i++){
175: size_t size=(size_t)PQgetlength(res, r, i);
176: void *ptr;
177: if(size) {
178: ptr=services.malloc(size);
179: memcpy(ptr, PQgetvalue(res, r, i), size);
180: } else
181: ptr=0;
182: handlers.add_row_cell(ptr, size);
183: }
184: }
185:
186: PQclear(res);
187: }
188:
189: private: // pgsql client library funcs
190:
191: typedef PGconn* (*t_PQsetdbLogin)(
192: const char *pghost,
193: const char *pgport,
194: const char *pgoptions,
195: const char *pgtty,
196: const char *dbName,
197: const char *login,
198: const char *pwd); t_PQsetdbLogin PQsetdbLogin;
199: typedef void (*t_PQfinish)(PGconn *conn); t_PQfinish PQfinish;
200: typedef char *(*t_PQerrorMessage)(const PGconn* conn); t_PQerrorMessage PQerrorMessage;
201: typedef ConnStatusType (*t_PQstatus)(const PGconn *conn); t_PQstatus PQstatus;
202: typedef PGresult *(*t_PQexec)(PGconn *conn,
203: const char *query); t_PQexec PQexec;
204: typedef ExecStatusType (*t_PQresultStatus)(const PGresult *res); t_PQresultStatus PQresultStatus;
205: typedef int (*t_PQgetlength)(const PGresult *res,
206: int tup_num,
207: int field_num); t_PQgetlength PQgetlength;
208: typedef char* (*t_PQgetvalue)(const PGresult *res,
209: int tup_num,
210: int field_num); t_PQgetvalue PQgetvalue;
211: typedef int (*t_PQntuples)(const PGresult *res); t_PQntuples PQntuples;
212: typedef char *(*t_PQfname)(const PGresult *res,
213: int field_index); t_PQfname PQfname;
214: typedef int (*t_PQnfields)(const PGresult *res); t_PQnfields PQnfields;
215: typedef void (*t_PQclear)(PGresult *res); t_PQclear PQclear;
216:
217:
218: private: // pgsql client library funcs linking
219:
220: const char *dlink(const char *dlopen_file_spec) {
221: lt_dlhandle handle=lt_dlopen(dlopen_file_spec);
222: if(!handle)
223: return "can not open the dynamic link module";
224:
225: #define DSLINK(name, action) \
226: name=(t_##name)lt_dlsym(handle, #name); \
227: if(!name) \
228: action;
229:
230: #define DLINK(name) DSLINK(name, return "function " #name " was not found")
231:
232: DLINK(PQsetdbLogin);
233: DLINK(PQerrorMessage);
234: DLINK(PQstatus);
235: DLINK(PQfinish);
236: DLINK(PQgetvalue);
237: DLINK(PQgetlength);
238: DLINK(PQntuples);
239: DLINK(PQfname);
240: DLINK(PQnfields);
241: DLINK(PQclear);
242: DLINK(PQresultStatus);
243: DLINK(PQexec);
244:
245: return 0;
246: }
247:
248: };
249:
250: extern "C" SQL_Driver *create() {
251: return new PgSQL_Driver();
252: }