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.

540 lines
13 KiB

21 years ago
21 years ago
21 years ago
21 years ago
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 5 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2006 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. | Author: Edin Kadribasic <edink@emini.dk> |
  16. +----------------------------------------------------------------------+
  17. */
  18. /* $Id$ */
  19. #ifdef HAVE_CONFIG_H
  20. #include "config.h"
  21. #endif
  22. #include "php.h"
  23. #include "php_ini.h"
  24. #include "ext/standard/info.h"
  25. #include "pdo/php_pdo.h"
  26. #include "pdo/php_pdo_driver.h"
  27. #include "php_pdo_pgsql.h"
  28. #include "php_pdo_pgsql_int.h"
  29. /* from postgresql/src/include/catalog/pg_type.h */
  30. #define BOOLOID 16
  31. #define BYTEAOID 17
  32. #define INT8OID 20
  33. #define INT2OID 21
  34. #define INT4OID 23
  35. #define OIDOID 26
  36. static int pgsql_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC)
  37. {
  38. pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
  39. if (S->result) {
  40. /* free the resource */
  41. PQclear(S->result);
  42. S->result = NULL;
  43. }
  44. #if HAVE_PQPREPARE
  45. if (S->stmt_name) {
  46. pdo_pgsql_db_handle *H = S->H;
  47. char *q = NULL;
  48. PGresult *res;
  49. if (S->is_prepared) {
  50. spprintf(&q, 0, "DEALLOCATE %s", S->stmt_name);
  51. res = PQexec(H->server, q);
  52. efree(q);
  53. if (res) {
  54. PQclear(res);
  55. }
  56. }
  57. efree(S->stmt_name);
  58. S->stmt_name = NULL;
  59. }
  60. if (S->param_lengths) {
  61. efree(S->param_lengths);
  62. S->param_lengths = NULL;
  63. }
  64. if (S->param_values) {
  65. efree(S->param_values);
  66. S->param_values = NULL;
  67. }
  68. if (S->param_formats) {
  69. efree(S->param_formats);
  70. S->param_formats = NULL;
  71. }
  72. #endif
  73. if (S->cursor_name) {
  74. pdo_pgsql_db_handle *H = S->H;
  75. char *q = NULL;
  76. PGresult *res;
  77. spprintf(&q, 0, "CLOSE %s", S->cursor_name);
  78. res = PQexec(H->server, q);
  79. efree(q);
  80. if (res) PQclear(res);
  81. efree(S->cursor_name);
  82. S->cursor_name = NULL;
  83. }
  84. if(S->cols) {
  85. efree(S->cols);
  86. S->cols = NULL;
  87. }
  88. efree(S);
  89. stmt->driver_data = NULL;
  90. return 1;
  91. }
  92. static int pgsql_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC)
  93. {
  94. pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
  95. pdo_pgsql_db_handle *H = S->H;
  96. ExecStatusType status;
  97. if (stmt->executed) {
  98. /* ensure that we free any previous unfetched results */
  99. if(S->result) {
  100. PQclear(S->result);
  101. S->result = NULL;
  102. }
  103. }
  104. S->current_row = 0;
  105. #if HAVE_PQPREPARE
  106. if (S->stmt_name) {
  107. /* using a prepared statement */
  108. S->result = PQexecPrepared(H->server, S->stmt_name,
  109. stmt->bound_params ?
  110. zend_hash_num_elements(stmt->bound_params) :
  111. 0,
  112. (const char**)S->param_values,
  113. S->param_lengths,
  114. NULL,
  115. 0);
  116. } else
  117. #endif
  118. if (S->cursor_name) {
  119. char *q = NULL;
  120. spprintf(&q, 0, "DECLARE %s CURSOR FOR %s", S->cursor_name, stmt->active_query_string);
  121. S->result = PQexec(H->server, q);
  122. efree(q);
  123. } else {
  124. S->result = PQexec(H->server, stmt->active_query_string);
  125. }
  126. status = PQresultStatus(S->result);
  127. if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
  128. pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result));
  129. return 0;
  130. }
  131. if(!stmt->executed) {
  132. stmt->column_count = (int) PQnfields(S->result);
  133. S->cols = ecalloc(stmt->column_count, sizeof(pdo_pgsql_column));
  134. }
  135. if (status == PGRES_COMMAND_OK) {
  136. stmt->row_count = (long)atoi(PQcmdTuples(S->result));
  137. H->pgoid = PQoidValue(S->result);
  138. } else {
  139. stmt->row_count = (long)PQntuples(S->result);
  140. }
  141. return 1;
  142. }
  143. static int pgsql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param,
  144. enum pdo_param_event event_type TSRMLS_DC)
  145. {
  146. pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
  147. #if HAVE_PQPREPARE
  148. if (S->stmt_name && param->is_param) {
  149. switch (event_type) {
  150. case PDO_PARAM_EVT_ALLOC:
  151. /* decode name from $1, $2 into 0, 1 etc. */
  152. if (param->name) {
  153. if (param->name[0] == '$') {
  154. param->paramno = atoi(param->name + 1);
  155. } else {
  156. /* resolve parameter name to rewritten name */
  157. char *nameptr;
  158. if (stmt->bound_param_map && SUCCESS == zend_hash_find(stmt->bound_param_map,
  159. param->name, param->namelen + 1, (void**)&nameptr)) {
  160. param->paramno = atoi(nameptr + 1) - 1;
  161. } else {
  162. pdo_pgsql_error_stmt(stmt, PGRES_FATAL_ERROR, "HY093");
  163. return 0;
  164. }
  165. }
  166. }
  167. break;
  168. case PDO_PARAM_EVT_EXEC_PRE:
  169. if (!S->param_values) {
  170. S->param_values = ecalloc(
  171. zend_hash_num_elements(stmt->bound_param_map),
  172. sizeof(char*));
  173. S->param_lengths = ecalloc(
  174. zend_hash_num_elements(stmt->bound_param_map),
  175. sizeof(int));
  176. S->param_formats = ecalloc(
  177. zend_hash_num_elements(stmt->bound_param_map),
  178. sizeof(int));
  179. }
  180. if (param->paramno >= 0) {
  181. if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL ||
  182. Z_TYPE_P(param->parameter) == IS_NULL) {
  183. S->param_values[param->paramno] = NULL;
  184. S->param_lengths[param->paramno] = 0;
  185. } else if (Z_TYPE_P(param->parameter) == IS_BOOL) {
  186. S->param_values[param->paramno] = Z_BVAL_P(param->parameter) ? "t" : "f";
  187. S->param_lengths[param->paramno] = 1;
  188. S->param_formats[param->paramno] = 1;
  189. } else {
  190. convert_to_string(param->parameter);
  191. S->param_values[param->paramno] = Z_STRVAL_P(param->parameter);
  192. S->param_lengths[param->paramno] = Z_STRLEN_P(param->parameter);
  193. S->param_formats[param->paramno] = 1;
  194. }
  195. }
  196. break;
  197. }
  198. }
  199. #endif
  200. return 1;
  201. }
  202. static int pgsql_stmt_fetch(pdo_stmt_t *stmt,
  203. enum pdo_fetch_orientation ori, long offset TSRMLS_DC)
  204. {
  205. pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
  206. if (S->cursor_name) {
  207. char *ori_str;
  208. char *q = NULL;
  209. ExecStatusType status;
  210. switch (ori) {
  211. case PDO_FETCH_ORI_NEXT: ori_str = "FORWARD"; break;
  212. case PDO_FETCH_ORI_PRIOR: ori_str = "BACKWARD"; break;
  213. case PDO_FETCH_ORI_REL: ori_str = "RELATIVE"; break;
  214. default:
  215. return 0;
  216. }
  217. spprintf(&q, 0, "FETCH %s %ld FROM %s", ori_str, offset, S->cursor_name);
  218. S->result = PQexec(S->H->server, q);
  219. efree(q);
  220. status = PQresultStatus(S->result);
  221. if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
  222. pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result));
  223. return 0;
  224. }
  225. S->current_row = 1;
  226. return 1;
  227. } else {
  228. if (S->current_row < stmt->row_count) {
  229. S->current_row++;
  230. return 1;
  231. } else {
  232. return 0;
  233. }
  234. }
  235. }
  236. static int pgsql_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC)
  237. {
  238. pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
  239. struct pdo_column_data *cols = stmt->columns;
  240. if (!S->result) {
  241. return 0;
  242. }
  243. cols[colno].name = estrdup(PQfname(S->result, colno));
  244. cols[colno].namelen = strlen(cols[colno].name);
  245. cols[colno].maxlen = PQfsize(S->result, colno);
  246. cols[colno].precision = PQfmod(S->result, colno);
  247. S->cols[colno].pgsql_type = PQftype(S->result, colno);
  248. switch(S->cols[colno].pgsql_type) {
  249. case BOOLOID:
  250. cols[colno].param_type = PDO_PARAM_BOOL;
  251. break;
  252. case INT2OID:
  253. case INT4OID:
  254. case OIDOID:
  255. cols[colno].param_type = PDO_PARAM_INT;
  256. break;
  257. case INT8OID:
  258. if (sizeof(long)>=8) {
  259. cols[colno].param_type = PDO_PARAM_INT;
  260. } else {
  261. cols[colno].param_type = PDO_PARAM_STR;
  262. }
  263. break;
  264. case BYTEAOID:
  265. cols[colno].param_type = PDO_PARAM_LOB;
  266. break;
  267. default:
  268. cols[colno].param_type = PDO_PARAM_STR;
  269. }
  270. return 1;
  271. }
  272. /* PQunescapeBytea() from PostgreSQL 7.3 to provide bytea unescape feature to 7.2 users.
  273. Renamed to php_pdo_pgsql_unescape_bytea() */
  274. /*
  275. * PQunescapeBytea - converts the null terminated string representation
  276. * of a bytea, strtext, into binary, filling a buffer. It returns a
  277. * pointer to the buffer which is NULL on error, and the size of the
  278. * buffer in retbuflen. The pointer may subsequently be used as an
  279. * argument to the function free(3). It is the reverse of PQescapeBytea.
  280. *
  281. * The following transformations are reversed:
  282. * '\0' == ASCII 0 == \000
  283. * '\'' == ASCII 39 == \'
  284. * '\\' == ASCII 92 == \\
  285. *
  286. * States:
  287. * 0 normal 0->1->2->3->4
  288. * 1 \ 1->5
  289. * 2 \0 1->6
  290. * 3 \00
  291. * 4 \000
  292. * 5 \'
  293. * 6 \\
  294. */
  295. static unsigned char *php_pdo_pgsql_unescape_bytea(unsigned char *strtext, size_t *retbuflen)
  296. {
  297. size_t buflen;
  298. unsigned char *buffer,
  299. *sp,
  300. *bp;
  301. unsigned int state = 0;
  302. if (strtext == NULL)
  303. return NULL;
  304. buflen = strlen(strtext); /* will shrink, also we discover if
  305. * strtext */
  306. buffer = (unsigned char *) emalloc(buflen); /* isn't NULL terminated */
  307. for (bp = buffer, sp = strtext; *sp != '\0'; bp++, sp++)
  308. {
  309. switch (state)
  310. {
  311. case 0:
  312. if (*sp == '\\')
  313. state = 1;
  314. *bp = *sp;
  315. break;
  316. case 1:
  317. if (*sp == '\'') /* state=5 */
  318. { /* replace \' with 39 */
  319. bp--;
  320. *bp = '\'';
  321. buflen--;
  322. state = 0;
  323. }
  324. else if (*sp == '\\') /* state=6 */
  325. { /* replace \\ with 92 */
  326. bp--;
  327. *bp = '\\';
  328. buflen--;
  329. state = 0;
  330. }
  331. else
  332. {
  333. if (isdigit(*sp))
  334. state = 2;
  335. else
  336. state = 0;
  337. *bp = *sp;
  338. }
  339. break;
  340. case 2:
  341. if (isdigit(*sp))
  342. state = 3;
  343. else
  344. state = 0;
  345. *bp = *sp;
  346. break;
  347. case 3:
  348. if (isdigit(*sp)) /* state=4 */
  349. {
  350. unsigned char *start, *end, buf[4]; /* 000 + '\0' */
  351. bp -= 3;
  352. memcpy(buf, sp-2, 3);
  353. buf[3] = '\0';
  354. start = buf;
  355. *bp = (unsigned char)strtoul(start, (char **)&end, 8);
  356. buflen -= 3;
  357. state = 0;
  358. }
  359. else
  360. {
  361. *bp = *sp;
  362. state = 0;
  363. }
  364. break;
  365. }
  366. }
  367. buffer = erealloc(buffer, buflen+1);
  368. buffer[buflen] = '\0';
  369. *retbuflen = buflen;
  370. return buffer;
  371. }
  372. static int pgsql_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, unsigned long *len, int *caller_frees TSRMLS_DC)
  373. {
  374. pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
  375. struct pdo_column_data *cols = stmt->columns;
  376. size_t tmp_len;
  377. if (!S->result) {
  378. return 0;
  379. }
  380. /* We have already increased count by 1 in pgsql_stmt_fetch() */
  381. if (PQgetisnull(S->result, S->current_row - 1, colno)) { /* Check if we got NULL */
  382. *ptr = NULL;
  383. *len = 0;
  384. } else {
  385. *ptr = PQgetvalue(S->result, S->current_row - 1, colno);
  386. *len = PQgetlength(S->result, S->current_row - 1, colno);
  387. switch(cols[colno].param_type) {
  388. case PDO_PARAM_INT:
  389. S->cols[colno].intval = atol(*ptr);
  390. *ptr = (char *) &(S->cols[colno].intval);
  391. *len = sizeof(long);
  392. break;
  393. case PDO_PARAM_BOOL:
  394. S->cols[colno].boolval = **ptr == 't' ? 1: 0;
  395. *ptr = (char *) &(S->cols[colno].boolval);
  396. *len = sizeof(zend_bool);
  397. break;
  398. case PDO_PARAM_LOB:
  399. *ptr = php_pdo_pgsql_unescape_bytea(*ptr, &tmp_len);
  400. *len = tmp_len;
  401. *caller_frees = 1;
  402. break;
  403. case PDO_PARAM_NULL:
  404. case PDO_PARAM_STR:
  405. case PDO_PARAM_STMT:
  406. case PDO_PARAM_INPUT_OUTPUT:
  407. break;
  408. }
  409. }
  410. return 1;
  411. }
  412. static int pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, long colno, zval *return_value TSRMLS_DC)
  413. {
  414. pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
  415. PGresult *res;
  416. char *q=NULL;
  417. ExecStatusType status;
  418. if (!S->result) {
  419. return FAILURE;
  420. }
  421. if (colno >= stmt->column_count) {
  422. return FAILURE;
  423. }
  424. array_init(return_value);
  425. add_assoc_long(return_value, "pgsql:oid", S->cols[colno].pgsql_type);
  426. /* Fetch metadata from Postgres system catalogue */
  427. spprintf(&q, 0, "SELECT TYPNAME FROM PG_TYPE WHERE OID=%d", S->cols[colno].pgsql_type);
  428. res = PQexec(S->H->server, q);
  429. efree(q);
  430. status = PQresultStatus(res);
  431. if (status != PGRES_TUPLES_OK) {
  432. /* Failed to get system catalogue, but return success
  433. * with the data we have collected so far
  434. */
  435. PQclear(res);
  436. return 1;
  437. }
  438. /* We want exactly one row returned */
  439. if (1 != PQntuples(res)) {
  440. PQclear(res);
  441. return 1;
  442. }
  443. add_assoc_string(return_value, "native_type", PQgetvalue(res, 0, 0), 1);
  444. PQclear(res);
  445. return 1;
  446. }
  447. static int pdo_pgsql_stmt_cursor_closer(pdo_stmt_t *stmt TSRMLS_DC)
  448. {
  449. #if HAVE_PQPREPARE
  450. return 1;
  451. #endif
  452. }
  453. struct pdo_stmt_methods pgsql_stmt_methods = {
  454. pgsql_stmt_dtor,
  455. pgsql_stmt_execute,
  456. pgsql_stmt_fetch,
  457. pgsql_stmt_describe,
  458. pgsql_stmt_get_col,
  459. pgsql_stmt_param_hook,
  460. NULL, /* set_attr */
  461. NULL, /* get_attr */
  462. pgsql_stmt_get_column_meta,
  463. NULL, /* next_rowset */
  464. pdo_pgsql_stmt_cursor_closer
  465. };
  466. /*
  467. * Local variables:
  468. * tab-width: 4
  469. * c-basic-offset: 4
  470. * End:
  471. * vim600: noet sw=4 ts=4 fdm=marker
  472. * vim<600: noet sw=4 ts=4
  473. */