1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Edin Kadribasic <edink@emini.dk> |
14 | Ilia Alshanestsky <ilia@prohost.org> |
15 | Wez Furlong <wez@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include "php.h"
24 #include "php_ini.h"
25 #include "ext/standard/info.h"
26 #include "ext/standard/php_string.h"
27 #include "main/php_network.h"
28 #include "pdo/php_pdo.h"
29 #include "pdo/php_pdo_driver.h"
30 #include "pdo/php_pdo_error.h"
31 #include "ext/standard/file.h"
32 #undef SIZEOF_OFF_T
33 #include "php_pdo_pgsql.h"
34 #include "php_pdo_pgsql_int.h"
35 #include "zend_exceptions.h"
36 #include "pgsql_driver_arginfo.h"
37
_pdo_pgsql_trim_message(const char * message,int persistent)38 static char * _pdo_pgsql_trim_message(const char *message, int persistent)
39 {
40 size_t i = strlen(message)-1;
41 char *tmp;
42
43 if (i>1 && (message[i-1] == '\r' || message[i-1] == '\n') && message[i] == '.') {
44 --i;
45 }
46 while (i>0 && (message[i] == '\r' || message[i] == '\n')) {
47 --i;
48 }
49 ++i;
50 tmp = pemalloc(i + 1, persistent);
51 memcpy(tmp, message, i);
52 tmp[i] = '\0';
53
54 return tmp;
55 }
56
_pdo_pgsql_escape_credentials(char * str)57 static zend_string* _pdo_pgsql_escape_credentials(char *str)
58 {
59 if (str) {
60 return php_addcslashes_str(str, strlen(str), "\\'", sizeof("\\'"));
61 }
62
63 return NULL;
64 }
65
_pdo_pgsql_error(pdo_dbh_t * dbh,pdo_stmt_t * stmt,int errcode,const char * sqlstate,const char * msg,const char * file,int line)66 int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line) /* {{{ */
67 {
68 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
69 pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code;
70 pdo_pgsql_error_info *einfo = &H->einfo;
71 char *errmsg = PQerrorMessage(H->server);
72
73 einfo->errcode = errcode;
74 einfo->file = file;
75 einfo->line = line;
76
77 if (einfo->errmsg) {
78 pefree(einfo->errmsg, dbh->is_persistent);
79 einfo->errmsg = NULL;
80 }
81
82 if (sqlstate == NULL || strlen(sqlstate) >= sizeof(pdo_error_type)) {
83 strcpy(*pdo_err, "HY000");
84 }
85 else {
86 strcpy(*pdo_err, sqlstate);
87 }
88
89 if (msg) {
90 einfo->errmsg = estrdup(msg);
91 }
92 else if (errmsg) {
93 einfo->errmsg = _pdo_pgsql_trim_message(errmsg, dbh->is_persistent);
94 }
95
96 if (!dbh->methods) {
97 pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err);
98 }
99
100 return errcode;
101 }
102 /* }}} */
103
_pdo_pgsql_notice(pdo_dbh_t * dbh,const char * message)104 static void _pdo_pgsql_notice(pdo_dbh_t *dbh, const char *message) /* {{{ */
105 {
106 /* pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; */
107 }
108 /* }}} */
109
pdo_pgsql_fetch_error_func(pdo_dbh_t * dbh,pdo_stmt_t * stmt,zval * info)110 static void pdo_pgsql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */
111 {
112 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
113 pdo_pgsql_error_info *einfo = &H->einfo;
114
115 if (einfo->errcode) {
116 add_next_index_long(info, einfo->errcode);
117 } else {
118 /* Add null to respect expected info array structure */
119 add_next_index_null(info);
120 }
121 if (einfo->errmsg) {
122 add_next_index_string(info, einfo->errmsg);
123 }
124 }
125 /* }}} */
126
127 /* {{{ pdo_pgsql_create_lob_stream */
pgsql_lob_write(php_stream * stream,const char * buf,size_t count)128 static ssize_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count)
129 {
130 struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
131 return lo_write(self->conn, self->lfd, (char*)buf, count);
132 }
133
pgsql_lob_read(php_stream * stream,char * buf,size_t count)134 static ssize_t pgsql_lob_read(php_stream *stream, char *buf, size_t count)
135 {
136 struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
137 return lo_read(self->conn, self->lfd, buf, count);
138 }
139
pgsql_lob_close(php_stream * stream,int close_handle)140 static int pgsql_lob_close(php_stream *stream, int close_handle)
141 {
142 struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
143
144 if (close_handle) {
145 lo_close(self->conn, self->lfd);
146 }
147 zval_ptr_dtor(&self->dbh);
148 efree(self);
149 return 0;
150 }
151
pgsql_lob_flush(php_stream * stream)152 static int pgsql_lob_flush(php_stream *stream)
153 {
154 return 0;
155 }
156
pgsql_lob_seek(php_stream * stream,zend_off_t offset,int whence,zend_off_t * newoffset)157 static int pgsql_lob_seek(php_stream *stream, zend_off_t offset, int whence,
158 zend_off_t *newoffset)
159 {
160 struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
161 #if defined(HAVE_PG_LO64) && defined(ZEND_ENABLE_ZVAL_LONG64)
162 zend_off_t pos = lo_lseek64(self->conn, self->lfd, offset, whence);
163 #else
164 zend_off_t pos = lo_lseek(self->conn, self->lfd, offset, whence);
165 #endif
166 *newoffset = pos;
167 return pos >= 0 ? 0 : -1;
168 }
169
170 const php_stream_ops pdo_pgsql_lob_stream_ops = {
171 pgsql_lob_write,
172 pgsql_lob_read,
173 pgsql_lob_close,
174 pgsql_lob_flush,
175 "pdo_pgsql lob stream",
176 pgsql_lob_seek,
177 NULL,
178 NULL,
179 NULL
180 };
181
pdo_pgsql_create_lob_stream(zval * dbh,int lfd,Oid oid)182 php_stream *pdo_pgsql_create_lob_stream(zval *dbh, int lfd, Oid oid)
183 {
184 php_stream *stm;
185 struct pdo_pgsql_lob_self *self = ecalloc(1, sizeof(*self));
186 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)(Z_PDO_DBH_P(dbh))->driver_data;
187
188 ZVAL_COPY_VALUE(&self->dbh, dbh);
189 self->lfd = lfd;
190 self->oid = oid;
191 self->conn = H->server;
192
193 stm = php_stream_alloc(&pdo_pgsql_lob_stream_ops, self, 0, "r+b");
194
195 if (stm) {
196 Z_ADDREF_P(dbh);
197 return stm;
198 }
199
200 efree(self);
201 return NULL;
202 }
203 /* }}} */
204
pgsql_handle_closer(pdo_dbh_t * dbh)205 static void pgsql_handle_closer(pdo_dbh_t *dbh) /* {{{ */
206 {
207 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
208 if (H) {
209 if (H->server) {
210 PQfinish(H->server);
211 H->server = NULL;
212 }
213 if (H->einfo.errmsg) {
214 pefree(H->einfo.errmsg, dbh->is_persistent);
215 H->einfo.errmsg = NULL;
216 }
217 pefree(H, dbh->is_persistent);
218 dbh->driver_data = NULL;
219 }
220 }
221 /* }}} */
222
pgsql_handle_preparer(pdo_dbh_t * dbh,zend_string * sql,pdo_stmt_t * stmt,zval * driver_options)223 static bool pgsql_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options)
224 {
225 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
226 pdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt));
227 int scrollable;
228 int ret;
229 zend_string *nsql = NULL;
230 int emulate = 0;
231 int execute_only = 0;
232
233 S->H = H;
234 stmt->driver_data = S;
235 stmt->methods = &pgsql_stmt_methods;
236
237 scrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR,
238 PDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL;
239
240 if (scrollable) {
241 if (S->cursor_name) {
242 efree(S->cursor_name);
243 }
244 spprintf(&S->cursor_name, 0, "pdo_crsr_%08x", ++H->stmt_counter);
245 emulate = 1;
246 } else if (driver_options) {
247 if (pdo_attr_lval(driver_options, PDO_ATTR_EMULATE_PREPARES, H->emulate_prepares) == 1) {
248 emulate = 1;
249 }
250 if (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_PREPARES, H->disable_prepares) == 1) {
251 execute_only = 1;
252 }
253 } else {
254 emulate = H->disable_native_prepares || H->emulate_prepares;
255 execute_only = H->disable_prepares;
256 }
257
258 if (!emulate && PQprotocolVersion(H->server) <= 2) {
259 emulate = 1;
260 }
261
262 if (emulate) {
263 stmt->supports_placeholders = PDO_PLACEHOLDER_NONE;
264 } else {
265 stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED;
266 stmt->named_rewrite_template = "$%d";
267 }
268
269 ret = pdo_parse_params(stmt, sql, &nsql);
270
271 if (ret == -1) {
272 /* couldn't grok it */
273 strcpy(dbh->error_code, stmt->error_code);
274 return false;
275 } else if (ret == 1) {
276 /* query was re-written */
277 S->query = nsql;
278 } else {
279 S->query = zend_string_copy(sql);
280 }
281
282 if (!emulate && !execute_only) {
283 /* prepared query: set the query name and defer the
284 actual prepare until the first execute call */
285 spprintf(&S->stmt_name, 0, "pdo_stmt_%08x", ++H->stmt_counter);
286 }
287
288 return true;
289 }
290
pgsql_handle_doer(pdo_dbh_t * dbh,const zend_string * sql)291 static zend_long pgsql_handle_doer(pdo_dbh_t *dbh, const zend_string *sql)
292 {
293 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
294 PGresult *res;
295 zend_long ret = 1;
296 ExecStatusType qs;
297
298 if (!(res = PQexec(H->server, ZSTR_VAL(sql)))) {
299 /* fatal error */
300 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
301 return -1;
302 }
303 qs = PQresultStatus(res);
304 if (qs != PGRES_COMMAND_OK && qs != PGRES_TUPLES_OK) {
305 pdo_pgsql_error(dbh, qs, pdo_pgsql_sqlstate(res));
306 PQclear(res);
307 return -1;
308 }
309 H->pgoid = PQoidValue(res);
310 if (qs == PGRES_COMMAND_OK) {
311 ret = ZEND_ATOL(PQcmdTuples(res));
312 } else {
313 ret = Z_L(0);
314 }
315 PQclear(res);
316
317 return ret;
318 }
319
pgsql_handle_quoter(pdo_dbh_t * dbh,const zend_string * unquoted,enum pdo_param_type paramtype)320 static zend_string* pgsql_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype)
321 {
322 unsigned char *escaped;
323 char *quoted;
324 size_t quotedlen;
325 zend_string *quoted_str;
326 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
327 size_t tmp_len;
328
329 switch (paramtype) {
330 case PDO_PARAM_LOB:
331 /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */
332 escaped = PQescapeByteaConn(H->server, (unsigned char *)ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), &tmp_len);
333 quotedlen = tmp_len + 1;
334 quoted = emalloc(quotedlen + 1);
335 memcpy(quoted+1, escaped, quotedlen-2);
336 quoted[0] = '\'';
337 quoted[quotedlen-1] = '\'';
338 quoted[quotedlen] = '\0';
339 PQfreemem(escaped);
340 break;
341 default:
342 quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3);
343 quoted[0] = '\'';
344 quotedlen = PQescapeStringConn(H->server, quoted + 1, ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), NULL);
345 quoted[quotedlen + 1] = '\'';
346 quoted[quotedlen + 2] = '\0';
347 quotedlen += 2;
348 }
349
350 quoted_str = zend_string_init(quoted, quotedlen, 0);
351 efree(quoted);
352 return quoted_str;
353 }
354
pdo_pgsql_last_insert_id(pdo_dbh_t * dbh,const zend_string * name)355 static zend_string *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const zend_string *name)
356 {
357 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
358 zend_string *id = NULL;
359 PGresult *res;
360 ExecStatusType status;
361
362 if (name == NULL) {
363 res = PQexec(H->server, "SELECT LASTVAL()");
364 } else {
365 const char *q[1];
366 q[0] = ZSTR_VAL(name);
367
368 res = PQexecParams(H->server, "SELECT CURRVAL($1)", 1, NULL, q, NULL, NULL, 0);
369 }
370 status = PQresultStatus(res);
371
372 if (res && (status == PGRES_TUPLES_OK)) {
373 id = zend_string_init((char *)PQgetvalue(res, 0, 0), PQgetlength(res, 0, 0), 0);
374 } else {
375 pdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res));
376 }
377
378 if (res) {
379 PQclear(res);
380 }
381
382 return id;
383 }
384
pdo_libpq_version(char * buf,size_t len)385 void pdo_libpq_version(char *buf, size_t len)
386 {
387 int version = PQlibVersion();
388 int major = version / 10000;
389 if (major >= 10) {
390 int minor = version % 10000;
391 snprintf(buf, len, "%d.%d", major, minor);
392 } else {
393 int minor = version / 100 % 100;
394 int revision = version % 100;
395 snprintf(buf, len, "%d.%d.%d", major, minor, revision);
396 }
397 }
398
pdo_pgsql_get_attribute(pdo_dbh_t * dbh,zend_long attr,zval * return_value)399 static int pdo_pgsql_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value)
400 {
401 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
402
403 switch (attr) {
404 case PDO_ATTR_EMULATE_PREPARES:
405 ZVAL_BOOL(return_value, H->emulate_prepares);
406 break;
407
408 case PDO_PGSQL_ATTR_DISABLE_PREPARES:
409 ZVAL_BOOL(return_value, H->disable_prepares);
410 break;
411
412 case PDO_ATTR_CLIENT_VERSION: {
413 char buf[16];
414 pdo_libpq_version(buf, sizeof(buf));
415 ZVAL_STRING(return_value, buf);
416 break;
417 }
418
419 case PDO_ATTR_SERVER_VERSION:
420 if (PQprotocolVersion(H->server) >= 3) { /* PostgreSQL 7.4 or later */
421 ZVAL_STRING(return_value, (char*)PQparameterStatus(H->server, "server_version"));
422 } else /* emulate above via a query */
423 {
424 PGresult *res = PQexec(H->server, "SELECT VERSION()");
425 if (res && PQresultStatus(res) == PGRES_TUPLES_OK) {
426 ZVAL_STRING(return_value, (char *)PQgetvalue(res, 0, 0));
427 }
428
429 if (res) {
430 PQclear(res);
431 }
432 }
433 break;
434
435 case PDO_ATTR_CONNECTION_STATUS:
436 switch (PQstatus(H->server)) {
437 case CONNECTION_STARTED:
438 ZVAL_STRINGL(return_value, "Waiting for connection to be made.", sizeof("Waiting for connection to be made.")-1);
439 break;
440
441 case CONNECTION_MADE:
442 case CONNECTION_OK:
443 ZVAL_STRINGL(return_value, "Connection OK; waiting to send.", sizeof("Connection OK; waiting to send.")-1);
444 break;
445
446 case CONNECTION_AWAITING_RESPONSE:
447 ZVAL_STRINGL(return_value, "Waiting for a response from the server.", sizeof("Waiting for a response from the server.")-1);
448 break;
449
450 case CONNECTION_AUTH_OK:
451 ZVAL_STRINGL(return_value, "Received authentication; waiting for backend start-up to finish.", sizeof("Received authentication; waiting for backend start-up to finish.")-1);
452 break;
453 #ifdef CONNECTION_SSL_STARTUP
454 case CONNECTION_SSL_STARTUP:
455 ZVAL_STRINGL(return_value, "Negotiating SSL encryption.", sizeof("Negotiating SSL encryption.")-1);
456 break;
457 #endif
458 case CONNECTION_SETENV:
459 ZVAL_STRINGL(return_value, "Negotiating environment-driven parameter settings.", sizeof("Negotiating environment-driven parameter settings.")-1);
460 break;
461
462 case CONNECTION_BAD:
463 default:
464 ZVAL_STRINGL(return_value, "Bad connection.", sizeof("Bad connection.")-1);
465 break;
466 }
467 break;
468
469 case PDO_ATTR_SERVER_INFO: {
470 int spid = PQbackendPID(H->server);
471
472
473 zend_string *str_info =
474 strpprintf(0,
475 "PID: %d; Client Encoding: %s; Is Superuser: %s; Session Authorization: %s; Date Style: %s",
476 spid,
477 (char*)PQparameterStatus(H->server, "client_encoding"),
478 (char*)PQparameterStatus(H->server, "is_superuser"),
479 (char*)PQparameterStatus(H->server, "session_authorization"),
480 (char*)PQparameterStatus(H->server, "DateStyle"));
481
482 ZVAL_STR(return_value, str_info);
483 break;
484 }
485
486 default:
487 return 0;
488 }
489
490 return 1;
491 }
492
493 /* {{{ */
pdo_pgsql_check_liveness(pdo_dbh_t * dbh)494 static zend_result pdo_pgsql_check_liveness(pdo_dbh_t *dbh)
495 {
496 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
497 if (!PQconsumeInput(H->server) || PQstatus(H->server) == CONNECTION_BAD) {
498 PQreset(H->server);
499 }
500 return (PQstatus(H->server) == CONNECTION_OK) ? SUCCESS : FAILURE;
501 }
502 /* }}} */
503
pgsql_handle_in_transaction(pdo_dbh_t * dbh)504 static bool pgsql_handle_in_transaction(pdo_dbh_t *dbh)
505 {
506 pdo_pgsql_db_handle *H;
507
508 H = (pdo_pgsql_db_handle *)dbh->driver_data;
509
510 return PQtransactionStatus(H->server) > PQTRANS_IDLE;
511 }
512
pdo_pgsql_transaction_cmd(const char * cmd,pdo_dbh_t * dbh)513 static bool pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh)
514 {
515 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
516 PGresult *res;
517 bool ret = true;
518
519 res = PQexec(H->server, cmd);
520
521 if (PQresultStatus(res) != PGRES_COMMAND_OK) {
522 pdo_pgsql_error(dbh, PQresultStatus(res), pdo_pgsql_sqlstate(res));
523 ret = false;
524 }
525
526 PQclear(res);
527 return ret;
528 }
529
pgsql_handle_begin(pdo_dbh_t * dbh)530 static bool pgsql_handle_begin(pdo_dbh_t *dbh)
531 {
532 return pdo_pgsql_transaction_cmd("BEGIN", dbh);
533 }
534
pgsql_handle_commit(pdo_dbh_t * dbh)535 static bool pgsql_handle_commit(pdo_dbh_t *dbh)
536 {
537 bool ret = pdo_pgsql_transaction_cmd("COMMIT", dbh);
538
539 /* When deferred constraints are used the commit could
540 fail, and a ROLLBACK implicitly ran. See bug #67462 */
541 if (!ret) {
542 dbh->in_txn = pgsql_handle_in_transaction(dbh);
543 }
544
545 return ret;
546 }
547
pgsql_handle_rollback(pdo_dbh_t * dbh)548 static bool pgsql_handle_rollback(pdo_dbh_t *dbh)
549 {
550 return pdo_pgsql_transaction_cmd("ROLLBACK", dbh);
551 }
552
553 /* {{{ Returns true if the copy worked fine or false if error */
PHP_METHOD(PDO_PGSql_Ext,pgsqlCopyFromArray)554 PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray)
555 {
556 pdo_dbh_t *dbh;
557 pdo_pgsql_db_handle *H;
558
559 zval *pg_rows;
560
561 char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
562 size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
563 char *query;
564
565 PGresult *pgsql_result;
566 ExecStatusType status;
567
568 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa|sss!",
569 &table_name, &table_name_len, &pg_rows,
570 &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
571 RETURN_THROWS();
572 }
573
574 if (!zend_hash_num_elements(Z_ARRVAL_P(pg_rows))) {
575 zend_argument_value_error(2, "cannot be empty");
576 RETURN_THROWS();
577 }
578
579 dbh = Z_PDO_DBH_P(ZEND_THIS);
580 PDO_CONSTRUCT_CHECK;
581 PDO_DBH_CLEAR_ERR();
582
583 /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
584 if (pg_fields) {
585 spprintf(&query, 0, "COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
586 } else {
587 spprintf(&query, 0, "COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
588 }
589
590 /* Obtain db Handle */
591 H = (pdo_pgsql_db_handle *)dbh->driver_data;
592
593 while ((pgsql_result = PQgetResult(H->server))) {
594 PQclear(pgsql_result);
595 }
596 pgsql_result = PQexec(H->server, query);
597
598 efree(query);
599 query = NULL;
600
601 if (pgsql_result) {
602 status = PQresultStatus(pgsql_result);
603 } else {
604 status = (ExecStatusType) PQstatus(H->server);
605 }
606
607 if (status == PGRES_COPY_IN && pgsql_result) {
608 int command_failed = 0;
609 size_t buffer_len = 0;
610 zval *tmp;
611
612 PQclear(pgsql_result);
613 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), tmp) {
614 size_t query_len;
615 if (!try_convert_to_string(tmp)) {
616 efree(query);
617 RETURN_THROWS();
618 }
619
620 if (buffer_len < Z_STRLEN_P(tmp)) {
621 buffer_len = Z_STRLEN_P(tmp);
622 query = erealloc(query, buffer_len + 2); /* room for \n\0 */
623 }
624 memcpy(query, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
625 query_len = Z_STRLEN_P(tmp);
626 if (query[query_len - 1] != '\n') {
627 query[query_len++] = '\n';
628 }
629 query[query_len] = '\0';
630 if (PQputCopyData(H->server, query, query_len) != 1) {
631 efree(query);
632 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
633 PDO_HANDLE_DBH_ERR();
634 RETURN_FALSE;
635 }
636 } ZEND_HASH_FOREACH_END();
637 if (query) {
638 efree(query);
639 }
640
641 if (PQputCopyEnd(H->server, NULL) != 1) {
642 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
643 PDO_HANDLE_DBH_ERR();
644 RETURN_FALSE;
645 }
646
647 while ((pgsql_result = PQgetResult(H->server))) {
648 if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
649 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
650 command_failed = 1;
651 }
652 PQclear(pgsql_result);
653 }
654
655 PDO_HANDLE_DBH_ERR();
656 RETURN_BOOL(!command_failed);
657 } else {
658 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
659 PQclear(pgsql_result);
660 PDO_HANDLE_DBH_ERR();
661 RETURN_FALSE;
662 }
663 }
664 /* }}} */
665
666 /* {{{ Returns true if the copy worked fine or false if error */
PHP_METHOD(PDO_PGSql_Ext,pgsqlCopyFromFile)667 PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile)
668 {
669 pdo_dbh_t *dbh;
670 pdo_pgsql_db_handle *H;
671
672 char *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
673 size_t table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
674 char *query;
675 PGresult *pgsql_result;
676 ExecStatusType status;
677 php_stream *stream;
678
679 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sp|sss!",
680 &table_name, &table_name_len, &filename, &filename_len,
681 &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
682 RETURN_THROWS();
683 }
684
685 /* Obtain db Handler */
686 dbh = Z_PDO_DBH_P(ZEND_THIS);
687 PDO_CONSTRUCT_CHECK;
688 PDO_DBH_CLEAR_ERR();
689
690 stream = php_stream_open_wrapper_ex(filename, "rb", 0, NULL, FG(default_context));
691 if (!stream) {
692 pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file");
693 PDO_HANDLE_DBH_ERR();
694 RETURN_FALSE;
695 }
696
697 /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
698 if (pg_fields) {
699 spprintf(&query, 0, "COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
700 } else {
701 spprintf(&query, 0, "COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
702 }
703
704 H = (pdo_pgsql_db_handle *)dbh->driver_data;
705
706 while ((pgsql_result = PQgetResult(H->server))) {
707 PQclear(pgsql_result);
708 }
709 pgsql_result = PQexec(H->server, query);
710
711 efree(query);
712
713 if (pgsql_result) {
714 status = PQresultStatus(pgsql_result);
715 } else {
716 status = (ExecStatusType) PQstatus(H->server);
717 }
718
719 if (status == PGRES_COPY_IN && pgsql_result) {
720 char *buf;
721 int command_failed = 0;
722 size_t line_len = 0;
723
724 PQclear(pgsql_result);
725 while ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) {
726 if (PQputCopyData(H->server, buf, line_len) != 1) {
727 efree(buf);
728 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
729 php_stream_close(stream);
730 PDO_HANDLE_DBH_ERR();
731 RETURN_FALSE;
732 }
733 efree(buf);
734 }
735 php_stream_close(stream);
736
737 if (PQputCopyEnd(H->server, NULL) != 1) {
738 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
739 PDO_HANDLE_DBH_ERR();
740 RETURN_FALSE;
741 }
742
743 while ((pgsql_result = PQgetResult(H->server))) {
744 if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
745 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
746 command_failed = 1;
747 }
748 PQclear(pgsql_result);
749 }
750
751 PDO_HANDLE_DBH_ERR();
752 RETURN_BOOL(!command_failed);
753 } else {
754 php_stream_close(stream);
755 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
756 PQclear(pgsql_result);
757 PDO_HANDLE_DBH_ERR();
758 RETURN_FALSE;
759 }
760 }
761 /* }}} */
762
763
764 /* {{{ Returns true if the copy worked fine or false if error */
PHP_METHOD(PDO_PGSql_Ext,pgsqlCopyToFile)765 PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile)
766 {
767 pdo_dbh_t *dbh;
768 pdo_pgsql_db_handle *H;
769
770 char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL;
771 size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len;
772 char *query;
773
774 PGresult *pgsql_result;
775 ExecStatusType status;
776
777 php_stream *stream;
778
779 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sp|sss!",
780 &table_name, &table_name_len, &filename, &filename_len,
781 &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
782 RETURN_THROWS();
783 }
784
785 dbh = Z_PDO_DBH_P(ZEND_THIS);
786 PDO_CONSTRUCT_CHECK;
787 PDO_DBH_CLEAR_ERR();
788
789 H = (pdo_pgsql_db_handle *)dbh->driver_data;
790
791 stream = php_stream_open_wrapper_ex(filename, "wb", 0, NULL, FG(default_context));
792 if (!stream) {
793 pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file for writing");
794 PDO_HANDLE_DBH_ERR();
795 RETURN_FALSE;
796 }
797
798 while ((pgsql_result = PQgetResult(H->server))) {
799 PQclear(pgsql_result);
800 }
801
802 /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
803 if (pg_fields) {
804 spprintf(&query, 0, "COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
805 } else {
806 spprintf(&query, 0, "COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
807 }
808 pgsql_result = PQexec(H->server, query);
809 efree(query);
810
811 if (pgsql_result) {
812 status = PQresultStatus(pgsql_result);
813 } else {
814 status = (ExecStatusType) PQstatus(H->server);
815 }
816
817 if (status == PGRES_COPY_OUT && pgsql_result) {
818 PQclear(pgsql_result);
819 while (1) {
820 char *csv = NULL;
821 int ret = PQgetCopyData(H->server, &csv, 0);
822
823 if (ret == -1) {
824 break; /* done */
825 } else if (ret > 0) {
826 if (php_stream_write(stream, csv, ret) != (size_t)ret) {
827 pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to write to file");
828 PQfreemem(csv);
829 php_stream_close(stream);
830 PDO_HANDLE_DBH_ERR();
831 RETURN_FALSE;
832 } else {
833 PQfreemem(csv);
834 }
835 } else {
836 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
837 php_stream_close(stream);
838 PDO_HANDLE_DBH_ERR();
839 RETURN_FALSE;
840 }
841 }
842 php_stream_close(stream);
843
844 while ((pgsql_result = PQgetResult(H->server))) {
845 PQclear(pgsql_result);
846 }
847 RETURN_TRUE;
848 } else {
849 php_stream_close(stream);
850 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
851 PQclear(pgsql_result);
852 PDO_HANDLE_DBH_ERR();
853 RETURN_FALSE;
854 }
855 }
856 /* }}} */
857
858 /* {{{ Returns true if the copy worked fine or false if error */
PHP_METHOD(PDO_PGSql_Ext,pgsqlCopyToArray)859 PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray)
860 {
861 pdo_dbh_t *dbh;
862 pdo_pgsql_db_handle *H;
863
864 char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
865 size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
866 char *query;
867
868 PGresult *pgsql_result;
869 ExecStatusType status;
870
871 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|sss!",
872 &table_name, &table_name_len,
873 &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
874 RETURN_THROWS();
875 }
876
877 dbh = Z_PDO_DBH_P(ZEND_THIS);
878 PDO_CONSTRUCT_CHECK;
879 PDO_DBH_CLEAR_ERR();
880
881 H = (pdo_pgsql_db_handle *)dbh->driver_data;
882
883 while ((pgsql_result = PQgetResult(H->server))) {
884 PQclear(pgsql_result);
885 }
886
887 /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
888 if (pg_fields) {
889 spprintf(&query, 0, "COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
890 } else {
891 spprintf(&query, 0, "COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
892 }
893 pgsql_result = PQexec(H->server, query);
894 efree(query);
895
896 if (pgsql_result) {
897 status = PQresultStatus(pgsql_result);
898 } else {
899 status = (ExecStatusType) PQstatus(H->server);
900 }
901
902 if (status == PGRES_COPY_OUT && pgsql_result) {
903 PQclear(pgsql_result);
904 array_init(return_value);
905
906 while (1) {
907 char *csv = NULL;
908 int ret = PQgetCopyData(H->server, &csv, 0);
909 if (ret == -1) {
910 break; /* copy done */
911 } else if (ret > 0) {
912 add_next_index_stringl(return_value, csv, ret);
913 PQfreemem(csv);
914 } else {
915 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
916 PDO_HANDLE_DBH_ERR();
917 RETURN_FALSE;
918 }
919 }
920
921 while ((pgsql_result = PQgetResult(H->server))) {
922 PQclear(pgsql_result);
923 }
924 } else {
925 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
926 PQclear(pgsql_result);
927 PDO_HANDLE_DBH_ERR();
928 RETURN_FALSE;
929 }
930 }
931 /* }}} */
932
933
934 /* {{{ Creates a new large object, returning its identifier. Must be called inside a transaction. */
PHP_METHOD(PDO_PGSql_Ext,pgsqlLOBCreate)935 PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate)
936 {
937 pdo_dbh_t *dbh;
938 pdo_pgsql_db_handle *H;
939 Oid lfd;
940
941 ZEND_PARSE_PARAMETERS_NONE();
942
943 dbh = Z_PDO_DBH_P(ZEND_THIS);
944 PDO_CONSTRUCT_CHECK;
945 PDO_DBH_CLEAR_ERR();
946
947 H = (pdo_pgsql_db_handle *)dbh->driver_data;
948 lfd = lo_creat(H->server, INV_READ|INV_WRITE);
949
950 if (lfd != InvalidOid) {
951 zend_string *buf = strpprintf(0, ZEND_ULONG_FMT, (zend_long) lfd);
952
953 RETURN_STR(buf);
954 }
955
956 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
957 PDO_HANDLE_DBH_ERR();
958 RETURN_FALSE;
959 }
960 /* }}} */
961
962 /* {{{ Opens an existing large object stream. Must be called inside a transaction. */
PHP_METHOD(PDO_PGSql_Ext,pgsqlLOBOpen)963 PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen)
964 {
965 pdo_dbh_t *dbh;
966 pdo_pgsql_db_handle *H;
967 Oid oid;
968 int lfd;
969 char *oidstr;
970 size_t oidstrlen;
971 char *modestr = "rb";
972 size_t modestrlen;
973 int mode = INV_READ;
974 char *end_ptr;
975
976 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s|s",
977 &oidstr, &oidstrlen, &modestr, &modestrlen)) {
978 RETURN_THROWS();
979 }
980
981 oid = (Oid)strtoul(oidstr, &end_ptr, 10);
982 if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {
983 RETURN_FALSE;
984 }
985
986 if (strpbrk(modestr, "+w")) {
987 mode = INV_READ|INV_WRITE;
988 }
989
990 dbh = Z_PDO_DBH_P(ZEND_THIS);
991 PDO_CONSTRUCT_CHECK;
992 PDO_DBH_CLEAR_ERR();
993
994 H = (pdo_pgsql_db_handle *)dbh->driver_data;
995
996 lfd = lo_open(H->server, oid, mode);
997
998 if (lfd >= 0) {
999 php_stream *stream = pdo_pgsql_create_lob_stream(ZEND_THIS, lfd, oid);
1000 if (stream) {
1001 php_stream_to_zval(stream, return_value);
1002 return;
1003 }
1004 } else {
1005 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
1006 }
1007
1008 PDO_HANDLE_DBH_ERR();
1009 RETURN_FALSE;
1010 }
1011 /* }}} */
1012
1013 /* {{{ Deletes the large object identified by oid. Must be called inside a transaction. */
PHP_METHOD(PDO_PGSql_Ext,pgsqlLOBUnlink)1014 PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink)
1015 {
1016 pdo_dbh_t *dbh;
1017 pdo_pgsql_db_handle *H;
1018 Oid oid;
1019 char *oidstr, *end_ptr;
1020 size_t oidlen;
1021
1022 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s",
1023 &oidstr, &oidlen)) {
1024 RETURN_THROWS();
1025 }
1026
1027 oid = (Oid)strtoul(oidstr, &end_ptr, 10);
1028 if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {
1029 RETURN_FALSE;
1030 }
1031
1032 dbh = Z_PDO_DBH_P(ZEND_THIS);
1033 PDO_CONSTRUCT_CHECK;
1034 PDO_DBH_CLEAR_ERR();
1035
1036 H = (pdo_pgsql_db_handle *)dbh->driver_data;
1037
1038 if (1 == lo_unlink(H->server, oid)) {
1039 RETURN_TRUE;
1040 }
1041
1042 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
1043 PDO_HANDLE_DBH_ERR();
1044 RETURN_FALSE;
1045 }
1046 /* }}} */
1047
1048 /* {{{ Get asynchronous notification */
PHP_METHOD(PDO_PGSql_Ext,pgsqlGetNotify)1049 PHP_METHOD(PDO_PGSql_Ext, pgsqlGetNotify)
1050 {
1051 pdo_dbh_t *dbh;
1052 pdo_pgsql_db_handle *H;
1053 zend_long result_type = PDO_FETCH_USE_DEFAULT;
1054 zend_long ms_timeout = 0;
1055 PGnotify *pgsql_notify;
1056
1057 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|ll",
1058 &result_type, &ms_timeout)) {
1059 RETURN_THROWS();
1060 }
1061
1062 dbh = Z_PDO_DBH_P(ZEND_THIS);
1063 PDO_CONSTRUCT_CHECK;
1064
1065 if (result_type == PDO_FETCH_USE_DEFAULT) {
1066 result_type = dbh->default_fetch_type;
1067 }
1068
1069 if (result_type != PDO_FETCH_BOTH && result_type != PDO_FETCH_ASSOC && result_type != PDO_FETCH_NUM) {
1070 zend_argument_value_error(1, "must be one of PDO::FETCH_BOTH, PDO::FETCH_ASSOC, or PDO::FETCH_NUM");
1071 RETURN_THROWS();
1072 }
1073
1074 if (ms_timeout < 0) {
1075 zend_argument_value_error(2, "must be greater than or equal to 0");
1076 RETURN_THROWS();
1077 #ifdef ZEND_ENABLE_ZVAL_LONG64
1078 } else if (ms_timeout > INT_MAX) {
1079 php_error_docref(NULL, E_WARNING, "Timeout was shrunk to %d", INT_MAX);
1080 ms_timeout = INT_MAX;
1081 #endif
1082 }
1083
1084 H = (pdo_pgsql_db_handle *)dbh->driver_data;
1085
1086 if (!PQconsumeInput(H->server)) {
1087 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
1088 PDO_HANDLE_DBH_ERR();
1089 RETURN_FALSE;
1090 }
1091 pgsql_notify = PQnotifies(H->server);
1092
1093 if (ms_timeout && !pgsql_notify) {
1094 php_pollfd_for_ms(PQsocket(H->server), PHP_POLLREADABLE, (int)ms_timeout);
1095
1096 if (!PQconsumeInput(H->server)) {
1097 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
1098 PDO_HANDLE_DBH_ERR();
1099 RETURN_FALSE;
1100 }
1101 pgsql_notify = PQnotifies(H->server);
1102 }
1103
1104 if (!pgsql_notify) {
1105 RETURN_FALSE;
1106 }
1107
1108 array_init(return_value);
1109 if (result_type == PDO_FETCH_NUM || result_type == PDO_FETCH_BOTH) {
1110 add_index_string(return_value, 0, pgsql_notify->relname);
1111 add_index_long(return_value, 1, pgsql_notify->be_pid);
1112 if (pgsql_notify->extra && pgsql_notify->extra[0]) {
1113 add_index_string(return_value, 2, pgsql_notify->extra);
1114 }
1115 }
1116 if (result_type == PDO_FETCH_ASSOC || result_type == PDO_FETCH_BOTH) {
1117 add_assoc_string(return_value, "message", pgsql_notify->relname);
1118 add_assoc_long(return_value, "pid", pgsql_notify->be_pid);
1119 if (pgsql_notify->extra && pgsql_notify->extra[0]) {
1120 add_assoc_string(return_value, "payload", pgsql_notify->extra);
1121 }
1122 }
1123
1124 PQfreemem(pgsql_notify);
1125 }
1126 /* }}} */
1127
1128 /* {{{ Get backend(server) pid */
PHP_METHOD(PDO_PGSql_Ext,pgsqlGetPid)1129 PHP_METHOD(PDO_PGSql_Ext, pgsqlGetPid)
1130 {
1131 pdo_dbh_t *dbh;
1132 pdo_pgsql_db_handle *H;
1133
1134 ZEND_PARSE_PARAMETERS_NONE();
1135
1136 dbh = Z_PDO_DBH_P(ZEND_THIS);
1137 PDO_CONSTRUCT_CHECK;
1138
1139 H = (pdo_pgsql_db_handle *)dbh->driver_data;
1140
1141 RETURN_LONG(PQbackendPID(H->server));
1142 }
1143 /* }}} */
1144
pdo_pgsql_get_driver_methods(pdo_dbh_t * dbh,int kind)1145 static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind)
1146 {
1147 switch (kind) {
1148 case PDO_DBH_DRIVER_METHOD_KIND_DBH:
1149 return class_PDO_PGSql_Ext_methods;
1150 default:
1151 return NULL;
1152 }
1153 }
1154
pdo_pgsql_set_attr(pdo_dbh_t * dbh,zend_long attr,zval * val)1155 static bool pdo_pgsql_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)
1156 {
1157 bool bval;
1158 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
1159
1160 switch (attr) {
1161 case PDO_ATTR_EMULATE_PREPARES:
1162 if (!pdo_get_bool_param(&bval, val)) {
1163 return false;
1164 }
1165 H->emulate_prepares = bval;
1166 return true;
1167 case PDO_PGSQL_ATTR_DISABLE_PREPARES:
1168 if (!pdo_get_bool_param(&bval, val)) {
1169 return false;
1170 }
1171 H->disable_prepares = bval;
1172 return true;
1173 default:
1174 return false;
1175 }
1176 }
1177
1178 static const struct pdo_dbh_methods pgsql_methods = {
1179 pgsql_handle_closer,
1180 pgsql_handle_preparer,
1181 pgsql_handle_doer,
1182 pgsql_handle_quoter,
1183 pgsql_handle_begin,
1184 pgsql_handle_commit,
1185 pgsql_handle_rollback,
1186 pdo_pgsql_set_attr,
1187 pdo_pgsql_last_insert_id,
1188 pdo_pgsql_fetch_error_func,
1189 pdo_pgsql_get_attribute,
1190 pdo_pgsql_check_liveness, /* check_liveness */
1191 pdo_pgsql_get_driver_methods, /* get_driver_methods */
1192 NULL,
1193 pgsql_handle_in_transaction,
1194 NULL /* get_gc */
1195 };
1196
pdo_pgsql_handle_factory(pdo_dbh_t * dbh,zval * driver_options)1197 static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */
1198 {
1199 pdo_pgsql_db_handle *H;
1200 int ret = 0;
1201 char *conn_str, *p, *e;
1202 zend_string *tmp_user, *tmp_pass;
1203 zend_long connect_timeout = 30;
1204
1205 H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent);
1206 dbh->driver_data = H;
1207
1208 dbh->skip_param_evt =
1209 1 << PDO_PARAM_EVT_EXEC_POST |
1210 1 << PDO_PARAM_EVT_FETCH_PRE |
1211 1 << PDO_PARAM_EVT_FETCH_POST;
1212
1213 H->einfo.errcode = 0;
1214 H->einfo.errmsg = NULL;
1215
1216 /* PostgreSQL wants params in the connect string to be separated by spaces,
1217 * if the PDO standard semicolons are used, we convert them to spaces
1218 */
1219 e = (char *) dbh->data_source + strlen(dbh->data_source);
1220 p = (char *) dbh->data_source;
1221 while ((p = memchr(p, ';', (e - p)))) {
1222 *p = ' ';
1223 }
1224
1225 if (driver_options) {
1226 connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30);
1227 }
1228
1229 /* escape username and password, if provided */
1230 tmp_user = _pdo_pgsql_escape_credentials(dbh->username);
1231 tmp_pass = _pdo_pgsql_escape_credentials(dbh->password);
1232
1233 /* support both full connection string & connection string + login and/or password */
1234 if (tmp_user && tmp_pass) {
1235 spprintf(&conn_str, 0, "%s user='%s' password='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_user), ZSTR_VAL(tmp_pass), connect_timeout);
1236 } else if (tmp_user) {
1237 spprintf(&conn_str, 0, "%s user='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_user), connect_timeout);
1238 } else if (tmp_pass) {
1239 spprintf(&conn_str, 0, "%s password='%s' connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_pass), connect_timeout);
1240 } else {
1241 spprintf(&conn_str, 0, "%s connect_timeout=" ZEND_LONG_FMT, (char *) dbh->data_source, connect_timeout);
1242 }
1243
1244 H->server = PQconnectdb(conn_str);
1245
1246 if (tmp_user) {
1247 zend_string_release_ex(tmp_user, 0);
1248 }
1249 if (tmp_pass) {
1250 zend_string_release_ex(tmp_pass, 0);
1251 }
1252
1253 efree(conn_str);
1254
1255 if (PQstatus(H->server) != CONNECTION_OK) {
1256 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE);
1257 goto cleanup;
1258 }
1259
1260 PQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)&dbh);
1261
1262 H->attached = 1;
1263 H->pgoid = -1;
1264
1265 dbh->methods = &pgsql_methods;
1266 dbh->alloc_own_columns = 1;
1267 dbh->max_escaped_char_length = 2;
1268
1269 ret = 1;
1270
1271 cleanup:
1272 dbh->methods = &pgsql_methods;
1273 if (!ret) {
1274 pgsql_handle_closer(dbh);
1275 }
1276
1277 return ret;
1278 }
1279 /* }}} */
1280
1281 const pdo_driver_t pdo_pgsql_driver = {
1282 PDO_DRIVER_HEADER(pgsql),
1283 pdo_pgsql_handle_factory
1284 };
1285