Annotation of embedaddon/php/ext/pdo_pgsql/pgsql_driver.c, revision 1.1.1.1
1.1 misho 1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
5: | Copyright (c) 1997-2012 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 321634 2012-01-01 13:15:04Z felipe $ */
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: #include "ext/standard/file.h"
33:
34: #undef PACKAGE_BUGREPORT
35: #undef PACKAGE_NAME
36: #undef PACKAGE_STRING
37: #undef PACKAGE_TARNAME
38: #undef PACKAGE_VERSION
39: #include "pg_config.h" /* needed for PG_VERSION */
40: #include "php_pdo_pgsql.h"
41: #include "php_pdo_pgsql_int.h"
42: #include "zend_exceptions.h"
43:
44: static char * _pdo_pgsql_trim_message(const char *message, int persistent)
45: {
46: register int i = strlen(message)-1;
47: char *tmp;
48:
49: if (i>1 && (message[i-1] == '\r' || message[i-1] == '\n') && message[i] == '.') {
50: --i;
51: }
52: while (i>0 && (message[i] == '\r' || message[i] == '\n')) {
53: --i;
54: }
55: ++i;
56: tmp = pemalloc(i + 1, persistent);
57: memcpy(tmp, message, i);
58: tmp[i] = '\0';
59:
60: return tmp;
61: }
62:
63: int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *file, int line TSRMLS_DC) /* {{{ */
64: {
65: pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
66: pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code;
67: pdo_pgsql_error_info *einfo = &H->einfo;
68: char *errmsg = PQerrorMessage(H->server);
69:
70: einfo->errcode = errcode;
71: einfo->file = file;
72: einfo->line = line;
73:
74: if (einfo->errmsg) {
75: pefree(einfo->errmsg, dbh->is_persistent);
76: einfo->errmsg = NULL;
77: }
78:
79: if (sqlstate == NULL) {
80: strcpy(*pdo_err, "HY000");
81: }
82: else {
83: strcpy(*pdo_err, sqlstate);
84: }
85:
86: if (errmsg) {
87: einfo->errmsg = _pdo_pgsql_trim_message(errmsg, dbh->is_persistent);
88: }
89:
90: if (!dbh->methods) {
91: zend_throw_exception_ex(php_pdo_get_exception(), einfo->errcode TSRMLS_CC, "SQLSTATE[%s] [%d] %s",
92: *pdo_err, einfo->errcode, einfo->errmsg);
93: }
94:
95: return errcode;
96: }
97: /* }}} */
98:
99: static void _pdo_pgsql_notice(pdo_dbh_t *dbh, const char *message) /* {{{ */
100: {
101: /* pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; */
102: }
103: /* }}} */
104:
105: static int pdo_pgsql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info TSRMLS_DC) /* {{{ */
106: {
107: pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
108: pdo_pgsql_error_info *einfo = &H->einfo;
109:
110: if (einfo->errcode) {
111: add_next_index_long(info, einfo->errcode);
112: add_next_index_string(info, einfo->errmsg, 1);
113: }
114:
115: return 1;
116: }
117: /* }}} */
118:
119: /* {{{ pdo_pgsql_create_lob_stream */
120: static size_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
121: {
122: struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
123: return lo_write(self->conn, self->lfd, (char*)buf, count);
124: }
125:
126: static size_t pgsql_lob_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
127: {
128: struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
129: return lo_read(self->conn, self->lfd, buf, count);
130: }
131:
132: static int pgsql_lob_close(php_stream *stream, int close_handle TSRMLS_DC)
133: {
134: struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
135: pdo_dbh_t *dbh = self->dbh;
136:
137: if (close_handle) {
138: lo_close(self->conn, self->lfd);
139: }
140: efree(self);
141: php_pdo_dbh_delref(dbh TSRMLS_CC);
142: return 0;
143: }
144:
145: static int pgsql_lob_flush(php_stream *stream TSRMLS_DC)
146: {
147: return 0;
148: }
149:
150: static int pgsql_lob_seek(php_stream *stream, off_t offset, int whence,
151: off_t *newoffset TSRMLS_DC)
152: {
153: struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
154: int pos = lo_lseek(self->conn, self->lfd, offset, whence);
155: *newoffset = pos;
156: return pos >= 0 ? 0 : -1;
157: }
158:
159: php_stream_ops pdo_pgsql_lob_stream_ops = {
160: pgsql_lob_write,
161: pgsql_lob_read,
162: pgsql_lob_close,
163: pgsql_lob_flush,
164: "pdo_pgsql lob stream",
165: pgsql_lob_seek,
166: NULL,
167: NULL,
168: NULL
169: };
170:
171: php_stream *pdo_pgsql_create_lob_stream(pdo_dbh_t *dbh, int lfd, Oid oid TSRMLS_DC)
172: {
173: php_stream *stm;
174: struct pdo_pgsql_lob_self *self = ecalloc(1, sizeof(*self));
175: pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
176:
177: self->dbh = dbh;
178: self->lfd = lfd;
179: self->oid = oid;
180: self->conn = H->server;
181:
182: stm = php_stream_alloc(&pdo_pgsql_lob_stream_ops, self, 0, "r+b");
183:
184: if (stm) {
185: php_pdo_dbh_addref(dbh TSRMLS_CC);
186: return stm;
187: }
188:
189: efree(self);
190: return NULL;
191: }
192: /* }}} */
193:
194: static int pgsql_handle_closer(pdo_dbh_t *dbh TSRMLS_DC) /* {{{ */
195: {
196: pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
197: if (H) {
198: if (H->server) {
199: PQfinish(H->server);
200: H->server = NULL;
201: }
202: if (H->einfo.errmsg) {
203: pefree(H->einfo.errmsg, dbh->is_persistent);
204: H->einfo.errmsg = NULL;
205: }
206: pefree(H, dbh->is_persistent);
207: dbh->driver_data = NULL;
208: }
209: return 0;
210: }
211: /* }}} */
212:
213: static int pgsql_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, pdo_stmt_t *stmt, zval *driver_options TSRMLS_DC)
214: {
215: pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
216: pdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt));
217: int scrollable;
218: #if HAVE_PQPREPARE
219: int ret;
220: char *nsql = NULL;
221: int nsql_len = 0;
222: int emulate = 0;
223: #endif
224:
225: S->H = H;
226: stmt->driver_data = S;
227: stmt->methods = &pgsql_stmt_methods;
228:
229: scrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR,
230: PDO_CURSOR_FWDONLY TSRMLS_CC) == PDO_CURSOR_SCROLL;
231:
232: if (scrollable) {
233: if (S->cursor_name) {
234: efree(S->cursor_name);
235: }
236: spprintf(&S->cursor_name, 0, "pdo_crsr_%08x", ++H->stmt_counter);
237: #if HAVE_PQPREPARE
238: emulate = 1;
239: #endif
240: }
241:
242: #if HAVE_PQPREPARE
243: else if (driver_options) {
244: if (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT, H->disable_native_prepares TSRMLS_CC) == 1 ||
245: pdo_attr_lval(driver_options, PDO_ATTR_EMULATE_PREPARES, H->emulate_prepares TSRMLS_CC) == 1) {
246: emulate = 1;
247: }
248: } else {
249: emulate = H->disable_native_prepares || H->emulate_prepares;
250: }
251:
252: if (!emulate && PQprotocolVersion(H->server) > 2) {
253: stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED;
254: stmt->named_rewrite_template = "$%d";
255: ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len TSRMLS_CC);
256:
257: if (ret == 1) {
258: /* query was re-written */
259: sql = nsql;
260: } else if (ret == -1) {
261: /* couldn't grok it */
262: strcpy(dbh->error_code, stmt->error_code);
263: return 0;
264: }
265:
266: spprintf(&S->stmt_name, 0, "pdo_stmt_%08x", ++H->stmt_counter);
267: /* that's all for now; we'll defer the actual prepare until the first execute call */
268:
269: if (nsql) {
270: S->query = nsql;
271: } else {
272: S->query = estrdup(sql);
273: }
274:
275: return 1;
276: }
277: #endif
278:
279: stmt->supports_placeholders = PDO_PLACEHOLDER_NONE;
280: return 1;
281: }
282:
283: static long pgsql_handle_doer(pdo_dbh_t *dbh, const char *sql, long sql_len TSRMLS_DC)
284: {
285: pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
286: PGresult *res;
287: long ret = 1;
288: ExecStatusType qs;
289:
290: if (!(res = PQexec(H->server, sql))) {
291: /* fatal error */
292: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
293: return -1;
294: }
295: qs = PQresultStatus(res);
296: if (qs != PGRES_COMMAND_OK && qs != PGRES_TUPLES_OK) {
297: pdo_pgsql_error(dbh, qs, pdo_pgsql_sqlstate(res));
298: PQclear(res);
299: return -1;
300: }
301: H->pgoid = PQoidValue(res);
302: ret = atol(PQcmdTuples(res));
303: PQclear(res);
304:
305: return ret;
306: }
307:
308: 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)
309: {
310: unsigned char *escaped;
311: pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
312: size_t tmp_len;
313:
314: switch (paramtype) {
315: case PDO_PARAM_LOB:
316: /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */
317: #ifdef HAVE_PQESCAPE_BYTEA_CONN
318: escaped = PQescapeByteaConn(H->server, unquoted, unquotedlen, &tmp_len);
319: #else
320: escaped = PQescapeBytea(unquoted, unquotedlen, &tmp_len);
321: #endif
322: *quotedlen = (int)tmp_len + 1;
323: *quoted = emalloc(*quotedlen + 1);
324: memcpy((*quoted)+1, escaped, *quotedlen-2);
325: (*quoted)[0] = '\'';
326: (*quoted)[*quotedlen-1] = '\'';
327: (*quoted)[*quotedlen] = '\0';
328: PQfreemem(escaped);
329: break;
330: default:
331: *quoted = safe_emalloc(2, unquotedlen, 3);
332: (*quoted)[0] = '\'';
333: #ifndef HAVE_PQESCAPE_CONN
334: *quotedlen = PQescapeString(*quoted + 1, unquoted, unquotedlen);
335: #else
336: *quotedlen = PQescapeStringConn(H->server, *quoted + 1, unquoted, unquotedlen, NULL);
337: #endif
338: (*quoted)[*quotedlen + 1] = '\'';
339: (*quoted)[*quotedlen + 2] = '\0';
340: *quotedlen += 2;
341: }
342: return 1;
343: }
344:
345: static char *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const char *name, unsigned int *len TSRMLS_DC)
346: {
347: pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
348: char *id = NULL;
349:
350: if (name == NULL) {
351: if (H->pgoid == InvalidOid) {
352: return NULL;
353: }
354: *len = spprintf(&id, 0, "%ld", (long) H->pgoid);
355: } else {
356: PGresult *res;
357: ExecStatusType status;
358: const char *q[1];
359: q[0] = name;
360: res = PQexecParams(H->server, "SELECT CURRVAL($1)", 1, NULL, q, NULL, NULL, 0);
361: status = PQresultStatus(res);
362:
363: if (res && (status == PGRES_TUPLES_OK)) {
364: id = estrdup((char *)PQgetvalue(res, 0, 0));
365: *len = PQgetlength(res, 0, 0);
366: } else {
367: pdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res));
368: }
369:
370: if (res) {
371: PQclear(res);
372: }
373: }
374: return id;
375: }
376:
377: static int pdo_pgsql_get_attribute(pdo_dbh_t *dbh, long attr, zval *return_value TSRMLS_DC)
378: {
379: pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
380:
381: switch (attr) {
382: case PDO_ATTR_CLIENT_VERSION:
383: ZVAL_STRING(return_value, PG_VERSION, 1);
384: break;
385:
386: case PDO_ATTR_SERVER_VERSION:
387: if (PQprotocolVersion(H->server) >= 3) { /* PostgreSQL 7.4 or later */
388: ZVAL_STRING(return_value, (char*)PQparameterStatus(H->server, "server_version"), 1);
389: } else /* emulate above via a query */
390: {
391: PGresult *res = PQexec(H->server, "SELECT VERSION()");
392: if (res && PQresultStatus(res) == PGRES_TUPLES_OK) {
393: ZVAL_STRING(return_value, (char *)PQgetvalue(res, 0, 0), 1);
394: }
395:
396: if (res) {
397: PQclear(res);
398: }
399: }
400: break;
401:
402: case PDO_ATTR_CONNECTION_STATUS:
403: switch (PQstatus(H->server)) {
404: case CONNECTION_STARTED:
405: ZVAL_STRINGL(return_value, "Waiting for connection to be made.", sizeof("Waiting for connection to be made.")-1, 1);
406: break;
407:
408: case CONNECTION_MADE:
409: case CONNECTION_OK:
410: ZVAL_STRINGL(return_value, "Connection OK; waiting to send.", sizeof("Connection OK; waiting to send.")-1, 1);
411: break;
412:
413: case CONNECTION_AWAITING_RESPONSE:
414: ZVAL_STRINGL(return_value, "Waiting for a response from the server.", sizeof("Waiting for a response from the server.")-1, 1);
415: break;
416:
417: case CONNECTION_AUTH_OK:
418: 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);
419: break;
420: #ifdef CONNECTION_SSL_STARTUP
421: case CONNECTION_SSL_STARTUP:
422: ZVAL_STRINGL(return_value, "Negotiating SSL encryption.", sizeof("Negotiating SSL encryption.")-1, 1);
423: break;
424: #endif
425: case CONNECTION_SETENV:
426: ZVAL_STRINGL(return_value, "Negotiating environment-driven parameter settings.", sizeof("Negotiating environment-driven parameter settings.")-1, 1);
427: break;
428:
429: case CONNECTION_BAD:
430: default:
431: ZVAL_STRINGL(return_value, "Bad connection.", sizeof("Bad connection.")-1, 1);
432: break;
433: }
434: break;
435:
436: case PDO_ATTR_SERVER_INFO: {
437: int spid = PQbackendPID(H->server);
438: char *tmp;
439: spprintf(&tmp, 0,
440: "PID: %d; Client Encoding: %s; Is Superuser: %s; Session Authorization: %s; Date Style: %s",
441: spid,
442: (char*)PQparameterStatus(H->server, "client_encoding"),
443: (char*)PQparameterStatus(H->server, "is_superuser"),
444: (char*)PQparameterStatus(H->server, "session_authorization"),
445: (char*)PQparameterStatus(H->server, "DateStyle"));
446: ZVAL_STRING(return_value, tmp, 0);
447: }
448: break;
449:
450: default:
451: return 0;
452: }
453:
454: return 1;
455: }
456:
457: /* {{{ */
458: static int pdo_pgsql_check_liveness(pdo_dbh_t *dbh TSRMLS_DC)
459: {
460: pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
461: if (PQstatus(H->server) == CONNECTION_BAD) {
462: PQreset(H->server);
463: }
464: return (PQstatus(H->server) == CONNECTION_OK) ? SUCCESS : FAILURE;
465: }
466: /* }}} */
467:
468: static int pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh TSRMLS_DC)
469: {
470: pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
471: PGresult *res;
472: int ret = 1;
473:
474: res = PQexec(H->server, cmd);
475:
476: if (PQresultStatus(res) != PGRES_COMMAND_OK) {
477: pdo_pgsql_error(dbh, PQresultStatus(res), pdo_pgsql_sqlstate(res));
478: ret = 0;
479: }
480:
481: PQclear(res);
482: return ret;
483: }
484:
485: static int pgsql_handle_begin(pdo_dbh_t *dbh TSRMLS_DC)
486: {
487: return pdo_pgsql_transaction_cmd("BEGIN", dbh TSRMLS_CC);
488: }
489:
490: static int pgsql_handle_commit(pdo_dbh_t *dbh TSRMLS_DC)
491: {
492: return pdo_pgsql_transaction_cmd("COMMIT", dbh TSRMLS_CC);
493: }
494:
495: static int pgsql_handle_rollback(pdo_dbh_t *dbh TSRMLS_DC)
496: {
497: return pdo_pgsql_transaction_cmd("ROLLBACK", dbh TSRMLS_CC);
498: }
499:
500: /* {{{ proto string PDO::pgsqlCopyFromArray(string $table_name , array $rows [, string $delimiter [, string $null_as ] [, string $fields])
501: Returns true if the copy worked fine or false if error */
502: static PHP_METHOD(PDO, pgsqlCopyFromArray)
503: {
504: pdo_dbh_t *dbh;
505: pdo_pgsql_db_handle *H;
506:
507: zval *pg_rows;
508:
509: char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
510: int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
511: char *query;
512:
513: PGresult *pgsql_result;
514: ExecStatusType status;
515:
516: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s/a|sss",
517: &table_name, &table_name_len, &pg_rows,
518: &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
519: return;
520: }
521:
522: if (!zend_hash_num_elements(Z_ARRVAL_P(pg_rows))) {
523: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot copy from an empty array");
524: RETURN_FALSE;
525: }
526:
527: dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
528: PDO_CONSTRUCT_CHECK;
529:
530: if (pg_fields) {
531: spprintf(&query, 0, "COPY %s (%s) FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
532: } else {
533: spprintf(&query, 0, "COPY %s FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
534: }
535:
536: /* Obtain db Handle */
537: H = (pdo_pgsql_db_handle *)dbh->driver_data;
538:
539: while ((pgsql_result = PQgetResult(H->server))) {
540: PQclear(pgsql_result);
541: }
542: pgsql_result = PQexec(H->server, query);
543:
544: efree(query);
545: query = NULL;
546:
547: if (pgsql_result) {
548: status = PQresultStatus(pgsql_result);
549: } else {
550: status = (ExecStatusType) PQstatus(H->server);
551: }
552:
553: if (status == PGRES_COPY_IN && pgsql_result) {
554: int command_failed = 0;
555: int buffer_len = 0;
556: zval **tmp;
557: HashPosition pos;
558:
559: PQclear(pgsql_result);
560: zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(pg_rows), &pos);
561: while (zend_hash_get_current_data_ex(Z_ARRVAL_P(pg_rows), (void **) &tmp, &pos) == SUCCESS) {
562: int query_len;
563: convert_to_string_ex(tmp);
564:
565: if (buffer_len < Z_STRLEN_PP(tmp)) {
566: buffer_len = Z_STRLEN_PP(tmp);
567: query = erealloc(query, buffer_len + 2); /* room for \n\0 */
568: }
569: memcpy(query, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
570: query_len = Z_STRLEN_PP(tmp);
571: if (query[query_len - 1] != '\n') {
572: query[query_len++] = '\n';
573: }
574: query[query_len] = '\0';
575: if (PQputCopyData(H->server, query, query_len) != 1) {
576: efree(query);
577: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "copy failed");
578: RETURN_FALSE;
579: }
580: zend_hash_move_forward_ex(Z_ARRVAL_P(pg_rows), &pos);
581: }
582: if (query) {
583: efree(query);
584: }
585:
586: if (PQputCopyEnd(H->server, NULL) != 1) {
587: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "putcopyend failed");
588: RETURN_FALSE;
589: }
590:
591: while ((pgsql_result = PQgetResult(H->server))) {
592: if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
593: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
594: command_failed = 1;
595: }
596: PQclear(pgsql_result);
597: }
598:
599: RETURN_BOOL(!command_failed);
600: } else {
601: PQclear(pgsql_result);
602: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
603: RETURN_FALSE;
604: }
605: }
606: /* }}} */
607:
608: /* {{{ proto string PDO::pgsqlCopyFromFile(string $table_name , string $filename [, string $delimiter [, string $null_as ] [, string $fields])
609: Returns true if the copy worked fine or false if error */
610: static PHP_METHOD(PDO, pgsqlCopyFromFile)
611: {
612: pdo_dbh_t *dbh;
613: pdo_pgsql_db_handle *H;
614:
615: char *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
616: int table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
617: char *query;
618: PGresult *pgsql_result;
619: ExecStatusType status;
620: php_stream *stream;
621:
622: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|sss",
623: &table_name, &table_name_len, &filename, &filename_len,
624: &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
625: return;
626: }
627:
628: /* Obtain db Handler */
629: dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
630: PDO_CONSTRUCT_CHECK;
631:
632: stream = php_stream_open_wrapper_ex(filename, "rb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, FG(default_context));
633: if (!stream) {
634: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to open the file");
635: RETURN_FALSE;
636: }
637:
638: if (pg_fields) {
639: spprintf(&query, 0, "COPY %s (%s) FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
640: } else {
641: spprintf(&query, 0, "COPY %s FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
642: }
643:
644: H = (pdo_pgsql_db_handle *)dbh->driver_data;
645:
646: while ((pgsql_result = PQgetResult(H->server))) {
647: PQclear(pgsql_result);
648: }
649: pgsql_result = PQexec(H->server, query);
650:
651: efree(query);
652:
653: if (pgsql_result) {
654: status = PQresultStatus(pgsql_result);
655: } else {
656: status = (ExecStatusType) PQstatus(H->server);
657: }
658:
659: if (status == PGRES_COPY_IN && pgsql_result) {
660: char *buf;
661: int command_failed = 0;
662: size_t line_len = 0;
663:
664: PQclear(pgsql_result);
665: while ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) {
666: if (PQputCopyData(H->server, buf, line_len) != 1) {
667: efree(buf);
668: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "copy failed");
669: php_stream_close(stream);
670: RETURN_FALSE;
671: }
672: efree(buf);
673: }
674: php_stream_close(stream);
675:
676: if (PQputCopyEnd(H->server, NULL) != 1) {
677: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "putcopyend failed");
678: RETURN_FALSE;
679: }
680:
681: while ((pgsql_result = PQgetResult(H->server))) {
682: if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
683: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
684: command_failed = 1;
685: }
686: PQclear(pgsql_result);
687: }
688:
689: RETURN_BOOL(!command_failed);
690: } else {
691: PQclear(pgsql_result);
692: php_stream_close(stream);
693: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
694: RETURN_FALSE;
695: }
696: }
697: /* }}} */
698:
699:
700: /* {{{ proto string PDO::pgsqlCopyToFile(string $table_name , $filename, [string $delimiter [, string $null_as [, string $fields]]])
701: Returns true if the copy worked fine or false if error */
702: static PHP_METHOD(PDO, pgsqlCopyToFile)
703: {
704: pdo_dbh_t *dbh;
705: pdo_pgsql_db_handle *H;
706:
707: char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL;
708: int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len;
709: char *query;
710:
711: PGresult *pgsql_result;
712: ExecStatusType status;
713:
714: php_stream *stream;
715:
716: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|sss",
717: &table_name, &table_name_len, &filename, &filename_len,
718: &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
719: return;
720: }
721:
722: dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
723: PDO_CONSTRUCT_CHECK;
724:
725: H = (pdo_pgsql_db_handle *)dbh->driver_data;
726:
727: stream = php_stream_open_wrapper_ex(filename, "wb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, FG(default_context));
728: if (!stream) {
729: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to open the file for writing");
730: RETURN_FALSE;
731: }
732:
733: while ((pgsql_result = PQgetResult(H->server))) {
734: PQclear(pgsql_result);
735: }
736:
737: if (pg_fields) {
738: spprintf(&query, 0, "COPY %s (%s) TO STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
739: } else {
740: spprintf(&query, 0, "COPY %s TO STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
741: }
742: pgsql_result = PQexec(H->server, query);
743: efree(query);
744:
745: if (pgsql_result) {
746: status = PQresultStatus(pgsql_result);
747: } else {
748: status = (ExecStatusType) PQstatus(H->server);
749: }
750:
751: if (status == PGRES_COPY_OUT && pgsql_result) {
752: PQclear(pgsql_result);
753: while (1) {
754: char *csv = NULL;
755: int ret = PQgetCopyData(H->server, &csv, 0);
756:
757: if (ret == -1) {
758: break; /* done */
759: } else if (ret > 0) {
760: if (php_stream_write(stream, csv, ret) != ret) {
761: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to write to file");
762: PQfreemem(csv);
763: php_stream_close(stream);
764: RETURN_FALSE;
765: } else {
766: PQfreemem(csv);
767: }
768: } else {
769: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed: getline failed");
770: php_stream_close(stream);
771: RETURN_FALSE;
772: }
773: }
774: php_stream_close(stream);
775:
776: while ((pgsql_result = PQgetResult(H->server))) {
777: PQclear(pgsql_result);
778: }
779: RETURN_TRUE;
780: } else {
781: php_stream_close(stream);
782: PQclear(pgsql_result);
783: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
784: RETURN_FALSE;
785: }
786: }
787: /* }}} */
788:
789: /* {{{ proto string PDO::pgsqlCopyToArray(string $table_name , [string $delimiter [, string $null_as [, string $fields]]])
790: Returns true if the copy worked fine or false if error */
791: static PHP_METHOD(PDO, pgsqlCopyToArray)
792: {
793: pdo_dbh_t *dbh;
794: pdo_pgsql_db_handle *H;
795:
796: char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
797: int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
798: char *query;
799:
800: PGresult *pgsql_result;
801: ExecStatusType status;
802:
803: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|sss",
804: &table_name, &table_name_len,
805: &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
806: return;
807: }
808:
809: dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
810: PDO_CONSTRUCT_CHECK;
811:
812: H = (pdo_pgsql_db_handle *)dbh->driver_data;
813:
814: while ((pgsql_result = PQgetResult(H->server))) {
815: PQclear(pgsql_result);
816: }
817:
818: if (pg_fields) {
819: spprintf(&query, 0, "COPY %s (%s) TO STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
820: } else {
821: spprintf(&query, 0, "COPY %s TO STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
822: }
823: pgsql_result = PQexec(H->server, query);
824: efree(query);
825:
826: if (pgsql_result) {
827: status = PQresultStatus(pgsql_result);
828: } else {
829: status = (ExecStatusType) PQstatus(H->server);
830: }
831:
832: if (status == PGRES_COPY_OUT && pgsql_result) {
833: PQclear(pgsql_result);
834: array_init(return_value);
835:
836: while (1) {
837: char *csv = NULL;
838: int ret = PQgetCopyData(H->server, &csv, 0);
839: if (ret == -1) {
840: break; /* copy done */
841: } else if (ret > 0) {
842: add_next_index_stringl(return_value, csv, ret, 1);
843: PQfreemem(csv);
844: } else {
845: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed: getline failed");
846: RETURN_FALSE;
847: }
848: }
849:
850: while ((pgsql_result = PQgetResult(H->server))) {
851: PQclear(pgsql_result);
852: }
853: } else {
854: PQclear(pgsql_result);
855: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
856: RETURN_FALSE;
857: }
858: }
859: /* }}} */
860:
861:
862: /* {{{ proto string PDO::pgsqlLOBCreate()
863: Creates a new large object, returning its identifier. Must be called inside a transaction. */
864: static PHP_METHOD(PDO, pgsqlLOBCreate)
865: {
866: pdo_dbh_t *dbh;
867: pdo_pgsql_db_handle *H;
868: Oid lfd;
869:
870: dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
871: PDO_CONSTRUCT_CHECK;
872:
873: H = (pdo_pgsql_db_handle *)dbh->driver_data;
874: lfd = lo_creat(H->server, INV_READ|INV_WRITE);
875:
876: if (lfd != InvalidOid) {
877: char *buf;
878: spprintf(&buf, 0, "%lu", (long) lfd);
879: RETURN_STRING(buf, 0);
880: }
881:
882: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000");
883: RETURN_FALSE;
884: }
885: /* }}} */
886:
887: /* {{{ proto resource PDO::pgsqlLOBOpen(string oid [, string mode = 'rb'])
888: Opens an existing large object stream. Must be called inside a transaction. */
889: static PHP_METHOD(PDO, pgsqlLOBOpen)
890: {
891: pdo_dbh_t *dbh;
892: pdo_pgsql_db_handle *H;
893: Oid oid;
894: int lfd;
895: char *oidstr;
896: int oidstrlen;
897: char *modestr = "rb";
898: int modestrlen;
899: int mode = INV_READ;
900: char *end_ptr;
901:
902: if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s",
903: &oidstr, &oidstrlen, &modestr, &modestrlen)) {
904: RETURN_FALSE;
905: }
906:
907: oid = (Oid)strtoul(oidstr, &end_ptr, 10);
908: if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {
909: RETURN_FALSE;
910: }
911:
912: if (strpbrk(modestr, "+w")) {
913: mode = INV_READ|INV_WRITE;
914: }
915:
916: dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
917: PDO_CONSTRUCT_CHECK;
918:
919: H = (pdo_pgsql_db_handle *)dbh->driver_data;
920:
921: lfd = lo_open(H->server, oid, mode);
922:
923: if (lfd >= 0) {
924: php_stream *stream = pdo_pgsql_create_lob_stream(dbh, lfd, oid TSRMLS_CC);
925: if (stream) {
926: php_stream_to_zval(stream, return_value);
927: return;
928: }
929: } else {
930: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000");
931: }
932: RETURN_FALSE;
933: }
934: /* }}} */
935:
936: /* {{{ proto bool PDO::pgsqlLOBUnlink(string oid)
937: Deletes the large object identified by oid. Must be called inside a transaction. */
938: static PHP_METHOD(PDO, pgsqlLOBUnlink)
939: {
940: pdo_dbh_t *dbh;
941: pdo_pgsql_db_handle *H;
942: Oid oid;
943: char *oidstr, *end_ptr;
944: int oidlen;
945:
946: if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
947: &oidstr, &oidlen)) {
948: RETURN_FALSE;
949: }
950:
951: oid = (Oid)strtoul(oidstr, &end_ptr, 10);
952: if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {
953: RETURN_FALSE;
954: }
955:
956: dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
957: PDO_CONSTRUCT_CHECK;
958:
959: H = (pdo_pgsql_db_handle *)dbh->driver_data;
960:
961: if (1 == lo_unlink(H->server, oid)) {
962: RETURN_TRUE;
963: }
964: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000");
965: RETURN_FALSE;
966: }
967: /* }}} */
968:
969:
970: static const zend_function_entry dbh_methods[] = {
971: PHP_ME(PDO, pgsqlLOBCreate, NULL, ZEND_ACC_PUBLIC)
972: PHP_ME(PDO, pgsqlLOBOpen, NULL, ZEND_ACC_PUBLIC)
973: PHP_ME(PDO, pgsqlLOBUnlink, NULL, ZEND_ACC_PUBLIC)
974: PHP_ME(PDO, pgsqlCopyFromArray, NULL, ZEND_ACC_PUBLIC)
975: PHP_ME(PDO, pgsqlCopyFromFile, NULL, ZEND_ACC_PUBLIC)
976: PHP_ME(PDO, pgsqlCopyToArray, NULL, ZEND_ACC_PUBLIC)
977: PHP_ME(PDO, pgsqlCopyToFile, NULL, ZEND_ACC_PUBLIC)
978: PHP_FE_END
979: };
980:
981: static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind TSRMLS_DC)
982: {
983: switch (kind) {
984: case PDO_DBH_DRIVER_METHOD_KIND_DBH:
985: return dbh_methods;
986: default:
987: return NULL;
988: }
989: }
990:
991: static int pdo_pgsql_set_attr(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC)
992: {
993: pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
994:
995: switch (attr) {
996: #if HAVE_PQPREPARE
997: case PDO_ATTR_EMULATE_PREPARES:
998: H->emulate_prepares = Z_LVAL_P(val);
999: return 1;
1000: case PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT:
1001: H->disable_native_prepares = Z_LVAL_P(val);
1002: return 1;
1003: #endif
1004:
1005: default:
1006: return 0;
1007: }
1008: }
1009:
1010: static struct pdo_dbh_methods pgsql_methods = {
1011: pgsql_handle_closer,
1012: pgsql_handle_preparer,
1013: pgsql_handle_doer,
1014: pgsql_handle_quoter,
1015: pgsql_handle_begin,
1016: pgsql_handle_commit,
1017: pgsql_handle_rollback,
1018: pdo_pgsql_set_attr,
1019: pdo_pgsql_last_insert_id,
1020: pdo_pgsql_fetch_error_func,
1021: pdo_pgsql_get_attribute,
1022: pdo_pgsql_check_liveness, /* check_liveness */
1023: pdo_pgsql_get_driver_methods, /* get_driver_methods */
1024: NULL,
1025: };
1026:
1027: static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC) /* {{{ */
1028: {
1029: pdo_pgsql_db_handle *H;
1030: int ret = 0;
1031: char *conn_str, *p, *e;
1032: long connect_timeout = 30;
1033:
1034: H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent);
1035: dbh->driver_data = H;
1036:
1037: H->einfo.errcode = 0;
1038: H->einfo.errmsg = NULL;
1039:
1040: /* PostgreSQL wants params in the connect string to be separated by spaces,
1041: * if the PDO standard semicolons are used, we convert them to spaces
1042: */
1043: e = (char *) dbh->data_source + strlen(dbh->data_source);
1044: p = (char *) dbh->data_source;
1045: while ((p = memchr(p, ';', (e - p)))) {
1046: *p = ' ';
1047: }
1048:
1049: if (driver_options) {
1050: connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30 TSRMLS_CC);
1051: }
1052:
1053: /* support both full connection string & connection string + login and/or password */
1054: if (dbh->username && dbh->password) {
1055: spprintf(&conn_str, 0, "%s user=%s password=%s connect_timeout=%ld", dbh->data_source, dbh->username, dbh->password, connect_timeout);
1056: } else if (dbh->username) {
1057: spprintf(&conn_str, 0, "%s user=%s connect_timeout=%ld", dbh->data_source, dbh->username, connect_timeout);
1058: } else if (dbh->password) {
1059: spprintf(&conn_str, 0, "%s password=%s connect_timeout=%ld", dbh->data_source, dbh->password, connect_timeout);
1060: } else {
1061: spprintf(&conn_str, 0, "%s connect_timeout=%ld", (char *) dbh->data_source, connect_timeout);
1062: }
1063:
1064: H->server = PQconnectdb(conn_str);
1065:
1066: efree(conn_str);
1067:
1068: if (PQstatus(H->server) != CONNECTION_OK) {
1069: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE);
1070: goto cleanup;
1071: }
1072:
1073: PQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)&dbh);
1074:
1075: H->attached = 1;
1076: H->pgoid = -1;
1077:
1078: dbh->methods = &pgsql_methods;
1079: dbh->alloc_own_columns = 1;
1080: dbh->max_escaped_char_length = 2;
1081:
1082: ret = 1;
1083:
1084: cleanup:
1085: dbh->methods = &pgsql_methods;
1086: if (!ret) {
1087: pgsql_handle_closer(dbh TSRMLS_CC);
1088: }
1089:
1090: return ret;
1091: }
1092: /* }}} */
1093:
1094: pdo_driver_t pdo_pgsql_driver = {
1095: PDO_DRIVER_HEADER(pgsql),
1096: pdo_pgsql_handle_factory
1097: };
1098:
1099: /*
1100: * Local variables:
1101: * tab-width: 4
1102: * c-basic-offset: 4
1103: * End:
1104: * vim600: noet sw=4 ts=4 fdm=marker
1105: * vim<600: noet sw=4 ts=4
1106: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>