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