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.

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