You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1116 lines
31 KiB

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