Annotation of embedaddon/php/ext/pdo_pgsql/pgsql_driver.c, revision 1.1.1.3
1.1 misho 1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
1.1.1.3 ! misho 5: | Copyright (c) 1997-2013 The PHP Group |
1.1 misho 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:
1.1.1.2 misho 21: /* $Id$ */
1.1 misho 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:
1.1.1.3 ! misho 79: if (sqlstate == NULL || strlen(sqlstate) >= sizeof(pdo_error_type)) {
1.1 misho 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);
1.1.1.2 misho 302: ret = (qs == PGRES_COMMAND_OK) ? atol(PQcmdTuples(res)) : 0L;
1.1 misho 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:
1.1.1.2 misho 500: static int pgsql_handle_in_transaction(pdo_dbh_t *dbh TSRMLS_DC)
501: {
502: pdo_pgsql_db_handle *H;
503:
504: H = (pdo_pgsql_db_handle *)dbh->driver_data;
505:
506: return PQtransactionStatus(H->server);
507: }
508:
1.1 misho 509: /* {{{ proto string PDO::pgsqlCopyFromArray(string $table_name , array $rows [, string $delimiter [, string $null_as ] [, string $fields])
510: Returns true if the copy worked fine or false if error */
511: static PHP_METHOD(PDO, pgsqlCopyFromArray)
512: {
513: pdo_dbh_t *dbh;
514: pdo_pgsql_db_handle *H;
515:
516: zval *pg_rows;
517:
518: char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
519: int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
520: char *query;
521:
522: PGresult *pgsql_result;
523: ExecStatusType status;
524:
525: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s/a|sss",
526: &table_name, &table_name_len, &pg_rows,
527: &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
528: return;
529: }
530:
531: if (!zend_hash_num_elements(Z_ARRVAL_P(pg_rows))) {
532: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot copy from an empty array");
533: RETURN_FALSE;
534: }
535:
536: dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
537: PDO_CONSTRUCT_CHECK;
538:
539: if (pg_fields) {
540: 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"));
541: } else {
542: 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"));
543: }
544:
545: /* Obtain db Handle */
546: H = (pdo_pgsql_db_handle *)dbh->driver_data;
547:
548: while ((pgsql_result = PQgetResult(H->server))) {
549: PQclear(pgsql_result);
550: }
551: pgsql_result = PQexec(H->server, query);
552:
553: efree(query);
554: query = NULL;
555:
556: if (pgsql_result) {
557: status = PQresultStatus(pgsql_result);
558: } else {
559: status = (ExecStatusType) PQstatus(H->server);
560: }
561:
562: if (status == PGRES_COPY_IN && pgsql_result) {
563: int command_failed = 0;
564: int buffer_len = 0;
565: zval **tmp;
566: HashPosition pos;
567:
568: PQclear(pgsql_result);
569: zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(pg_rows), &pos);
570: while (zend_hash_get_current_data_ex(Z_ARRVAL_P(pg_rows), (void **) &tmp, &pos) == SUCCESS) {
571: int query_len;
572: convert_to_string_ex(tmp);
573:
574: if (buffer_len < Z_STRLEN_PP(tmp)) {
575: buffer_len = Z_STRLEN_PP(tmp);
576: query = erealloc(query, buffer_len + 2); /* room for \n\0 */
577: }
578: memcpy(query, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
579: query_len = Z_STRLEN_PP(tmp);
580: if (query[query_len - 1] != '\n') {
581: query[query_len++] = '\n';
582: }
583: query[query_len] = '\0';
584: if (PQputCopyData(H->server, query, query_len) != 1) {
585: efree(query);
586: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "copy failed");
587: RETURN_FALSE;
588: }
589: zend_hash_move_forward_ex(Z_ARRVAL_P(pg_rows), &pos);
590: }
591: if (query) {
592: efree(query);
593: }
594:
595: if (PQputCopyEnd(H->server, NULL) != 1) {
596: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "putcopyend failed");
597: RETURN_FALSE;
598: }
599:
600: while ((pgsql_result = PQgetResult(H->server))) {
601: if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
602: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
603: command_failed = 1;
604: }
605: PQclear(pgsql_result);
606: }
607:
608: RETURN_BOOL(!command_failed);
609: } else {
610: PQclear(pgsql_result);
611: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
612: RETURN_FALSE;
613: }
614: }
615: /* }}} */
616:
617: /* {{{ proto string PDO::pgsqlCopyFromFile(string $table_name , string $filename [, string $delimiter [, string $null_as ] [, string $fields])
618: Returns true if the copy worked fine or false if error */
619: static PHP_METHOD(PDO, pgsqlCopyFromFile)
620: {
621: pdo_dbh_t *dbh;
622: pdo_pgsql_db_handle *H;
623:
624: char *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
625: int table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
626: char *query;
627: PGresult *pgsql_result;
628: ExecStatusType status;
629: php_stream *stream;
630:
1.1.1.2 misho 631: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sp|sss",
1.1 misho 632: &table_name, &table_name_len, &filename, &filename_len,
633: &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
634: return;
635: }
636:
637: /* Obtain db Handler */
638: dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
639: PDO_CONSTRUCT_CHECK;
640:
641: stream = php_stream_open_wrapper_ex(filename, "rb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, FG(default_context));
642: if (!stream) {
643: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to open the file");
644: RETURN_FALSE;
645: }
646:
647: if (pg_fields) {
648: 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"));
649: } else {
650: 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"));
651: }
652:
653: H = (pdo_pgsql_db_handle *)dbh->driver_data;
654:
655: while ((pgsql_result = PQgetResult(H->server))) {
656: PQclear(pgsql_result);
657: }
658: pgsql_result = PQexec(H->server, query);
659:
660: efree(query);
661:
662: if (pgsql_result) {
663: status = PQresultStatus(pgsql_result);
664: } else {
665: status = (ExecStatusType) PQstatus(H->server);
666: }
667:
668: if (status == PGRES_COPY_IN && pgsql_result) {
669: char *buf;
670: int command_failed = 0;
671: size_t line_len = 0;
672:
673: PQclear(pgsql_result);
674: while ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) {
675: if (PQputCopyData(H->server, buf, line_len) != 1) {
676: efree(buf);
677: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "copy failed");
678: php_stream_close(stream);
679: RETURN_FALSE;
680: }
681: efree(buf);
682: }
683: php_stream_close(stream);
684:
685: if (PQputCopyEnd(H->server, NULL) != 1) {
686: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "putcopyend failed");
687: RETURN_FALSE;
688: }
689:
690: while ((pgsql_result = PQgetResult(H->server))) {
691: if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
692: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
693: command_failed = 1;
694: }
695: PQclear(pgsql_result);
696: }
697:
698: RETURN_BOOL(!command_failed);
699: } else {
700: PQclear(pgsql_result);
701: php_stream_close(stream);
702: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
703: RETURN_FALSE;
704: }
705: }
706: /* }}} */
707:
708:
709: /* {{{ proto string PDO::pgsqlCopyToFile(string $table_name , $filename, [string $delimiter [, string $null_as [, string $fields]]])
710: Returns true if the copy worked fine or false if error */
711: static PHP_METHOD(PDO, pgsqlCopyToFile)
712: {
713: pdo_dbh_t *dbh;
714: pdo_pgsql_db_handle *H;
715:
716: char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL;
717: int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len;
718: char *query;
719:
720: PGresult *pgsql_result;
721: ExecStatusType status;
722:
723: php_stream *stream;
724:
1.1.1.2 misho 725: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sp|sss",
1.1 misho 726: &table_name, &table_name_len, &filename, &filename_len,
727: &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
728: return;
729: }
730:
731: dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
732: PDO_CONSTRUCT_CHECK;
733:
734: H = (pdo_pgsql_db_handle *)dbh->driver_data;
735:
736: stream = php_stream_open_wrapper_ex(filename, "wb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, FG(default_context));
737: if (!stream) {
738: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to open the file for writing");
739: RETURN_FALSE;
740: }
741:
742: while ((pgsql_result = PQgetResult(H->server))) {
743: PQclear(pgsql_result);
744: }
745:
746: if (pg_fields) {
747: 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"));
748: } else {
749: 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"));
750: }
751: pgsql_result = PQexec(H->server, query);
752: efree(query);
753:
754: if (pgsql_result) {
755: status = PQresultStatus(pgsql_result);
756: } else {
757: status = (ExecStatusType) PQstatus(H->server);
758: }
759:
760: if (status == PGRES_COPY_OUT && pgsql_result) {
761: PQclear(pgsql_result);
762: while (1) {
763: char *csv = NULL;
764: int ret = PQgetCopyData(H->server, &csv, 0);
765:
766: if (ret == -1) {
767: break; /* done */
768: } else if (ret > 0) {
769: if (php_stream_write(stream, csv, ret) != ret) {
770: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to write to file");
771: PQfreemem(csv);
772: php_stream_close(stream);
773: RETURN_FALSE;
774: } else {
775: PQfreemem(csv);
776: }
777: } else {
778: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed: getline failed");
779: php_stream_close(stream);
780: RETURN_FALSE;
781: }
782: }
783: php_stream_close(stream);
784:
785: while ((pgsql_result = PQgetResult(H->server))) {
786: PQclear(pgsql_result);
787: }
788: RETURN_TRUE;
789: } else {
790: php_stream_close(stream);
791: PQclear(pgsql_result);
792: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
793: RETURN_FALSE;
794: }
795: }
796: /* }}} */
797:
798: /* {{{ proto string PDO::pgsqlCopyToArray(string $table_name , [string $delimiter [, string $null_as [, string $fields]]])
799: Returns true if the copy worked fine or false if error */
800: static PHP_METHOD(PDO, pgsqlCopyToArray)
801: {
802: pdo_dbh_t *dbh;
803: pdo_pgsql_db_handle *H;
804:
805: char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
806: int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
807: char *query;
808:
809: PGresult *pgsql_result;
810: ExecStatusType status;
811:
812: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|sss",
813: &table_name, &table_name_len,
814: &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
815: return;
816: }
817:
818: dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
819: PDO_CONSTRUCT_CHECK;
820:
821: H = (pdo_pgsql_db_handle *)dbh->driver_data;
822:
823: while ((pgsql_result = PQgetResult(H->server))) {
824: PQclear(pgsql_result);
825: }
826:
827: if (pg_fields) {
828: 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"));
829: } else {
830: 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"));
831: }
832: pgsql_result = PQexec(H->server, query);
833: efree(query);
834:
835: if (pgsql_result) {
836: status = PQresultStatus(pgsql_result);
837: } else {
838: status = (ExecStatusType) PQstatus(H->server);
839: }
840:
841: if (status == PGRES_COPY_OUT && pgsql_result) {
842: PQclear(pgsql_result);
843: array_init(return_value);
844:
845: while (1) {
846: char *csv = NULL;
847: int ret = PQgetCopyData(H->server, &csv, 0);
848: if (ret == -1) {
849: break; /* copy done */
850: } else if (ret > 0) {
851: add_next_index_stringl(return_value, csv, ret, 1);
852: PQfreemem(csv);
853: } else {
854: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed: getline failed");
855: RETURN_FALSE;
856: }
857: }
858:
859: while ((pgsql_result = PQgetResult(H->server))) {
860: PQclear(pgsql_result);
861: }
862: } else {
863: PQclear(pgsql_result);
864: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
865: RETURN_FALSE;
866: }
867: }
868: /* }}} */
869:
870:
871: /* {{{ proto string PDO::pgsqlLOBCreate()
872: Creates a new large object, returning its identifier. Must be called inside a transaction. */
873: static PHP_METHOD(PDO, pgsqlLOBCreate)
874: {
875: pdo_dbh_t *dbh;
876: pdo_pgsql_db_handle *H;
877: Oid lfd;
878:
879: dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
880: PDO_CONSTRUCT_CHECK;
881:
882: H = (pdo_pgsql_db_handle *)dbh->driver_data;
883: lfd = lo_creat(H->server, INV_READ|INV_WRITE);
884:
885: if (lfd != InvalidOid) {
886: char *buf;
887: spprintf(&buf, 0, "%lu", (long) lfd);
888: RETURN_STRING(buf, 0);
889: }
890:
891: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000");
892: RETURN_FALSE;
893: }
894: /* }}} */
895:
896: /* {{{ proto resource PDO::pgsqlLOBOpen(string oid [, string mode = 'rb'])
897: Opens an existing large object stream. Must be called inside a transaction. */
898: static PHP_METHOD(PDO, pgsqlLOBOpen)
899: {
900: pdo_dbh_t *dbh;
901: pdo_pgsql_db_handle *H;
902: Oid oid;
903: int lfd;
904: char *oidstr;
905: int oidstrlen;
906: char *modestr = "rb";
907: int modestrlen;
908: int mode = INV_READ;
909: char *end_ptr;
910:
911: if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s",
912: &oidstr, &oidstrlen, &modestr, &modestrlen)) {
913: RETURN_FALSE;
914: }
915:
916: oid = (Oid)strtoul(oidstr, &end_ptr, 10);
917: if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {
918: RETURN_FALSE;
919: }
920:
921: if (strpbrk(modestr, "+w")) {
922: mode = INV_READ|INV_WRITE;
923: }
924:
925: dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
926: PDO_CONSTRUCT_CHECK;
927:
928: H = (pdo_pgsql_db_handle *)dbh->driver_data;
929:
930: lfd = lo_open(H->server, oid, mode);
931:
932: if (lfd >= 0) {
933: php_stream *stream = pdo_pgsql_create_lob_stream(dbh, lfd, oid TSRMLS_CC);
934: if (stream) {
935: php_stream_to_zval(stream, return_value);
936: return;
937: }
938: } else {
939: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000");
940: }
941: RETURN_FALSE;
942: }
943: /* }}} */
944:
945: /* {{{ proto bool PDO::pgsqlLOBUnlink(string oid)
946: Deletes the large object identified by oid. Must be called inside a transaction. */
947: static PHP_METHOD(PDO, pgsqlLOBUnlink)
948: {
949: pdo_dbh_t *dbh;
950: pdo_pgsql_db_handle *H;
951: Oid oid;
952: char *oidstr, *end_ptr;
953: int oidlen;
954:
955: if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
956: &oidstr, &oidlen)) {
957: RETURN_FALSE;
958: }
959:
960: oid = (Oid)strtoul(oidstr, &end_ptr, 10);
961: if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {
962: RETURN_FALSE;
963: }
964:
965: dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
966: PDO_CONSTRUCT_CHECK;
967:
968: H = (pdo_pgsql_db_handle *)dbh->driver_data;
969:
970: if (1 == lo_unlink(H->server, oid)) {
971: RETURN_TRUE;
972: }
973: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000");
974: RETURN_FALSE;
975: }
976: /* }}} */
977:
978:
979: static const zend_function_entry dbh_methods[] = {
980: PHP_ME(PDO, pgsqlLOBCreate, NULL, ZEND_ACC_PUBLIC)
981: PHP_ME(PDO, pgsqlLOBOpen, NULL, ZEND_ACC_PUBLIC)
982: PHP_ME(PDO, pgsqlLOBUnlink, NULL, ZEND_ACC_PUBLIC)
983: PHP_ME(PDO, pgsqlCopyFromArray, NULL, ZEND_ACC_PUBLIC)
984: PHP_ME(PDO, pgsqlCopyFromFile, NULL, ZEND_ACC_PUBLIC)
985: PHP_ME(PDO, pgsqlCopyToArray, NULL, ZEND_ACC_PUBLIC)
986: PHP_ME(PDO, pgsqlCopyToFile, NULL, ZEND_ACC_PUBLIC)
987: PHP_FE_END
988: };
989:
990: static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind TSRMLS_DC)
991: {
992: switch (kind) {
993: case PDO_DBH_DRIVER_METHOD_KIND_DBH:
994: return dbh_methods;
995: default:
996: return NULL;
997: }
998: }
999:
1000: static int pdo_pgsql_set_attr(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC)
1001: {
1002: pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
1003:
1004: switch (attr) {
1005: #if HAVE_PQPREPARE
1006: case PDO_ATTR_EMULATE_PREPARES:
1007: H->emulate_prepares = Z_LVAL_P(val);
1008: return 1;
1009: case PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT:
1010: H->disable_native_prepares = Z_LVAL_P(val);
1011: return 1;
1012: #endif
1013:
1014: default:
1015: return 0;
1016: }
1017: }
1018:
1019: static struct pdo_dbh_methods pgsql_methods = {
1020: pgsql_handle_closer,
1021: pgsql_handle_preparer,
1022: pgsql_handle_doer,
1023: pgsql_handle_quoter,
1024: pgsql_handle_begin,
1025: pgsql_handle_commit,
1026: pgsql_handle_rollback,
1027: pdo_pgsql_set_attr,
1028: pdo_pgsql_last_insert_id,
1029: pdo_pgsql_fetch_error_func,
1030: pdo_pgsql_get_attribute,
1031: pdo_pgsql_check_liveness, /* check_liveness */
1032: pdo_pgsql_get_driver_methods, /* get_driver_methods */
1033: NULL,
1.1.1.2 misho 1034: pgsql_handle_in_transaction,
1.1 misho 1035: };
1036:
1037: static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC) /* {{{ */
1038: {
1039: pdo_pgsql_db_handle *H;
1040: int ret = 0;
1041: char *conn_str, *p, *e;
1042: long connect_timeout = 30;
1043:
1044: H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent);
1045: dbh->driver_data = H;
1046:
1047: H->einfo.errcode = 0;
1048: H->einfo.errmsg = NULL;
1049:
1050: /* PostgreSQL wants params in the connect string to be separated by spaces,
1051: * if the PDO standard semicolons are used, we convert them to spaces
1052: */
1053: e = (char *) dbh->data_source + strlen(dbh->data_source);
1054: p = (char *) dbh->data_source;
1055: while ((p = memchr(p, ';', (e - p)))) {
1056: *p = ' ';
1057: }
1058:
1059: if (driver_options) {
1060: connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30 TSRMLS_CC);
1061: }
1062:
1063: /* support both full connection string & connection string + login and/or password */
1064: if (dbh->username && dbh->password) {
1065: spprintf(&conn_str, 0, "%s user=%s password=%s connect_timeout=%ld", dbh->data_source, dbh->username, dbh->password, connect_timeout);
1066: } else if (dbh->username) {
1067: spprintf(&conn_str, 0, "%s user=%s connect_timeout=%ld", dbh->data_source, dbh->username, connect_timeout);
1068: } else if (dbh->password) {
1069: spprintf(&conn_str, 0, "%s password=%s connect_timeout=%ld", dbh->data_source, dbh->password, connect_timeout);
1070: } else {
1071: spprintf(&conn_str, 0, "%s connect_timeout=%ld", (char *) dbh->data_source, connect_timeout);
1072: }
1073:
1074: H->server = PQconnectdb(conn_str);
1075:
1076: efree(conn_str);
1077:
1078: if (PQstatus(H->server) != CONNECTION_OK) {
1079: pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE);
1080: goto cleanup;
1081: }
1082:
1083: PQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)&dbh);
1084:
1085: H->attached = 1;
1086: H->pgoid = -1;
1087:
1088: dbh->methods = &pgsql_methods;
1089: dbh->alloc_own_columns = 1;
1090: dbh->max_escaped_char_length = 2;
1091:
1092: ret = 1;
1093:
1094: cleanup:
1095: dbh->methods = &pgsql_methods;
1096: if (!ret) {
1097: pgsql_handle_closer(dbh TSRMLS_CC);
1098: }
1099:
1100: return ret;
1101: }
1102: /* }}} */
1103:
1104: pdo_driver_t pdo_pgsql_driver = {
1105: PDO_DRIVER_HEADER(pgsql),
1106: pdo_pgsql_handle_factory
1107: };
1108:
1109: /*
1110: * Local variables:
1111: * tab-width: 4
1112: * c-basic-offset: 4
1113: * End:
1114: * vim600: noet sw=4 ts=4 fdm=marker
1115: * vim<600: noet sw=4 ts=4
1116: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>