1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 6 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2009 The PHP Group |
6 : +----------------------------------------------------------------------+
7 : | This source file is subject to version 3.01 of the PHP license, |
8 : | that is bundled with this package in the file LICENSE, and is |
9 : | available through the world-wide-web at the following url: |
10 : | http://www.php.net/license/3_01.txt |
11 : | If you did not receive a copy of the PHP license and are unable to |
12 : | obtain it through the world-wide-web, please send a note to |
13 : | license@php.net so we can mail you a copy immediately. |
14 : +----------------------------------------------------------------------+
15 : | Authors: Edin Kadribasic <edink@emini.dk> |
16 : | Ilia Alshanestsky <ilia@prohost.org> |
17 : | Wez Furlong <wez@php.net> |
18 : +----------------------------------------------------------------------+
19 : */
20 :
21 : /* $Id: pgsql_driver.c 290214 2009-11-04 19:32:27Z mbeccati $ */
22 :
23 : #ifdef HAVE_CONFIG_H
24 : #include "config.h"
25 : #endif
26 :
27 : #include "php.h"
28 : #include "php_ini.h"
29 : #include "ext/standard/info.h"
30 : #include "pdo/php_pdo.h"
31 : #include "pdo/php_pdo_driver.h"
32 :
33 : #undef PACKAGE_BUGREPORT
34 : #undef PACKAGE_NAME
35 : #undef PACKAGE_STRING
36 : #undef PACKAGE_TARNAME
37 : #undef PACKAGE_VERSION
38 : #include "pg_config.h" /* needed for PG_VERSION */
39 : #include "php_pdo_pgsql.h"
40 : #include "php_pdo_pgsql_int.h"
41 : #include "zend_exceptions.h"
42 :
43 : static char * _pdo_pgsql_trim_message(const char *message, int persistent)
44 316 : {
45 316 : register int i = strlen(message)-1;
46 : char *tmp;
47 :
48 316 : if (i>1 && (message[i-1] == '\r' || message[i-1] == '\n') && message[i] == '.') {
49 0 : --i;
50 : }
51 948 : while (i>0 && (message[i] == '\r' || message[i] == '\n')) {
52 316 : --i;
53 : }
54 316 : ++i;
55 316 : tmp = pemalloc(i + 1, persistent);
56 316 : memcpy(tmp, message, i);
57 316 : tmp[i] = '\0';
58 :
59 316 : return tmp;
60 : }
61 :
62 : int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *file, int line TSRMLS_DC) /* {{{ */
63 316 : {
64 316 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
65 316 : pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code;
66 316 : pdo_pgsql_error_info *einfo = &H->einfo;
67 316 : char *errmsg = PQerrorMessage(H->server);
68 :
69 316 : einfo->errcode = errcode;
70 316 : einfo->file = file;
71 316 : einfo->line = line;
72 :
73 316 : if (einfo->errmsg) {
74 204 : pefree(einfo->errmsg, dbh->is_persistent);
75 204 : einfo->errmsg = NULL;
76 : }
77 :
78 316 : if (sqlstate == NULL) {
79 0 : strcpy(*pdo_err, "HY000");
80 : }
81 : else {
82 316 : strcpy(*pdo_err, sqlstate);
83 : }
84 :
85 316 : if (errmsg) {
86 316 : einfo->errmsg = _pdo_pgsql_trim_message(errmsg, dbh->is_persistent);
87 : }
88 :
89 316 : if (!dbh->methods) {
90 0 : zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "SQLSTATE[%s] [%d] %s",
91 : *pdo_err, einfo->errcode, einfo->errmsg);
92 : }
93 :
94 316 : return errcode;
95 : }
96 : /* }}} */
97 :
98 : static void _pdo_pgsql_notice(pdo_dbh_t *dbh, const char *message) /* {{{ */
99 43 : {
100 : /* pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; */
101 43 : }
102 : /* }}} */
103 :
104 : static int pdo_pgsql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info TSRMLS_DC) /* {{{ */
105 34 : {
106 34 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
107 34 : pdo_pgsql_error_info *einfo = &H->einfo;
108 :
109 34 : if (einfo->errcode) {
110 34 : add_next_index_long(info, einfo->errcode);
111 34 : add_next_index_string(info, einfo->errmsg, 1);
112 : }
113 :
114 34 : return 1;
115 : }
116 : /* }}} */
117 :
118 : /* {{{ pdo_pgsql_create_lob_stream */
119 : static size_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
120 0 : {
121 0 : struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
122 0 : return lo_write(self->conn, self->lfd, (char*)buf, count);
123 : }
124 :
125 : static size_t pgsql_lob_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
126 0 : {
127 0 : struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
128 0 : return lo_read(self->conn, self->lfd, buf, count);
129 : }
130 :
131 : static int pgsql_lob_close(php_stream *stream, int close_handle TSRMLS_DC)
132 0 : {
133 0 : struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
134 0 : pdo_dbh_t *dbh = self->dbh;
135 :
136 0 : if (close_handle) {
137 0 : lo_close(self->conn, self->lfd);
138 : }
139 0 : efree(self);
140 0 : php_pdo_dbh_delref(dbh TSRMLS_CC);
141 0 : return 0;
142 : }
143 :
144 : static int pgsql_lob_flush(php_stream *stream TSRMLS_DC)
145 0 : {
146 0 : return 0;
147 : }
148 :
149 : static int pgsql_lob_seek(php_stream *stream, off_t offset, int whence,
150 : off_t *newoffset TSRMLS_DC)
151 0 : {
152 0 : struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
153 0 : int pos = lo_lseek(self->conn, self->lfd, offset, whence);
154 0 : *newoffset = pos;
155 0 : return pos >= 0 ? 0 : -1;
156 : }
157 :
158 : php_stream_ops pdo_pgsql_lob_stream_ops = {
159 : pgsql_lob_write,
160 : pgsql_lob_read,
161 : pgsql_lob_close,
162 : pgsql_lob_flush,
163 : "pdo_pgsql lob stream",
164 : pgsql_lob_seek,
165 : NULL,
166 : NULL,
167 : NULL
168 : };
169 :
170 : php_stream *pdo_pgsql_create_lob_stream(pdo_dbh_t *dbh, int lfd, Oid oid TSRMLS_DC)
171 0 : {
172 : php_stream *stm;
173 0 : struct pdo_pgsql_lob_self *self = ecalloc(1, sizeof(*self));
174 0 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
175 :
176 0 : self->dbh = dbh;
177 0 : self->lfd = lfd;
178 0 : self->oid = oid;
179 0 : self->conn = H->server;
180 :
181 0 : stm = php_stream_alloc(&pdo_pgsql_lob_stream_ops, self, 0, "r+b");
182 :
183 0 : if (stm) {
184 0 : php_pdo_dbh_addref(dbh TSRMLS_CC);
185 0 : return stm;
186 : }
187 :
188 0 : efree(self);
189 0 : return NULL;
190 : }
191 : /* }}} */
192 :
193 : static int pgsql_handle_closer(pdo_dbh_t *dbh TSRMLS_DC) /* {{{ */
194 111 : {
195 111 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
196 111 : if (H) {
197 111 : if (H->server) {
198 111 : PQfinish(H->server);
199 111 : H->server = NULL;
200 : }
201 111 : if (H->einfo.errmsg) {
202 111 : pefree(H->einfo.errmsg, dbh->is_persistent);
203 111 : H->einfo.errmsg = NULL;
204 : }
205 111 : pefree(H, dbh->is_persistent);
206 111 : dbh->driver_data = NULL;
207 : }
208 111 : return 0;
209 : }
210 : /* }}} */
211 :
212 : static int pgsql_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, pdo_stmt_t *stmt, zval *driver_options TSRMLS_DC)
213 128 : {
214 128 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
215 128 : pdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt));
216 : int scrollable;
217 : #if HAVE_PQPREPARE
218 : int ret;
219 128 : char *nsql = NULL;
220 128 : int nsql_len = 0;
221 128 : int emulate = 0;
222 : #endif
223 :
224 128 : S->H = H;
225 128 : stmt->driver_data = S;
226 128 : stmt->methods = &pgsql_stmt_methods;
227 :
228 128 : scrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR,
229 : PDO_CURSOR_FWDONLY TSRMLS_CC) == PDO_CURSOR_SCROLL;
230 :
231 128 : if (scrollable) {
232 2 : if (S->cursor_name) {
233 0 : efree(S->cursor_name);
234 : }
235 2 : spprintf(&S->cursor_name, 0, "pdo_crsr_%08x", ++H->stmt_counter);
236 : #if HAVE_PQPREPARE
237 2 : emulate = 1;
238 : #endif
239 : }
240 :
241 : #if HAVE_PQPREPARE
242 126 : else if (driver_options) {
243 20 : if (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT, H->disable_native_prepares TSRMLS_CC) == 1 ||
244 : pdo_attr_lval(driver_options, PDO_ATTR_EMULATE_PREPARES, H->emulate_prepares TSRMLS_CC) == 1) {
245 12 : emulate = 1;
246 : }
247 : } else {
248 106 : emulate = H->disable_native_prepares || H->emulate_prepares;
249 : }
250 :
251 128 : if (!emulate && PQprotocolVersion(H->server) > 2) {
252 114 : stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED;
253 114 : stmt->named_rewrite_template = "$%d";
254 114 : ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len TSRMLS_CC);
255 :
256 114 : if (ret == 1) {
257 : /* query was re-written */
258 33 : sql = nsql;
259 81 : } else if (ret == -1) {
260 : /* couldn't grok it */
261 0 : strcpy(dbh->error_code, stmt->error_code);
262 0 : return 0;
263 : }
264 :
265 114 : spprintf(&S->stmt_name, 0, "pdo_stmt_%08x", ++H->stmt_counter);
266 : /* that's all for now; we'll defer the actual prepare until the first execute call */
267 :
268 114 : if (nsql) {
269 33 : S->query = nsql;
270 : } else {
271 81 : S->query = estrdup(sql);
272 : }
273 :
274 114 : return 1;
275 : }
276 : #endif
277 :
278 14 : stmt->supports_placeholders = PDO_PLACEHOLDER_NONE;
279 14 : return 1;
280 : }
281 :
282 : static long pgsql_handle_doer(pdo_dbh_t *dbh, const char *sql, long sql_len TSRMLS_DC)
283 466 : {
284 466 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
285 : PGresult *res;
286 466 : long ret = 1;
287 : ExecStatusType qs;
288 :
289 466 : if (!(res = PQexec(H->server, sql))) {
290 : /* fatal error */
291 0 : pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
292 0 : return -1;
293 : }
294 466 : qs = PQresultStatus(res);
295 466 : if (qs != PGRES_COMMAND_OK && qs != PGRES_TUPLES_OK) {
296 293 : pdo_pgsql_error(dbh, qs, pdo_pgsql_sqlstate(res));
297 293 : PQclear(res);
298 293 : return -1;
299 : }
300 173 : H->pgoid = PQoidValue(res);
301 173 : ret = atol(PQcmdTuples(res));
302 173 : PQclear(res);
303 :
304 173 : return ret;
305 : }
306 :
307 : static int pgsql_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, int unquotedlen, char **quoted, int *quotedlen, enum pdo_param_type paramtype TSRMLS_DC)
308 18 : {
309 : unsigned char *escaped;
310 18 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
311 : size_t tmp_len;
312 :
313 18 : switch (paramtype) {
314 : case PDO_PARAM_LOB:
315 : /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */
316 : #ifdef HAVE_PQESCAPE_BYTEA_CONN
317 0 : escaped = PQescapeByteaConn(H->server, unquoted, unquotedlen, &tmp_len);
318 : #else
319 : escaped = PQescapeBytea(unquoted, unquotedlen, &tmp_len);
320 : #endif
321 0 : *quotedlen = (int)tmp_len + 1;
322 0 : *quoted = emalloc(*quotedlen + 1);
323 0 : memcpy((*quoted)+1, escaped, *quotedlen-2);
324 0 : (*quoted)[0] = '\'';
325 0 : (*quoted)[*quotedlen-1] = '\'';
326 0 : (*quoted)[*quotedlen] = '\0';
327 0 : PQfreemem(escaped);
328 0 : break;
329 : default:
330 18 : *quoted = safe_emalloc(2, unquotedlen, 3);
331 18 : (*quoted)[0] = '\'';
332 : #ifndef HAVE_PQESCAPE_CONN
333 : *quotedlen = PQescapeString(*quoted + 1, unquoted, unquotedlen);
334 : #else
335 18 : *quotedlen = PQescapeStringConn(H->server, *quoted + 1, unquoted, unquotedlen, NULL);
336 : #endif
337 18 : (*quoted)[*quotedlen + 1] = '\'';
338 18 : (*quoted)[*quotedlen + 2] = '\0';
339 18 : *quotedlen += 2;
340 : }
341 18 : return 1;
342 : }
343 :
344 : static char *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const char *name, unsigned int *len TSRMLS_DC)
345 0 : {
346 0 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
347 0 : char *id = NULL;
348 :
349 0 : if (name == NULL) {
350 0 : if (H->pgoid == InvalidOid) {
351 0 : return NULL;
352 : }
353 0 : *len = spprintf(&id, 0, "%ld", (long) H->pgoid);
354 : } else {
355 : PGresult *res;
356 : ExecStatusType status;
357 : const char *q[1];
358 0 : q[0] = name;
359 0 : res = PQexecParams(H->server, "SELECT CURRVAL($1)", 1, NULL, q, NULL, NULL, 0);
360 0 : status = PQresultStatus(res);
361 :
362 0 : if (res && (status == PGRES_TUPLES_OK)) {
363 0 : id = estrdup((char *)PQgetvalue(res, 0, 0));
364 0 : *len = PQgetlength(res, 0, 0);
365 : } else {
366 0 : pdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res));
367 : }
368 :
369 0 : if (res) {
370 0 : PQclear(res);
371 : }
372 : }
373 0 : return id;
374 : }
375 :
376 : static int pdo_pgsql_get_attribute(pdo_dbh_t *dbh, long attr, zval *return_value TSRMLS_DC)
377 2 : {
378 2 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
379 :
380 2 : switch (attr) {
381 : case PDO_ATTR_CLIENT_VERSION:
382 1 : ZVAL_STRING(return_value, PG_VERSION, 1);
383 1 : break;
384 :
385 : case PDO_ATTR_SERVER_VERSION:
386 1 : if (PQprotocolVersion(H->server) >= 3) { /* PostgreSQL 7.4 or later */
387 1 : ZVAL_STRING(return_value, (char*)PQparameterStatus(H->server, "server_version"), 1);
388 : } else /* emulate above via a query */
389 : {
390 0 : PGresult *res = PQexec(H->server, "SELECT VERSION()");
391 0 : if (res && PQresultStatus(res) == PGRES_TUPLES_OK) {
392 0 : ZVAL_STRING(return_value, (char *)PQgetvalue(res, 0, 0), 1);
393 : }
394 :
395 0 : if (res) {
396 0 : PQclear(res);
397 : }
398 : }
399 1 : break;
400 :
401 : case PDO_ATTR_CONNECTION_STATUS:
402 0 : switch (PQstatus(H->server)) {
403 : case CONNECTION_STARTED:
404 0 : ZVAL_STRINGL(return_value, "Waiting for connection to be made.", sizeof("Waiting for connection to be made.")-1, 1);
405 0 : break;
406 :
407 : case CONNECTION_MADE:
408 : case CONNECTION_OK:
409 0 : ZVAL_STRINGL(return_value, "Connection OK; waiting to send.", sizeof("Connection OK; waiting to send.")-1, 1);
410 0 : break;
411 :
412 : case CONNECTION_AWAITING_RESPONSE:
413 0 : ZVAL_STRINGL(return_value, "Waiting for a response from the server.", sizeof("Waiting for a response from the server.")-1, 1);
414 0 : break;
415 :
416 : case CONNECTION_AUTH_OK:
417 0 : ZVAL_STRINGL(return_value, "Received authentication; waiting for backend start-up to finish.", sizeof("Received authentication; waiting for backend start-up to finish.")-1, 1);
418 0 : break;
419 : #ifdef CONNECTION_SSL_STARTUP
420 : case CONNECTION_SSL_STARTUP:
421 : ZVAL_STRINGL(return_value, "Negotiating SSL encryption.", sizeof("Negotiating SSL encryption.")-1, 1);
422 : break;
423 : #endif
424 : case CONNECTION_SETENV:
425 0 : ZVAL_STRINGL(return_value, "Negotiating environment-driven parameter settings.", sizeof("Negotiating environment-driven parameter settings.")-1, 1);
426 0 : break;
427 :
428 : case CONNECTION_BAD:
429 : default:
430 0 : ZVAL_STRINGL(return_value, "Bad connection.", sizeof("Bad connection.")-1, 1);
431 : break;
432 : }
433 0 : break;
434 :
435 : case PDO_ATTR_SERVER_INFO: {
436 0 : int spid = PQbackendPID(H->server);
437 : char *tmp;
438 0 : spprintf(&tmp, 0,
439 : "PID: %d; Client Encoding: %s; Is Superuser: %s; Session Authorization: %s; Date Style: %s",
440 : spid,
441 : (char*)PQparameterStatus(H->server, "client_encoding"),
442 : (char*)PQparameterStatus(H->server, "is_superuser"),
443 : (char*)PQparameterStatus(H->server, "session_authorization"),
444 : (char*)PQparameterStatus(H->server, "DateStyle"));
445 0 : ZVAL_STRING(return_value, tmp, 0);
446 : }
447 0 : break;
448 :
449 : default:
450 0 : return 0;
451 : }
452 :
453 2 : return 1;
454 : }
455 :
456 : /* {{{ */
457 : static int pdo_pgsql_check_liveness(pdo_dbh_t *dbh TSRMLS_DC)
458 0 : {
459 0 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
460 0 : if (PQstatus(H->server) == CONNECTION_BAD) {
461 0 : PQreset(H->server);
462 : }
463 0 : return (PQstatus(H->server) == CONNECTION_OK) ? SUCCESS : FAILURE;
464 : }
465 : /* }}} */
466 :
467 : static int pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh TSRMLS_DC)
468 14 : {
469 14 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
470 : PGresult *res;
471 14 : int ret = 1;
472 :
473 14 : res = PQexec(H->server, cmd);
474 :
475 14 : if (PQresultStatus(res) != PGRES_COMMAND_OK) {
476 0 : pdo_pgsql_error(dbh, PQresultStatus(res), pdo_pgsql_sqlstate(res));
477 0 : ret = 0;
478 : }
479 :
480 14 : PQclear(res);
481 14 : return ret;
482 : }
483 :
484 : static int pgsql_handle_begin(pdo_dbh_t *dbh TSRMLS_DC)
485 7 : {
486 7 : return pdo_pgsql_transaction_cmd("BEGIN", dbh TSRMLS_CC);
487 : }
488 :
489 : static int pgsql_handle_commit(pdo_dbh_t *dbh TSRMLS_DC)
490 2 : {
491 2 : return pdo_pgsql_transaction_cmd("COMMIT", dbh TSRMLS_CC);
492 : }
493 :
494 : static int pgsql_handle_rollback(pdo_dbh_t *dbh TSRMLS_DC)
495 5 : {
496 5 : return pdo_pgsql_transaction_cmd("ROLLBACK", dbh TSRMLS_CC);
497 : }
498 :
499 : /* {{{ proto string PDO::pgsqlLOBCreate()
500 : Creates a new large object, returning its identifier. Must be called inside a transaction. */
501 : static PHP_METHOD(PDO, pgsqlLOBCreate)
502 0 : {
503 : pdo_dbh_t *dbh;
504 : pdo_pgsql_db_handle *H;
505 : Oid lfd;
506 :
507 0 : dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
508 0 : PDO_CONSTRUCT_CHECK;
509 :
510 0 : H = (pdo_pgsql_db_handle *)dbh->driver_data;
511 0 : lfd = lo_creat(H->server, INV_READ|INV_WRITE);
512 :
513 0 : if (lfd != InvalidOid) {
514 : char *buf;
515 0 : spprintf(&buf, 0, "%lu", (long) lfd);
516 0 : RETURN_STRING(buf, 0);
517 : }
518 :
519 0 : pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000");
520 0 : RETURN_FALSE;
521 : }
522 : /* }}} */
523 :
524 : /* {{{ proto resource PDO::pgsqlLOBOpen(string oid [, string mode = 'rb'])
525 : Opens an existing large object stream. Must be called inside a transaction. */
526 : static PHP_METHOD(PDO, pgsqlLOBOpen)
527 0 : {
528 : pdo_dbh_t *dbh;
529 : pdo_pgsql_db_handle *H;
530 : Oid oid;
531 : int lfd;
532 : char *oidstr;
533 : int oidstrlen;
534 0 : char *modestr = "rb";
535 : int modestrlen;
536 0 : int mode = INV_READ;
537 : char *end_ptr;
538 :
539 0 : if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s",
540 : &oidstr, &oidstrlen, &modestr, &modestrlen)) {
541 0 : RETURN_FALSE;
542 : }
543 :
544 0 : oid = (Oid)strtoul(oidstr, &end_ptr, 10);
545 0 : if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {
546 0 : RETURN_FALSE;
547 : }
548 :
549 0 : if (strpbrk(modestr, "+w")) {
550 0 : mode = INV_READ|INV_WRITE;
551 : }
552 :
553 0 : dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
554 0 : PDO_CONSTRUCT_CHECK;
555 :
556 0 : H = (pdo_pgsql_db_handle *)dbh->driver_data;
557 :
558 0 : lfd = lo_open(H->server, oid, mode);
559 :
560 0 : if (lfd >= 0) {
561 0 : php_stream *stream = pdo_pgsql_create_lob_stream(dbh, lfd, oid TSRMLS_CC);
562 0 : if (stream) {
563 0 : php_stream_to_zval(stream, return_value);
564 0 : return;
565 : }
566 : } else {
567 0 : pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000");
568 : }
569 0 : RETURN_FALSE;
570 : }
571 : /* }}} */
572 :
573 : /* {{{ proto bool PDO::pgsqlLOBUnlink(string oid)
574 : Deletes the large object identified by oid. Must be called inside a transaction. */
575 : static PHP_METHOD(PDO, pgsqlLOBUnlink)
576 0 : {
577 : pdo_dbh_t *dbh;
578 : pdo_pgsql_db_handle *H;
579 : Oid oid;
580 : char *oidstr, *end_ptr;
581 : int oidlen;
582 :
583 0 : if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
584 : &oidstr, &oidlen)) {
585 0 : RETURN_FALSE;
586 : }
587 :
588 0 : oid = (Oid)strtoul(oidstr, &end_ptr, 10);
589 0 : if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {
590 0 : RETURN_FALSE;
591 : }
592 :
593 0 : dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
594 0 : PDO_CONSTRUCT_CHECK;
595 :
596 0 : H = (pdo_pgsql_db_handle *)dbh->driver_data;
597 :
598 0 : if (1 == lo_unlink(H->server, oid)) {
599 0 : RETURN_TRUE;
600 : }
601 0 : pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000");
602 0 : RETURN_FALSE;
603 : }
604 : /* }}} */
605 :
606 :
607 : static const zend_function_entry dbh_methods[] = {
608 : PHP_ME(PDO, pgsqlLOBCreate, NULL, ZEND_ACC_PUBLIC)
609 : PHP_ME(PDO, pgsqlLOBOpen, NULL, ZEND_ACC_PUBLIC)
610 : PHP_ME(PDO, pgsqlLOBUnlink, NULL, ZEND_ACC_PUBLIC)
611 : {NULL, NULL, NULL}
612 : };
613 :
614 : static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind TSRMLS_DC)
615 1 : {
616 1 : switch (kind) {
617 : case PDO_DBH_DRIVER_METHOD_KIND_DBH:
618 1 : return dbh_methods;
619 : default:
620 0 : return NULL;
621 : }
622 : }
623 :
624 : static int pdo_pgsql_set_attr(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC)
625 8 : {
626 8 : pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
627 :
628 8 : switch (attr) {
629 : #if HAVE_PQPREPARE
630 : case PDO_ATTR_EMULATE_PREPARES:
631 4 : H->emulate_prepares = Z_LVAL_P(val);
632 4 : return 1;
633 : case PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT:
634 4 : H->disable_native_prepares = Z_LVAL_P(val);
635 4 : return 1;
636 : #endif
637 :
638 : default:
639 0 : return 0;
640 : }
641 : }
642 :
643 : static struct pdo_dbh_methods pgsql_methods = {
644 : pgsql_handle_closer,
645 : pgsql_handle_preparer,
646 : pgsql_handle_doer,
647 : pgsql_handle_quoter,
648 : pgsql_handle_begin,
649 : pgsql_handle_commit,
650 : pgsql_handle_rollback,
651 : pdo_pgsql_set_attr,
652 : pdo_pgsql_last_insert_id,
653 : pdo_pgsql_fetch_error_func,
654 : pdo_pgsql_get_attribute,
655 : pdo_pgsql_check_liveness, /* check_liveness */
656 : pdo_pgsql_get_driver_methods /* get_driver_methods */
657 : };
658 :
659 : static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC) /* {{{ */
660 112 : {
661 : pdo_pgsql_db_handle *H;
662 112 : int ret = 0;
663 : char *conn_str, *p, *e;
664 112 : long connect_timeout = 30;
665 :
666 112 : H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent);
667 112 : dbh->driver_data = H;
668 :
669 112 : H->einfo.errcode = 0;
670 112 : H->einfo.errmsg = NULL;
671 :
672 : /* PostgreSQL wants params in the connect string to be separated by spaces,
673 : * if the PDO standard semicolons are used, we convert them to spaces
674 : */
675 112 : e = (char *) dbh->data_source + strlen(dbh->data_source);
676 112 : p = (char *) dbh->data_source;
677 224 : while ((p = memchr(p, ';', (e - p)))) {
678 0 : *p = ' ';
679 : }
680 :
681 112 : if (driver_options) {
682 3 : connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30 TSRMLS_CC);
683 : }
684 :
685 : /* support both full connection string & connection string + login and/or password */
686 112 : if (dbh->username && dbh->password) {
687 0 : spprintf(&conn_str, 0, "%s user=%s password=%s connect_timeout=%ld", dbh->data_source, dbh->username, dbh->password, connect_timeout);
688 112 : } else if (dbh->username) {
689 0 : spprintf(&conn_str, 0, "%s user=%s connect_timeout=%ld", dbh->data_source, dbh->username, connect_timeout);
690 112 : } else if (dbh->password) {
691 0 : spprintf(&conn_str, 0, "%s password=%s connect_timeout=%ld", dbh->data_source, dbh->password, connect_timeout);
692 : } else {
693 112 : spprintf(&conn_str, 0, "%s connect_timeout=%ld", (char *) dbh->data_source, connect_timeout);
694 : }
695 :
696 112 : H->server = PQconnectdb(conn_str);
697 :
698 112 : efree(conn_str);
699 :
700 112 : if (PQstatus(H->server) != CONNECTION_OK) {
701 0 : pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE);
702 0 : goto cleanup;
703 : }
704 :
705 112 : PQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)&dbh);
706 :
707 112 : H->attached = 1;
708 112 : H->pgoid = -1;
709 :
710 112 : dbh->methods = &pgsql_methods;
711 112 : dbh->alloc_own_columns = 1;
712 112 : dbh->max_escaped_char_length = 2;
713 :
714 112 : ret = 1;
715 :
716 112 : cleanup:
717 112 : dbh->methods = &pgsql_methods;
718 112 : if (!ret) {
719 0 : pgsql_handle_closer(dbh TSRMLS_CC);
720 : }
721 :
722 112 : return ret;
723 : }
724 : /* }}} */
725 :
726 : pdo_driver_t pdo_pgsql_driver = {
727 : PDO_DRIVER_HEADER(pgsql),
728 : pdo_pgsql_handle_factory
729 : };
730 :
731 : /*
732 : * Local variables:
733 : * tab-width: 4
734 : * c-basic-offset: 4
735 : * End:
736 : * vim600: noet sw=4 ts=4 fdm=marker
737 : * vim<600: noet sw=4 ts=4
738 : */
|