xref: /PHP-7.3/ext/pgsql/pgsql.c (revision c7a86a38)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2018 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: Zeev Suraski <zeev@php.net>                                 |
16    |          Jouni Ahto <jouni.ahto@exdec.fi>                            |
17    |          Yasuo Ohgaki <yohgaki@php.net>                              |
18    |          Youichi Iwakiri <yiwakiri@st.rim.or.jp> (pg_copy_*)         |
19    |          Chris Kings-Lynne <chriskl@php.net> (v3 protocol)           |
20    +----------------------------------------------------------------------+
21  */
22 
23 #include <stdlib.h>
24 
25 #define PHP_PGSQL_PRIVATE 1
26 
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30 
31 #define SMART_STR_PREALLOC 512
32 
33 #include "php.h"
34 #include "php_ini.h"
35 #include "ext/standard/php_standard.h"
36 #include "zend_smart_str.h"
37 #include "ext/pcre/php_pcre.h"
38 #ifdef PHP_WIN32
39 # include "win32/time.h"
40 #endif
41 
42 #undef PACKAGE_BUGREPORT
43 #undef PACKAGE_NAME
44 #undef PACKAGE_STRING
45 #undef PACKAGE_TARNAME
46 #undef PACKAGE_VERSION
47 #include "php_pgsql.h"
48 #include "php_globals.h"
49 #include "zend_exceptions.h"
50 
51 #if HAVE_PGSQL
52 
53 #ifndef InvalidOid
54 #define InvalidOid ((Oid) 0)
55 #endif
56 
57 #define PGSQL_ASSOC           1<<0
58 #define PGSQL_NUM             1<<1
59 #define PGSQL_BOTH            (PGSQL_ASSOC|PGSQL_NUM)
60 
61 #define PGSQL_NOTICE_LAST     1  /* Get the last notice */
62 #define PGSQL_NOTICE_ALL      2  /* Get all notices */
63 #define PGSQL_NOTICE_CLEAR    3  /* Remove notices */
64 
65 #define PGSQL_STATUS_LONG     1
66 #define PGSQL_STATUS_STRING   2
67 
68 #define PGSQL_MAX_LENGTH_OF_LONG   30
69 #define PGSQL_MAX_LENGTH_OF_DOUBLE 60
70 
71 #if ZEND_LONG_MAX < UINT_MAX
72 #define PGSQL_RETURN_OID(oid) do { \
73 	if (oid > ZEND_LONG_MAX) { \
74 		smart_str s = {0}; \
75 		smart_str_append_unsigned(&s, oid); \
76 		smart_str_0(&s); \
77 		RETURN_NEW_STR(s.s); \
78 	} \
79 	RETURN_LONG((zend_long)oid); \
80 } while(0)
81 #else
82 #define PGSQL_RETURN_OID(oid) RETURN_LONG((zend_long)oid)
83 #endif
84 
85 #if HAVE_PQSETNONBLOCKING
86 #define PQ_SETNONBLOCKING(pg_link, flag) PQsetnonblocking(pg_link, flag)
87 #else
88 #define PQ_SETNONBLOCKING(pg_link, flag) 0
89 #endif
90 
91 #define CHECK_DEFAULT_LINK(x) if ((x) == NULL) { php_error_docref(NULL, E_WARNING, "No PostgreSQL link opened yet"); RETURN_FALSE; }
92 #define FETCH_DEFAULT_LINK()  PGG(default_link)
93 
94 #ifndef HAVE_PQFREEMEM
95 #define PQfreemem free
96 #endif
97 
98 ZEND_DECLARE_MODULE_GLOBALS(pgsql)
99 static PHP_GINIT_FUNCTION(pgsql);
100 
101 /* {{{ arginfo */
102 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connect, 0, 0, 1)
103 	ZEND_ARG_INFO(0, connection_string)
104 	ZEND_ARG_INFO(0, connect_type)
105 	ZEND_ARG_INFO(0, host)
106 	ZEND_ARG_INFO(0, port)
107 	ZEND_ARG_INFO(0, options)
108 	ZEND_ARG_INFO(0, tty)
109 	ZEND_ARG_INFO(0, database)
110 ZEND_END_ARG_INFO()
111 
112 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_pconnect, 0, 0, 1)
113 	ZEND_ARG_INFO(0, connection_string)
114 	ZEND_ARG_INFO(0, host)
115 	ZEND_ARG_INFO(0, port)
116 	ZEND_ARG_INFO(0, options)
117 	ZEND_ARG_INFO(0, tty)
118 	ZEND_ARG_INFO(0, database)
119 ZEND_END_ARG_INFO()
120 
121 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connect_poll, 0, 0, 0)
122 	ZEND_ARG_INFO(0, connection)
123 ZEND_END_ARG_INFO()
124 
125 #if HAVE_PQPARAMETERSTATUS
126 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_parameter_status, 0, 0, 1)
127 	ZEND_ARG_INFO(0, connection)
128 	ZEND_ARG_INFO(0, param_name)
129 ZEND_END_ARG_INFO()
130 #endif
131 
132 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_close, 0, 0, 0)
133 	ZEND_ARG_INFO(0, connection)
134 ZEND_END_ARG_INFO()
135 
136 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_dbname, 0, 0, 0)
137 	ZEND_ARG_INFO(0, connection)
138 ZEND_END_ARG_INFO()
139 
140 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_last_error, 0, 0, 0)
141 	ZEND_ARG_INFO(0, connection)
142 ZEND_END_ARG_INFO()
143 
144 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_options, 0, 0, 0)
145 	ZEND_ARG_INFO(0, connection)
146 ZEND_END_ARG_INFO()
147 
148 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_port, 0, 0, 0)
149 	ZEND_ARG_INFO(0, connection)
150 ZEND_END_ARG_INFO()
151 
152 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_tty, 0, 0, 0)
153 	ZEND_ARG_INFO(0, connection)
154 ZEND_END_ARG_INFO()
155 
156 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_host, 0, 0, 0)
157 	ZEND_ARG_INFO(0, connection)
158 ZEND_END_ARG_INFO()
159 
160 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_version, 0, 0, 0)
161 	ZEND_ARG_INFO(0, connection)
162 ZEND_END_ARG_INFO()
163 
164 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_ping, 0, 0, 0)
165 	ZEND_ARG_INFO(0, connection)
166 ZEND_END_ARG_INFO()
167 
168 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_query, 0, 0, 0)
169 	ZEND_ARG_INFO(0, connection)
170 	ZEND_ARG_INFO(0, query)
171 ZEND_END_ARG_INFO()
172 
173 #if HAVE_PQEXECPARAMS
174 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_query_params, 0, 0, 0)
175 	ZEND_ARG_INFO(0, connection)
176 	ZEND_ARG_INFO(0, query)
177 	ZEND_ARG_INFO(0, params)
178 ZEND_END_ARG_INFO()
179 #endif
180 
181 #if HAVE_PQPREPARE
182 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_prepare, 0, 0, 0)
183 	ZEND_ARG_INFO(0, connection)
184 	ZEND_ARG_INFO(0, stmtname)
185 	ZEND_ARG_INFO(0, query)
186 ZEND_END_ARG_INFO()
187 #endif
188 
189 #if HAVE_PQEXECPREPARED
190 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_execute, 0, 0, 0)
191 	ZEND_ARG_INFO(0, connection)
192 	ZEND_ARG_INFO(0, stmtname)
193 	ZEND_ARG_INFO(0, params)
194 ZEND_END_ARG_INFO()
195 #endif
196 
197 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_num_rows, 0, 0, 1)
198 	ZEND_ARG_INFO(0, result)
199 ZEND_END_ARG_INFO()
200 
201 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_num_fields, 0, 0, 1)
202 	ZEND_ARG_INFO(0, result)
203 ZEND_END_ARG_INFO()
204 
205 #if HAVE_PQCMDTUPLES
206 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_affected_rows, 0, 0, 1)
207 	ZEND_ARG_INFO(0, result)
208 ZEND_END_ARG_INFO()
209 #endif
210 
211 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_last_notice, 0, 0, 1)
212 	ZEND_ARG_INFO(0, connection)
213 	ZEND_ARG_INFO(0, option)
214 ZEND_END_ARG_INFO()
215 
216 #ifdef HAVE_PQFTABLE
217 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_table, 0, 0, 2)
218 	ZEND_ARG_INFO(0, result)
219 	ZEND_ARG_INFO(0, field_number)
220 	ZEND_ARG_INFO(0, oid_only)
221 ZEND_END_ARG_INFO()
222 #endif
223 
224 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_name, 0, 0, 2)
225 	ZEND_ARG_INFO(0, result)
226 	ZEND_ARG_INFO(0, field_number)
227 ZEND_END_ARG_INFO()
228 
229 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_size, 0, 0, 2)
230 	ZEND_ARG_INFO(0, result)
231 	ZEND_ARG_INFO(0, field_number)
232 ZEND_END_ARG_INFO()
233 
234 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_type, 0, 0, 2)
235 	ZEND_ARG_INFO(0, result)
236 	ZEND_ARG_INFO(0, field_number)
237 ZEND_END_ARG_INFO()
238 
239 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_type_oid, 0, 0, 2)
240 	ZEND_ARG_INFO(0, result)
241 	ZEND_ARG_INFO(0, field_number)
242 ZEND_END_ARG_INFO()
243 
244 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_num, 0, 0, 2)
245 	ZEND_ARG_INFO(0, result)
246 	ZEND_ARG_INFO(0, field_name)
247 ZEND_END_ARG_INFO()
248 
249 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_result, 0, 0, 1)
250 	ZEND_ARG_INFO(0, result)
251 	ZEND_ARG_INFO(0, row_number)
252 	ZEND_ARG_INFO(0, field_name)
253 ZEND_END_ARG_INFO()
254 
255 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_row, 0, 0, 1)
256 	ZEND_ARG_INFO(0, result)
257 	ZEND_ARG_INFO(0, row)
258 	ZEND_ARG_INFO(0, result_type)
259 ZEND_END_ARG_INFO()
260 
261 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_assoc, 0, 0, 1)
262 	ZEND_ARG_INFO(0, result)
263 	ZEND_ARG_INFO(0, row)
264 ZEND_END_ARG_INFO()
265 
266 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_array, 0, 0, 1)
267 	ZEND_ARG_INFO(0, result)
268 	ZEND_ARG_INFO(0, row)
269 	ZEND_ARG_INFO(0, result_type)
270 ZEND_END_ARG_INFO()
271 
272 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_object, 0, 0, 1)
273 	ZEND_ARG_INFO(0, result)
274 	ZEND_ARG_INFO(0, row)
275 	ZEND_ARG_INFO(0, class_name)
276 	ZEND_ARG_INFO(0, l)
277 	ZEND_ARG_INFO(0, ctor_params)
278 ZEND_END_ARG_INFO()
279 
280 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_all, 0, 0, 1)
281 	ZEND_ARG_INFO(0, result)
282 	ZEND_ARG_INFO(0, result_type)
283 ZEND_END_ARG_INFO()
284 
285 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_all_columns, 0, 0, 1)
286 	ZEND_ARG_INFO(0, result)
287 	ZEND_ARG_INFO(0, column_number)
288 ZEND_END_ARG_INFO()
289 
290 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_result_seek, 0, 0, 2)
291 	ZEND_ARG_INFO(0, result)
292 	ZEND_ARG_INFO(0, offset)
293 ZEND_END_ARG_INFO()
294 
295 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_prtlen, 0, 0, 1)
296 	ZEND_ARG_INFO(0, result)
297 	ZEND_ARG_INFO(0, row)
298 	ZEND_ARG_INFO(0, field_name_or_number)
299 ZEND_END_ARG_INFO()
300 
301 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_is_null, 0, 0, 1)
302 	ZEND_ARG_INFO(0, result)
303 	ZEND_ARG_INFO(0, row)
304 	ZEND_ARG_INFO(0, field_name_or_number)
305 ZEND_END_ARG_INFO()
306 
307 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_free_result, 0, 0, 1)
308 	ZEND_ARG_INFO(0, result)
309 ZEND_END_ARG_INFO()
310 
311 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_last_oid, 0, 0, 1)
312 	ZEND_ARG_INFO(0, result)
313 ZEND_END_ARG_INFO()
314 
315 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_trace, 0, 0, 1)
316 	ZEND_ARG_INFO(0, filename)
317 	ZEND_ARG_INFO(0, mode)
318 	ZEND_ARG_INFO(0, connection)
319 ZEND_END_ARG_INFO()
320 
321 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_untrace, 0, 0, 0)
322 	ZEND_ARG_INFO(0, connection)
323 ZEND_END_ARG_INFO()
324 
325 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_create, 0, 0, 0)
326 	ZEND_ARG_INFO(0, connection)
327 	ZEND_ARG_INFO(0, large_object_id)
328 ZEND_END_ARG_INFO()
329 
330 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_unlink, 0, 0, 0)
331 	ZEND_ARG_INFO(0, connection)
332 	ZEND_ARG_INFO(0, large_object_oid)
333 ZEND_END_ARG_INFO()
334 
335 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_open, 0, 0, 0)
336 	ZEND_ARG_INFO(0, connection)
337 	ZEND_ARG_INFO(0, large_object_oid)
338 	ZEND_ARG_INFO(0, mode)
339 ZEND_END_ARG_INFO()
340 
341 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_close, 0, 0, 1)
342 	ZEND_ARG_INFO(0, large_object)
343 ZEND_END_ARG_INFO()
344 
345 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_read, 0, 0, 1)
346 	ZEND_ARG_INFO(0, large_object)
347 	ZEND_ARG_INFO(0, len)
348 ZEND_END_ARG_INFO()
349 
350 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_write, 0, 0, 2)
351 	ZEND_ARG_INFO(0, large_object)
352 	ZEND_ARG_INFO(0, buf)
353 	ZEND_ARG_INFO(0, len)
354 ZEND_END_ARG_INFO()
355 
356 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_read_all, 0, 0, 1)
357 	ZEND_ARG_INFO(0, large_object)
358 ZEND_END_ARG_INFO()
359 
360 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_import, 0, 0, 0)
361 	ZEND_ARG_INFO(0, connection)
362 	ZEND_ARG_INFO(0, filename)
363 	ZEND_ARG_INFO(0, large_object_oid)
364 ZEND_END_ARG_INFO()
365 
366 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_export, 0, 0, 0)
367 	ZEND_ARG_INFO(0, connection)
368 	ZEND_ARG_INFO(0, objoid)
369 	ZEND_ARG_INFO(0, filename)
370 ZEND_END_ARG_INFO()
371 
372 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_seek, 0, 0, 2)
373 	ZEND_ARG_INFO(0, large_object)
374 	ZEND_ARG_INFO(0, offset)
375 	ZEND_ARG_INFO(0, whence)
376 ZEND_END_ARG_INFO()
377 
378 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_tell, 0, 0, 1)
379 	ZEND_ARG_INFO(0, large_object)
380 ZEND_END_ARG_INFO()
381 
382 #if HAVE_PG_LO_TRUNCATE
383 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_truncate, 0, 0, 1)
384 	ZEND_ARG_INFO(0, large_object)
385 	ZEND_ARG_INFO(0, size)
386 ZEND_END_ARG_INFO()
387 #endif
388 
389 #if HAVE_PQSETERRORVERBOSITY
390 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_set_error_verbosity, 0, 0, 0)
391 	ZEND_ARG_INFO(0, connection)
392 	ZEND_ARG_INFO(0, verbosity)
393 ZEND_END_ARG_INFO()
394 #endif
395 
396 #if HAVE_PQCLIENTENCODING
397 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_set_client_encoding, 0, 0, 0)
398 	ZEND_ARG_INFO(0, connection)
399 	ZEND_ARG_INFO(0, encoding)
400 ZEND_END_ARG_INFO()
401 
402 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_client_encoding, 0, 0, 0)
403 	ZEND_ARG_INFO(0, connection)
404 ZEND_END_ARG_INFO()
405 #endif
406 
407 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_end_copy, 0, 0, 0)
408 	ZEND_ARG_INFO(0, connection)
409 ZEND_END_ARG_INFO()
410 
411 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_put_line, 0, 0, 0)
412 	ZEND_ARG_INFO(0, connection)
413 	ZEND_ARG_INFO(0, query)
414 ZEND_END_ARG_INFO()
415 
416 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_copy_to, 0, 0, 2)
417 	ZEND_ARG_INFO(0, connection)
418 	ZEND_ARG_INFO(0, table_name)
419 	ZEND_ARG_INFO(0, delimiter)
420 	ZEND_ARG_INFO(0, null_as)
421 ZEND_END_ARG_INFO()
422 
423 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_copy_from, 0, 0, 3)
424 	ZEND_ARG_INFO(0, connection)
425 	ZEND_ARG_INFO(0, table_name)
426 	ZEND_ARG_INFO(0, rows)
427 	ZEND_ARG_INFO(0, delimiter)
428 	ZEND_ARG_INFO(0, null_as)
429 ZEND_END_ARG_INFO()
430 
431 #if HAVE_PQESCAPE
432 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape_string, 0, 0, 0)
433 	ZEND_ARG_INFO(0, connection)
434 	ZEND_ARG_INFO(0, data)
435 ZEND_END_ARG_INFO()
436 
437 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape_bytea, 0, 0, 0)
438 	ZEND_ARG_INFO(0, connection)
439 	ZEND_ARG_INFO(0, data)
440 ZEND_END_ARG_INFO()
441 
442 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_unescape_bytea, 0, 0, 1)
443 	ZEND_ARG_INFO(0, data)
444 ZEND_END_ARG_INFO()
445 #endif
446 
447 #if HAVE_PQESCAPE
448 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape_literal, 0, 0, 0)
449 	ZEND_ARG_INFO(0, connection)
450 	ZEND_ARG_INFO(0, data)
451 ZEND_END_ARG_INFO()
452 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape_identifier, 0, 0, 0)
453 	ZEND_ARG_INFO(0, connection)
454 	ZEND_ARG_INFO(0, data)
455 ZEND_END_ARG_INFO()
456 #endif
457 
458 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_result_error, 0, 0, 1)
459 	ZEND_ARG_INFO(0, result)
460 ZEND_END_ARG_INFO()
461 
462 #if HAVE_PQRESULTERRORFIELD
463 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_result_error_field, 0, 0, 2)
464 	ZEND_ARG_INFO(0, result)
465 	ZEND_ARG_INFO(0, fieldcode)
466 ZEND_END_ARG_INFO()
467 #endif
468 
469 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connection_status, 0, 0, 1)
470 	ZEND_ARG_INFO(0, connection)
471 ZEND_END_ARG_INFO()
472 
473 #if HAVE_PGTRANSACTIONSTATUS
474 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_transaction_status, 0, 0, 1)
475 	ZEND_ARG_INFO(0, connection)
476 ZEND_END_ARG_INFO()
477 #endif
478 
479 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connection_reset, 0, 0, 1)
480 	ZEND_ARG_INFO(0, connection)
481 ZEND_END_ARG_INFO()
482 
483 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_cancel_query, 0, 0, 1)
484 	ZEND_ARG_INFO(0, connection)
485 ZEND_END_ARG_INFO()
486 
487 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connection_busy, 0, 0, 1)
488 	ZEND_ARG_INFO(0, connection)
489 ZEND_END_ARG_INFO()
490 
491 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_query, 0, 0, 2)
492 	ZEND_ARG_INFO(0, connection)
493 	ZEND_ARG_INFO(0, query)
494 ZEND_END_ARG_INFO()
495 
496 #if HAVE_PQSENDQUERYPARAMS
497 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_query_params, 0, 0, 3)
498 	ZEND_ARG_INFO(0, connection)
499 	ZEND_ARG_INFO(0, query)
500 	ZEND_ARG_INFO(0, params)
501 ZEND_END_ARG_INFO()
502 #endif
503 
504 #if HAVE_PQSENDPREPARE
505 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_prepare, 0, 0, 3)
506 	ZEND_ARG_INFO(0, connection)
507 	ZEND_ARG_INFO(0, stmtname)
508 	ZEND_ARG_INFO(0, query)
509 ZEND_END_ARG_INFO()
510 #endif
511 
512 #if HAVE_PQSENDQUERYPREPARED
513 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_execute, 0, 0, 3)
514 	ZEND_ARG_INFO(0, connection)
515 	ZEND_ARG_INFO(0, stmtname)
516 	ZEND_ARG_INFO(0, params)
517 ZEND_END_ARG_INFO()
518 #endif
519 
520 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_get_result, 0, 0, 1)
521 	ZEND_ARG_INFO(0, connection)
522 ZEND_END_ARG_INFO()
523 
524 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_result_status, 0, 0, 1)
525 	ZEND_ARG_INFO(0, result)
526 	ZEND_ARG_INFO(0, result_type)
527 ZEND_END_ARG_INFO()
528 
529 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_get_notify, 0, 0, 0)
530 	ZEND_ARG_INFO(0, connection)
531 	ZEND_ARG_INFO(0, e)
532 ZEND_END_ARG_INFO()
533 
534 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_get_pid, 0, 0, 0)
535 	ZEND_ARG_INFO(0, connection)
536 ZEND_END_ARG_INFO()
537 
538 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_socket, 0, 0, 1)
539 	ZEND_ARG_INFO(0, connection)
540 ZEND_END_ARG_INFO()
541 
542 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_consume_input, 0, 0, 1)
543 	ZEND_ARG_INFO(0, connection)
544 ZEND_END_ARG_INFO()
545 
546 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_flush, 0, 0, 1)
547 	ZEND_ARG_INFO(0, connection)
548 ZEND_END_ARG_INFO()
549 
550 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_meta_data, 0, 0, 2)
551 	ZEND_ARG_INFO(0, db)
552 	ZEND_ARG_INFO(0, table)
553 ZEND_END_ARG_INFO()
554 
555 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_convert, 0, 0, 3)
556 	ZEND_ARG_INFO(0, db)
557 	ZEND_ARG_INFO(0, table)
558 	ZEND_ARG_INFO(0, values)
559 	ZEND_ARG_INFO(0, options)
560 ZEND_END_ARG_INFO()
561 
562 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_insert, 0, 0, 3)
563 	ZEND_ARG_INFO(0, db)
564 	ZEND_ARG_INFO(0, table)
565 	ZEND_ARG_INFO(0, values)
566 	ZEND_ARG_INFO(0, options)
567 ZEND_END_ARG_INFO()
568 
569 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_update, 0, 0, 4)
570 	ZEND_ARG_INFO(0, db)
571 	ZEND_ARG_INFO(0, table)
572 	ZEND_ARG_INFO(0, fields)
573 	ZEND_ARG_INFO(0, ids)
574 	ZEND_ARG_INFO(0, options)
575 ZEND_END_ARG_INFO()
576 
577 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_delete, 0, 0, 3)
578 	ZEND_ARG_INFO(0, db)
579 	ZEND_ARG_INFO(0, table)
580 	ZEND_ARG_INFO(0, ids)
581 	ZEND_ARG_INFO(0, options)
582 ZEND_END_ARG_INFO()
583 
584 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_select, 0, 0, 3)
585 	ZEND_ARG_INFO(0, db)
586 	ZEND_ARG_INFO(0, table)
587 	ZEND_ARG_INFO(0, ids)
588 	ZEND_ARG_INFO(0, options)
589 	ZEND_ARG_INFO(0, result_type)
590 ZEND_END_ARG_INFO()
591 /* }}} */
592 
593 /* {{{ pgsql_functions[]
594  */
595 static const zend_function_entry pgsql_functions[] = {
596 	/* connection functions */
597 	PHP_FE(pg_connect,		arginfo_pg_connect)
598 	PHP_FE(pg_pconnect,		arginfo_pg_pconnect)
599 	PHP_FE(pg_connect_poll,	arginfo_pg_connect_poll)
600 	PHP_FE(pg_close,		arginfo_pg_close)
601 	PHP_FE(pg_connection_status,	arginfo_pg_connection_status)
602 	PHP_FE(pg_connection_busy,		arginfo_pg_connection_busy)
603 	PHP_FE(pg_connection_reset,		arginfo_pg_connection_reset)
604 	PHP_FE(pg_host,			arginfo_pg_host)
605 	PHP_FE(pg_dbname,		arginfo_pg_dbname)
606 	PHP_FE(pg_port,			arginfo_pg_port)
607 	PHP_FE(pg_tty,			arginfo_pg_tty)
608 	PHP_FE(pg_options,		arginfo_pg_options)
609 	PHP_FE(pg_version,		arginfo_pg_version)
610 	PHP_FE(pg_ping,			arginfo_pg_ping)
611 #if HAVE_PQPARAMETERSTATUS
612 	PHP_FE(pg_parameter_status, arginfo_pg_parameter_status)
613 #endif
614 #if HAVE_PGTRANSACTIONSTATUS
615 	PHP_FE(pg_transaction_status, arginfo_pg_transaction_status)
616 #endif
617 	/* query functions */
618 	PHP_FE(pg_query,		arginfo_pg_query)
619 #if HAVE_PQEXECPARAMS
620 	PHP_FE(pg_query_params,		arginfo_pg_query_params)
621 #endif
622 #if HAVE_PQPREPARE
623 	PHP_FE(pg_prepare,		arginfo_pg_prepare)
624 #endif
625 #if HAVE_PQEXECPREPARED
626 	PHP_FE(pg_execute,		arginfo_pg_execute)
627 #endif
628 	PHP_FE(pg_send_query,	arginfo_pg_send_query)
629 #if HAVE_PQSENDQUERYPARAMS
630 	PHP_FE(pg_send_query_params,	arginfo_pg_send_query_params)
631 #endif
632 #if HAVE_PQSENDPREPARE
633 	PHP_FE(pg_send_prepare,	arginfo_pg_send_prepare)
634 #endif
635 #if HAVE_PQSENDQUERYPREPARED
636 	PHP_FE(pg_send_execute,	arginfo_pg_send_execute)
637 #endif
638 	PHP_FE(pg_cancel_query, arginfo_pg_cancel_query)
639 	/* result functions */
640 	PHP_FE(pg_fetch_result,	arginfo_pg_fetch_result)
641 	PHP_FE(pg_fetch_row,	arginfo_pg_fetch_row)
642 	PHP_FE(pg_fetch_assoc,	arginfo_pg_fetch_assoc)
643 	PHP_FE(pg_fetch_array,	arginfo_pg_fetch_array)
644 	PHP_FE(pg_fetch_object,	arginfo_pg_fetch_object)
645 	PHP_FE(pg_fetch_all,	arginfo_pg_fetch_all)
646 	PHP_FE(pg_fetch_all_columns,	arginfo_pg_fetch_all_columns)
647 #if HAVE_PQCMDTUPLES
648 	PHP_FE(pg_affected_rows,arginfo_pg_affected_rows)
649 #endif
650 	PHP_FE(pg_get_result,	arginfo_pg_get_result)
651 	PHP_FE(pg_result_seek,	arginfo_pg_result_seek)
652 	PHP_FE(pg_result_status,arginfo_pg_result_status)
653 	PHP_FE(pg_free_result,	arginfo_pg_free_result)
654 	PHP_FE(pg_last_oid,	    arginfo_pg_last_oid)
655 	PHP_FE(pg_num_rows,		arginfo_pg_num_rows)
656 	PHP_FE(pg_num_fields,	arginfo_pg_num_fields)
657 	PHP_FE(pg_field_name,	arginfo_pg_field_name)
658 	PHP_FE(pg_field_num,	arginfo_pg_field_num)
659 	PHP_FE(pg_field_size,	arginfo_pg_field_size)
660 	PHP_FE(pg_field_type,	arginfo_pg_field_type)
661 	PHP_FE(pg_field_type_oid, arginfo_pg_field_type_oid)
662 	PHP_FE(pg_field_prtlen,	arginfo_pg_field_prtlen)
663 	PHP_FE(pg_field_is_null,arginfo_pg_field_is_null)
664 #ifdef HAVE_PQFTABLE
665 	PHP_FE(pg_field_table,  arginfo_pg_field_table)
666 #endif
667 	/* async message function */
668 	PHP_FE(pg_get_notify,   arginfo_pg_get_notify)
669 	PHP_FE(pg_socket,		arginfo_pg_socket)
670 	PHP_FE(pg_consume_input,arginfo_pg_consume_input)
671 	PHP_FE(pg_flush,		arginfo_pg_flush)
672 	PHP_FE(pg_get_pid,      arginfo_pg_get_pid)
673 	/* error message functions */
674 	PHP_FE(pg_result_error, arginfo_pg_result_error)
675 #if HAVE_PQRESULTERRORFIELD
676 	PHP_FE(pg_result_error_field, arginfo_pg_result_error_field)
677 #endif
678 	PHP_FE(pg_last_error,   arginfo_pg_last_error)
679 	PHP_FE(pg_last_notice,  arginfo_pg_last_notice)
680 	/* copy functions */
681 	PHP_FE(pg_put_line,		arginfo_pg_put_line)
682 	PHP_FE(pg_end_copy,		arginfo_pg_end_copy)
683 	PHP_FE(pg_copy_to,      arginfo_pg_copy_to)
684 	PHP_FE(pg_copy_from,    arginfo_pg_copy_from)
685 	/* debug functions */
686 	PHP_FE(pg_trace,		arginfo_pg_trace)
687 	PHP_FE(pg_untrace,		arginfo_pg_untrace)
688 	/* large object functions */
689 	PHP_FE(pg_lo_create,	arginfo_pg_lo_create)
690 	PHP_FE(pg_lo_unlink,	arginfo_pg_lo_unlink)
691 	PHP_FE(pg_lo_open,		arginfo_pg_lo_open)
692 	PHP_FE(pg_lo_close,		arginfo_pg_lo_close)
693 	PHP_FE(pg_lo_read,		arginfo_pg_lo_read)
694 	PHP_FE(pg_lo_write,		arginfo_pg_lo_write)
695 	PHP_FE(pg_lo_read_all,	arginfo_pg_lo_read_all)
696 	PHP_FE(pg_lo_import,	arginfo_pg_lo_import)
697 	PHP_FE(pg_lo_export,	arginfo_pg_lo_export)
698 	PHP_FE(pg_lo_seek,		arginfo_pg_lo_seek)
699 	PHP_FE(pg_lo_tell,		arginfo_pg_lo_tell)
700 #if HAVE_PG_LO_TRUNCATE
701 	PHP_FE(pg_lo_truncate,	arginfo_pg_lo_truncate)
702 #endif
703 	/* utility functions */
704 #if HAVE_PQESCAPE
705 	PHP_FE(pg_escape_string,	arginfo_pg_escape_string)
706 	PHP_FE(pg_escape_bytea, 	arginfo_pg_escape_bytea)
707 	PHP_FE(pg_unescape_bytea, 	arginfo_pg_unescape_bytea)
708 	PHP_FE(pg_escape_literal,	arginfo_pg_escape_literal)
709 	PHP_FE(pg_escape_identifier,	arginfo_pg_escape_identifier)
710 #endif
711 #if HAVE_PQSETERRORVERBOSITY
712 	PHP_FE(pg_set_error_verbosity,	arginfo_pg_set_error_verbosity)
713 #endif
714 #if HAVE_PQCLIENTENCODING
715 	PHP_FE(pg_client_encoding,		arginfo_pg_client_encoding)
716 	PHP_FE(pg_set_client_encoding,	arginfo_pg_set_client_encoding)
717 #endif
718 	/* misc function */
719 	PHP_FE(pg_meta_data,	arginfo_pg_meta_data)
720 	PHP_FE(pg_convert,      arginfo_pg_convert)
721 	PHP_FE(pg_insert,       arginfo_pg_insert)
722 	PHP_FE(pg_update,       arginfo_pg_update)
723 	PHP_FE(pg_delete,       arginfo_pg_delete)
724 	PHP_FE(pg_select,       arginfo_pg_select)
725 	/* aliases for downwards compatibility */
726 	PHP_FALIAS(pg_exec,          pg_query,          arginfo_pg_query)
727 	PHP_FALIAS(pg_getlastoid,    pg_last_oid,       arginfo_pg_last_oid)
728 #if HAVE_PQCMDTUPLES
729 	PHP_FALIAS(pg_cmdtuples,	 pg_affected_rows,  arginfo_pg_affected_rows)
730 #endif
731 	PHP_FALIAS(pg_errormessage,	 pg_last_error,     arginfo_pg_last_error)
732 	PHP_FALIAS(pg_numrows,		 pg_num_rows,       arginfo_pg_num_rows)
733 	PHP_FALIAS(pg_numfields,	 pg_num_fields,     arginfo_pg_num_fields)
734 	PHP_FALIAS(pg_fieldname,	 pg_field_name,     arginfo_pg_field_name)
735 	PHP_FALIAS(pg_fieldsize,     pg_field_size,     arginfo_pg_field_size)
736 	PHP_FALIAS(pg_fieldtype,	 pg_field_type,     arginfo_pg_field_type)
737 	PHP_FALIAS(pg_fieldnum,	     pg_field_num,      arginfo_pg_field_num)
738 	PHP_FALIAS(pg_fieldprtlen,	 pg_field_prtlen,   arginfo_pg_field_prtlen)
739 	PHP_FALIAS(pg_fieldisnull,	 pg_field_is_null,  arginfo_pg_field_is_null)
740 	PHP_FALIAS(pg_freeresult,    pg_free_result,    arginfo_pg_free_result)
741 	PHP_FALIAS(pg_result,	     pg_fetch_result,   arginfo_pg_get_result)
742 	PHP_FALIAS(pg_loreadall,	 pg_lo_read_all,    arginfo_pg_lo_read_all)
743 	PHP_FALIAS(pg_locreate,	     pg_lo_create,      arginfo_pg_lo_create)
744 	PHP_FALIAS(pg_lounlink,	     pg_lo_unlink,      arginfo_pg_lo_unlink)
745 	PHP_FALIAS(pg_loopen,	     pg_lo_open,        arginfo_pg_lo_open)
746 	PHP_FALIAS(pg_loclose,	     pg_lo_close,       arginfo_pg_lo_close)
747 	PHP_FALIAS(pg_loread,	     pg_lo_read,        arginfo_pg_lo_read)
748 	PHP_FALIAS(pg_lowrite,	     pg_lo_write,       arginfo_pg_lo_write)
749 	PHP_FALIAS(pg_loimport,	     pg_lo_import,      arginfo_pg_lo_import)
750 	PHP_FALIAS(pg_loexport,	     pg_lo_export,      arginfo_pg_lo_export)
751 #if HAVE_PQCLIENTENCODING
752 	PHP_FALIAS(pg_clientencoding,		pg_client_encoding,		arginfo_pg_client_encoding)
753 	PHP_FALIAS(pg_setclientencoding,	pg_set_client_encoding,	arginfo_pg_set_client_encoding)
754 #endif
755 	PHP_FE_END
756 };
757 /* }}} */
758 
759 /* {{{ pgsql_module_entry
760  */
761 zend_module_entry pgsql_module_entry = {
762 	STANDARD_MODULE_HEADER,
763 	"pgsql",
764 	pgsql_functions,
765 	PHP_MINIT(pgsql),
766 	PHP_MSHUTDOWN(pgsql),
767 	PHP_RINIT(pgsql),
768 	PHP_RSHUTDOWN(pgsql),
769 	PHP_MINFO(pgsql),
770 	PHP_PGSQL_VERSION,
771 	PHP_MODULE_GLOBALS(pgsql),
772 	PHP_GINIT(pgsql),
773 	NULL,
774 	NULL,
775 	STANDARD_MODULE_PROPERTIES_EX
776 };
777 /* }}} */
778 
779 #ifdef COMPILE_DL_PGSQL
780 #ifdef ZTS
781 ZEND_TSRMLS_CACHE_DEFINE()
782 #endif
783 ZEND_GET_MODULE(pgsql)
784 #endif
785 
786 static int le_link, le_plink, le_result, le_lofp, le_string;
787 
788 /* Compatibility definitions */
789 
790 #ifndef HAVE_PGSQL_WITH_MULTIBYTE_SUPPORT
791 #define pg_encoding_to_char(x) "SQL_ASCII"
792 #endif
793 
794 #if !HAVE_PQESCAPE_CONN
795 #define PQescapeStringConn(conn, to, from, len, error) PQescapeString(to, from, len)
796 #endif
797 
798 #if HAVE_PQESCAPELITERAL
799 #define PGSQLescapeLiteral(conn, str, len) PQescapeLiteral(conn, str, len)
800 #define PGSQLescapeIdentifier(conn, str, len) PQescapeIdentifier(conn, str, len)
801 #define PGSQLfree(a) PQfreemem(a)
802 #else
803 #define PGSQLescapeLiteral(conn, str, len) php_pgsql_PQescapeInternal(conn, str, len, 1, 0)
804 #define PGSQLescapeLiteral2(conn, str, len) php_pgsql_PQescapeInternal(conn, str, len, 1, 1)
805 #define PGSQLescapeIdentifier(conn, str, len) php_pgsql_PQescapeInternal(conn, str, len, 0, 0)
806 #define PGSQLfree(a) efree(a)
807 
808 /* emulate libpq's PQescapeInternal() 9.0 or later */
php_pgsql_PQescapeInternal(PGconn * conn,const char * str,size_t len,int escape_literal,int safe)809 static char *php_pgsql_PQescapeInternal(PGconn *conn, const char *str, size_t len, int escape_literal, int safe) /* {{{ */
810 {
811 	char *result, *rp, *s;
812 
813 	if (!conn) {
814 		return NULL;
815 	}
816 
817 	/* allocate enough memory */
818 	rp = result = (char *)safe_emalloc(len, 2, 5); /* leading " E" needs extra 2 bytes + quote_chars on both end for 2 bytes + NULL */
819 
820 	if (escape_literal) {
821 		if (safe) {
822 			size_t new_len;
823 			char *tmp = (char *)safe_emalloc(len, 2, 1);
824 			*rp++ = '\'';
825 			/* PQescapeString does not escape \, but it handles multibyte chars safely.
826 			   This escape is incompatible with PQescapeLiteral. */
827 			new_len = PQescapeStringConn(conn, tmp, str, len, NULL);
828 			strncpy(rp, tmp, new_len);
829 			efree(tmp);
830 			rp += new_len;
831 		} else {
832 			char *encoding;
833 			size_t tmp_len;
834 			/* This is compatible with PQescapeLiteral, but it cannot handle multbyte chars
835 			   such as SJIS, BIG5. Raise warning and return NULL by checking
836 			   client_encoding. */
837 			encoding = (char *) pg_encoding_to_char(PQclientEncoding(conn));
838 			if (!strncmp(encoding, "SJIS", sizeof("SJIS")-1) ||
839 				!strncmp(encoding, "SHIFT_JIS_2004", sizeof("SHIFT_JIS_2004")-1) ||
840 				!strncmp(encoding, "BIG5", sizeof("BIG5")-1) ||
841 				!strncmp(encoding, "GB18030", sizeof("GB18030")-1) ||
842 				!strncmp(encoding, "GBK", sizeof("GBK")-1) ||
843 				!strncmp(encoding, "JOHAB", sizeof("JOHAB")-1) ||
844 				!strncmp(encoding, "UHC", sizeof("UHC")-1) ) {
845 
846 				php_error_docref(NULL, E_WARNING, "Unsafe encoding is used. Do not use '%s' encoding or use PostgreSQL 9.0 or later libpq.", encoding);
847 			}
848 			/* check backslashes */
849 			tmp_len = strspn(str, "\\");
850 			if (tmp_len != len) {
851 				/* add " E" for escaping slashes */
852 				*rp++ = ' ';
853 				*rp++ = 'E';
854 			}
855 			*rp++ = '\'';
856 			for (s = (char *)str; s - str < len; ++s) {
857 				if (*s == '\'' || *s == '\\') {
858 					*rp++ = *s;
859 					*rp++ = *s;
860 				} else {
861 					*rp++ = *s;
862 				}
863 			}
864 		}
865 		*rp++ = '\'';
866 	} else {
867 		/* Identifier escape. */
868 		*rp++ = '"';
869 		for (s = (char *)str; s - str < len; ++s) {
870 			if (*s == '"') {
871 				*rp++ = '"';
872 				*rp++ = '"';
873 			} else {
874 				*rp++ = *s;
875 			}
876 		}
877 		*rp++ = '"';
878 	}
879 	*rp = '\0';
880 
881 	return result;
882 }
883 /* }}} */
884 #endif
885 
886 /* {{{ _php_pgsql_trim_message */
_php_pgsql_trim_message(const char * message,size_t * len)887 static char * _php_pgsql_trim_message(const char *message, size_t *len)
888 {
889 	register size_t i = strlen(message);
890 
891 	if (i>2 && (message[i-2] == '\r' || message[i-2] == '\n') && message[i-1] == '.') {
892 		--i;
893 	}
894 	while (i>1 && (message[i-1] == '\r' || message[i-1] == '\n')) {
895 		--i;
896 	}
897 	if (len) {
898 		*len = i;
899 	}
900 	return estrndup(message, i);
901 }
902 /* }}} */
903 
904 /* {{{ _php_pgsql_trim_result */
_php_pgsql_trim_result(PGconn * pgsql,char ** buf)905 static inline char * _php_pgsql_trim_result(PGconn * pgsql, char **buf)
906 {
907 	return *buf = _php_pgsql_trim_message(PQerrorMessage(pgsql), NULL);
908 }
909 /* }}} */
910 
911 #define PQErrorMessageTrim(pgsql, buf) _php_pgsql_trim_result(pgsql, buf)
912 
913 #define PHP_PQ_ERROR(text, pgsql) {										\
914 		char *msgbuf = _php_pgsql_trim_message(PQerrorMessage(pgsql), NULL); \
915 		php_error_docref(NULL, E_WARNING, text, msgbuf);		\
916 		efree(msgbuf);													\
917 } \
918 
919 /* {{{ php_pgsql_set_default_link
920  */
php_pgsql_set_default_link(zend_resource * res)921 static void php_pgsql_set_default_link(zend_resource *res)
922 {
923 	GC_ADDREF(res);
924 
925 	if (PGG(default_link) != NULL) {
926 		zend_list_delete(PGG(default_link));
927 	}
928 
929 	PGG(default_link) = res;
930 }
931 /* }}} */
932 
933 /* {{{ _close_pgsql_link
934  */
_close_pgsql_link(zend_resource * rsrc)935 static void _close_pgsql_link(zend_resource *rsrc)
936 {
937 	PGconn *link = (PGconn *)rsrc->ptr;
938 	PGresult *res;
939 	zval *hash;
940 
941 	while ((res = PQgetResult(link))) {
942 		PQclear(res);
943 	}
944 	PQfinish(link);
945 	PGG(num_links)--;
946 
947 	/* Remove connection hash for this link */
948 	hash = zend_hash_index_find(&PGG(hashes), (uintptr_t) link);
949 	if (hash) {
950 		zend_hash_index_del(&PGG(hashes), (uintptr_t) link);
951 		zend_hash_del(&EG(regular_list), Z_STR_P(hash));
952 	}
953 }
954 /* }}} */
955 
956 /* {{{ _close_pgsql_plink
957  */
_close_pgsql_plink(zend_resource * rsrc)958 static void _close_pgsql_plink(zend_resource *rsrc)
959 {
960 	PGconn *link = (PGconn *)rsrc->ptr;
961 	PGresult *res;
962 
963 	while ((res = PQgetResult(link))) {
964 		PQclear(res);
965 	}
966 	PQfinish(link);
967 	PGG(num_persistent)--;
968 	PGG(num_links)--;
969 }
970 /* }}} */
971 
972 /* {{{ _php_pgsql_notice_handler
973  */
_php_pgsql_notice_handler(void * resource_id,const char * message)974 static void _php_pgsql_notice_handler(void *resource_id, const char *message)
975 {
976 	zval *notices;
977 	zval tmp;
978 	char *trimed_message;
979 	size_t trimed_message_len;
980 
981 	if (! PGG(ignore_notices)) {
982 		notices = zend_hash_index_find(&PGG(notices), (zend_ulong)resource_id);
983 		if (!notices) {
984 			array_init(&tmp);
985 			notices = &tmp;
986 			zend_hash_index_update(&PGG(notices), (zend_ulong)resource_id, notices);
987 		}
988 		trimed_message = _php_pgsql_trim_message(message, &trimed_message_len);
989 		if (PGG(log_notices)) {
990 			php_error_docref(NULL, E_NOTICE, "%s", trimed_message);
991 		}
992 		add_next_index_stringl(notices, trimed_message, trimed_message_len);
993 		efree(trimed_message);
994 	}
995 }
996 /* }}} */
997 
998 /* {{{ _rollback_transactions
999  */
_rollback_transactions(zval * el)1000 static int _rollback_transactions(zval *el)
1001 {
1002 	PGconn *link;
1003 	PGresult *res;
1004 	zend_resource *rsrc = Z_RES_P(el);
1005 
1006 	if (rsrc->type != le_plink)
1007 		return 0;
1008 
1009 	link = (PGconn *) rsrc->ptr;
1010 
1011 	if (PQ_SETNONBLOCKING(link, 0)) {
1012 		php_error_docref("ref.pgsql", E_NOTICE, "Cannot set connection to blocking mode");
1013 		return -1;
1014 	}
1015 
1016 	while ((res = PQgetResult(link))) {
1017 		PQclear(res);
1018 	}
1019 #if HAVE_PGTRANSACTIONSTATUS && HAVE_PQPROTOCOLVERSION
1020 	if ((PQprotocolVersion(link) >= 3 && PQtransactionStatus(link) != PQTRANS_IDLE) || PQprotocolVersion(link) < 3)
1021 #endif
1022 	{
1023 		int orig = PGG(ignore_notices);
1024 		PGG(ignore_notices) = 1;
1025 #if HAVE_PGTRANSACTIONSTATUS && HAVE_PQPROTOCOLVERSION
1026 		res = PQexec(link,"ROLLBACK;");
1027 #else
1028 		res = PQexec(link,"BEGIN;");
1029 		PQclear(res);
1030 		res = PQexec(link,"ROLLBACK;");
1031 #endif
1032 		PQclear(res);
1033 		PGG(ignore_notices) = orig;
1034 	}
1035 
1036 	return 0;
1037 }
1038 /* }}} */
1039 
1040 /* {{{ _free_ptr
1041  */
_free_ptr(zend_resource * rsrc)1042 static void _free_ptr(zend_resource *rsrc)
1043 {
1044 	pgLofp *lofp = (pgLofp *)rsrc->ptr;
1045 	efree(lofp);
1046 }
1047 /* }}} */
1048 
1049 /* {{{ _free_result
1050  */
_free_result(zend_resource * rsrc)1051 static void _free_result(zend_resource *rsrc)
1052 {
1053 	pgsql_result_handle *pg_result = (pgsql_result_handle *)rsrc->ptr;
1054 
1055 	PQclear(pg_result->result);
1056 	efree(pg_result);
1057 }
1058 /* }}} */
1059 
_php_pgsql_detect_identifier_escape(const char * identifier,size_t len)1060 static int _php_pgsql_detect_identifier_escape(const char *identifier, size_t len) /* {{{ */
1061 {
1062 	/* Handle edge case. Cannot be a escaped string */
1063 	if (len <= 2) {
1064 		return FAILURE;
1065 	}
1066 	/* Detect double qoutes */
1067 	if (identifier[0] == '"' && identifier[len-1] == '"') {
1068 		size_t i;
1069 
1070 		/* Detect wrong format of " inside of escaped string */
1071 		for (i = 1; i < len-1; i++) {
1072 			if (identifier[i] == '"' && (identifier[++i] != '"' || i == len-1)) {
1073 				return FAILURE;
1074 			}
1075 		}
1076 	} else {
1077 		return FAILURE;
1078 	}
1079 	/* Escaped properly */
1080 	return SUCCESS;
1081 }
1082 /* }}} */
1083 
1084 /* {{{ PHP_INI
1085  */
1086 PHP_INI_BEGIN()
1087 STD_PHP_INI_BOOLEAN( "pgsql.allow_persistent",      "1",  PHP_INI_SYSTEM, OnUpdateBool, allow_persistent,      zend_pgsql_globals, pgsql_globals)
1088 STD_PHP_INI_ENTRY_EX("pgsql.max_persistent",       "-1",  PHP_INI_SYSTEM, OnUpdateLong, max_persistent,        zend_pgsql_globals, pgsql_globals, display_link_numbers)
1089 STD_PHP_INI_ENTRY_EX("pgsql.max_links",            "-1",  PHP_INI_SYSTEM, OnUpdateLong, max_links,             zend_pgsql_globals, pgsql_globals, display_link_numbers)
1090 STD_PHP_INI_BOOLEAN( "pgsql.auto_reset_persistent", "0",  PHP_INI_SYSTEM, OnUpdateBool, auto_reset_persistent, zend_pgsql_globals, pgsql_globals)
1091 STD_PHP_INI_BOOLEAN( "pgsql.ignore_notice",         "0",  PHP_INI_ALL,    OnUpdateBool, ignore_notices,        zend_pgsql_globals, pgsql_globals)
1092 STD_PHP_INI_BOOLEAN( "pgsql.log_notice",            "0",  PHP_INI_ALL,    OnUpdateBool, log_notices,           zend_pgsql_globals, pgsql_globals)
PHP_INI_END()1093 PHP_INI_END()
1094 /* }}} */
1095 
1096 /* {{{ PHP_GINIT_FUNCTION
1097  */
1098 static PHP_GINIT_FUNCTION(pgsql)
1099 {
1100 #if defined(COMPILE_DL_PGSQL) && defined(ZTS)
1101 	ZEND_TSRMLS_CACHE_UPDATE();
1102 #endif
1103 	memset(pgsql_globals, 0, sizeof(zend_pgsql_globals));
1104 	/* Initialize notice message hash at MINIT only */
1105 	zend_hash_init_ex(&pgsql_globals->notices, 0, NULL, ZVAL_PTR_DTOR, 1, 0);
1106 	zend_hash_init_ex(&pgsql_globals->hashes, 0, NULL, ZVAL_PTR_DTOR, 1, 0);
1107 }
1108 /* }}} */
1109 
1110 /* {{{ PHP_MINIT_FUNCTION
1111  */
PHP_MINIT_FUNCTION(pgsql)1112 PHP_MINIT_FUNCTION(pgsql)
1113 {
1114 	REGISTER_INI_ENTRIES();
1115 
1116 	le_link = zend_register_list_destructors_ex(_close_pgsql_link, NULL, "pgsql link", module_number);
1117 	le_plink = zend_register_list_destructors_ex(NULL, _close_pgsql_plink, "pgsql link persistent", module_number);
1118 	le_result = zend_register_list_destructors_ex(_free_result, NULL, "pgsql result", module_number);
1119 	le_lofp = zend_register_list_destructors_ex(_free_ptr, NULL, "pgsql large object", module_number);
1120 	le_string = zend_register_list_destructors_ex(_free_ptr, NULL, "pgsql string", module_number);
1121 #if HAVE_PG_CONFIG_H
1122 	/* PG_VERSION - libpq version */
1123 	REGISTER_STRING_CONSTANT("PGSQL_LIBPQ_VERSION", PG_VERSION, CONST_CS | CONST_PERSISTENT);
1124 	REGISTER_STRING_CONSTANT("PGSQL_LIBPQ_VERSION_STR", PG_VERSION_STR, CONST_CS | CONST_PERSISTENT);
1125 #endif
1126 	/* For connection option */
1127 	REGISTER_LONG_CONSTANT("PGSQL_CONNECT_FORCE_NEW", PGSQL_CONNECT_FORCE_NEW, CONST_CS | CONST_PERSISTENT);
1128 	REGISTER_LONG_CONSTANT("PGSQL_CONNECT_ASYNC", PGSQL_CONNECT_ASYNC, CONST_CS | CONST_PERSISTENT);
1129 	/* For pg_fetch_array() */
1130 	REGISTER_LONG_CONSTANT("PGSQL_ASSOC", PGSQL_ASSOC, CONST_CS | CONST_PERSISTENT);
1131 	REGISTER_LONG_CONSTANT("PGSQL_NUM", PGSQL_NUM, CONST_CS | CONST_PERSISTENT);
1132 	REGISTER_LONG_CONSTANT("PGSQL_BOTH", PGSQL_BOTH, CONST_CS | CONST_PERSISTENT);
1133 	/* For pg_last_notice() */
1134 	REGISTER_LONG_CONSTANT("PGSQL_NOTICE_LAST", PGSQL_NOTICE_LAST, CONST_CS | CONST_PERSISTENT);
1135 	REGISTER_LONG_CONSTANT("PGSQL_NOTICE_ALL", PGSQL_NOTICE_ALL, CONST_CS | CONST_PERSISTENT);
1136 	REGISTER_LONG_CONSTANT("PGSQL_NOTICE_CLEAR", PGSQL_NOTICE_CLEAR, CONST_CS | CONST_PERSISTENT);
1137 	/* For pg_connection_status() */
1138 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_BAD", CONNECTION_BAD, CONST_CS | CONST_PERSISTENT);
1139 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_OK", CONNECTION_OK, CONST_CS | CONST_PERSISTENT);
1140 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_STARTED", CONNECTION_STARTED, CONST_CS | CONST_PERSISTENT);
1141 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_MADE", CONNECTION_MADE, CONST_CS | CONST_PERSISTENT);
1142 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_AWAITING_RESPONSE", CONNECTION_AWAITING_RESPONSE, CONST_CS | CONST_PERSISTENT);
1143 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_AUTH_OK", CONNECTION_AUTH_OK, CONST_CS | CONST_PERSISTENT);
1144 #ifdef CONNECTION_SSL_STARTUP
1145 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_SSL_STARTUP", CONNECTION_SSL_STARTUP, CONST_CS | CONST_PERSISTENT);
1146 #endif
1147 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_SETENV", CONNECTION_SETENV, CONST_CS | CONST_PERSISTENT);
1148 	/* For pg_connect_poll() */
1149 	REGISTER_LONG_CONSTANT("PGSQL_POLLING_FAILED", PGRES_POLLING_FAILED, CONST_CS | CONST_PERSISTENT);
1150 	REGISTER_LONG_CONSTANT("PGSQL_POLLING_READING", PGRES_POLLING_READING, CONST_CS | CONST_PERSISTENT);
1151 	REGISTER_LONG_CONSTANT("PGSQL_POLLING_WRITING", PGRES_POLLING_WRITING, CONST_CS | CONST_PERSISTENT);
1152 	REGISTER_LONG_CONSTANT("PGSQL_POLLING_OK", PGRES_POLLING_OK, CONST_CS | CONST_PERSISTENT);
1153 	REGISTER_LONG_CONSTANT("PGSQL_POLLING_ACTIVE", PGRES_POLLING_ACTIVE, CONST_CS | CONST_PERSISTENT);
1154 #if HAVE_PGTRANSACTIONSTATUS
1155 	/* For pg_transaction_status() */
1156 	REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_IDLE", PQTRANS_IDLE, CONST_CS | CONST_PERSISTENT);
1157 	REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_ACTIVE", PQTRANS_ACTIVE, CONST_CS | CONST_PERSISTENT);
1158 	REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_INTRANS", PQTRANS_INTRANS, CONST_CS | CONST_PERSISTENT);
1159 	REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_INERROR", PQTRANS_INERROR, CONST_CS | CONST_PERSISTENT);
1160 	REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_UNKNOWN", PQTRANS_UNKNOWN, CONST_CS | CONST_PERSISTENT);
1161 #endif
1162 #if HAVE_PQSETERRORVERBOSITY
1163 	/* For pg_set_error_verbosity() */
1164 	REGISTER_LONG_CONSTANT("PGSQL_ERRORS_TERSE", PQERRORS_TERSE, CONST_CS | CONST_PERSISTENT);
1165 	REGISTER_LONG_CONSTANT("PGSQL_ERRORS_DEFAULT", PQERRORS_DEFAULT, CONST_CS | CONST_PERSISTENT);
1166 	REGISTER_LONG_CONSTANT("PGSQL_ERRORS_VERBOSE", PQERRORS_VERBOSE, CONST_CS | CONST_PERSISTENT);
1167 #endif
1168 	/* For lo_seek() */
1169 	REGISTER_LONG_CONSTANT("PGSQL_SEEK_SET", SEEK_SET, CONST_CS | CONST_PERSISTENT);
1170 	REGISTER_LONG_CONSTANT("PGSQL_SEEK_CUR", SEEK_CUR, CONST_CS | CONST_PERSISTENT);
1171 	REGISTER_LONG_CONSTANT("PGSQL_SEEK_END", SEEK_END, CONST_CS | CONST_PERSISTENT);
1172 	/* For pg_result_status() return value type */
1173 	REGISTER_LONG_CONSTANT("PGSQL_STATUS_LONG", PGSQL_STATUS_LONG, CONST_CS | CONST_PERSISTENT);
1174 	REGISTER_LONG_CONSTANT("PGSQL_STATUS_STRING", PGSQL_STATUS_STRING, CONST_CS | CONST_PERSISTENT);
1175 	/* For pg_result_status() return value */
1176 	REGISTER_LONG_CONSTANT("PGSQL_EMPTY_QUERY", PGRES_EMPTY_QUERY, CONST_CS | CONST_PERSISTENT);
1177 	REGISTER_LONG_CONSTANT("PGSQL_COMMAND_OK", PGRES_COMMAND_OK, CONST_CS | CONST_PERSISTENT);
1178 	REGISTER_LONG_CONSTANT("PGSQL_TUPLES_OK", PGRES_TUPLES_OK, CONST_CS | CONST_PERSISTENT);
1179 	REGISTER_LONG_CONSTANT("PGSQL_COPY_OUT", PGRES_COPY_OUT, CONST_CS | CONST_PERSISTENT);
1180 	REGISTER_LONG_CONSTANT("PGSQL_COPY_IN", PGRES_COPY_IN, CONST_CS | CONST_PERSISTENT);
1181 	REGISTER_LONG_CONSTANT("PGSQL_BAD_RESPONSE", PGRES_BAD_RESPONSE, CONST_CS | CONST_PERSISTENT);
1182 	REGISTER_LONG_CONSTANT("PGSQL_NONFATAL_ERROR", PGRES_NONFATAL_ERROR, CONST_CS | CONST_PERSISTENT);
1183 	REGISTER_LONG_CONSTANT("PGSQL_FATAL_ERROR", PGRES_FATAL_ERROR, CONST_CS | CONST_PERSISTENT);
1184 #if HAVE_PQRESULTERRORFIELD
1185 	/* For pg_result_error_field() field codes */
1186 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SEVERITY", PG_DIAG_SEVERITY, CONST_CS | CONST_PERSISTENT);
1187 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SQLSTATE", PG_DIAG_SQLSTATE, CONST_CS | CONST_PERSISTENT);
1188 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_MESSAGE_PRIMARY", PG_DIAG_MESSAGE_PRIMARY, CONST_CS | CONST_PERSISTENT);
1189 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_MESSAGE_DETAIL", PG_DIAG_MESSAGE_DETAIL, CONST_CS | CONST_PERSISTENT);
1190 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_MESSAGE_HINT", PG_DIAG_MESSAGE_HINT, CONST_CS | CONST_PERSISTENT);
1191 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_STATEMENT_POSITION", PG_DIAG_STATEMENT_POSITION, CONST_CS | CONST_PERSISTENT);
1192 #ifdef PG_DIAG_INTERNAL_POSITION
1193 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_INTERNAL_POSITION", PG_DIAG_INTERNAL_POSITION, CONST_CS | CONST_PERSISTENT);
1194 #endif
1195 #ifdef PG_DIAG_INTERNAL_QUERY
1196 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_INTERNAL_QUERY", PG_DIAG_INTERNAL_QUERY, CONST_CS | CONST_PERSISTENT);
1197 #endif
1198 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_CONTEXT", PG_DIAG_CONTEXT, CONST_CS | CONST_PERSISTENT);
1199 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SOURCE_FILE", PG_DIAG_SOURCE_FILE, CONST_CS | CONST_PERSISTENT);
1200 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SOURCE_LINE", PG_DIAG_SOURCE_LINE, CONST_CS | CONST_PERSISTENT);
1201 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SOURCE_FUNCTION", PG_DIAG_SOURCE_FUNCTION, CONST_CS | CONST_PERSISTENT);
1202 #ifdef PG_DIAG_SCHEMA_NAME
1203 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SCHEMA_NAME", PG_DIAG_SCHEMA_NAME, CONST_CS | CONST_PERSISTENT);
1204 #endif
1205 #ifdef PG_DIAG_TABLE_NAME
1206 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_TABLE_NAME", PG_DIAG_TABLE_NAME, CONST_CS | CONST_PERSISTENT);
1207 #endif
1208 #ifdef PG_DIAG_COLUMN_NAME
1209 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_COLUMN_NAME", PG_DIAG_COLUMN_NAME, CONST_CS | CONST_PERSISTENT);
1210 #endif
1211 #ifdef PG_DIAG_DATATYPE_NAME
1212 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_DATATYPE_NAME", PG_DIAG_DATATYPE_NAME, CONST_CS | CONST_PERSISTENT);
1213 #endif
1214 #ifdef PG_DIAG_CONSTRAINT_NAME
1215 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_CONSTRAINT_NAME", PG_DIAG_CONSTRAINT_NAME, CONST_CS | CONST_PERSISTENT);
1216 #endif
1217 #ifdef PG_DIAG_SEVERITY_NONLOCALIZED
1218 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SEVERITY_NONLOCALIZED", PG_DIAG_SEVERITY_NONLOCALIZED, CONST_CS | CONST_PERSISTENT);
1219 #endif
1220 #endif
1221 	/* pg_convert options */
1222 	REGISTER_LONG_CONSTANT("PGSQL_CONV_IGNORE_DEFAULT", PGSQL_CONV_IGNORE_DEFAULT, CONST_CS | CONST_PERSISTENT);
1223 	REGISTER_LONG_CONSTANT("PGSQL_CONV_FORCE_NULL", PGSQL_CONV_FORCE_NULL, CONST_CS | CONST_PERSISTENT);
1224 	REGISTER_LONG_CONSTANT("PGSQL_CONV_IGNORE_NOT_NULL", PGSQL_CONV_IGNORE_NOT_NULL, CONST_CS | CONST_PERSISTENT);
1225 	/* pg_insert/update/delete/select options */
1226 	REGISTER_LONG_CONSTANT("PGSQL_DML_ESCAPE", PGSQL_DML_ESCAPE, CONST_CS | CONST_PERSISTENT);
1227 	REGISTER_LONG_CONSTANT("PGSQL_DML_NO_CONV", PGSQL_DML_NO_CONV, CONST_CS | CONST_PERSISTENT);
1228 	REGISTER_LONG_CONSTANT("PGSQL_DML_EXEC", PGSQL_DML_EXEC, CONST_CS | CONST_PERSISTENT);
1229 	REGISTER_LONG_CONSTANT("PGSQL_DML_ASYNC", PGSQL_DML_ASYNC, CONST_CS | CONST_PERSISTENT);
1230 	REGISTER_LONG_CONSTANT("PGSQL_DML_STRING", PGSQL_DML_STRING, CONST_CS | CONST_PERSISTENT);
1231 	return SUCCESS;
1232 }
1233 /* }}} */
1234 
1235 /* {{{ PHP_MSHUTDOWN_FUNCTION
1236  */
PHP_MSHUTDOWN_FUNCTION(pgsql)1237 PHP_MSHUTDOWN_FUNCTION(pgsql)
1238 {
1239 	UNREGISTER_INI_ENTRIES();
1240 	zend_hash_destroy(&PGG(notices));
1241 	zend_hash_destroy(&PGG(hashes));
1242 
1243 	return SUCCESS;
1244 }
1245 /* }}} */
1246 
1247 /* {{{ PHP_RINIT_FUNCTION
1248  */
PHP_RINIT_FUNCTION(pgsql)1249 PHP_RINIT_FUNCTION(pgsql)
1250 {
1251 	PGG(default_link) = NULL;
1252 	PGG(num_links) = PGG(num_persistent);
1253 	return SUCCESS;
1254 }
1255 /* }}} */
1256 
1257 /* {{{ PHP_RSHUTDOWN_FUNCTION
1258  */
PHP_RSHUTDOWN_FUNCTION(pgsql)1259 PHP_RSHUTDOWN_FUNCTION(pgsql)
1260 {
1261 	/* clean up notice messages */
1262 	zend_hash_clean(&PGG(notices));
1263 	zend_hash_clean(&PGG(hashes));
1264 	/* clean up persistent connection */
1265 	zend_hash_apply(&EG(persistent_list), (apply_func_t) _rollback_transactions);
1266 	return SUCCESS;
1267 }
1268 /* }}} */
1269 
1270 /* {{{ PHP_MINFO_FUNCTION
1271  */
PHP_MINFO_FUNCTION(pgsql)1272 PHP_MINFO_FUNCTION(pgsql)
1273 {
1274 	char buf[256];
1275 
1276 	php_info_print_table_start();
1277 	php_info_print_table_header(2, "PostgreSQL Support", "enabled");
1278 #if HAVE_PG_CONFIG_H
1279 	php_info_print_table_row(2, "PostgreSQL(libpq) Version", PG_VERSION);
1280 	php_info_print_table_row(2, "PostgreSQL(libpq) ", PG_VERSION_STR);
1281 #ifdef HAVE_PGSQL_WITH_MULTIBYTE_SUPPORT
1282 	php_info_print_table_row(2, "Multibyte character support", "enabled");
1283 #else
1284 	php_info_print_table_row(2, "Multibyte character support", "disabled");
1285 #endif
1286 #if defined(USE_SSL) || defined(USE_OPENSSL)
1287 	php_info_print_table_row(2, "SSL support", "enabled");
1288 #else
1289 	php_info_print_table_row(2, "SSL support", "disabled");
1290 #endif
1291 #endif /* HAVE_PG_CONFIG_H */
1292 	snprintf(buf, sizeof(buf), ZEND_LONG_FMT, PGG(num_persistent));
1293 	php_info_print_table_row(2, "Active Persistent Links", buf);
1294 	snprintf(buf, sizeof(buf), ZEND_LONG_FMT, PGG(num_links));
1295 	php_info_print_table_row(2, "Active Links", buf);
1296 	php_info_print_table_end();
1297 
1298 	DISPLAY_INI_ENTRIES();
1299 }
1300 /* }}} */
1301 
1302 /* {{{ php_pgsql_do_connect
1303  */
php_pgsql_do_connect(INTERNAL_FUNCTION_PARAMETERS,int persistent)1304 static void php_pgsql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
1305 {
1306 	char *host=NULL,*port=NULL,*options=NULL,*tty=NULL,*dbname=NULL,*connstring=NULL;
1307 	PGconn *pgsql;
1308 	smart_str str = {0};
1309 	zval *args;
1310 	uint32_t i;
1311 	int connect_type = 0;
1312 	PGresult *pg_result;
1313 
1314 	args = (zval *)safe_emalloc(ZEND_NUM_ARGS(), sizeof(zval), 0);
1315 	if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 5
1316 			|| zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) {
1317 		efree(args);
1318 		WRONG_PARAM_COUNT;
1319 	}
1320 
1321 	smart_str_appends(&str, "pgsql");
1322 
1323 	for (i = 0; i < ZEND_NUM_ARGS(); i++) {
1324 		/* make sure that the PGSQL_CONNECT_FORCE_NEW bit is not part of the hash so that subsequent connections
1325 		 * can re-use this connection. Bug #39979
1326 		 */
1327 		if (i == 1 && ZEND_NUM_ARGS() == 2 && Z_TYPE(args[i]) == IS_LONG) {
1328 			if (Z_LVAL(args[1]) == PGSQL_CONNECT_FORCE_NEW) {
1329 				continue;
1330 			} else if (Z_LVAL(args[1]) & PGSQL_CONNECT_FORCE_NEW) {
1331 				smart_str_append_long(&str, Z_LVAL(args[1]) ^ PGSQL_CONNECT_FORCE_NEW);
1332 			}
1333 		}
1334 		ZVAL_STR(&args[i], zval_get_string(&args[i]));
1335 		smart_str_appendc(&str, '_');
1336 		smart_str_appendl(&str, Z_STRVAL(args[i]), Z_STRLEN(args[i]));
1337 	}
1338 
1339 	smart_str_0(&str);
1340 
1341 	if (ZEND_NUM_ARGS() == 1) { /* new style, using connection string */
1342 		connstring = Z_STRVAL(args[0]);
1343 	} else if (ZEND_NUM_ARGS() == 2 ) { /* Safe to add conntype_option, since 2 args was illegal */
1344 		connstring = Z_STRVAL(args[0]);
1345 		connect_type = (int)zval_get_long(&args[1]);
1346 	} else {
1347 		host = Z_STRVAL(args[0]);
1348 		port = Z_STRVAL(args[1]);
1349 		dbname = Z_STRVAL(args[ZEND_NUM_ARGS()-1]);
1350 
1351 		switch (ZEND_NUM_ARGS()) {
1352 		case 5:
1353 			tty = Z_STRVAL(args[3]);
1354 			/* fall through */
1355 		case 4:
1356 			options = Z_STRVAL(args[2]);
1357 			break;
1358 		}
1359 	}
1360 
1361 	if (persistent && PGG(allow_persistent)) {
1362 		zend_resource *le;
1363 
1364 		/* try to find if we already have this link in our persistent list */
1365 		if ((le = zend_hash_find_ptr(&EG(persistent_list), str.s)) == NULL) {  /* we don't */
1366 			if (PGG(max_links) != -1 && PGG(num_links) >= PGG(max_links)) {
1367 				php_error_docref(NULL, E_WARNING,
1368 								 "Cannot create new link. Too many open links (" ZEND_LONG_FMT ")", PGG(num_links));
1369 				goto err;
1370 			}
1371 			if (PGG(max_persistent) != -1 && PGG(num_persistent) >= PGG(max_persistent)) {
1372 				php_error_docref(NULL, E_WARNING,
1373 								 "Cannot create new link. Too many open persistent links (" ZEND_LONG_FMT ")", PGG(num_persistent));
1374 				goto err;
1375 			}
1376 
1377 			/* create the link */
1378 			if (connstring) {
1379 				pgsql = PQconnectdb(connstring);
1380 			} else {
1381 				pgsql = PQsetdb(host, port, options, tty, dbname);
1382 			}
1383 			if (pgsql == NULL || PQstatus(pgsql) == CONNECTION_BAD) {
1384 				PHP_PQ_ERROR("Unable to connect to PostgreSQL server: %s", pgsql)
1385 				if (pgsql) {
1386 					PQfinish(pgsql);
1387 				}
1388 				goto err;
1389 			}
1390 
1391 			/* hash it up */
1392 			if (zend_register_persistent_resource(ZSTR_VAL(str.s), ZSTR_LEN(str.s), pgsql, le_plink) == NULL) {
1393 				goto err;
1394 			}
1395 			PGG(num_links)++;
1396 			PGG(num_persistent)++;
1397 		} else {  /* we do */
1398 			if (le->type != le_plink) {
1399 				goto err;
1400 			}
1401 			/* ensure that the link did not die */
1402 			if (PGG(auto_reset_persistent) & 1) {
1403 				/* need to send & get something from backend to
1404 				   make sure we catch CONNECTION_BAD every time */
1405 				PGresult *pg_result;
1406 				pg_result = PQexec(le->ptr, "select 1");
1407 				PQclear(pg_result);
1408 			}
1409 			if (PQstatus(le->ptr) == CONNECTION_BAD) { /* the link died */
1410 				if (le->ptr == NULL) {
1411 					if (connstring) {
1412 						le->ptr = PQconnectdb(connstring);
1413 					} else {
1414 						le->ptr = PQsetdb(host,port,options,tty,dbname);
1415 					}
1416 				}
1417 				else {
1418 					PQreset(le->ptr);
1419 				}
1420 				if (le->ptr == NULL || PQstatus(le->ptr) == CONNECTION_BAD) {
1421 					php_error_docref(NULL, E_WARNING,"PostgreSQL link lost, unable to reconnect");
1422 					zend_hash_del(&EG(persistent_list), str.s);
1423 					goto err;
1424 				}
1425 			}
1426 			pgsql = (PGconn *) le->ptr;
1427 #if HAVE_PQPROTOCOLVERSION && HAVE_PQPARAMETERSTATUS
1428 			if (PQprotocolVersion(pgsql) >= 3 && atof(PQparameterStatus(pgsql, "server_version")) >= 7.2) {
1429 #else
1430 			if (atof(PG_VERSION) >= 7.2) {
1431 #endif
1432 				pg_result = PQexec(pgsql, "RESET ALL;");
1433 				PQclear(pg_result);
1434 			}
1435 		}
1436 		RETVAL_RES(zend_register_resource(pgsql, le_plink));
1437 	} else { /* Non persistent connection */
1438 		zend_resource *index_ptr, new_index_ptr;
1439 
1440 		/* first we check the hash for the hashed_details key.  if it exists,
1441 		 * it should point us to the right offset where the actual pgsql link sits.
1442 		 * if it doesn't, open a new pgsql link, add it to the resource list,
1443 		 * and add a pointer to it with hashed_details as the key.
1444 		 */
1445 		if (!(connect_type & PGSQL_CONNECT_FORCE_NEW)
1446 			&& (index_ptr = zend_hash_find_ptr(&EG(regular_list), str.s)) != NULL) {
1447 			zend_resource *link;
1448 
1449 			if (index_ptr->type != le_index_ptr) {
1450 				goto err;
1451 			}
1452 
1453 			link = (zend_resource *)index_ptr->ptr;
1454 			ZEND_ASSERT(link->ptr && (link->type == le_link || link->type == le_plink));
1455 			php_pgsql_set_default_link(link);
1456 			GC_ADDREF(link);
1457 			RETVAL_RES(link);
1458 			goto cleanup;
1459 		}
1460 		if (PGG(max_links) != -1 && PGG(num_links) >= PGG(max_links)) {
1461 			php_error_docref(NULL, E_WARNING, "Cannot create new link. Too many open links (" ZEND_LONG_FMT ")", PGG(num_links));
1462 			goto err;
1463 		}
1464 
1465 		/* Non-blocking connect */
1466 		if (connect_type & PGSQL_CONNECT_ASYNC) {
1467 			if (connstring) {
1468 				pgsql = PQconnectStart(connstring);
1469 				if (pgsql==NULL || PQstatus(pgsql)==CONNECTION_BAD) {
1470 					PHP_PQ_ERROR("Unable to connect to PostgreSQL server: %s", pgsql);
1471 					if (pgsql) {
1472 						PQfinish(pgsql);
1473 					}
1474 					goto err;
1475 				}
1476 			} else {
1477 				php_error_docref(NULL, E_WARNING, "Connection string required for async connections");
1478 				goto err;
1479 			}
1480 		} else {
1481 			if (connstring) {
1482 				pgsql = PQconnectdb(connstring);
1483 			} else {
1484 				pgsql = PQsetdb(host,port,options,tty,dbname);
1485 			}
1486 			if (pgsql==NULL || PQstatus(pgsql)==CONNECTION_BAD) {
1487 				PHP_PQ_ERROR("Unable to connect to PostgreSQL server: %s", pgsql);
1488 				if (pgsql) {
1489 					PQfinish(pgsql);
1490 				}
1491 				goto err;
1492 			}
1493 		}
1494 
1495 		/* add it to the list */
1496 		RETVAL_RES(zend_register_resource(pgsql, le_link));
1497 
1498 		/* add it to the hash */
1499 		new_index_ptr.ptr = (void *) Z_RES_P(return_value);
1500 		new_index_ptr.type = le_index_ptr;
1501 		zend_hash_update_mem(&EG(regular_list), str.s, (void *) &new_index_ptr, sizeof(zend_resource));
1502 
1503 		/* Keep track of link => hash mapping, so we can remove the hash entry from regular_list
1504 		 * when the connection is closed. This uses the address of the connection rather than the
1505 		 * zend_resource, because the resource destructor is passed a stack copy of the resource
1506 		 * structure. */
1507 		{
1508 			zval tmp;
1509 			ZVAL_STR_COPY(&tmp, str.s);
1510 			zend_hash_index_update(&PGG(hashes), (uintptr_t) pgsql, &tmp);
1511 		}
1512 		PGG(num_links)++;
1513 	}
1514 	/* set notice processor */
1515 	if (! PGG(ignore_notices) && Z_TYPE_P(return_value) == IS_RESOURCE) {
1516 		PQsetNoticeProcessor(pgsql, _php_pgsql_notice_handler, (void*)(zend_uintptr_t)Z_RES_HANDLE_P(return_value));
1517 	}
1518 	php_pgsql_set_default_link(Z_RES_P(return_value));
1519 
1520 cleanup:
1521 	for (i = 0; i < ZEND_NUM_ARGS(); i++) {
1522 		zval_ptr_dtor(&args[i]);
1523 	}
1524 	efree(args);
1525 	smart_str_free(&str);
1526 	return;
1527 
1528 err:
1529 	for (i = 0; i < ZEND_NUM_ARGS(); i++) {
1530 		zval_ptr_dtor(&args[i]);
1531 	}
1532 	efree(args);
1533 	smart_str_free(&str);
1534 	RETURN_FALSE;
1535 }
1536 /* }}} */
1537 
1538 /* {{{ proto resource pg_connect(string connection_string[, int connect_type] | [string host, string port [, string options [, string tty,]]] string database)
1539    Open a PostgreSQL connection */
1540 PHP_FUNCTION(pg_connect)
1541 {
1542 	php_pgsql_do_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU,0);
1543 }
1544 /* }}} */
1545 
1546 /* {{{ proto resource pg_connect_poll(resource connection)
1547    Poll the status of an in-progress async PostgreSQL connection attempt*/
1548 PHP_FUNCTION(pg_connect_poll)
1549 {
1550 	zval *pgsql_link;
1551 	PGconn *pgsql;
1552 	int ret;
1553 
1554 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pgsql_link) == FAILURE) {
1555 		return;
1556 	}
1557 
1558 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
1559 		RETURN_FALSE;
1560 	}
1561 
1562 	ret = PQconnectPoll(pgsql);
1563 
1564 	RETURN_LONG(ret);
1565 }
1566 /* }}} */
1567 
1568 /* {{{ proto resource pg_pconnect(string connection_string | [string host, string port [, string options [, string tty,]]] string database)
1569    Open a persistent PostgreSQL connection */
1570 PHP_FUNCTION(pg_pconnect)
1571 {
1572 	php_pgsql_do_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU,1);
1573 }
1574 /* }}} */
1575 
1576 /* {{{ proto bool pg_close([resource connection])
1577    Close a PostgreSQL connection */
1578 PHP_FUNCTION(pg_close)
1579 {
1580 	zval *pgsql_link = NULL;
1581 	zend_resource *link;
1582 
1583 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|r", &pgsql_link) == FAILURE) {
1584 		return;
1585 	}
1586 
1587 	if (!pgsql_link) {
1588 		link = PGG(default_link);
1589 		CHECK_DEFAULT_LINK(link);
1590 		zend_list_delete(link);
1591 		PGG(default_link) = NULL;
1592 		RETURN_TRUE;
1593 	}
1594 
1595 	link = Z_RES_P(pgsql_link);
1596 	if (zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink) == NULL) {
1597 		RETURN_FALSE;
1598 	}
1599 
1600 	if (link == PGG(default_link)) {
1601 		zend_list_delete(link);
1602 		PGG(default_link) = NULL;
1603 	}
1604 	zend_list_close(link);
1605 
1606 	RETURN_TRUE;
1607 }
1608 /* }}} */
1609 
1610 #define PHP_PG_DBNAME 1
1611 #define PHP_PG_ERROR_MESSAGE 2
1612 #define PHP_PG_OPTIONS 3
1613 #define PHP_PG_PORT 4
1614 #define PHP_PG_TTY 5
1615 #define PHP_PG_HOST 6
1616 #define PHP_PG_VERSION 7
1617 
1618 /* {{{ php_pgsql_get_link_info
1619  */
1620 static void php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
1621 {
1622 	zend_resource *link;
1623 	zval *pgsql_link = NULL;
1624 	int argc = ZEND_NUM_ARGS();
1625 	PGconn *pgsql;
1626 	char *msgbuf;
1627 	char *result;
1628 
1629 	if (zend_parse_parameters(argc, "|r", &pgsql_link) == FAILURE) {
1630 		return;
1631 	}
1632 
1633 	if (argc == 0) {
1634 		link = FETCH_DEFAULT_LINK();
1635 		CHECK_DEFAULT_LINK(link);
1636 	} else {
1637 		link = Z_RES_P(pgsql_link);
1638 	}
1639 
1640 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
1641 		RETURN_FALSE;
1642 	}
1643 
1644 	switch(entry_type) {
1645 		case PHP_PG_DBNAME:
1646 			result = PQdb(pgsql);
1647 			break;
1648 		case PHP_PG_ERROR_MESSAGE:
1649 			result = PQErrorMessageTrim(pgsql, &msgbuf);
1650 			RETVAL_STRING(result);
1651 			efree(result);
1652 			return;
1653 		case PHP_PG_OPTIONS:
1654 			result = PQoptions(pgsql);
1655 			break;
1656 		case PHP_PG_PORT:
1657 			result = PQport(pgsql);
1658 			break;
1659 		case PHP_PG_TTY:
1660 			result = PQtty(pgsql);
1661 			break;
1662 		case PHP_PG_HOST:
1663 			result = PQhost(pgsql);
1664 			break;
1665 		case PHP_PG_VERSION:
1666 			array_init(return_value);
1667 			add_assoc_string(return_value, "client", PG_VERSION);
1668 #if HAVE_PQPROTOCOLVERSION
1669 			add_assoc_long(return_value, "protocol", PQprotocolVersion(pgsql));
1670 #if HAVE_PQPARAMETERSTATUS
1671 			if (PQprotocolVersion(pgsql) >= 3) {
1672 				/* 8.0 or grater supports protorol version 3 */
1673 				char *tmp;
1674 				add_assoc_string(return_value, "server", (char*)PQparameterStatus(pgsql, "server_version"));
1675 
1676 #define PHP_PQ_COPY_PARAM(_x) tmp = (char*)PQparameterStatus(pgsql, _x); \
1677 	if(tmp) add_assoc_string(return_value, _x, tmp); \
1678 	else add_assoc_null(return_value, _x);
1679 
1680 				PHP_PQ_COPY_PARAM("server_encoding");
1681 				PHP_PQ_COPY_PARAM("client_encoding");
1682 				PHP_PQ_COPY_PARAM("is_superuser");
1683 				PHP_PQ_COPY_PARAM("session_authorization");
1684 				PHP_PQ_COPY_PARAM("DateStyle");
1685 				PHP_PQ_COPY_PARAM("IntervalStyle");
1686 				PHP_PQ_COPY_PARAM("TimeZone");
1687 				PHP_PQ_COPY_PARAM("integer_datetimes");
1688 				PHP_PQ_COPY_PARAM("standard_conforming_strings");
1689 				PHP_PQ_COPY_PARAM("application_name");
1690 			}
1691 #endif
1692 #endif
1693 			return;
1694 		default:
1695 			RETURN_FALSE;
1696 	}
1697 	if (result) {
1698 		RETURN_STRING(result);
1699 	} else {
1700 		RETURN_EMPTY_STRING();
1701 	}
1702 }
1703 /* }}} */
1704 
1705 /* {{{ proto string pg_dbname([resource connection])
1706    Get the database name */
1707 PHP_FUNCTION(pg_dbname)
1708 {
1709 	php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_DBNAME);
1710 }
1711 /* }}} */
1712 
1713 /* {{{ proto string pg_last_error([resource connection])
1714    Get the error message string */
1715 PHP_FUNCTION(pg_last_error)
1716 {
1717 	php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_ERROR_MESSAGE);
1718 }
1719 /* }}} */
1720 
1721 /* {{{ proto string pg_options([resource connection])
1722    Get the options associated with the connection */
1723 PHP_FUNCTION(pg_options)
1724 {
1725 	php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_OPTIONS);
1726 }
1727 /* }}} */
1728 
1729 /* {{{ proto int pg_port([resource connection])
1730    Return the port number associated with the connection */
1731 PHP_FUNCTION(pg_port)
1732 {
1733 	php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_PORT);
1734 }
1735 /* }}} */
1736 
1737 /* {{{ proto string pg_tty([resource connection])
1738    Return the tty name associated with the connection */
1739 PHP_FUNCTION(pg_tty)
1740 {
1741 	php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_TTY);
1742 }
1743 /* }}} */
1744 
1745 /* {{{ proto string pg_host([resource connection])
1746    Returns the host name associated with the connection */
1747 PHP_FUNCTION(pg_host)
1748 {
1749 	php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_HOST);
1750 }
1751 /* }}} */
1752 
1753 /* {{{ proto array pg_version([resource connection])
1754    Returns an array with client, protocol and server version (when available) */
1755 PHP_FUNCTION(pg_version)
1756 {
1757 	php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_VERSION);
1758 }
1759 /* }}} */
1760 
1761 #if HAVE_PQPARAMETERSTATUS
1762 /* {{{ proto string|false pg_parameter_status([resource connection,] string param_name)
1763    Returns the value of a server parameter */
1764 PHP_FUNCTION(pg_parameter_status)
1765 {
1766 	zval *pgsql_link = NULL;
1767 	zend_resource *link;
1768 	PGconn *pgsql;
1769 	char *param;
1770 	size_t len;
1771 
1772 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "rs", &pgsql_link, &param, &len) == FAILURE) {
1773 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &param, &len) == SUCCESS) {
1774 			link = FETCH_DEFAULT_LINK();
1775 			CHECK_DEFAULT_LINK(link);
1776 		} else {
1777 			RETURN_FALSE;
1778 		}
1779 	} else {
1780 		link = Z_RES_P(pgsql_link);
1781 	}
1782 
1783 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
1784 		RETURN_FALSE;
1785 	}
1786 
1787 	param = (char*)PQparameterStatus(pgsql, param);
1788 	if (param) {
1789 		RETURN_STRING(param);
1790 	} else {
1791 		RETURN_FALSE;
1792 	}
1793 }
1794 /* }}} */
1795 #endif
1796 
1797 /* {{{ proto bool pg_ping([resource connection])
1798    Ping database. If connection is bad, try to reconnect. */
1799 PHP_FUNCTION(pg_ping)
1800 {
1801 	zval *pgsql_link;
1802 	PGconn *pgsql;
1803 	PGresult *res;
1804 	zend_resource *link;
1805 
1806 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r", &pgsql_link) == SUCCESS) {
1807 		link = Z_RES_P(pgsql_link);
1808 	} else {
1809 		link = FETCH_DEFAULT_LINK();
1810 		CHECK_DEFAULT_LINK(link);
1811 	}
1812 
1813 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
1814 		RETURN_FALSE;
1815 	}
1816 
1817 	/* ping connection */
1818 	res = PQexec(pgsql, "SELECT 1;");
1819 	PQclear(res);
1820 
1821 	/* check status. */
1822 	if (PQstatus(pgsql) == CONNECTION_OK)
1823 		RETURN_TRUE;
1824 
1825 	/* reset connection if it's broken */
1826 	PQreset(pgsql);
1827 	if (PQstatus(pgsql) == CONNECTION_OK) {
1828 		RETURN_TRUE;
1829 	}
1830 	RETURN_FALSE;
1831 }
1832 /* }}} */
1833 
1834 /* {{{ proto resource pg_query([resource connection,] string query)
1835    Execute a query */
1836 PHP_FUNCTION(pg_query)
1837 {
1838 	zval *pgsql_link = NULL;
1839 	char *query;
1840 	int  argc = ZEND_NUM_ARGS();
1841 	size_t query_len;
1842 	int leftover = 0;
1843 	zend_resource *link;
1844 	PGconn *pgsql;
1845 	PGresult *pgsql_result;
1846 	ExecStatusType status;
1847 
1848 	if (argc == 1) {
1849 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &query, &query_len) == FAILURE) {
1850 			return;
1851 		}
1852 		link = FETCH_DEFAULT_LINK();
1853 		CHECK_DEFAULT_LINK(link);
1854 	} else {
1855 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &pgsql_link, &query, &query_len) == FAILURE) {
1856 			return;
1857 		}
1858 		link = Z_RES_P(pgsql_link);
1859 	}
1860 
1861 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
1862 		RETURN_FALSE;
1863 	}
1864 
1865 	if (PQ_SETNONBLOCKING(pgsql, 0)) {
1866 		php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode");
1867 		RETURN_FALSE;
1868 	}
1869 	while ((pgsql_result = PQgetResult(pgsql))) {
1870 		PQclear(pgsql_result);
1871 		leftover = 1;
1872 	}
1873 	if (leftover) {
1874 		php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first");
1875 	}
1876 	pgsql_result = PQexec(pgsql, query);
1877 	if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
1878 		PQclear(pgsql_result);
1879 		PQreset(pgsql);
1880 		pgsql_result = PQexec(pgsql, query);
1881 	}
1882 
1883 	if (pgsql_result) {
1884 		status = PQresultStatus(pgsql_result);
1885 	} else {
1886 		status = (ExecStatusType) PQstatus(pgsql);
1887 	}
1888 
1889 	switch (status) {
1890 		case PGRES_EMPTY_QUERY:
1891 		case PGRES_BAD_RESPONSE:
1892 		case PGRES_NONFATAL_ERROR:
1893 		case PGRES_FATAL_ERROR:
1894 			PHP_PQ_ERROR("Query failed: %s", pgsql);
1895 			PQclear(pgsql_result);
1896 			RETURN_FALSE;
1897 			break;
1898 		case PGRES_COMMAND_OK: /* successful command that did not return rows */
1899 		default:
1900 			if (pgsql_result) {
1901 				pgsql_result_handle *pg_result = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
1902 				pg_result->conn = pgsql;
1903 				pg_result->result = pgsql_result;
1904 				pg_result->row = 0;
1905 				RETURN_RES(zend_register_resource(pg_result, le_result));
1906 			} else {
1907 				PQclear(pgsql_result);
1908 				RETURN_FALSE;
1909 			}
1910 			break;
1911 	}
1912 }
1913 /* }}} */
1914 
1915 #if HAVE_PQEXECPARAMS || HAVE_PQEXECPREPARED || HAVE_PQSENDQUERYPARAMS || HAVE_PQSENDQUERYPREPARED
1916 /* {{{ _php_pgsql_free_params */
1917 static void _php_pgsql_free_params(char **params, int num_params)
1918 {
1919 	if (num_params > 0) {
1920 		int i;
1921 		for (i = 0; i < num_params; i++) {
1922 			if (params[i]) {
1923 				efree(params[i]);
1924 			}
1925 		}
1926 		efree(params);
1927 	}
1928 }
1929 /* }}} */
1930 #endif
1931 
1932 #if HAVE_PQEXECPARAMS
1933 /* {{{ proto resource pg_query_params([resource connection,] string query, array params)
1934    Execute a query */
1935 PHP_FUNCTION(pg_query_params)
1936 {
1937 	zval *pgsql_link = NULL;
1938 	zval *pv_param_arr, *tmp;
1939 	char *query;
1940 	size_t query_len;
1941 	int argc = ZEND_NUM_ARGS();
1942 	int leftover = 0;
1943 	int num_params = 0;
1944 	char **params = NULL;
1945 	zend_resource *link;
1946 	PGconn *pgsql;
1947 	PGresult *pgsql_result;
1948 	ExecStatusType status;
1949 	pgsql_result_handle *pg_result;
1950 
1951 	if (argc == 2) {
1952 		if (zend_parse_parameters(argc, "sa", &query, &query_len, &pv_param_arr) == FAILURE) {
1953 			return;
1954 		}
1955 		link = FETCH_DEFAULT_LINK();
1956 		CHECK_DEFAULT_LINK(link);
1957 	} else {
1958 		if (zend_parse_parameters(argc, "rsa", &pgsql_link, &query, &query_len, &pv_param_arr) == FAILURE) {
1959 			return;
1960 		}
1961 		link = Z_RES_P(pgsql_link);
1962 	}
1963 
1964 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
1965 		RETURN_FALSE;
1966 	}
1967 
1968 	if (PQ_SETNONBLOCKING(pgsql, 0)) {
1969 		php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode");
1970 		RETURN_FALSE;
1971 	}
1972 	while ((pgsql_result = PQgetResult(pgsql))) {
1973 		PQclear(pgsql_result);
1974 		leftover = 1;
1975 	}
1976 	if (leftover) {
1977 		php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first");
1978 	}
1979 
1980 	num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
1981 	if (num_params > 0) {
1982 		int i = 0;
1983 		params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
1984 
1985 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pv_param_arr), tmp) {
1986 			ZVAL_DEREF(tmp);
1987 			if (Z_TYPE_P(tmp) == IS_NULL) {
1988 				params[i] = NULL;
1989 			} else {
1990 				zval tmp_val;
1991 
1992 				ZVAL_COPY(&tmp_val, tmp);
1993 				convert_to_cstring(&tmp_val);
1994 				if (Z_TYPE(tmp_val) != IS_STRING) {
1995 					php_error_docref(NULL, E_WARNING,"Error converting parameter");
1996 					zval_ptr_dtor(&tmp_val);
1997 					_php_pgsql_free_params(params, num_params);
1998 					RETURN_FALSE;
1999 				}
2000 				params[i] = estrndup(Z_STRVAL(tmp_val), Z_STRLEN(tmp_val));
2001 				zval_ptr_dtor(&tmp_val);
2002 			}
2003 			i++;
2004 		} ZEND_HASH_FOREACH_END();
2005 	}
2006 
2007 	pgsql_result = PQexecParams(pgsql, query, num_params,
2008 					NULL, (const char * const *)params, NULL, NULL, 0);
2009 	if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
2010 		PQclear(pgsql_result);
2011 		PQreset(pgsql);
2012 		pgsql_result = PQexecParams(pgsql, query, num_params,
2013 						NULL, (const char * const *)params, NULL, NULL, 0);
2014 	}
2015 
2016 	if (pgsql_result) {
2017 		status = PQresultStatus(pgsql_result);
2018 	} else {
2019 		status = (ExecStatusType) PQstatus(pgsql);
2020 	}
2021 
2022 	_php_pgsql_free_params(params, num_params);
2023 
2024 	switch (status) {
2025 		case PGRES_EMPTY_QUERY:
2026 		case PGRES_BAD_RESPONSE:
2027 		case PGRES_NONFATAL_ERROR:
2028 		case PGRES_FATAL_ERROR:
2029 			PHP_PQ_ERROR("Query failed: %s", pgsql);
2030 			PQclear(pgsql_result);
2031 			RETURN_FALSE;
2032 			break;
2033 		case PGRES_COMMAND_OK: /* successful command that did not return rows */
2034 		default:
2035 			if (pgsql_result) {
2036 				pg_result = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
2037 				pg_result->conn = pgsql;
2038 				pg_result->result = pgsql_result;
2039 				pg_result->row = 0;
2040 				RETURN_RES(zend_register_resource(pg_result, le_result));
2041 			} else {
2042 				PQclear(pgsql_result);
2043 				RETURN_FALSE;
2044 			}
2045 			break;
2046 	}
2047 }
2048 /* }}} */
2049 #endif
2050 
2051 #if HAVE_PQPREPARE
2052 /* {{{ proto resource pg_prepare([resource connection,] string stmtname, string query)
2053    Prepare a query for future execution */
2054 PHP_FUNCTION(pg_prepare)
2055 {
2056 	zval *pgsql_link = NULL;
2057 	char *query, *stmtname;
2058 	size_t query_len, stmtname_len;
2059 	int argc = ZEND_NUM_ARGS();
2060 	int leftover = 0;
2061 	PGconn *pgsql;
2062 	zend_resource *link;
2063 	PGresult *pgsql_result;
2064 	ExecStatusType status;
2065 	pgsql_result_handle *pg_result;
2066 
2067 	if (argc == 2) {
2068 		if (zend_parse_parameters(argc, "ss", &stmtname, &stmtname_len, &query, &query_len) == FAILURE) {
2069 			return;
2070 		}
2071 		link = FETCH_DEFAULT_LINK();
2072 		CHECK_DEFAULT_LINK(link);
2073 	} else {
2074 		if (zend_parse_parameters(argc, "rss", &pgsql_link, &stmtname, &stmtname_len, &query, &query_len) == FAILURE) {
2075 			return;
2076 		}
2077 		link = Z_RES_P(pgsql_link);
2078 	}
2079 
2080 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
2081 		RETURN_FALSE;
2082 	}
2083 
2084 	if (PQ_SETNONBLOCKING(pgsql, 0)) {
2085 		php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode");
2086 		RETURN_FALSE;
2087 	}
2088 	while ((pgsql_result = PQgetResult(pgsql))) {
2089 		PQclear(pgsql_result);
2090 		leftover = 1;
2091 	}
2092 	if (leftover) {
2093 		php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first");
2094 	}
2095 	pgsql_result = PQprepare(pgsql, stmtname, query, 0, NULL);
2096 	if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
2097 		PQclear(pgsql_result);
2098 		PQreset(pgsql);
2099 		pgsql_result = PQprepare(pgsql, stmtname, query, 0, NULL);
2100 	}
2101 
2102 	if (pgsql_result) {
2103 		status = PQresultStatus(pgsql_result);
2104 	} else {
2105 		status = (ExecStatusType) PQstatus(pgsql);
2106 	}
2107 
2108 	switch (status) {
2109 		case PGRES_EMPTY_QUERY:
2110 		case PGRES_BAD_RESPONSE:
2111 		case PGRES_NONFATAL_ERROR:
2112 		case PGRES_FATAL_ERROR:
2113 			PHP_PQ_ERROR("Query failed: %s", pgsql);
2114 			PQclear(pgsql_result);
2115 			RETURN_FALSE;
2116 			break;
2117 		case PGRES_COMMAND_OK: /* successful command that did not return rows */
2118 		default:
2119 			if (pgsql_result) {
2120 				pg_result = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
2121 				pg_result->conn = pgsql;
2122 				pg_result->result = pgsql_result;
2123 				pg_result->row = 0;
2124 				RETURN_RES(zend_register_resource(pg_result, le_result));
2125 			} else {
2126 				PQclear(pgsql_result);
2127 				RETURN_FALSE;
2128 			}
2129 			break;
2130 	}
2131 }
2132 /* }}} */
2133 #endif
2134 
2135 #if HAVE_PQEXECPREPARED
2136 /* {{{ proto resource pg_execute([resource connection,] string stmtname, array params)
2137    Execute a prepared query  */
2138 PHP_FUNCTION(pg_execute)
2139 {
2140 	zval *pgsql_link = NULL;
2141 	zval *pv_param_arr, *tmp;
2142 	char *stmtname;
2143 	size_t stmtname_len;
2144 	int argc = ZEND_NUM_ARGS();
2145 	int leftover = 0;
2146 	int num_params = 0;
2147 	char **params = NULL;
2148 	PGconn *pgsql;
2149 	zend_resource *link;
2150 	PGresult *pgsql_result;
2151 	ExecStatusType status;
2152 	pgsql_result_handle *pg_result;
2153 
2154 	if (argc == 2) {
2155 		if (zend_parse_parameters(argc, "sa", &stmtname, &stmtname_len, &pv_param_arr)==FAILURE) {
2156 			return;
2157 		}
2158 		link = FETCH_DEFAULT_LINK();
2159 		CHECK_DEFAULT_LINK(link);
2160 	} else {
2161 		if (zend_parse_parameters(argc, "rsa", &pgsql_link, &stmtname, &stmtname_len, &pv_param_arr) == FAILURE) {
2162 			return;
2163 		}
2164 		link = Z_RES_P(pgsql_link);
2165 	}
2166 
2167 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
2168 		RETURN_FALSE;
2169 	}
2170 
2171 	if (PQ_SETNONBLOCKING(pgsql, 0)) {
2172 		php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode");
2173 		RETURN_FALSE;
2174 	}
2175 	while ((pgsql_result = PQgetResult(pgsql))) {
2176 		PQclear(pgsql_result);
2177 		leftover = 1;
2178 	}
2179 	if (leftover) {
2180 		php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first");
2181 	}
2182 
2183 	num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
2184 	if (num_params > 0) {
2185 		int i = 0;
2186 		params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
2187 
2188 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pv_param_arr), tmp) {
2189 
2190 			if (Z_TYPE_P(tmp) == IS_NULL) {
2191 				params[i] = NULL;
2192 			} else {
2193 				zend_string *tmp_str;
2194 				zend_string *str = zval_get_tmp_string(tmp, &tmp_str);
2195 
2196 				params[i] = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
2197 				zend_tmp_string_release(tmp_str);
2198 			}
2199 
2200 			i++;
2201 		} ZEND_HASH_FOREACH_END();
2202 	}
2203 
2204 	pgsql_result = PQexecPrepared(pgsql, stmtname, num_params,
2205 					(const char * const *)params, NULL, NULL, 0);
2206 	if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
2207 		PQclear(pgsql_result);
2208 		PQreset(pgsql);
2209 		pgsql_result = PQexecPrepared(pgsql, stmtname, num_params,
2210 						(const char * const *)params, NULL, NULL, 0);
2211 	}
2212 
2213 	if (pgsql_result) {
2214 		status = PQresultStatus(pgsql_result);
2215 	} else {
2216 		status = (ExecStatusType) PQstatus(pgsql);
2217 	}
2218 
2219 	_php_pgsql_free_params(params, num_params);
2220 
2221 	switch (status) {
2222 		case PGRES_EMPTY_QUERY:
2223 		case PGRES_BAD_RESPONSE:
2224 		case PGRES_NONFATAL_ERROR:
2225 		case PGRES_FATAL_ERROR:
2226 			PHP_PQ_ERROR("Query failed: %s", pgsql);
2227 			PQclear(pgsql_result);
2228 			RETURN_FALSE;
2229 			break;
2230 		case PGRES_COMMAND_OK: /* successful command that did not return rows */
2231 		default:
2232 			if (pgsql_result) {
2233 				pg_result = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
2234 				pg_result->conn = pgsql;
2235 				pg_result->result = pgsql_result;
2236 				pg_result->row = 0;
2237 				RETURN_RES(zend_register_resource(pg_result, le_result));
2238 			} else {
2239 				PQclear(pgsql_result);
2240 				RETURN_FALSE;
2241 			}
2242 			break;
2243 	}
2244 }
2245 /* }}} */
2246 #endif
2247 
2248 #define PHP_PG_NUM_ROWS 1
2249 #define PHP_PG_NUM_FIELDS 2
2250 #define PHP_PG_CMD_TUPLES 3
2251 
2252 /* {{{ php_pgsql_get_result_info
2253  */
2254 static void php_pgsql_get_result_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
2255 {
2256 	zval *result;
2257 	PGresult *pgsql_result;
2258 	pgsql_result_handle *pg_result;
2259 
2260 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &result) == FAILURE) {
2261 		return;
2262 	}
2263 
2264 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2265 		RETURN_FALSE;
2266 	}
2267 
2268 	pgsql_result = pg_result->result;
2269 
2270 	switch (entry_type) {
2271 		case PHP_PG_NUM_ROWS:
2272 			RETVAL_LONG(PQntuples(pgsql_result));
2273 			break;
2274 		case PHP_PG_NUM_FIELDS:
2275 			RETVAL_LONG(PQnfields(pgsql_result));
2276 			break;
2277 		case PHP_PG_CMD_TUPLES:
2278 #if HAVE_PQCMDTUPLES
2279 			RETVAL_LONG(atoi(PQcmdTuples(pgsql_result)));
2280 #else
2281 			php_error_docref(NULL, E_WARNING, "Not supported under this build");
2282 			RETVAL_LONG(0);
2283 #endif
2284 			break;
2285 		default:
2286 			RETURN_FALSE;
2287 	}
2288 }
2289 /* }}} */
2290 
2291 /* {{{ proto int pg_num_rows(resource result)
2292    Return the number of rows in the result */
2293 PHP_FUNCTION(pg_num_rows)
2294 {
2295 	php_pgsql_get_result_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_NUM_ROWS);
2296 }
2297 /* }}} */
2298 
2299 /* {{{ proto int pg_num_fields(resource result)
2300    Return the number of fields in the result */
2301 PHP_FUNCTION(pg_num_fields)
2302 {
2303 	php_pgsql_get_result_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_NUM_FIELDS);
2304 }
2305 /* }}} */
2306 
2307 #if HAVE_PQCMDTUPLES
2308 /* {{{ proto int pg_affected_rows(resource result)
2309    Returns the number of affected tuples */
2310 PHP_FUNCTION(pg_affected_rows)
2311 {
2312 	php_pgsql_get_result_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_CMD_TUPLES);
2313 }
2314 /* }}} */
2315 #endif
2316 
2317 /* {{{ proto mixed pg_last_notice(resource connection [, int option])
2318    Returns the last notice set by the backend */
2319 PHP_FUNCTION(pg_last_notice)
2320 {
2321 	zval *pgsql_link = NULL;
2322 	zval *notice, *notices;
2323 	PGconn *pg_link;
2324 	zend_long option = PGSQL_NOTICE_LAST;
2325 
2326 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &pgsql_link, &option) == FAILURE) {
2327 		return;
2328 	}
2329 
2330 	/* Just to check if user passed valid resoruce */
2331 	if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
2332 		RETURN_FALSE;
2333 	}
2334 
2335 	notices = zend_hash_index_find(&PGG(notices), (zend_ulong)Z_RES_HANDLE_P(pgsql_link));
2336 	switch (option) {
2337 		case PGSQL_NOTICE_LAST:
2338 			if (notices) {
2339 				zend_hash_internal_pointer_end(Z_ARRVAL_P(notices));
2340 				if ((notice = zend_hash_get_current_data(Z_ARRVAL_P(notices))) == NULL) {
2341 					RETURN_EMPTY_STRING();
2342 				}
2343 				RETURN_ZVAL(notice, 1, 0);
2344 			} else {
2345 				RETURN_EMPTY_STRING();
2346 			}
2347 			break;
2348 		case PGSQL_NOTICE_ALL:
2349 			if (notices) {
2350 				RETURN_ZVAL(notices, 1, 0);
2351 			} else {
2352 				array_init(return_value);
2353 				return;
2354 			}
2355 			break;
2356 		case PGSQL_NOTICE_CLEAR:
2357 			if (notices) {
2358 				zend_hash_clean(&PGG(notices));
2359 			}
2360 			RETURN_TRUE;
2361 			break;
2362 		default:
2363 			php_error_docref(NULL, E_WARNING,
2364 				"Invalid option specified (" ZEND_LONG_FMT ")", option);
2365 	}
2366 	RETURN_FALSE;
2367 }
2368 /* }}} */
2369 
2370 /* {{{ get_field_name
2371  */
2372 static char *get_field_name(PGconn *pgsql, Oid oid, HashTable *list)
2373 {
2374 	smart_str str = {0};
2375 	zend_resource *field_type;
2376 	char *ret=NULL;
2377 
2378 	/* try to lookup the type in the resource list */
2379 	smart_str_appends(&str, "pgsql_oid_");
2380 	smart_str_append_unsigned(&str, oid);
2381 	smart_str_0(&str);
2382 
2383 	if ((field_type = zend_hash_find_ptr(list, str.s)) != NULL) {
2384 		ret = estrdup((char *)field_type->ptr);
2385 	} else { /* hash all oid's */
2386 		int i, num_rows;
2387 		int oid_offset,name_offset;
2388 		char *tmp_oid, *end_ptr, *tmp_name;
2389 		zend_resource new_oid_entry;
2390 		PGresult *result;
2391 
2392 		if ((result = PQexec(pgsql, "select oid,typname from pg_type")) == NULL || PQresultStatus(result) != PGRES_TUPLES_OK) {
2393 			if (result) {
2394 				PQclear(result);
2395 			}
2396 			smart_str_free(&str);
2397 			return estrndup("", sizeof("") - 1);
2398 		}
2399 		num_rows = PQntuples(result);
2400 		oid_offset = PQfnumber(result,"oid");
2401 		name_offset = PQfnumber(result,"typname");
2402 
2403 		for (i=0; i<num_rows; i++) {
2404 			if ((tmp_oid = PQgetvalue(result,i,oid_offset))==NULL) {
2405 				continue;
2406 			}
2407 
2408 			smart_str_free(&str);
2409 			smart_str_appends(&str, "pgsql_oid_");
2410 			smart_str_appends(&str, tmp_oid);
2411 			smart_str_0(&str);
2412 
2413 			if ((tmp_name = PQgetvalue(result,i,name_offset))==NULL) {
2414 				continue;
2415 			}
2416 			new_oid_entry.type = le_string;
2417 			new_oid_entry.ptr = estrdup(tmp_name);
2418 			zend_hash_update_mem(list, str.s, (void *) &new_oid_entry, sizeof(zend_resource));
2419 			if (!ret && strtoul(tmp_oid, &end_ptr, 10)==oid) {
2420 				ret = estrdup(tmp_name);
2421 			}
2422 		}
2423 		PQclear(result);
2424 	}
2425 
2426 	smart_str_free(&str);
2427 	return ret;
2428 }
2429 /* }}} */
2430 
2431 #ifdef HAVE_PQFTABLE
2432 /* {{{ proto mixed pg_field_table(resource result, int field_number[, bool oid_only])
2433    Returns the name of the table field belongs to, or table's oid if oid_only is true */
2434 PHP_FUNCTION(pg_field_table)
2435 {
2436 	zval *result;
2437 	pgsql_result_handle *pg_result;
2438 	zend_long fnum = -1;
2439 	zend_bool return_oid = 0;
2440 	Oid oid;
2441 	smart_str hash_key = {0};
2442 	char *table_name;
2443 	zend_resource *field_table;
2444 
2445 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl|b", &result, &fnum, &return_oid) == FAILURE) {
2446 		return;
2447 	}
2448 
2449 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2450 		RETURN_FALSE;
2451 	}
2452 
2453 	if (fnum < 0 || fnum >= PQnfields(pg_result->result)) {
2454 		php_error_docref(NULL, E_WARNING, "Bad field offset specified");
2455 		RETURN_FALSE;
2456 	}
2457 
2458 	oid = PQftable(pg_result->result, (int)fnum);
2459 
2460 	if (InvalidOid == oid) {
2461 		RETURN_FALSE;
2462 	}
2463 
2464 	if (return_oid) {
2465 #if UINT_MAX > ZEND_LONG_MAX /* Oid is unsigned int, we don't need this code, where LONG is wider */
2466 		if (oid > ZEND_LONG_MAX) {
2467 			smart_str oidstr = {0};
2468 			smart_str_append_unsigned(&oidstr, oid);
2469 			smart_str_0(&oidstr);
2470 			RETURN_NEW_STR(oidstr.s);
2471 		} else
2472 #endif
2473 			RETURN_LONG((zend_long)oid);
2474 	}
2475 
2476 	/* try to lookup the table name in the resource list */
2477 	smart_str_appends(&hash_key, "pgsql_table_oid_");
2478 	smart_str_append_unsigned(&hash_key, oid);
2479 	smart_str_0(&hash_key);
2480 
2481 	if ((field_table = zend_hash_find_ptr(&EG(regular_list), hash_key.s)) != NULL) {
2482 		smart_str_free(&hash_key);
2483 		RETURN_STRING((char *)field_table->ptr);
2484 	} else { /* Not found, lookup by querying PostgreSQL system tables */
2485 		PGresult *tmp_res;
2486 		smart_str querystr = {0};
2487 		zend_resource new_field_table;
2488 
2489 		smart_str_appends(&querystr, "select relname from pg_class where oid=");
2490 		smart_str_append_unsigned(&querystr, oid);
2491 		smart_str_0(&querystr);
2492 
2493 		if ((tmp_res = PQexec(pg_result->conn, ZSTR_VAL(querystr.s))) == NULL || PQresultStatus(tmp_res) != PGRES_TUPLES_OK) {
2494 			if (tmp_res) {
2495 				PQclear(tmp_res);
2496 			}
2497 			smart_str_free(&querystr);
2498 			smart_str_free(&hash_key);
2499 			RETURN_FALSE;
2500 		}
2501 
2502 		smart_str_free(&querystr);
2503 
2504 		if ((table_name = PQgetvalue(tmp_res, 0, 0)) == NULL) {
2505 			PQclear(tmp_res);
2506 			smart_str_free(&hash_key);
2507 			RETURN_FALSE;
2508 		}
2509 
2510 		new_field_table.type = le_string;
2511 		new_field_table.ptr = estrdup(table_name);
2512 		zend_hash_update_mem(&EG(regular_list), hash_key.s, (void *)&new_field_table, sizeof(zend_resource));
2513 
2514 		smart_str_free(&hash_key);
2515 		PQclear(tmp_res);
2516 		RETURN_STRING(table_name);
2517 	}
2518 
2519 }
2520 /* }}} */
2521 #endif
2522 
2523 #define PHP_PG_FIELD_NAME 1
2524 #define PHP_PG_FIELD_SIZE 2
2525 #define PHP_PG_FIELD_TYPE 3
2526 #define PHP_PG_FIELD_TYPE_OID 4
2527 
2528 /* {{{ php_pgsql_get_field_info
2529  */
2530 static void php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
2531 {
2532 	zval *result;
2533 	zend_long field;
2534 	PGresult *pgsql_result;
2535 	pgsql_result_handle *pg_result;
2536 	Oid oid;
2537 
2538 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &result, &field) == FAILURE) {
2539 		return;
2540 	}
2541 
2542 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2543 		RETURN_FALSE;
2544 	}
2545 
2546 
2547 	pgsql_result = pg_result->result;
2548 
2549 	if (field < 0 || field >= PQnfields(pgsql_result)) {
2550 		php_error_docref(NULL, E_WARNING, "Bad field offset specified");
2551 		RETURN_FALSE;
2552 	}
2553 
2554 	switch (entry_type) {
2555 		case PHP_PG_FIELD_NAME:
2556 			RETURN_STRING(PQfname(pgsql_result, (int)field));
2557 			break;
2558 		case PHP_PG_FIELD_SIZE:
2559 			RETURN_LONG(PQfsize(pgsql_result, (int)field));
2560 			break;
2561 		case PHP_PG_FIELD_TYPE: {
2562 				char *name = get_field_name(pg_result->conn, PQftype(pgsql_result, (int)field), &EG(regular_list));
2563 				RETVAL_STRING(name);
2564 				efree(name);
2565 			}
2566 			break;
2567 		case PHP_PG_FIELD_TYPE_OID:
2568 
2569 			oid = PQftype(pgsql_result, (int)field);
2570 #if UINT_MAX > ZEND_LONG_MAX
2571 			if (oid > ZEND_LONG_MAX) {
2572 				smart_str s = {0};
2573 				smart_str_append_unsigned(&s, oid);
2574 				smart_str_0(&s);
2575 				RETURN_NEW_STR(s.s);
2576 			} else
2577 #endif
2578 			{
2579 				RETURN_LONG((zend_long)oid);
2580 			}
2581 			break;
2582 		default:
2583 			RETURN_FALSE;
2584 	}
2585 }
2586 /* }}} */
2587 
2588 /* {{{ proto string pg_field_name(resource result, int field_number)
2589    Returns the name of the field */
2590 PHP_FUNCTION(pg_field_name)
2591 {
2592 	php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_NAME);
2593 }
2594 /* }}} */
2595 
2596 /* {{{ proto int pg_field_size(resource result, int field_number)
2597    Returns the internal size of the field */
2598 PHP_FUNCTION(pg_field_size)
2599 {
2600 	php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_SIZE);
2601 }
2602 /* }}} */
2603 
2604 /* {{{ proto string pg_field_type(resource result, int field_number)
2605    Returns the type name for the given field */
2606 PHP_FUNCTION(pg_field_type)
2607 {
2608 	php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_TYPE);
2609 }
2610 /* }}} */
2611 
2612 /* {{{ proto string pg_field_type_oid(resource result, int field_number)
2613    Returns the type oid for the given field */
2614 PHP_FUNCTION(pg_field_type_oid)
2615 {
2616 	php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_TYPE_OID);
2617 }
2618 /* }}} */
2619 
2620 /* {{{ proto int pg_field_num(resource result, string field_name)
2621    Returns the field number of the named field */
2622 PHP_FUNCTION(pg_field_num)
2623 {
2624 	zval *result;
2625 	char *field;
2626 	size_t field_len;
2627 	PGresult *pgsql_result;
2628 	pgsql_result_handle *pg_result;
2629 
2630 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &result, &field, &field_len) == FAILURE) {
2631 		return;
2632 	}
2633 
2634 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2635 		RETURN_FALSE;
2636 	}
2637 
2638 	pgsql_result = pg_result->result;
2639 
2640 	RETURN_LONG(PQfnumber(pgsql_result, field));
2641 }
2642 /* }}} */
2643 
2644 /* {{{ proto mixed pg_fetch_result(resource result, [int row_number,] mixed field_name)
2645    Returns values from a result identifier */
2646 PHP_FUNCTION(pg_fetch_result)
2647 {
2648 	zval *result, *field=NULL;
2649 	zend_long row;
2650 	PGresult *pgsql_result;
2651 	pgsql_result_handle *pg_result;
2652 	int field_offset, pgsql_row, argc = ZEND_NUM_ARGS();
2653 
2654 	if (argc == 2) {
2655 		if (zend_parse_parameters(argc, "rz", &result, &field) == FAILURE) {
2656 			return;
2657 		}
2658 	} else {
2659 		if (zend_parse_parameters(argc, "rlz", &result, &row, &field) == FAILURE) {
2660 			return;
2661 		}
2662 	}
2663 
2664 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2665 		RETURN_FALSE;
2666 	}
2667 
2668 	pgsql_result = pg_result->result;
2669 	if (argc == 2) {
2670 		if (pg_result->row < 0) {
2671 			pg_result->row = 0;
2672 		}
2673 		pgsql_row = pg_result->row;
2674 		if (pgsql_row >= PQntuples(pgsql_result)) {
2675 			RETURN_FALSE;
2676 		}
2677 		pg_result->row++;
2678 	} else {
2679 		if (row < 0 || row >= PQntuples(pgsql_result)) {
2680 			php_error_docref(NULL, E_WARNING, "Unable to jump to row " ZEND_LONG_FMT " on PostgreSQL result index " ZEND_LONG_FMT,
2681 							row, Z_LVAL_P(result));
2682 			RETURN_FALSE;
2683 		}
2684 		pgsql_row = (int)row;
2685 	}
2686 	switch (Z_TYPE_P(field)) {
2687 		case IS_STRING:
2688 			field_offset = PQfnumber(pgsql_result, Z_STRVAL_P(field));
2689 			if (field_offset < 0 || field_offset >= PQnfields(pgsql_result)) {
2690 				php_error_docref(NULL, E_WARNING, "Bad column offset specified");
2691 				RETURN_FALSE;
2692 			}
2693 			break;
2694 		default:
2695 			convert_to_long_ex(field);
2696 			if (Z_LVAL_P(field) < 0 || Z_LVAL_P(field) >= PQnfields(pgsql_result)) {
2697 				php_error_docref(NULL, E_WARNING, "Bad column offset specified");
2698 				RETURN_FALSE;
2699 			}
2700 			field_offset = (int)Z_LVAL_P(field);
2701 			break;
2702 	}
2703 
2704 	if (PQgetisnull(pgsql_result, pgsql_row, field_offset)) {
2705 		RETVAL_NULL();
2706 	} else {
2707 		RETVAL_STRINGL(PQgetvalue(pgsql_result, pgsql_row, field_offset),
2708 				PQgetlength(pgsql_result, pgsql_row, field_offset));
2709 	}
2710 }
2711 /* }}} */
2712 
2713 /* {{{ void php_pgsql_fetch_hash */
2714 static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_type, int into_object)
2715 {
2716 	zval                *result, *zrow = NULL;
2717 	PGresult            *pgsql_result;
2718 	pgsql_result_handle *pg_result;
2719 	int             i, num_fields, pgsql_row, use_row;
2720 	zend_long            row = -1;
2721 	char            *field_name;
2722 	zval            *ctor_params = NULL;
2723 	zend_class_entry *ce = NULL;
2724 
2725 	if (into_object) {
2726 		zend_string *class_name = NULL;
2727 
2728 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|z!Sz", &result, &zrow, &class_name, &ctor_params) == FAILURE) {
2729 			return;
2730 		}
2731 		if (!class_name) {
2732 			ce = zend_standard_class_def;
2733 		} else {
2734 			ce = zend_fetch_class(class_name, ZEND_FETCH_CLASS_AUTO);
2735 		}
2736 		if (!ce) {
2737 			php_error_docref(NULL, E_WARNING, "Could not find class '%s'", ZSTR_VAL(class_name));
2738 			return;
2739 		}
2740 		result_type = PGSQL_ASSOC;
2741 	} else {
2742 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|z!l", &result, &zrow, &result_type) == FAILURE) {
2743 			return;
2744 		}
2745 	}
2746 	if (zrow == NULL) {
2747 		row = -1;
2748 	} else {
2749 		convert_to_long(zrow);
2750 		row = Z_LVAL_P(zrow);
2751 		if (row < 0) {
2752 			php_error_docref(NULL, E_WARNING, "The row parameter must be greater or equal to zero");
2753 			RETURN_FALSE;
2754 		}
2755 	}
2756 	use_row = ZEND_NUM_ARGS() > 1 && row != -1;
2757 
2758 	if (!(result_type & PGSQL_BOTH)) {
2759 		php_error_docref(NULL, E_WARNING, "Invalid result type");
2760 		RETURN_FALSE;
2761 	}
2762 
2763 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2764 		RETURN_FALSE;
2765 	}
2766 
2767 	pgsql_result = pg_result->result;
2768 
2769 	if (use_row) {
2770 		if (row < 0 || row >= PQntuples(pgsql_result)) {
2771 			php_error_docref(NULL, E_WARNING, "Unable to jump to row " ZEND_LONG_FMT " on PostgreSQL result index " ZEND_LONG_FMT,
2772 							row, Z_LVAL_P(result));
2773 			RETURN_FALSE;
2774 		}
2775 		pgsql_row = (int)row;
2776 		pg_result->row = pgsql_row;
2777 	} else {
2778 		/* If 2nd param is NULL, use internal row counter to access next row */
2779 		pgsql_row = pg_result->row;
2780 		if (pgsql_row < 0 || pgsql_row >= PQntuples(pgsql_result)) {
2781 			RETURN_FALSE;
2782 		}
2783 		pg_result->row++;
2784 	}
2785 
2786 	array_init(return_value);
2787 	for (i = 0, num_fields = PQnfields(pgsql_result); i < num_fields; i++) {
2788 		if (PQgetisnull(pgsql_result, pgsql_row, i)) {
2789 			if (result_type & PGSQL_NUM) {
2790 				add_index_null(return_value, i);
2791 			}
2792 			if (result_type & PGSQL_ASSOC) {
2793 				field_name = PQfname(pgsql_result, i);
2794 				add_assoc_null(return_value, field_name);
2795 			}
2796 		} else {
2797 			char *element = PQgetvalue(pgsql_result, pgsql_row, i);
2798 			if (element) {
2799 				const size_t element_len = strlen(element);
2800 
2801 				if (result_type & PGSQL_NUM) {
2802 					add_index_stringl(return_value, i, element, element_len);
2803 				}
2804 
2805 				if (result_type & PGSQL_ASSOC) {
2806 					field_name = PQfname(pgsql_result, i);
2807 					add_assoc_stringl(return_value, field_name, element, element_len);
2808 				}
2809 			}
2810 		}
2811 	}
2812 
2813 	if (into_object) {
2814 		zval dataset;
2815 		zend_fcall_info fci;
2816 		zend_fcall_info_cache fcc;
2817 		zval retval;
2818 
2819 		ZVAL_COPY_VALUE(&dataset, return_value);
2820 		object_and_properties_init(return_value, ce, NULL);
2821 		if (!ce->default_properties_count && !ce->__set) {
2822 			Z_OBJ_P(return_value)->properties = Z_ARR(dataset);
2823 		} else {
2824 			zend_merge_properties(return_value, Z_ARRVAL(dataset));
2825 			zval_ptr_dtor(&dataset);
2826 		}
2827 
2828 		if (ce->constructor) {
2829 			fci.size = sizeof(fci);
2830 			ZVAL_UNDEF(&fci.function_name);
2831 			fci.object = Z_OBJ_P(return_value);
2832 			fci.retval = &retval;
2833 			fci.params = NULL;
2834 			fci.param_count = 0;
2835 			fci.no_separation = 1;
2836 
2837 			if (ctor_params && Z_TYPE_P(ctor_params) != IS_NULL) {
2838 				if (zend_fcall_info_args(&fci, ctor_params) == FAILURE) {
2839 					/* Two problems why we throw exceptions here: PHP is typeless
2840 					 * and hence passing one argument that's not an array could be
2841 					 * by mistake and the other way round is possible, too. The
2842 					 * single value is an array. Also we'd have to make that one
2843 					 * argument passed by reference.
2844 					 */
2845 					zend_throw_exception(zend_ce_exception, "Parameter ctor_params must be an array", 0);
2846 					return;
2847 				}
2848 			}
2849 
2850 			fcc.function_handler = ce->constructor;
2851 			fcc.called_scope = Z_OBJCE_P(return_value);
2852 			fcc.object = Z_OBJ_P(return_value);
2853 
2854 			if (zend_call_function(&fci, &fcc) == FAILURE) {
2855 				zend_throw_exception_ex(zend_ce_exception, 0, "Could not execute %s::%s()", ZSTR_VAL(ce->name), ZSTR_VAL(ce->constructor->common.function_name));
2856 			} else {
2857 				zval_ptr_dtor(&retval);
2858 			}
2859 			if (fci.params) {
2860 				efree(fci.params);
2861 			}
2862 		} else if (ctor_params) {
2863 			zend_throw_exception_ex(zend_ce_exception, 0, "Class %s does not have a constructor hence you cannot use ctor_params", ZSTR_VAL(ce->name));
2864 		}
2865 	}
2866 }
2867 /* }}} */
2868 
2869 /* {{{ proto array pg_fetch_row(resource result [, int row [, int result_type]])
2870    Get a row as an enumerated array */
2871 PHP_FUNCTION(pg_fetch_row)
2872 {
2873 	php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_NUM, 0);
2874 }
2875 /* }}} */
2876 
2877 /* {{{ proto array pg_fetch_assoc(resource result [, int row])
2878    Fetch a row as an assoc array */
2879 PHP_FUNCTION(pg_fetch_assoc)
2880 {
2881 	/* pg_fetch_assoc() is added from PHP 4.3.0. It should raise error, when
2882 	   there is 3rd parameter */
2883 	if (ZEND_NUM_ARGS() > 2)
2884 		WRONG_PARAM_COUNT;
2885 	php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_ASSOC, 0);
2886 }
2887 /* }}} */
2888 
2889 /* {{{ proto array pg_fetch_array(resource result [, int row [, int result_type]])
2890    Fetch a row as an array */
2891 PHP_FUNCTION(pg_fetch_array)
2892 {
2893 	php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_BOTH, 0);
2894 }
2895 /* }}} */
2896 
2897 /* {{{ proto object pg_fetch_object(resource result [, int row [, string class_name [, NULL|array ctor_params]]])
2898    Fetch a row as an object */
2899 PHP_FUNCTION(pg_fetch_object)
2900 {
2901 	/* pg_fetch_object() allowed result_type used to be. 3rd parameter
2902 	   must be allowed for compatibility */
2903 	php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_ASSOC, 1);
2904 }
2905 /* }}} */
2906 
2907 /* {{{ proto array pg_fetch_all(resource result [, int result_type])
2908    Fetch all rows into array */
2909 PHP_FUNCTION(pg_fetch_all)
2910 {
2911 	zval *result;
2912 	long result_type = PGSQL_ASSOC;
2913 	PGresult *pgsql_result;
2914 	pgsql_result_handle *pg_result;
2915 
2916 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &result, &result_type) == FAILURE) {
2917 		return;
2918 	}
2919 
2920 	if (!(result_type & PGSQL_BOTH)) {
2921 		php_error_docref(NULL, E_WARNING, "Invalid result type");
2922 		RETURN_FALSE;
2923 	}
2924 
2925 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2926 		RETURN_FALSE;
2927 	}
2928 
2929 	pgsql_result = pg_result->result;
2930 	array_init(return_value);
2931 	if (php_pgsql_result2array(pgsql_result, return_value, result_type) == FAILURE) {
2932 		zend_array_destroy(Z_ARR_P(return_value));
2933 		RETURN_FALSE;
2934 	}
2935 }
2936 /* }}} */
2937 
2938 /* {{{ proto array pg_fetch_all_columns(resource result [, int column_number])
2939    Fetch all rows into array */
2940 PHP_FUNCTION(pg_fetch_all_columns)
2941 {
2942 	zval *result;
2943 	PGresult *pgsql_result;
2944 	pgsql_result_handle *pg_result;
2945 	zend_long colno=0;
2946 	int pg_numrows, pg_row;
2947 	size_t num_fields;
2948 
2949 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &result, &colno) == FAILURE) {
2950 		RETURN_FALSE;
2951 	}
2952 
2953 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2954 		RETURN_FALSE;
2955 	}
2956 
2957 	pgsql_result = pg_result->result;
2958 
2959 	num_fields = PQnfields(pgsql_result);
2960 	if (colno >= (zend_long)num_fields || colno < 0) {
2961 		php_error_docref(NULL, E_WARNING, "Invalid column number '" ZEND_LONG_FMT "'", colno);
2962 		RETURN_FALSE;
2963 	}
2964 
2965 	array_init(return_value);
2966 
2967 	if ((pg_numrows = PQntuples(pgsql_result)) <= 0) {
2968 		return;
2969 	}
2970 
2971 	for (pg_row = 0; pg_row < pg_numrows; pg_row++) {
2972 		if (PQgetisnull(pgsql_result, pg_row, (int)colno)) {
2973 			add_next_index_null(return_value);
2974 		} else {
2975 			add_next_index_string(return_value, PQgetvalue(pgsql_result, pg_row, (int)colno));
2976 		}
2977 	}
2978 }
2979 /* }}} */
2980 
2981 /* {{{ proto bool pg_result_seek(resource result, int offset)
2982    Set internal row offset */
2983 PHP_FUNCTION(pg_result_seek)
2984 {
2985 	zval *result;
2986 	zend_long row;
2987 	pgsql_result_handle *pg_result;
2988 
2989 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &result, &row) == FAILURE) {
2990 		return;
2991 	}
2992 
2993 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2994 		RETURN_FALSE;
2995 	}
2996 
2997 	if (row < 0 || row >= PQntuples(pg_result->result)) {
2998 		RETURN_FALSE;
2999 	}
3000 
3001 	/* seek to offset */
3002 	pg_result->row = (int)row;
3003 	RETURN_TRUE;
3004 }
3005 /* }}} */
3006 
3007 #define PHP_PG_DATA_LENGTH 1
3008 #define PHP_PG_DATA_ISNULL 2
3009 
3010 /* {{{ php_pgsql_data_info
3011  */
3012 static void php_pgsql_data_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
3013 {
3014 	zval *result, *field;
3015 	zend_long row;
3016 	PGresult *pgsql_result;
3017 	pgsql_result_handle *pg_result;
3018 	int field_offset, pgsql_row, argc = ZEND_NUM_ARGS();
3019 
3020 	if (argc == 2) {
3021 		if (zend_parse_parameters(argc, "rz", &result, &field) == FAILURE) {
3022 			return;
3023 		}
3024 	} else {
3025 		if (zend_parse_parameters(argc, "rlz", &result, &row, &field) == FAILURE) {
3026 			return;
3027 		}
3028 	}
3029 
3030 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
3031 		RETURN_FALSE;
3032 	}
3033 
3034 	pgsql_result = pg_result->result;
3035 	if (argc == 2) {
3036 		if (pg_result->row < 0) {
3037 			pg_result->row = 0;
3038 		}
3039 		pgsql_row = pg_result->row;
3040 		if (pgsql_row < 0 || pgsql_row >= PQntuples(pgsql_result)) {
3041 			RETURN_FALSE;
3042 		}
3043 	} else {
3044 		if (row < 0 || row >= PQntuples(pgsql_result)) {
3045 			php_error_docref(NULL, E_WARNING, "Unable to jump to row " ZEND_LONG_FMT " on PostgreSQL result index " ZEND_LONG_FMT,
3046 							row, Z_LVAL_P(result));
3047 			RETURN_FALSE;
3048 		}
3049 		pgsql_row = (int)row;
3050 	}
3051 
3052 	switch (Z_TYPE_P(field)) {
3053 		case IS_STRING:
3054 			convert_to_string_ex(field);
3055 			field_offset = PQfnumber(pgsql_result, Z_STRVAL_P(field));
3056 			if (field_offset < 0 || field_offset >= PQnfields(pgsql_result)) {
3057 				php_error_docref(NULL, E_WARNING, "Bad column offset specified");
3058 				RETURN_FALSE;
3059 			}
3060 			break;
3061 		default:
3062 			convert_to_long_ex(field);
3063 			if (Z_LVAL_P(field) < 0 || Z_LVAL_P(field) >= PQnfields(pgsql_result)) {
3064 				php_error_docref(NULL, E_WARNING, "Bad column offset specified");
3065 				RETURN_FALSE;
3066 			}
3067 			field_offset = (int)Z_LVAL_P(field);
3068 			break;
3069 	}
3070 
3071 	switch (entry_type) {
3072 		case PHP_PG_DATA_LENGTH:
3073 			RETVAL_LONG(PQgetlength(pgsql_result, pgsql_row, field_offset));
3074 			break;
3075 		case PHP_PG_DATA_ISNULL:
3076 			RETVAL_LONG(PQgetisnull(pgsql_result, pgsql_row, field_offset));
3077 			break;
3078 	}
3079 }
3080 /* }}} */
3081 
3082 /* {{{ proto int pg_field_prtlen(resource result, [int row,] mixed field_name_or_number)
3083    Returns the printed length */
3084 PHP_FUNCTION(pg_field_prtlen)
3085 {
3086 	php_pgsql_data_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_DATA_LENGTH);
3087 }
3088 /* }}} */
3089 
3090 /* {{{ proto int pg_field_is_null(resource result, [int row,] mixed field_name_or_number)
3091    Test if a field is NULL */
3092 PHP_FUNCTION(pg_field_is_null)
3093 {
3094 	php_pgsql_data_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_DATA_ISNULL);
3095 }
3096 /* }}} */
3097 
3098 /* {{{ proto bool pg_free_result(resource result)
3099    Free result memory */
3100 PHP_FUNCTION(pg_free_result)
3101 {
3102 	zval *result;
3103 	pgsql_result_handle *pg_result;
3104 
3105 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &result) == FAILURE) {
3106 		return;
3107 	}
3108 
3109 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
3110 		RETURN_FALSE;
3111 	}
3112 
3113 	zend_list_close(Z_RES_P(result));
3114 	RETURN_TRUE;
3115 }
3116 /* }}} */
3117 
3118 /* {{{ proto string pg_last_oid(resource result)
3119    Returns the last object identifier */
3120 PHP_FUNCTION(pg_last_oid)
3121 {
3122 	zval *result;
3123 	PGresult *pgsql_result;
3124 	pgsql_result_handle *pg_result;
3125 #ifdef HAVE_PQOIDVALUE
3126 	Oid oid;
3127 #endif
3128 
3129 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &result) == FAILURE) {
3130 		return;
3131 	}
3132 
3133 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
3134 		RETURN_FALSE;
3135 	}
3136 
3137 	pgsql_result = pg_result->result;
3138 #ifdef HAVE_PQOIDVALUE
3139 	oid = PQoidValue(pgsql_result);
3140 	if (oid == InvalidOid) {
3141 		RETURN_FALSE;
3142 	}
3143 	PGSQL_RETURN_OID(oid);
3144 #else
3145 	Z_STRVAL_P(return_value) = (char *) PQoidStatus(pgsql_result);
3146 	if (Z_STRVAL_P(return_value)) {
3147 		RETURN_STRING(Z_STRVAL_P(return_value));
3148 	}
3149 	RETURN_EMPTY_STRING();
3150 #endif
3151 }
3152 /* }}} */
3153 
3154 /* {{{ proto bool pg_trace(string filename [, string mode [, resource connection]])
3155    Enable tracing a PostgreSQL connection */
3156 PHP_FUNCTION(pg_trace)
3157 {
3158 	char *z_filename, *mode = "w";
3159 	size_t z_filename_len, mode_len;
3160 	zval *pgsql_link = NULL;
3161 	int argc = ZEND_NUM_ARGS();
3162 	PGconn *pgsql;
3163 	FILE *fp = NULL;
3164 	php_stream *stream;
3165 	zend_resource *link;
3166 
3167 	if (zend_parse_parameters(argc, "p|sr", &z_filename, &z_filename_len, &mode, &mode_len, &pgsql_link) == FAILURE) {
3168 		return;
3169 	}
3170 
3171 	if (argc < 3) {
3172 		link = FETCH_DEFAULT_LINK();
3173 		CHECK_DEFAULT_LINK(link);
3174 	} else {
3175 		link = Z_RES_P(pgsql_link);
3176 	}
3177 
3178 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3179 		RETURN_FALSE;
3180 	}
3181 
3182 	stream = php_stream_open_wrapper(z_filename, mode, REPORT_ERRORS, NULL);
3183 
3184 	if (!stream) {
3185 		RETURN_FALSE;
3186 	}
3187 
3188 	if (FAILURE == php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void**)&fp, REPORT_ERRORS))	{
3189 		php_stream_close(stream);
3190 		RETURN_FALSE;
3191 	}
3192 	php_stream_auto_cleanup(stream);
3193 	PQtrace(pgsql, fp);
3194 	RETURN_TRUE;
3195 }
3196 /* }}} */
3197 
3198 /* {{{ proto bool pg_untrace([resource connection])
3199    Disable tracing of a PostgreSQL connection */
3200 PHP_FUNCTION(pg_untrace)
3201 {
3202 	zval *pgsql_link = NULL;
3203 	int argc = ZEND_NUM_ARGS();
3204 	PGconn *pgsql;
3205 	zend_resource *link;
3206 
3207 	if (zend_parse_parameters(argc, "|r", &pgsql_link) == FAILURE) {
3208 		return;
3209 	}
3210 
3211 	if (argc == 0) {
3212 		link = FETCH_DEFAULT_LINK();
3213 		CHECK_DEFAULT_LINK(link);
3214 	} else {
3215 		link = Z_RES_P(pgsql_link);
3216 	}
3217 
3218 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3219 		RETURN_FALSE;
3220 	}
3221 
3222 	PQuntrace(pgsql);
3223 	RETURN_TRUE;
3224 }
3225 /* }}} */
3226 
3227 /* {{{ proto mixed pg_lo_create([resource connection],[mixed large_object_oid])
3228    Create a large object */
3229 PHP_FUNCTION(pg_lo_create)
3230 {
3231 	zval *pgsql_link = NULL, *oid = NULL;
3232 	PGconn *pgsql;
3233 	Oid pgsql_oid, wanted_oid = InvalidOid;
3234 	int argc = ZEND_NUM_ARGS();
3235 	zend_resource *link;
3236 
3237 	if (zend_parse_parameters(argc, "|zz", &pgsql_link, &oid) == FAILURE) {
3238 		return;
3239 	}
3240 
3241 	if ((argc == 1) && (Z_TYPE_P(pgsql_link) != IS_RESOURCE)) {
3242 		oid = pgsql_link;
3243 		pgsql_link = NULL;
3244 	}
3245 
3246 	if (pgsql_link == NULL) {
3247 		link = FETCH_DEFAULT_LINK();
3248 		CHECK_DEFAULT_LINK(link);
3249 	} else if ((Z_TYPE_P(pgsql_link) == IS_RESOURCE)) {
3250 		link = Z_RES_P(pgsql_link);
3251 	} else {
3252 		link = NULL;
3253 	}
3254 
3255 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3256 		RETURN_FALSE;
3257 	}
3258 
3259 	if (oid) {
3260 #ifndef HAVE_PG_LO_CREATE
3261 		php_error_docref(NULL, E_NOTICE, "Passing OID value is not supported. Upgrade your PostgreSQL");
3262 #else
3263 		switch (Z_TYPE_P(oid)) {
3264 		case IS_STRING:
3265 			{
3266 				char *end_ptr;
3267 				wanted_oid = (Oid)strtoul(Z_STRVAL_P(oid), &end_ptr, 10);
3268 				if ((Z_STRVAL_P(oid)+Z_STRLEN_P(oid)) != end_ptr) {
3269 				/* wrong integer format */
3270 				php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3271 				RETURN_FALSE;
3272 				}
3273 			}
3274 			break;
3275 		case IS_LONG:
3276 			if (Z_LVAL_P(oid) < (zend_long)InvalidOid) {
3277 				php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3278 				RETURN_FALSE;
3279 			}
3280 			wanted_oid = (Oid)Z_LVAL_P(oid);
3281 			break;
3282 		default:
3283 			php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3284 			RETURN_FALSE;
3285         }
3286 		if ((pgsql_oid = lo_create(pgsql, wanted_oid)) == InvalidOid) {
3287 			php_error_docref(NULL, E_WARNING, "Unable to create PostgreSQL large object");
3288 			RETURN_FALSE;
3289 		}
3290 
3291 		PGSQL_RETURN_OID(pgsql_oid);
3292 #endif
3293 	}
3294 
3295 	if ((pgsql_oid = lo_creat(pgsql, INV_READ|INV_WRITE)) == InvalidOid) {
3296 		php_error_docref(NULL, E_WARNING, "Unable to create PostgreSQL large object");
3297 		RETURN_FALSE;
3298 	}
3299 
3300 	PGSQL_RETURN_OID(pgsql_oid);
3301 }
3302 /* }}} */
3303 
3304 /* {{{ proto bool pg_lo_unlink([resource connection,] string large_object_oid)
3305    Delete a large object */
3306 PHP_FUNCTION(pg_lo_unlink)
3307 {
3308 	zval *pgsql_link = NULL;
3309 	zend_long oid_long;
3310 	char *oid_string, *end_ptr;
3311 	size_t oid_strlen;
3312 	PGconn *pgsql;
3313 	Oid oid;
3314 	zend_resource *link;
3315 	int argc = ZEND_NUM_ARGS();
3316 
3317 	/* accept string type since Oid type is unsigned int */
3318 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3319 								 "rs", &pgsql_link, &oid_string, &oid_strlen) == SUCCESS) {
3320 		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3321 		if ((oid_string+oid_strlen) != end_ptr) {
3322 			/* wrong integer format */
3323 			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3324 			RETURN_FALSE;
3325 		}
3326 		link = Z_RES_P(pgsql_link);
3327 	}
3328 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3329 								 "rl", &pgsql_link, &oid_long) == SUCCESS) {
3330 		if (oid_long <= (zend_long)InvalidOid) {
3331 			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3332 			RETURN_FALSE;
3333 		}
3334 		oid = (Oid)oid_long;
3335 		link = Z_RES_P(pgsql_link);
3336 	}
3337 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3338 								 "s", &oid_string, &oid_strlen) == SUCCESS) {
3339 		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3340 		if ((oid_string+oid_strlen) != end_ptr) {
3341 			/* wrong integer format */
3342 			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3343 			RETURN_FALSE;
3344 		}
3345 		link = FETCH_DEFAULT_LINK();
3346 		CHECK_DEFAULT_LINK(link);
3347 	}
3348 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3349 								 "l", &oid_long) == SUCCESS) {
3350 		if (oid_long <= (zend_long)InvalidOid) {
3351 			php_error_docref(NULL, E_NOTICE, "Invalid OID is specified");
3352 			RETURN_FALSE;
3353 		}
3354 		oid = (Oid)oid_long;
3355 		link = FETCH_DEFAULT_LINK();
3356 		CHECK_DEFAULT_LINK(link);
3357 	}
3358 	else {
3359 		php_error_docref(NULL, E_WARNING, "Requires 1 or 2 arguments");
3360 		RETURN_FALSE;
3361 	}
3362 
3363 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3364 		RETURN_FALSE;
3365 	}
3366 
3367 	if (lo_unlink(pgsql, oid) == -1) {
3368 		php_error_docref(NULL, E_WARNING, "Unable to delete PostgreSQL large object %u", oid);
3369 		RETURN_FALSE;
3370 	}
3371 	RETURN_TRUE;
3372 }
3373 /* }}} */
3374 
3375 /* {{{ proto resource pg_lo_open([resource connection,] int large_object_oid, string mode)
3376    Open a large object and return fd */
3377 PHP_FUNCTION(pg_lo_open)
3378 {
3379 	zval *pgsql_link = NULL;
3380 	zend_long oid_long;
3381 	char *oid_string, *end_ptr, *mode_string;
3382 	size_t oid_strlen, mode_strlen;
3383 	PGconn *pgsql;
3384 	Oid oid;
3385 	int pgsql_mode=0, pgsql_lofd;
3386 	int create = 0;
3387 	pgLofp *pgsql_lofp;
3388 	int argc = ZEND_NUM_ARGS();
3389 	zend_resource *link;
3390 
3391 	/* accept string type since Oid is unsigned int */
3392 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3393 								 "rss", &pgsql_link, &oid_string, &oid_strlen, &mode_string, &mode_strlen) == SUCCESS) {
3394 		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3395 		if ((oid_string+oid_strlen) != end_ptr) {
3396 			/* wrong integer format */
3397 			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3398 			RETURN_FALSE;
3399 		}
3400 		link = Z_RES_P(pgsql_link);
3401 	}
3402 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3403 								 "rls", &pgsql_link, &oid_long, &mode_string, &mode_strlen) == SUCCESS) {
3404 		if (oid_long <= (zend_long)InvalidOid) {
3405 			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3406 			RETURN_FALSE;
3407 		}
3408 		oid = (Oid)oid_long;
3409 		link = Z_RES_P(pgsql_link);
3410 	}
3411 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3412 								 "ss", &oid_string, &oid_strlen, &mode_string, &mode_strlen) == SUCCESS) {
3413 		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3414 		if ((oid_string+oid_strlen) != end_ptr) {
3415 			/* wrong integer format */
3416 			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3417 			RETURN_FALSE;
3418 		}
3419 		link = FETCH_DEFAULT_LINK();
3420 		CHECK_DEFAULT_LINK(link);
3421 	}
3422 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3423 								 "ls", &oid_long, &mode_string, &mode_strlen) == SUCCESS) {
3424 		if (oid_long <= (zend_long)InvalidOid) {
3425 			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3426 			RETURN_FALSE;
3427 		}
3428 		oid = (Oid)oid_long;
3429 		link = FETCH_DEFAULT_LINK();
3430 		CHECK_DEFAULT_LINK(link);
3431 	}
3432 	else {
3433 		php_error_docref(NULL, E_WARNING, "Requires 1 or 2 arguments");
3434 		RETURN_FALSE;
3435 	}
3436 
3437 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3438 		RETURN_FALSE;
3439 	}
3440 
3441 	/* r/w/+ is little bit more PHP-like than INV_READ/INV_WRITE and a lot of
3442 	   faster to type. Unfortunately, doesn't behave the same way as fopen()...
3443 	   (Jouni)
3444 	*/
3445 
3446 	if (strchr(mode_string, 'r') == mode_string) {
3447 		pgsql_mode |= INV_READ;
3448 		if (strchr(mode_string, '+') == mode_string+1) {
3449 			pgsql_mode |= INV_WRITE;
3450 		}
3451 	}
3452 	if (strchr(mode_string, 'w') == mode_string) {
3453 		pgsql_mode |= INV_WRITE;
3454 		create = 1;
3455 		if (strchr(mode_string, '+') == mode_string+1) {
3456 			pgsql_mode |= INV_READ;
3457 		}
3458 	}
3459 
3460 	pgsql_lofp = (pgLofp *) emalloc(sizeof(pgLofp));
3461 
3462 	if ((pgsql_lofd = lo_open(pgsql, oid, pgsql_mode)) == -1) {
3463 		if (create) {
3464 			if ((oid = lo_creat(pgsql, INV_READ|INV_WRITE)) == 0) {
3465 				efree(pgsql_lofp);
3466 				php_error_docref(NULL, E_WARNING, "Unable to create PostgreSQL large object");
3467 				RETURN_FALSE;
3468 			} else {
3469 				if ((pgsql_lofd = lo_open(pgsql, oid, pgsql_mode)) == -1) {
3470 					if (lo_unlink(pgsql, oid) == -1) {
3471 						efree(pgsql_lofp);
3472 						php_error_docref(NULL, E_WARNING, "Something is really messed up! Your database is badly corrupted in a way NOT related to PHP");
3473 						RETURN_FALSE;
3474 					}
3475 					efree(pgsql_lofp);
3476 					php_error_docref(NULL, E_WARNING, "Unable to open PostgreSQL large object");
3477 					RETURN_FALSE;
3478 				} else {
3479 					pgsql_lofp->conn = pgsql;
3480 					pgsql_lofp->lofd = pgsql_lofd;
3481 					RETURN_RES(zend_register_resource(pgsql_lofp, le_lofp));
3482 				}
3483 			}
3484 		} else {
3485 			efree(pgsql_lofp);
3486 			php_error_docref(NULL, E_WARNING, "Unable to open PostgreSQL large object");
3487 			RETURN_FALSE;
3488 		}
3489 	} else {
3490 		pgsql_lofp->conn = pgsql;
3491 		pgsql_lofp->lofd = pgsql_lofd;
3492 		RETURN_RES(zend_register_resource(pgsql_lofp, le_lofp));
3493 	}
3494 }
3495 /* }}} */
3496 
3497 /* {{{ proto bool pg_lo_close(resource large_object)
3498    Close a large object */
3499 PHP_FUNCTION(pg_lo_close)
3500 {
3501 	zval *pgsql_lofp;
3502 	pgLofp *pgsql;
3503 
3504 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pgsql_lofp) == FAILURE) {
3505 		return;
3506 	}
3507 
3508 	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_lofp), "PostgreSQL large object", le_lofp)) == NULL) {
3509 		RETURN_FALSE;
3510 	}
3511 
3512 	if (lo_close((PGconn *)pgsql->conn, pgsql->lofd) < 0) {
3513 		php_error_docref(NULL, E_WARNING, "Unable to close PostgreSQL large object descriptor %d", pgsql->lofd);
3514 		RETVAL_FALSE;
3515 	} else {
3516 		RETVAL_TRUE;
3517 	}
3518 
3519 	zend_list_close(Z_RES_P(pgsql_lofp));
3520 	return;
3521 }
3522 /* }}} */
3523 
3524 #define PGSQL_LO_READ_BUF_SIZE  8192
3525 
3526 /* {{{ proto string pg_lo_read(resource large_object [, int len])
3527    Read a large object */
3528 PHP_FUNCTION(pg_lo_read)
3529 {
3530 	zval *pgsql_id;
3531 	zend_long len;
3532 	size_t buf_len = PGSQL_LO_READ_BUF_SIZE;
3533 	int nbytes, argc = ZEND_NUM_ARGS();
3534 	zend_string *buf;
3535 	pgLofp *pgsql;
3536 
3537 	if (zend_parse_parameters(argc, "r|l", &pgsql_id, &len) == FAILURE) {
3538 		return;
3539 	}
3540 
3541 	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3542 		RETURN_FALSE;
3543 	}
3544 
3545 	if (argc > 1) {
3546 		buf_len = len < 0 ? 0 : len;
3547 	}
3548 
3549 	buf = zend_string_alloc(buf_len, 0);
3550 	if ((nbytes = lo_read((PGconn *)pgsql->conn, pgsql->lofd, ZSTR_VAL(buf), ZSTR_LEN(buf)))<0) {
3551 		zend_string_efree(buf);
3552 		RETURN_FALSE;
3553 	}
3554 
3555 	ZSTR_LEN(buf) = nbytes;
3556 	ZSTR_VAL(buf)[ZSTR_LEN(buf)] = '\0';
3557 	RETURN_NEW_STR(buf);
3558 }
3559 /* }}} */
3560 
3561 /* {{{ proto int pg_lo_write(resource large_object, string buf [, int len])
3562    Write a large object */
3563 PHP_FUNCTION(pg_lo_write)
3564 {
3565   	zval *pgsql_id;
3566   	char *str;
3567   	zend_long z_len;
3568 	size_t str_len, nbytes;
3569 	size_t len;
3570 	pgLofp *pgsql;
3571 	int argc = ZEND_NUM_ARGS();
3572 
3573 	if (zend_parse_parameters(argc, "rs|l", &pgsql_id, &str, &str_len, &z_len) == FAILURE) {
3574 		return;
3575 	}
3576 
3577 	if (argc > 2) {
3578 		if (z_len > (zend_long)str_len) {
3579 			php_error_docref(NULL, E_WARNING, "Cannot write more than buffer size %zu. Tried to write " ZEND_LONG_FMT, str_len, z_len);
3580 			RETURN_FALSE;
3581 		}
3582 		if (z_len < 0) {
3583 			php_error_docref(NULL, E_WARNING, "Buffer size must be larger than 0, but " ZEND_LONG_FMT " was specified", z_len);
3584 			RETURN_FALSE;
3585 		}
3586 		len = z_len;
3587 	}
3588 	else {
3589 		len = str_len;
3590 	}
3591 
3592 	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3593 		RETURN_FALSE;
3594 	}
3595 
3596 	if ((nbytes = lo_write((PGconn *)pgsql->conn, pgsql->lofd, str, len)) == (size_t)-1) {
3597 		RETURN_FALSE;
3598 	}
3599 
3600 	RETURN_LONG(nbytes);
3601 }
3602 /* }}} */
3603 
3604 /* {{{ proto int pg_lo_read_all(resource large_object)
3605    Read a large object and send straight to browser */
3606 PHP_FUNCTION(pg_lo_read_all)
3607 {
3608   	zval *pgsql_id;
3609 	int tbytes;
3610 	volatile int nbytes;
3611 	char buf[PGSQL_LO_READ_BUF_SIZE];
3612 	pgLofp *pgsql;
3613 
3614 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pgsql_id) == FAILURE) {
3615 		return;
3616 	}
3617 
3618 	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3619 		RETURN_FALSE;
3620 	}
3621 
3622 	tbytes = 0;
3623 	while ((nbytes = lo_read((PGconn *)pgsql->conn, pgsql->lofd, buf, PGSQL_LO_READ_BUF_SIZE))>0) {
3624 		PHPWRITE(buf, nbytes);
3625 		tbytes += nbytes;
3626 	}
3627 	RETURN_LONG(tbytes);
3628 }
3629 /* }}} */
3630 
3631 /* {{{ proto int pg_lo_import([resource connection, ] string filename [, mixed oid])
3632    Import large object direct from filesystem */
3633 PHP_FUNCTION(pg_lo_import)
3634 {
3635 	zval *pgsql_link = NULL, *oid = NULL;
3636 	char *file_in;
3637 	size_t name_len;
3638 	int argc = ZEND_NUM_ARGS();
3639 	PGconn *pgsql;
3640 	Oid returned_oid;
3641 	zend_resource *link;
3642 
3643 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3644 								 "rp|z", &pgsql_link, &file_in, &name_len, &oid) == SUCCESS) {
3645 		link = Z_RES_P(pgsql_link);
3646 	}
3647 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3648 									  "p|z", &file_in, &name_len, &oid) == SUCCESS) {
3649 		link = FETCH_DEFAULT_LINK();
3650 		CHECK_DEFAULT_LINK(link);
3651 	}
3652 	/* old calling convention, deprecated since PHP 4.2 */
3653 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3654 									  "pr", &file_in, &name_len, &pgsql_link ) == SUCCESS) {
3655 		php_error_docref(NULL, E_NOTICE, "Old API is used");
3656 		link = Z_RES_P(pgsql_link);
3657 	}
3658 	else {
3659 		WRONG_PARAM_COUNT;
3660 	}
3661 
3662 	if (php_check_open_basedir(file_in)) {
3663 		RETURN_FALSE;
3664 	}
3665 
3666 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3667 		RETURN_FALSE;
3668 	}
3669 
3670 	if (oid) {
3671 #ifndef HAVE_PG_LO_IMPORT_WITH_OID
3672 		php_error_docref(NULL, E_NOTICE, "OID value passing not supported");
3673 #else
3674 		Oid wanted_oid;
3675 		switch (Z_TYPE_P(oid)) {
3676 		case IS_STRING:
3677 			{
3678 				char *end_ptr;
3679 				wanted_oid = (Oid)strtoul(Z_STRVAL_P(oid), &end_ptr, 10);
3680 				if ((Z_STRVAL_P(oid)+Z_STRLEN_P(oid)) != end_ptr) {
3681 				/* wrong integer format */
3682 				php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3683 				RETURN_FALSE;
3684 				}
3685 			}
3686 			break;
3687 		case IS_LONG:
3688 			if (Z_LVAL_P(oid) < (zend_long)InvalidOid) {
3689 				php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3690 				RETURN_FALSE;
3691 			}
3692 			wanted_oid = (Oid)Z_LVAL_P(oid);
3693 			break;
3694 		default:
3695 			php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3696 			RETURN_FALSE;
3697         }
3698 
3699        returned_oid = lo_import_with_oid(pgsql, file_in, wanted_oid);
3700 
3701 	   if (returned_oid == InvalidOid) {
3702 		   RETURN_FALSE;
3703 	   }
3704 
3705 	   PGSQL_RETURN_OID(returned_oid);
3706 #endif
3707 	}
3708 
3709 	returned_oid = lo_import(pgsql, file_in);
3710 
3711 	if (returned_oid == InvalidOid) {
3712 		RETURN_FALSE;
3713 	}
3714 	PGSQL_RETURN_OID(returned_oid);
3715 }
3716 /* }}} */
3717 
3718 /* {{{ proto bool pg_lo_export([resource connection, ] int objoid, string filename)
3719    Export large object direct to filesystem */
3720 PHP_FUNCTION(pg_lo_export)
3721 {
3722 	zval *pgsql_link = NULL;
3723 	char *file_out, *oid_string, *end_ptr;
3724 	size_t oid_strlen;
3725 	size_t name_len;
3726 	zend_long oid_long;
3727 	Oid oid;
3728 	PGconn *pgsql;
3729 	int argc = ZEND_NUM_ARGS();
3730 	zend_resource *link;
3731 
3732 	/* allow string to handle large OID value correctly */
3733 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3734 								 "rlp", &pgsql_link, &oid_long, &file_out, &name_len) == SUCCESS) {
3735 		if (oid_long <= (zend_long)InvalidOid) {
3736 			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3737 			RETURN_FALSE;
3738 		}
3739 		oid = (Oid)oid_long;
3740 		link = Z_RES_P(pgsql_link);
3741 	}
3742 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3743 								 "rss", &pgsql_link, &oid_string, &oid_strlen, &file_out, &name_len) == SUCCESS) {
3744 		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3745 		if ((oid_string+oid_strlen) != end_ptr) {
3746 			/* wrong integer format */
3747 			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3748 			RETURN_FALSE;
3749 		}
3750 		link = Z_RES_P(pgsql_link);
3751 	}
3752 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3753 									  "lp",  &oid_long, &file_out, &name_len) == SUCCESS) {
3754 		if (oid_long <= (zend_long)InvalidOid) {
3755 			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3756 			RETURN_FALSE;
3757 		}
3758 		oid = (Oid)oid_long;
3759 		link = FETCH_DEFAULT_LINK();
3760 		CHECK_DEFAULT_LINK(link);
3761 	}
3762 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3763 								 "sp", &oid_string, &oid_strlen, &file_out, &name_len) == SUCCESS) {
3764 		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3765 		if ((oid_string+oid_strlen) != end_ptr) {
3766 			/* wrong integer format */
3767 			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3768 			RETURN_FALSE;
3769 		}
3770 		link = FETCH_DEFAULT_LINK();
3771 		CHECK_DEFAULT_LINK(link);
3772 	}
3773 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3774 								 "spr", &oid_string, &oid_strlen, &file_out, &name_len, &pgsql_link) == SUCCESS) {
3775 		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3776 		if ((oid_string+oid_strlen) != end_ptr) {
3777 			/* wrong integer format */
3778 			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3779 			RETURN_FALSE;
3780 		}
3781 		link = Z_RES_P(pgsql_link);
3782 	}
3783 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3784 									  "lpr", &oid_long, &file_out, &name_len, &pgsql_link) == SUCCESS) {
3785 		php_error_docref(NULL, E_NOTICE, "Old API is used");
3786 		if (oid_long <= (zend_long)InvalidOid) {
3787 			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3788 			RETURN_FALSE;
3789 		}
3790 		oid = (Oid)oid_long;
3791 		link = Z_RES_P(pgsql_link);
3792 	}
3793 	else {
3794 		php_error_docref(NULL, E_WARNING, "Requires 2 or 3 arguments");
3795 		RETURN_FALSE;
3796 	}
3797 
3798 	if (php_check_open_basedir(file_out)) {
3799 		RETURN_FALSE;
3800 	}
3801 
3802 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3803 		RETURN_FALSE;
3804 	}
3805 
3806 	if (lo_export(pgsql, oid, file_out) == -1) {
3807 		RETURN_FALSE;
3808 	}
3809 	RETURN_TRUE;
3810 }
3811 /* }}} */
3812 
3813 /* {{{ proto bool pg_lo_seek(resource large_object, int offset [, int whence])
3814    Seeks position of large object */
3815 PHP_FUNCTION(pg_lo_seek)
3816 {
3817 	zval *pgsql_id = NULL;
3818 	zend_long result, offset = 0, whence = SEEK_CUR;
3819 	pgLofp *pgsql;
3820 	int argc = ZEND_NUM_ARGS();
3821 
3822 	if (zend_parse_parameters(argc, "rl|l", &pgsql_id, &offset, &whence) == FAILURE) {
3823 		return;
3824 	}
3825 	if (whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) {
3826 		php_error_docref(NULL, E_WARNING, "Invalid whence parameter");
3827 		return;
3828 	}
3829 
3830 	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3831 		RETURN_FALSE;
3832 	}
3833 
3834 #if HAVE_PG_LO64
3835 	if (PQserverVersion((PGconn *)pgsql->conn) >= 90300) {
3836 		result = lo_lseek64((PGconn *)pgsql->conn, pgsql->lofd, offset, (int)whence);
3837 	} else {
3838 		result = lo_lseek((PGconn *)pgsql->conn, pgsql->lofd, (int)offset, (int)whence);
3839 	}
3840 #else
3841 	result = lo_lseek((PGconn *)pgsql->conn, pgsql->lofd, offset, whence);
3842 #endif
3843 	if (result > -1) {
3844 		RETURN_TRUE;
3845 	} else {
3846 		RETURN_FALSE;
3847 	}
3848 }
3849 /* }}} */
3850 
3851 /* {{{ proto int pg_lo_tell(resource large_object)
3852    Returns current position of large object */
3853 PHP_FUNCTION(pg_lo_tell)
3854 {
3855 	zval *pgsql_id = NULL;
3856 	zend_long offset = 0;
3857 	pgLofp *pgsql;
3858 	int argc = ZEND_NUM_ARGS();
3859 
3860 	if (zend_parse_parameters(argc, "r", &pgsql_id) == FAILURE) {
3861 		return;
3862 	}
3863 
3864 	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3865 		RETURN_FALSE;
3866 	}
3867 
3868 #if HAVE_PG_LO64
3869 	if (PQserverVersion((PGconn *)pgsql->conn) >= 90300) {
3870 		offset = lo_tell64((PGconn *)pgsql->conn, pgsql->lofd);
3871 	} else {
3872 		offset = lo_tell((PGconn *)pgsql->conn, pgsql->lofd);
3873 	}
3874 #else
3875 	offset = lo_tell((PGconn *)pgsql->conn, pgsql->lofd);
3876 #endif
3877 	RETURN_LONG(offset);
3878 }
3879 /* }}} */
3880 
3881 #if HAVE_PG_LO_TRUNCATE
3882 /* {{{ proto bool pg_lo_truncate(resource large_object, int size)
3883    Truncate large object to size */
3884 PHP_FUNCTION(pg_lo_truncate)
3885 {
3886 	zval *pgsql_id = NULL;
3887 	size_t size;
3888 	pgLofp *pgsql;
3889 	int argc = ZEND_NUM_ARGS();
3890 	int result;
3891 
3892 	if (zend_parse_parameters(argc, "rl", &pgsql_id, &size) == FAILURE) {
3893 		return;
3894 	}
3895 
3896 	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3897 		RETURN_FALSE;
3898 	}
3899 
3900 #if HAVE_PG_LO64
3901 	if (PQserverVersion((PGconn *)pgsql->conn) >= 90300) {
3902 		result = lo_truncate64((PGconn *)pgsql->conn, pgsql->lofd, size);
3903 	} else {
3904 		result = lo_truncate((PGconn *)pgsql->conn, pgsql->lofd, size);
3905 	}
3906 #else
3907 	result = lo_truncate((PGconn *)pgsql->conn, pgsql->lofd, size);
3908 #endif
3909 	if (!result) {
3910 		RETURN_TRUE;
3911 	} else {
3912 		RETURN_FALSE;
3913 	}
3914 }
3915 /* }}} */
3916 #endif
3917 
3918 #if HAVE_PQSETERRORVERBOSITY
3919 /* {{{ proto int pg_set_error_verbosity([resource connection,] int verbosity)
3920    Set error verbosity */
3921 PHP_FUNCTION(pg_set_error_verbosity)
3922 {
3923 	zval *pgsql_link = NULL;
3924 	zend_long verbosity;
3925 	int argc = ZEND_NUM_ARGS();
3926 	PGconn *pgsql;
3927 	zend_resource *link;
3928 
3929 	if (argc == 1) {
3930 		if (zend_parse_parameters(argc, "l", &verbosity) == FAILURE) {
3931 			return;
3932 		}
3933 		link = FETCH_DEFAULT_LINK();
3934 		CHECK_DEFAULT_LINK(link);
3935 	} else {
3936 		if (zend_parse_parameters(argc, "rl", &pgsql_link, &verbosity) == FAILURE) {
3937 			return;
3938 		}
3939 		link = Z_RES_P(pgsql_link);
3940 	}
3941 
3942 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3943 		RETURN_FALSE;
3944 	}
3945 
3946 	if (verbosity & (PQERRORS_TERSE|PQERRORS_DEFAULT|PQERRORS_VERBOSE)) {
3947 		RETURN_LONG(PQsetErrorVerbosity(pgsql, verbosity));
3948 	} else {
3949 		RETURN_FALSE;
3950 	}
3951 }
3952 /* }}} */
3953 #endif
3954 
3955 #ifdef HAVE_PQCLIENTENCODING
3956 /* {{{ proto int pg_set_client_encoding([resource connection,] string encoding)
3957    Set client encoding */
3958 PHP_FUNCTION(pg_set_client_encoding)
3959 {
3960 	char *encoding;
3961 	size_t encoding_len;
3962 	zval *pgsql_link = NULL;
3963 	int argc = ZEND_NUM_ARGS();
3964 	PGconn *pgsql;
3965 	zend_resource *link;
3966 
3967 	if (argc == 1) {
3968 		if (zend_parse_parameters(argc, "s", &encoding, &encoding_len) == FAILURE) {
3969 			return;
3970 		}
3971 		link = FETCH_DEFAULT_LINK();
3972 		CHECK_DEFAULT_LINK(link);
3973 	} else {
3974 		if (zend_parse_parameters(argc, "rs", &pgsql_link, &encoding, &encoding_len) == FAILURE) {
3975 			return;
3976 		}
3977 		link = Z_RES_P(pgsql_link);
3978 	}
3979 
3980 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3981 		RETURN_FALSE;
3982 	}
3983 
3984 	RETURN_LONG(PQsetClientEncoding(pgsql, encoding));
3985 }
3986 /* }}} */
3987 
3988 /* {{{ proto string pg_client_encoding([resource connection])
3989    Get the current client encoding */
3990 PHP_FUNCTION(pg_client_encoding)
3991 {
3992 	zval *pgsql_link = NULL;
3993 	int argc = ZEND_NUM_ARGS();
3994 	PGconn *pgsql;
3995 	zend_resource *link;
3996 
3997 	if (zend_parse_parameters(argc, "|r", &pgsql_link) == FAILURE) {
3998 		return;
3999 	}
4000 
4001 	if (argc == 0) {
4002 		link = FETCH_DEFAULT_LINK();
4003 		CHECK_DEFAULT_LINK(link);
4004 	} else {
4005 		link = Z_RES_P(pgsql_link);
4006 	}
4007 
4008 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4009 		RETURN_FALSE;
4010 	}
4011 
4012 	/* Just do the same as found in PostgreSQL sources... */
4013 
4014 	RETURN_STRING((char *) pg_encoding_to_char(PQclientEncoding(pgsql)));
4015 }
4016 /* }}} */
4017 #endif
4018 
4019 #if !HAVE_PQGETCOPYDATA
4020 #define	COPYBUFSIZ	8192
4021 #endif
4022 
4023 /* {{{ proto bool pg_end_copy([resource connection])
4024    Sync with backend. Completes the Copy command */
4025 PHP_FUNCTION(pg_end_copy)
4026 {
4027 	zval *pgsql_link = NULL;
4028 	int argc = ZEND_NUM_ARGS();
4029 	PGconn *pgsql;
4030 	int result = 0;
4031 	zend_resource *link;
4032 
4033 	if (zend_parse_parameters(argc, "|r", &pgsql_link) == FAILURE) {
4034 		return;
4035 	}
4036 
4037 	if (argc == 0) {
4038 		link = FETCH_DEFAULT_LINK();
4039 		CHECK_DEFAULT_LINK(link);
4040 	} else {
4041 		link = Z_RES_P(pgsql_link);
4042 	}
4043 
4044 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4045 		RETURN_FALSE;
4046 	}
4047 
4048 	result = PQendcopy(pgsql);
4049 
4050 	if (result!=0) {
4051 		PHP_PQ_ERROR("Query failed: %s", pgsql);
4052 		RETURN_FALSE;
4053 	}
4054 	RETURN_TRUE;
4055 }
4056 /* }}} */
4057 
4058 /* {{{ proto bool pg_put_line([resource connection,] string query)
4059    Send null-terminated string to backend server*/
4060 PHP_FUNCTION(pg_put_line)
4061 {
4062 	char *query;
4063 	zval *pgsql_link = NULL;
4064 	size_t query_len;
4065 	PGconn *pgsql;
4066 	zend_resource *link;
4067 	int result = 0, argc = ZEND_NUM_ARGS();
4068 
4069 	if (argc == 1) {
4070 		if (zend_parse_parameters(argc, "s", &query, &query_len) == FAILURE) {
4071 			return;
4072 		}
4073 		link = FETCH_DEFAULT_LINK();
4074 		CHECK_DEFAULT_LINK(link);
4075 	} else {
4076 		if (zend_parse_parameters(argc, "rs", &pgsql_link, &query, &query_len) == FAILURE) {
4077 			return;
4078 		}
4079 		link = Z_RES_P(pgsql_link);
4080 	}
4081 
4082 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4083 		RETURN_FALSE;
4084 	}
4085 
4086 	result = PQputline(pgsql, query);
4087 	if (result==EOF) {
4088 		PHP_PQ_ERROR("Query failed: %s", pgsql);
4089 		RETURN_FALSE;
4090 	}
4091 	RETURN_TRUE;
4092 }
4093 /* }}} */
4094 
4095 /* {{{ proto array pg_copy_to(resource connection, string table_name [, string delimiter [, string null_as]])
4096    Copy table to array */
4097 PHP_FUNCTION(pg_copy_to)
4098 {
4099 	zval *pgsql_link;
4100 	char *table_name, *pg_delim = NULL, *pg_null_as = NULL;
4101 	size_t table_name_len, pg_delim_len, pg_null_as_len, free_pg_null = 0;
4102 	char *query;
4103 	PGconn *pgsql;
4104 	PGresult *pgsql_result;
4105 	ExecStatusType status;
4106 	char *csv = (char *)NULL;
4107 	int argc = ZEND_NUM_ARGS();
4108 
4109 	if (zend_parse_parameters(argc, "rs|ss",
4110 							  &pgsql_link, &table_name, &table_name_len,
4111 							  &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len) == FAILURE) {
4112 		return;
4113 	}
4114 
4115 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4116 		RETURN_FALSE;
4117 	}
4118 
4119 	if (!pg_delim) {
4120 		pg_delim = "\t";
4121 	}
4122 	if (!pg_null_as) {
4123 		pg_null_as = estrdup("\\\\N");
4124 		free_pg_null = 1;
4125 	}
4126 
4127 	spprintf(&query, 0, "COPY %s TO STDOUT DELIMITER E'%c' NULL AS E'%s'", table_name, *pg_delim, pg_null_as);
4128 
4129 	while ((pgsql_result = PQgetResult(pgsql))) {
4130 		PQclear(pgsql_result);
4131 	}
4132 	pgsql_result = PQexec(pgsql, query);
4133 	if (free_pg_null) {
4134 		efree(pg_null_as);
4135 	}
4136 	efree(query);
4137 
4138 	if (pgsql_result) {
4139 		status = PQresultStatus(pgsql_result);
4140 	} else {
4141 		status = (ExecStatusType) PQstatus(pgsql);
4142 	}
4143 
4144 	switch (status) {
4145 		case PGRES_COPY_OUT:
4146 			if (pgsql_result) {
4147 				int copydone = 0;
4148 #if !HAVE_PQGETCOPYDATA
4149 				char copybuf[COPYBUFSIZ];
4150 #endif
4151 
4152 				PQclear(pgsql_result);
4153 				array_init(return_value);
4154 #if HAVE_PQGETCOPYDATA
4155 				while (!copydone)
4156 				{
4157 					int ret = PQgetCopyData(pgsql, &csv, 0);
4158 					switch (ret) {
4159 						case -1:
4160 							copydone = 1;
4161 							break;
4162 						case 0:
4163 						case -2:
4164 							PHP_PQ_ERROR("getline failed: %s", pgsql);
4165 							RETURN_FALSE;
4166 							break;
4167 						default:
4168 							add_next_index_string(return_value, csv);
4169 							PQfreemem(csv);
4170 							break;
4171 					}
4172 				}
4173 #else
4174 				while (!copydone)
4175 				{
4176 					if ((ret = PQgetline(pgsql, copybuf, COPYBUFSIZ))) {
4177 						PHP_PQ_ERROR("getline failed: %s", pgsql);
4178 						RETURN_FALSE;
4179 					}
4180 
4181 					if (copybuf[0] == '\\' &&
4182 						copybuf[1] == '.' &&
4183 						copybuf[2] == '\0')
4184 					{
4185 						copydone = 1;
4186 					}
4187 					else
4188 					{
4189 						if (csv == (char *)NULL) {
4190 							csv = estrdup(copybuf);
4191 						} else {
4192 							csv = (char *)erealloc(csv, strlen(csv) + sizeof(char)*(COPYBUFSIZ+1));
4193 							strcat(csv, copybuf);
4194 						}
4195 
4196 						switch (ret)
4197 						{
4198 							case EOF:
4199 								copydone = 1;
4200 							case 0:
4201 								add_next_index_string(return_value, csv);
4202 								efree(csv);
4203 								csv = (char *)NULL;
4204 								break;
4205 							case 1:
4206 								break;
4207 						}
4208 					}
4209 				}
4210 				if (PQendcopy(pgsql)) {
4211 					PHP_PQ_ERROR("endcopy failed: %s", pgsql);
4212 					RETURN_FALSE;
4213 				}
4214 #endif
4215 				while ((pgsql_result = PQgetResult(pgsql))) {
4216 					PQclear(pgsql_result);
4217 				}
4218 			} else {
4219 				PQclear(pgsql_result);
4220 				RETURN_FALSE;
4221 			}
4222 			break;
4223 		default:
4224 			PQclear(pgsql_result);
4225 			PHP_PQ_ERROR("Copy command failed: %s", pgsql);
4226 			RETURN_FALSE;
4227 			break;
4228 	}
4229 }
4230 /* }}} */
4231 
4232 /* {{{ proto bool pg_copy_from(resource connection, string table_name , array rows [, string delimiter [, string null_as]])
4233    Copy table from array */
4234 PHP_FUNCTION(pg_copy_from)
4235 {
4236 	zval *pgsql_link = NULL, *pg_rows;
4237 	zval *value;
4238 	char *table_name, *pg_delim = NULL, *pg_null_as = NULL;
4239 	size_t  table_name_len, pg_delim_len, pg_null_as_len;
4240 	int  pg_null_as_free = 0;
4241 	char *query;
4242 	PGconn *pgsql;
4243 	PGresult *pgsql_result;
4244 	ExecStatusType status;
4245 	int argc = ZEND_NUM_ARGS();
4246 
4247 	if (zend_parse_parameters(argc, "rsa|ss",
4248 							  &pgsql_link, &table_name, &table_name_len, &pg_rows,
4249 							  &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len) == FAILURE) {
4250 		return;
4251 	}
4252 
4253 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4254 		RETURN_FALSE;
4255 	}
4256 
4257 	if (!pg_delim) {
4258 		pg_delim = "\t";
4259 	}
4260 	if (!pg_null_as) {
4261 		pg_null_as = estrdup("\\\\N");
4262 		pg_null_as_free = 1;
4263 	}
4264 
4265 	spprintf(&query, 0, "COPY %s FROM STDIN DELIMITER E'%c' NULL AS E'%s'", table_name, *pg_delim, pg_null_as);
4266 	while ((pgsql_result = PQgetResult(pgsql))) {
4267 		PQclear(pgsql_result);
4268 	}
4269 	pgsql_result = PQexec(pgsql, query);
4270 
4271 	if (pg_null_as_free) {
4272 		efree(pg_null_as);
4273 	}
4274 	efree(query);
4275 
4276 	if (pgsql_result) {
4277 		status = PQresultStatus(pgsql_result);
4278 	} else {
4279 		status = (ExecStatusType) PQstatus(pgsql);
4280 	}
4281 
4282 	switch (status) {
4283 		case PGRES_COPY_IN:
4284 			if (pgsql_result) {
4285 				int command_failed = 0;
4286 				PQclear(pgsql_result);
4287 #if HAVE_PQPUTCOPYDATA
4288 				ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), value) {
4289 					zval tmp;
4290 					ZVAL_COPY(&tmp, value);
4291 					convert_to_string_ex(&tmp);
4292 					query = (char *)emalloc(Z_STRLEN(tmp) + 2);
4293 					strlcpy(query, Z_STRVAL(tmp), Z_STRLEN(tmp) + 2);
4294 					if(Z_STRLEN(tmp) > 0 && *(query + Z_STRLEN(tmp) - 1) != '\n') {
4295 						strlcat(query, "\n", Z_STRLEN(tmp) + 2);
4296 					}
4297 					if (PQputCopyData(pgsql, query, (int)strlen(query)) != 1) {
4298 						efree(query);
4299 						zval_ptr_dtor_str(&tmp);
4300 						PHP_PQ_ERROR("copy failed: %s", pgsql);
4301 						RETURN_FALSE;
4302 					}
4303 					efree(query);
4304 					zval_ptr_dtor_str(&tmp);
4305 				} ZEND_HASH_FOREACH_END();
4306 
4307 				if (PQputCopyEnd(pgsql, NULL) != 1) {
4308 					PHP_PQ_ERROR("putcopyend failed: %s", pgsql);
4309 					RETURN_FALSE;
4310 				}
4311 #else
4312 				ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), value) {
4313 					zval tmp;
4314 					ZVAL_COPY(&tmp, value);
4315 					convert_to_string_ex(&tmp);
4316 					query = (char *)emalloc(Z_STRLEN(tmp) + 2);
4317 					strlcpy(query, Z_STRVAL(tmp), Z_STRLEN(tmp) + 2);
4318 					if(Z_STRLEN(tmp) > 0 && *(query + Z_STRLEN(tmp) - 1) != '\n') {
4319 						strlcat(query, "\n", Z_STRLEN(tmp) + 2);
4320 					}
4321 					if (PQputline(pgsql, query)==EOF) {
4322 						efree(query);
4323 						zval_ptr_dtor_str(&tmp);
4324 						PHP_PQ_ERROR("copy failed: %s", pgsql);
4325 						RETURN_FALSE;
4326 					}
4327 					efree(query);
4328 					zval_ptr_dtor_str(&tmp);
4329 				} ZEND_HASH_FOREACH_END();
4330 
4331 				if (PQputline(pgsql, "\\.\n") == EOF) {
4332 					PHP_PQ_ERROR("putline failed: %s", pgsql);
4333 					RETURN_FALSE;
4334 				}
4335 				if (PQendcopy(pgsql)) {
4336 					PHP_PQ_ERROR("endcopy failed: %s", pgsql);
4337 					RETURN_FALSE;
4338 				}
4339 #endif
4340 				while ((pgsql_result = PQgetResult(pgsql))) {
4341 					if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
4342 						PHP_PQ_ERROR("Copy command failed: %s", pgsql);
4343 						command_failed = 1;
4344 					}
4345 					PQclear(pgsql_result);
4346 				}
4347 				if (command_failed) {
4348 					RETURN_FALSE;
4349 				}
4350 			} else {
4351 				PQclear(pgsql_result);
4352 				RETURN_FALSE;
4353 			}
4354 			RETURN_TRUE;
4355 			break;
4356 		default:
4357 			PQclear(pgsql_result);
4358 			PHP_PQ_ERROR("Copy command failed: %s", pgsql);
4359 			RETURN_FALSE;
4360 			break;
4361 	}
4362 }
4363 /* }}} */
4364 
4365 #ifdef HAVE_PQESCAPE
4366 /* {{{ proto string pg_escape_string([resource connection,] string data)
4367    Escape string for text/char type */
4368 PHP_FUNCTION(pg_escape_string)
4369 {
4370 	zend_string *from = NULL, *to = NULL;
4371 	zval *pgsql_link;
4372 	zend_resource *link;
4373 #ifdef HAVE_PQESCAPE_CONN
4374 	PGconn *pgsql;
4375 #endif
4376 
4377 	switch (ZEND_NUM_ARGS()) {
4378 		case 1:
4379 			if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &from) == FAILURE) {
4380 				return;
4381 			}
4382 			link = FETCH_DEFAULT_LINK();
4383 			break;
4384 		default:
4385 			if (zend_parse_parameters(ZEND_NUM_ARGS(), "rS", &pgsql_link, &from) == FAILURE) {
4386 				return;
4387 			}
4388 			link = Z_RES_P(pgsql_link);
4389 			break;
4390 	}
4391 
4392 	to = zend_string_safe_alloc(ZSTR_LEN(from), 2, 0, 0);
4393 #ifdef HAVE_PQESCAPE_CONN
4394 	if (link) {
4395 		if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4396 			RETURN_FALSE;
4397 		}
4398 		ZSTR_LEN(to) = PQescapeStringConn(pgsql, ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from), NULL);
4399 	} else
4400 #endif
4401 	{
4402 		ZSTR_LEN(to) = PQescapeString(ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from));
4403 	}
4404 
4405 	to = zend_string_truncate(to, ZSTR_LEN(to), 0);
4406 	RETURN_NEW_STR(to);
4407 }
4408 /* }}} */
4409 
4410 /* {{{ proto string pg_escape_bytea([resource connection,] string data)
4411    Escape binary for bytea type  */
4412 PHP_FUNCTION(pg_escape_bytea)
4413 {
4414 	char *from = NULL, *to = NULL;
4415 	size_t to_len;
4416 	size_t from_len;
4417 #ifdef HAVE_PQESCAPE_BYTEA_CONN
4418 	PGconn *pgsql;
4419 #endif
4420 	zval *pgsql_link;
4421 	zend_resource *link;
4422 
4423 	switch (ZEND_NUM_ARGS()) {
4424 		case 1:
4425 			if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &from, &from_len) == FAILURE) {
4426 				return;
4427 			}
4428 			link = FETCH_DEFAULT_LINK();
4429 			break;
4430 		default:
4431 			if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &pgsql_link, &from, &from_len) == FAILURE) {
4432 				return;
4433 			}
4434 			link = Z_RES_P(pgsql_link);
4435 			break;
4436 	}
4437 
4438 #ifdef HAVE_PQESCAPE_BYTEA_CONN
4439 	if (link) {
4440 		if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4441 			RETURN_FALSE;
4442 		}
4443 		to = (char *)PQescapeByteaConn(pgsql, (unsigned char *)from, (size_t)from_len, &to_len);
4444 	} else
4445 #endif
4446 		to = (char *)PQescapeBytea((unsigned char*)from, from_len, &to_len);
4447 
4448 	RETVAL_STRINGL(to, to_len-1); /* to_len includes additional '\0' */
4449 	PQfreemem(to);
4450 }
4451 /* }}} */
4452 
4453 #if !HAVE_PQUNESCAPEBYTEA
4454 /* PQunescapeBytea() from PostgreSQL 7.3 to provide bytea unescape feature to 7.2 users.
4455    Renamed to php_pgsql_unescape_bytea() */
4456 /*
4457  *		PQunescapeBytea - converts the null terminated string representation
4458  *		of a bytea, strtext, into binary, filling a buffer. It returns a
4459  *		pointer to the buffer which is NULL on error, and the size of the
4460  *		buffer in retbuflen. The pointer may subsequently be used as an
4461  *		argument to the function free(3). It is the reverse of PQescapeBytea.
4462  *
4463  *		The following transformations are reversed:
4464  *		'\0' == ASCII  0 == \000
4465  *		'\'' == ASCII 39 == \'
4466  *		'\\' == ASCII 92 == \\
4467  *
4468  *		States:
4469  *		0	normal		0->1->2->3->4
4470  *		1	\			   1->5
4471  *		2	\0			   1->6
4472  *		3	\00
4473  *		4	\000
4474  *		5	\'
4475  *		6	\\
4476  */
4477 static unsigned char * php_pgsql_unescape_bytea(unsigned char *strtext, size_t *retbuflen) /* {{{ */
4478 {
4479 	size_t     buflen;
4480 	unsigned char *buffer,
4481 			   *sp,
4482 			   *bp;
4483 	unsigned int state = 0;
4484 
4485 	if (strtext == NULL)
4486 		return NULL;
4487 	buflen = strlen(strtext);	/* will shrink, also we discover if
4488 								 * strtext */
4489 	buffer = (unsigned char *) emalloc(buflen);	/* isn't NULL terminated */
4490 	for (bp = buffer, sp = strtext; *sp != '\0'; bp++, sp++)
4491 	{
4492 		switch (state)
4493 		{
4494 			case 0:
4495 				if (*sp == '\\')
4496 					state = 1;
4497 				*bp = *sp;
4498 				break;
4499 			case 1:
4500 				if (*sp == '\'')	/* state=5 */
4501 				{				/* replace \' with 39 */
4502 					bp--;
4503 					*bp = '\'';
4504 					buflen--;
4505 					state = 0;
4506 				}
4507 				else if (*sp == '\\')	/* state=6 */
4508 				{				/* replace \\ with 92 */
4509 					bp--;
4510 					*bp = '\\';
4511 					buflen--;
4512 					state = 0;
4513 				}
4514 				else
4515 				{
4516 					if (isdigit(*sp))
4517 						state = 2;
4518 					else
4519 						state = 0;
4520 					*bp = *sp;
4521 				}
4522 				break;
4523 			case 2:
4524 				if (isdigit(*sp))
4525 					state = 3;
4526 				else
4527 					state = 0;
4528 				*bp = *sp;
4529 				break;
4530 			case 3:
4531 				if (isdigit(*sp))		/* state=4 */
4532 				{
4533 					unsigned char *start, *end, buf[4]; /* 000 + '\0' */
4534 
4535 					bp -= 3;
4536 					memcpy(buf, sp-2, 3);
4537 					buf[3] = '\0';
4538 					start = buf;
4539 					*bp = (unsigned char)strtoul(start, (char **)&end, 8);
4540 					buflen -= 3;
4541 					state = 0;
4542 				}
4543 				else
4544 				{
4545 					*bp = *sp;
4546 					state = 0;
4547 				}
4548 				break;
4549 		}
4550 	}
4551 	buffer = erealloc(buffer, buflen+1);
4552 	buffer[buflen] = '\0';
4553 
4554 	*retbuflen = buflen;
4555 	return buffer;
4556 }
4557 /* }}} */
4558 #endif
4559 
4560 /* {{{ proto string pg_unescape_bytea(string data)
4561    Unescape binary for bytea type  */
4562 PHP_FUNCTION(pg_unescape_bytea)
4563 {
4564 	char *from = NULL, *to = NULL, *tmp = NULL;
4565 	size_t to_len;
4566 	size_t from_len;
4567 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
4568 							  &from, &from_len) == FAILURE) {
4569 		return;
4570 	}
4571 
4572 #if HAVE_PQUNESCAPEBYTEA
4573 	tmp = (char *)PQunescapeBytea((unsigned char*)from, &to_len);
4574 	to = estrndup(tmp, to_len);
4575 	PQfreemem(tmp);
4576 #else
4577 	to = (char *)php_pgsql_unescape_bytea((unsigned char*)from, &to_len);
4578 #endif
4579 	if (!to) {
4580 		php_error_docref(NULL, E_WARNING,"Invalid parameter");
4581 		RETURN_FALSE;
4582 	}
4583 	RETVAL_STRINGL(to, to_len);
4584 	efree(to);
4585 }
4586 /* }}} */
4587 #endif
4588 
4589 #ifdef HAVE_PQESCAPE
4590 static void php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAMETERS, int escape_literal) /* {{{ */ {
4591 	char *from = NULL;
4592 	zval *pgsql_link = NULL;
4593 	PGconn *pgsql;
4594 	size_t from_len;
4595 	char *tmp;
4596 	zend_resource *link;
4597 
4598 	switch (ZEND_NUM_ARGS()) {
4599 		case 1:
4600 			if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &from, &from_len) == FAILURE) {
4601 				return;
4602 			}
4603 			link = FETCH_DEFAULT_LINK();
4604 			CHECK_DEFAULT_LINK(link);
4605 			break;
4606 
4607 		default:
4608 			if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &pgsql_link, &from, &from_len) == FAILURE) {
4609 				return;
4610 			}
4611 			link = Z_RES_P(pgsql_link);
4612 			break;
4613 	}
4614 
4615 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4616 		RETURN_FALSE;
4617 	}
4618 
4619 	if (pgsql == NULL) {
4620 		php_error_docref(NULL, E_WARNING,"Cannot get pgsql link");
4621 		RETURN_FALSE;
4622 	}
4623 
4624 	if (escape_literal) {
4625 		tmp = PGSQLescapeLiteral(pgsql, from, (size_t)from_len);
4626 	} else {
4627 		tmp = PGSQLescapeIdentifier(pgsql, from, (size_t)from_len);
4628 	}
4629 	if (!tmp) {
4630 		php_error_docref(NULL, E_WARNING,"Failed to escape");
4631 		RETURN_FALSE;
4632 	}
4633 
4634 	RETVAL_STRING(tmp);
4635 	PGSQLfree(tmp);
4636 }
4637 /* }}} */
4638 
4639 /* {{{ proto string pg_escape_literal([resource connection,] string data)
4640    Escape parameter as string literal (i.e. parameter)	*/
4641 PHP_FUNCTION(pg_escape_literal)
4642 {
4643 	php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4644 }
4645 /* }}} */
4646 
4647 /* {{{ proto string pg_escape_identifier([resource connection,] string data)
4648    Escape identifier (i.e. table name, field name)	*/
4649 PHP_FUNCTION(pg_escape_identifier)
4650 {
4651 	php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4652 }
4653 /* }}} */
4654 #endif
4655 
4656 /* {{{ proto string pg_result_error(resource result)
4657    Get error message associated with result */
4658 PHP_FUNCTION(pg_result_error)
4659 {
4660 	zval *result;
4661 	PGresult *pgsql_result;
4662 	pgsql_result_handle *pg_result;
4663 	char *err = NULL;
4664 
4665 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
4666 								 &result) == FAILURE) {
4667 		RETURN_FALSE;
4668 	}
4669 
4670 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
4671 		RETURN_FALSE;
4672 	}
4673 
4674 	pgsql_result = pg_result->result;
4675 	if (!pgsql_result) {
4676 		RETURN_FALSE;
4677 	}
4678 	err = (char *)PQresultErrorMessage(pgsql_result);
4679 	RETURN_STRING(err);
4680 }
4681 /* }}} */
4682 
4683 #if HAVE_PQRESULTERRORFIELD
4684 /* {{{ proto string pg_result_error_field(resource result, int fieldcode)
4685    Get error message field associated with result */
4686 PHP_FUNCTION(pg_result_error_field)
4687 {
4688 	zval *result;
4689 	zend_long fieldcode;
4690 	PGresult *pgsql_result;
4691 	pgsql_result_handle *pg_result;
4692 	char *field = NULL;
4693 
4694 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "rl",
4695 								 &result, &fieldcode) == FAILURE) {
4696 		RETURN_FALSE;
4697 	}
4698 
4699 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
4700 		RETURN_FALSE;
4701 	}
4702 
4703 	pgsql_result = pg_result->result;
4704 	if (!pgsql_result) {
4705 		RETURN_FALSE;
4706 	}
4707 	if (fieldcode & (PG_DIAG_SEVERITY|PG_DIAG_SQLSTATE|PG_DIAG_MESSAGE_PRIMARY|PG_DIAG_MESSAGE_DETAIL
4708 				|PG_DIAG_MESSAGE_HINT|PG_DIAG_STATEMENT_POSITION
4709 #if PG_DIAG_INTERNAL_POSITION
4710 				|PG_DIAG_INTERNAL_POSITION
4711 #endif
4712 #if PG_DIAG_INTERNAL_QUERY
4713 				|PG_DIAG_INTERNAL_QUERY
4714 #endif
4715 				|PG_DIAG_CONTEXT|PG_DIAG_SOURCE_FILE|PG_DIAG_SOURCE_LINE
4716 				|PG_DIAG_SOURCE_FUNCTION)) {
4717 		field = (char *)PQresultErrorField(pgsql_result, (int)fieldcode);
4718 		if (field == NULL) {
4719 			RETURN_NULL();
4720 		} else {
4721 			RETURN_STRING(field);
4722 		}
4723 	} else {
4724 		RETURN_FALSE;
4725 	}
4726 }
4727 /* }}} */
4728 #endif
4729 
4730 /* {{{ proto int pg_connection_status(resource connection)
4731    Get connection status */
4732 PHP_FUNCTION(pg_connection_status)
4733 {
4734 	zval *pgsql_link = NULL;
4735 	PGconn *pgsql;
4736 
4737 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
4738 								 &pgsql_link) == FAILURE) {
4739 		RETURN_FALSE;
4740 	}
4741 
4742 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4743 		RETURN_FALSE;
4744 	}
4745 
4746 	RETURN_LONG(PQstatus(pgsql));
4747 }
4748 
4749 /* }}} */
4750 
4751 #if HAVE_PGTRANSACTIONSTATUS
4752 /* {{{ proto int pg_transaction_status(resource connection)
4753    Get transaction status */
4754 PHP_FUNCTION(pg_transaction_status)
4755 {
4756 	zval *pgsql_link = NULL;
4757 	PGconn *pgsql;
4758 
4759 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
4760 								 &pgsql_link) == FAILURE) {
4761 		RETURN_FALSE;
4762 	}
4763 
4764 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4765 		RETURN_FALSE;
4766 	}
4767 
4768 	RETURN_LONG(PQtransactionStatus(pgsql));
4769 }
4770 #endif
4771 
4772 /* }}} */
4773 
4774 /* {{{ proto bool pg_connection_reset(resource connection)
4775    Reset connection (reconnect) */
4776 PHP_FUNCTION(pg_connection_reset)
4777 {
4778 	zval *pgsql_link;
4779 	PGconn *pgsql;
4780 
4781 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
4782 								 &pgsql_link) == FAILURE) {
4783 		RETURN_FALSE;
4784 	}
4785 
4786 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4787 		RETURN_FALSE;
4788 	}
4789 
4790 	PQreset(pgsql);
4791 	if (PQstatus(pgsql) == CONNECTION_BAD) {
4792 		RETURN_FALSE;
4793 	}
4794 	RETURN_TRUE;
4795 }
4796 /* }}} */
4797 
4798 #define PHP_PG_ASYNC_IS_BUSY		1
4799 #define PHP_PG_ASYNC_REQUEST_CANCEL 2
4800 
4801 /* {{{ php_pgsql_flush_query
4802  */
4803 static int php_pgsql_flush_query(PGconn *pgsql)
4804 {
4805 	PGresult *res;
4806 	int leftover = 0;
4807 
4808 	if (PQ_SETNONBLOCKING(pgsql, 1)) {
4809 		php_error_docref(NULL, E_NOTICE,"Cannot set connection to nonblocking mode");
4810 		return -1;
4811 	}
4812 	while ((res = PQgetResult(pgsql))) {
4813 		PQclear(res);
4814 		leftover++;
4815 	}
4816 	PQ_SETNONBLOCKING(pgsql, 0);
4817 	return leftover;
4818 }
4819 /* }}} */
4820 
4821 /* {{{ php_pgsql_do_async
4822  */
4823 static void php_pgsql_do_async(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
4824 {
4825 	zval *pgsql_link;
4826 	PGconn *pgsql;
4827 	PGresult *pgsql_result;
4828 
4829 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
4830 								 &pgsql_link) == FAILURE) {
4831 		RETURN_FALSE;
4832 	}
4833 
4834 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4835 		RETURN_FALSE;
4836 	}
4837 
4838 	if (PQ_SETNONBLOCKING(pgsql, 1)) {
4839 		php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
4840 		RETURN_FALSE;
4841 	}
4842 	switch(entry_type) {
4843 		case PHP_PG_ASYNC_IS_BUSY:
4844 			PQconsumeInput(pgsql);
4845 			RETVAL_LONG(PQisBusy(pgsql));
4846 			break;
4847 		case PHP_PG_ASYNC_REQUEST_CANCEL:
4848 			RETVAL_LONG(PQrequestCancel(pgsql));
4849 			while ((pgsql_result = PQgetResult(pgsql))) {
4850 				PQclear(pgsql_result);
4851 			}
4852 			break;
4853 		default:
4854 			php_error_docref(NULL, E_ERROR, "PostgreSQL module error, please report this error");
4855 			break;
4856 	}
4857 	if (PQ_SETNONBLOCKING(pgsql, 0)) {
4858 		php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
4859 	}
4860 	convert_to_boolean_ex(return_value);
4861 }
4862 /* }}} */
4863 
4864 /* {{{ proto bool pg_cancel_query(resource connection)
4865    Cancel request */
4866 PHP_FUNCTION(pg_cancel_query)
4867 {
4868 	php_pgsql_do_async(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_ASYNC_REQUEST_CANCEL);
4869 }
4870 /* }}} */
4871 
4872 /* {{{ proto bool pg_connection_busy(resource connection)
4873    Get connection is busy or not */
4874 PHP_FUNCTION(pg_connection_busy)
4875 {
4876 	php_pgsql_do_async(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_ASYNC_IS_BUSY);
4877 }
4878 /* }}} */
4879 
4880 static int _php_pgsql_link_has_results(PGconn *pgsql) /* {{{ */
4881 {
4882 	PGresult *result;
4883 	while ((result = PQgetResult(pgsql))) {
4884 		PQclear(result);
4885 		return 1;
4886 	}
4887 	return 0;
4888 }
4889 /* }}} */
4890 
4891 /* {{{ proto bool pg_send_query(resource connection, string query)
4892    Send asynchronous query */
4893 PHP_FUNCTION(pg_send_query)
4894 {
4895 	zval *pgsql_link;
4896 	char *query;
4897 	size_t len;
4898 	PGconn *pgsql;
4899 	int is_non_blocking;
4900 	int ret;
4901 
4902 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &pgsql_link, &query, &len) == FAILURE) {
4903 		return;
4904 	}
4905 
4906 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4907 		RETURN_FALSE;
4908 	}
4909 
4910 	is_non_blocking = PQisnonblocking(pgsql);
4911 
4912 	if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) {
4913 		php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
4914 		RETURN_FALSE;
4915 	}
4916 
4917 	if (_php_pgsql_link_has_results(pgsql)) {
4918 		php_error_docref(NULL, E_NOTICE,
4919 			"There are results on this connection. Call pg_get_result() until it returns FALSE");
4920 	}
4921 
4922 	if (is_non_blocking) {
4923 		if (!PQsendQuery(pgsql, query)) {
4924 			RETURN_FALSE;
4925 		}
4926 		ret = PQflush(pgsql);
4927 	} else {
4928 		if (!PQsendQuery(pgsql, query)) {
4929 			if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
4930 				PQreset(pgsql);
4931 			}
4932 			if (!PQsendQuery(pgsql, query)) {
4933 				RETURN_FALSE;
4934 			}
4935 		}
4936 
4937 		/* Wait to finish sending buffer */
4938 		while ((ret = PQflush(pgsql))) {
4939 			if (ret == -1) {
4940 				php_error_docref(NULL, E_NOTICE, "Could not empty PostgreSQL send buffer");
4941 				break;
4942 			}
4943 			usleep(10000);
4944 		}
4945 
4946 		if (PQ_SETNONBLOCKING(pgsql, 0)) {
4947 			php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
4948 		}
4949 	}
4950 
4951 	if (ret == 0) {
4952 		RETURN_TRUE;
4953 	} else if (ret == -1) {
4954 		RETURN_FALSE;
4955 	} else {
4956 		RETURN_LONG(0);
4957 	}
4958 }
4959 /* }}} */
4960 
4961 #if HAVE_PQSENDQUERYPARAMS
4962 /* {{{ proto bool pg_send_query_params(resource connection, string query, array params)
4963    Send asynchronous parameterized query */
4964 PHP_FUNCTION(pg_send_query_params)
4965 {
4966 	zval *pgsql_link, *pv_param_arr, *tmp;
4967 	int num_params = 0;
4968 	char **params = NULL;
4969 	char *query;
4970 	size_t query_len;
4971 	PGconn *pgsql;
4972 	int is_non_blocking;
4973 	int ret;
4974 
4975 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsa", &pgsql_link, &query, &query_len, &pv_param_arr) == FAILURE) {
4976 		return;
4977 	}
4978 
4979 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4980 		RETURN_FALSE;
4981 	}
4982 
4983 	is_non_blocking = PQisnonblocking(pgsql);
4984 
4985 	if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) {
4986 		php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
4987 		RETURN_FALSE;
4988 	}
4989 
4990 	if (_php_pgsql_link_has_results(pgsql)) {
4991 		php_error_docref(NULL, E_NOTICE,
4992 			"There are results on this connection. Call pg_get_result() until it returns FALSE");
4993 	}
4994 
4995 	num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
4996 	if (num_params > 0) {
4997 		int i = 0;
4998 		params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
4999 
5000 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pv_param_arr), tmp) {
5001 
5002 			if (Z_TYPE_P(tmp) == IS_NULL) {
5003 				params[i] = NULL;
5004 			} else {
5005 				zend_string *tmp_str;
5006 				zend_string *str = zval_get_tmp_string(tmp, &tmp_str);
5007 
5008 				params[i] = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
5009 				zend_tmp_string_release(tmp_str);
5010 			}
5011 
5012 			i++;
5013 		} ZEND_HASH_FOREACH_END();
5014 	}
5015 
5016 	if (PQsendQueryParams(pgsql, query, num_params, NULL, (const char * const *)params, NULL, NULL, 0)) {
5017 		_php_pgsql_free_params(params, num_params);
5018 	} else if (is_non_blocking) {
5019 		_php_pgsql_free_params(params, num_params);
5020 		RETURN_FALSE;
5021 	} else {
5022 		if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
5023 			PQreset(pgsql);
5024 		}
5025 		if (!PQsendQueryParams(pgsql, query, num_params, NULL, (const char * const *)params, NULL, NULL, 0)) {
5026 			_php_pgsql_free_params(params, num_params);
5027 			RETURN_FALSE;
5028 		}
5029 	}
5030 
5031 	if (is_non_blocking) {
5032 		ret = PQflush(pgsql);
5033 	} else {
5034 		/* Wait to finish sending buffer */
5035 		while ((ret = PQflush(pgsql))) {
5036 			if (ret == -1) {
5037 				php_error_docref(NULL, E_NOTICE, "Could not empty PostgreSQL send buffer");
5038 				break;
5039 			}
5040 			usleep(10000);
5041 		}
5042 
5043 		if (PQ_SETNONBLOCKING(pgsql, 0) != 0) {
5044 			php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
5045 		}
5046 	}
5047 
5048 	if (ret == 0) {
5049 		RETURN_TRUE;
5050 	} else if (ret == -1) {
5051 		RETURN_FALSE;
5052 	} else {
5053 		RETURN_LONG(0);
5054 	}
5055 }
5056 /* }}} */
5057 #endif
5058 
5059 #if HAVE_PQSENDPREPARE
5060 /* {{{ proto bool pg_send_prepare(resource connection, string stmtname, string query)
5061    Asynchronously prepare a query for future execution */
5062 PHP_FUNCTION(pg_send_prepare)
5063 {
5064 	zval *pgsql_link;
5065 	char *query, *stmtname;
5066 	size_t stmtname_len, query_len;
5067 	PGconn *pgsql;
5068 	int is_non_blocking;
5069 	int ret;
5070 
5071 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rss", &pgsql_link, &stmtname, &stmtname_len, &query, &query_len) == FAILURE) {
5072 		return;
5073 	}
5074 
5075 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5076 		RETURN_FALSE;
5077 	}
5078 
5079 	is_non_blocking = PQisnonblocking(pgsql);
5080 
5081 	if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) {
5082 		php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
5083 		RETURN_FALSE;
5084 	}
5085 
5086 	if (_php_pgsql_link_has_results(pgsql)) {
5087 		php_error_docref(NULL, E_NOTICE,
5088 			"There are results on this connection. Call pg_get_result() until it returns FALSE");
5089 	}
5090 
5091 	if (!PQsendPrepare(pgsql, stmtname, query, 0, NULL)) {
5092 		if (is_non_blocking) {
5093 			RETURN_FALSE;
5094 		} else {
5095 			if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
5096 				PQreset(pgsql);
5097 			}
5098 			if (!PQsendPrepare(pgsql, stmtname, query, 0, NULL)) {
5099 				RETURN_FALSE;
5100 			}
5101 		}
5102 	}
5103 
5104 	if (is_non_blocking) {
5105 		ret = PQflush(pgsql);
5106 	} else {
5107 		/* Wait to finish sending buffer */
5108 		while ((ret = PQflush(pgsql))) {
5109 			if (ret == -1) {
5110 				php_error_docref(NULL, E_NOTICE, "Could not empty PostgreSQL send buffer");
5111 				break;
5112 			}
5113 			usleep(10000);
5114 		}
5115 		if (PQ_SETNONBLOCKING(pgsql, 0) != 0) {
5116 			php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
5117 		}
5118 	}
5119 
5120 	if (ret == 0) {
5121 		RETURN_TRUE;
5122 	} else if (ret == -1) {
5123 		RETURN_FALSE;
5124 	} else {
5125 		RETURN_LONG(0);
5126 	}
5127 }
5128 /* }}} */
5129 #endif
5130 
5131 #if HAVE_PQSENDQUERYPREPARED
5132 /* {{{ proto bool pg_send_execute(resource connection, string stmtname, array params)
5133    Executes prevriously prepared stmtname asynchronously */
5134 PHP_FUNCTION(pg_send_execute)
5135 {
5136 	zval *pgsql_link;
5137 	zval *pv_param_arr, *tmp;
5138 	int num_params = 0;
5139 	char **params = NULL;
5140 	char *stmtname;
5141 	size_t stmtname_len;
5142 	PGconn *pgsql;
5143 	int is_non_blocking;
5144 	int ret;
5145 
5146 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsa", &pgsql_link, &stmtname, &stmtname_len, &pv_param_arr) == FAILURE) {
5147 		return;
5148 	}
5149 
5150 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5151 		RETURN_FALSE;
5152 	}
5153 
5154 	is_non_blocking = PQisnonblocking(pgsql);
5155 
5156 	if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) {
5157 		php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
5158 		RETURN_FALSE;
5159 	}
5160 
5161 	if (_php_pgsql_link_has_results(pgsql)) {
5162 		php_error_docref(NULL, E_NOTICE,
5163 			"There are results on this connection. Call pg_get_result() until it returns FALSE");
5164 	}
5165 
5166 	num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
5167 	if (num_params > 0) {
5168 		int i = 0;
5169 		params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
5170 
5171 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pv_param_arr), tmp) {
5172 
5173 			if (Z_TYPE_P(tmp) == IS_NULL) {
5174 				params[i] = NULL;
5175 			} else {
5176 				zval tmp_val;
5177 				ZVAL_COPY(&tmp_val, tmp);
5178 				convert_to_string(&tmp_val);
5179 				if (Z_TYPE(tmp_val) != IS_STRING) {
5180 					php_error_docref(NULL, E_WARNING,"Error converting parameter");
5181 					zval_ptr_dtor(&tmp_val);
5182 					_php_pgsql_free_params(params, num_params);
5183 					RETURN_FALSE;
5184 				}
5185 				params[i] = estrndup(Z_STRVAL(tmp_val), Z_STRLEN(tmp_val));
5186 				zval_ptr_dtor(&tmp_val);
5187 			}
5188 
5189 			i++;
5190 		} ZEND_HASH_FOREACH_END();
5191 	}
5192 
5193 	if (PQsendQueryPrepared(pgsql, stmtname, num_params, (const char * const *)params, NULL, NULL, 0)) {
5194 		_php_pgsql_free_params(params, num_params);
5195 	} else if (is_non_blocking) {
5196 		_php_pgsql_free_params(params, num_params);
5197 		RETURN_FALSE;
5198 	} else {
5199 		if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
5200 			PQreset(pgsql);
5201 		}
5202 		if (!PQsendQueryPrepared(pgsql, stmtname, num_params, (const char * const *)params, NULL, NULL, 0)) {
5203 			_php_pgsql_free_params(params, num_params);
5204 			RETURN_FALSE;
5205 		}
5206 	}
5207 
5208 	if (is_non_blocking) {
5209 		ret = PQflush(pgsql);
5210 	} else {
5211 		/* Wait to finish sending buffer */
5212 		while ((ret = PQflush(pgsql))) {
5213 			if (ret == -1) {
5214 				php_error_docref(NULL, E_NOTICE, "Could not empty PostgreSQL send buffer");
5215 				break;
5216 			}
5217 			usleep(10000);
5218 		}
5219 		if (PQ_SETNONBLOCKING(pgsql, 0) != 0) {
5220 			php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
5221 		}
5222 	}
5223 
5224 	if (ret == 0) {
5225 		RETURN_TRUE;
5226 	} else if (ret == -1) {
5227 		RETURN_FALSE;
5228 	} else {
5229 		RETURN_LONG(0);
5230 	}
5231 }
5232 /* }}} */
5233 #endif
5234 
5235 /* {{{ proto resource pg_get_result(resource connection)
5236    Get asynchronous query result */
5237 PHP_FUNCTION(pg_get_result)
5238 {
5239 	zval *pgsql_link;
5240 	PGconn *pgsql;
5241 	PGresult *pgsql_result;
5242 	pgsql_result_handle *pg_result;
5243 
5244 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r", &pgsql_link) == FAILURE) {
5245 		RETURN_FALSE;
5246 	}
5247 
5248 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5249 		RETURN_FALSE;
5250 	}
5251 
5252 	pgsql_result = PQgetResult(pgsql);
5253 	if (!pgsql_result) {
5254 		/* no result */
5255 		RETURN_FALSE;
5256 	}
5257 	pg_result = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
5258 	pg_result->conn = pgsql;
5259 	pg_result->result = pgsql_result;
5260 	pg_result->row = 0;
5261 	RETURN_RES(zend_register_resource(pg_result, le_result));
5262 }
5263 /* }}} */
5264 
5265 /* {{{ proto mixed pg_result_status(resource result[, int result_type])
5266    Get status of query result */
5267 PHP_FUNCTION(pg_result_status)
5268 {
5269 	zval *result;
5270 	zend_long result_type = PGSQL_STATUS_LONG;
5271 	ExecStatusType status;
5272 	PGresult *pgsql_result;
5273 	pgsql_result_handle *pg_result;
5274 
5275 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r|l",
5276 								 &result, &result_type) == FAILURE) {
5277 		RETURN_FALSE;
5278 	}
5279 
5280 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
5281 		RETURN_FALSE;
5282 	}
5283 
5284 	pgsql_result = pg_result->result;
5285 	if (result_type == PGSQL_STATUS_LONG) {
5286 		status = PQresultStatus(pgsql_result);
5287 		RETURN_LONG((int)status);
5288 	}
5289 	else if (result_type == PGSQL_STATUS_STRING) {
5290 		RETURN_STRING(PQcmdStatus(pgsql_result));
5291 	}
5292 	else {
5293 		php_error_docref(NULL, E_WARNING, "Optional 2nd parameter should be PGSQL_STATUS_LONG or PGSQL_STATUS_STRING");
5294 		RETURN_FALSE;
5295 	}
5296 }
5297 /* }}} */
5298 
5299 /* {{{ proto mixed pg_get_notify([resource connection[, int result_type]])
5300    Get asynchronous notification */
5301 PHP_FUNCTION(pg_get_notify)
5302 {
5303 	zval *pgsql_link;
5304 	zend_long result_type = PGSQL_ASSOC;
5305 	PGconn *pgsql;
5306 	PGnotify *pgsql_notify;
5307 
5308 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r|l",
5309 								 &pgsql_link, &result_type) == FAILURE) {
5310 		RETURN_FALSE;
5311 	}
5312 
5313 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5314 		RETURN_FALSE;
5315 	}
5316 
5317 	if (!(result_type & PGSQL_BOTH)) {
5318 		php_error_docref(NULL, E_WARNING, "Invalid result type");
5319 		RETURN_FALSE;
5320 	}
5321 
5322 	PQconsumeInput(pgsql);
5323 	pgsql_notify = PQnotifies(pgsql);
5324 	if (!pgsql_notify) {
5325 		/* no notify message */
5326 		RETURN_FALSE;
5327 	}
5328 	array_init(return_value);
5329 	if (result_type & PGSQL_NUM) {
5330 		add_index_string(return_value, 0, pgsql_notify->relname);
5331 		add_index_long(return_value, 1, pgsql_notify->be_pid);
5332 #if HAVE_PQPROTOCOLVERSION && HAVE_PQPARAMETERSTATUS
5333 		if (PQprotocolVersion(pgsql) >= 3 && atof(PQparameterStatus(pgsql, "server_version")) >= 9.0) {
5334 #else
5335 		if (atof(PG_VERSION) >= 9.0) {
5336 #endif
5337 #if HAVE_PQPARAMETERSTATUS
5338 			add_index_string(return_value, 2, pgsql_notify->extra);
5339 #endif
5340 		}
5341 	}
5342 	if (result_type & PGSQL_ASSOC) {
5343 		add_assoc_string(return_value, "message", pgsql_notify->relname);
5344 		add_assoc_long(return_value, "pid", pgsql_notify->be_pid);
5345 #if HAVE_PQPROTOCOLVERSION && HAVE_PQPARAMETERSTATUS
5346 		if (PQprotocolVersion(pgsql) >= 3 && atof(PQparameterStatus(pgsql, "server_version")) >= 9.0) {
5347 #else
5348 		if (atof(PG_VERSION) >= 9.0) {
5349 #endif
5350 #if HAVE_PQPARAMETERSTATUS
5351 			add_assoc_string(return_value, "payload", pgsql_notify->extra);
5352 #endif
5353 		}
5354 	}
5355 	PQfreemem(pgsql_notify);
5356 }
5357 /* }}} */
5358 
5359 /* {{{ proto int pg_get_pid([resource connection)
5360    Get backend(server) pid */
5361 PHP_FUNCTION(pg_get_pid)
5362 {
5363 	zval *pgsql_link;
5364 	PGconn *pgsql;
5365 
5366 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
5367 								 &pgsql_link) == FAILURE) {
5368 		RETURN_FALSE;
5369 	}
5370 
5371 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5372 		RETURN_FALSE;
5373 	}
5374 
5375 	RETURN_LONG(PQbackendPID(pgsql));
5376 }
5377 /* }}} */
5378 
5379 static size_t php_pgsql_fd_write(php_stream *stream, const char *buf, size_t count) /* {{{ */
5380 {
5381 	return 0;
5382 }
5383 /* }}} */
5384 
5385 static size_t php_pgsql_fd_read(php_stream *stream, char *buf, size_t count) /* {{{ */
5386 {
5387 	return 0;
5388 }
5389 /* }}} */
5390 
5391 static int php_pgsql_fd_close(php_stream *stream, int close_handle) /* {{{ */
5392 {
5393 	return EOF;
5394 }
5395 /* }}} */
5396 
5397 static int php_pgsql_fd_flush(php_stream *stream) /* {{{ */
5398 {
5399 	return FAILURE;
5400 }
5401 /* }}} */
5402 
5403 static int php_pgsql_fd_set_option(php_stream *stream, int option, int value, void *ptrparam) /* {{{ */
5404 {
5405 	PGconn *pgsql = (PGconn *) stream->abstract;
5406 	switch (option) {
5407 		case PHP_STREAM_OPTION_BLOCKING:
5408 			return PQ_SETNONBLOCKING(pgsql, value);
5409 		default:
5410 			return FAILURE;
5411 	}
5412 }
5413 /* }}} */
5414 
5415 static int php_pgsql_fd_cast(php_stream *stream, int cast_as, void **ret) /* {{{ */
5416 {
5417 	PGconn *pgsql = (PGconn *) stream->abstract;
5418 
5419 	switch (cast_as)	{
5420 		case PHP_STREAM_AS_FD_FOR_SELECT:
5421 		case PHP_STREAM_AS_FD:
5422 		case PHP_STREAM_AS_SOCKETD:
5423 			if (ret) {
5424 				int fd_number = PQsocket(pgsql);
5425 				if (fd_number == -1) {
5426 					return FAILURE;
5427 				}
5428 
5429 				*(php_socket_t *)ret = fd_number;
5430 				return SUCCESS;
5431 			}
5432 		default:
5433 			return FAILURE;
5434 	}
5435 }
5436 /* }}} */
5437 
5438 /* {{{ proto resource pg_socket(resource connection)
5439    Get a read-only handle to the socket underlying the pgsql connection */
5440 PHP_FUNCTION(pg_socket)
5441 {
5442 	zval *pgsql_link;
5443 	php_stream *stream;
5444 	PGconn *pgsql;
5445 
5446 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pgsql_link) == FAILURE) {
5447 		return;
5448 	}
5449 
5450 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5451 		RETURN_FALSE;
5452 	}
5453 
5454 	stream = php_stream_alloc(&php_stream_pgsql_fd_ops, pgsql, NULL, "r");
5455 
5456 	if (stream) {
5457 		php_stream_to_zval(stream, return_value);
5458 		return;
5459 	}
5460 
5461 	RETURN_FALSE;
5462 }
5463 /* }}} */
5464 
5465 /* {{{ proto bool pg_consume_input(resource connection)
5466    Reads input on the connection */
5467 PHP_FUNCTION(pg_consume_input)
5468 {
5469 	zval *pgsql_link;
5470 	PGconn *pgsql;
5471 
5472 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pgsql_link) == FAILURE) {
5473 		return;
5474 	}
5475 
5476 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5477 		RETURN_FALSE;
5478 	}
5479 
5480 	RETURN_BOOL(PQconsumeInput(pgsql));
5481 }
5482 /* }}} */
5483 
5484 /* {{{ proto mixed pg_flush(resource connection)
5485    Flush outbound query data on the connection */
5486 PHP_FUNCTION(pg_flush)
5487 {
5488 	zval *pgsql_link;
5489 	PGconn *pgsql;
5490 	int ret;
5491 	int is_non_blocking;
5492 
5493 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pgsql_link) == FAILURE) {
5494 		return;
5495 	}
5496 
5497 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5498 		RETURN_FALSE;
5499 	}
5500 
5501 	is_non_blocking = PQisnonblocking(pgsql);
5502 
5503 	if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) {
5504 		php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
5505 		RETURN_FALSE;
5506 	}
5507 
5508 	ret = PQflush(pgsql);
5509 
5510 	if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 0) == -1) {
5511 		php_error_docref(NULL, E_NOTICE, "Failed resetting connection to blocking mode");
5512 	}
5513 
5514 	switch (ret) {
5515 		case 0: RETURN_TRUE; break;
5516 		case 1: RETURN_LONG(0); break;
5517 		default: RETURN_FALSE;
5518 	}
5519 }
5520 /* }}} */
5521 
5522 /* {{{ php_pgsql_meta_data
5523  * TODO: Add meta_data cache for better performance
5524  */
5525 PHP_PGSQL_API int php_pgsql_meta_data(PGconn *pg_link, const char *table_name, zval *meta, zend_bool extended)
5526 {
5527 	PGresult *pg_result;
5528 	char *src, *tmp_name, *tmp_name2 = NULL;
5529 	char *escaped;
5530 	smart_str querystr = {0};
5531 	size_t new_len;
5532 	int i, num_rows;
5533 	zval elem;
5534 
5535 	if (!*table_name) {
5536 		php_error_docref(NULL, E_WARNING, "The table name must be specified");
5537 		return FAILURE;
5538 	}
5539 
5540 	src = estrdup(table_name);
5541 	tmp_name = php_strtok_r(src, ".", &tmp_name2);
5542 	if (!tmp_name) {
5543 		efree(src);
5544 		php_error_docref(NULL, E_WARNING, "The table name must be specified");
5545 		return FAILURE;
5546 	}
5547 	if (!tmp_name2 || !*tmp_name2) {
5548 		/* Default schema */
5549 		tmp_name2 = tmp_name;
5550 		tmp_name = "public";
5551 	}
5552 
5553 	if (extended) {
5554 		smart_str_appends(&querystr,
5555 						  "SELECT a.attname, a.attnum, t.typname, a.attlen, a.attnotNULL, a.atthasdef, a.attndims, t.typtype, "
5556 						  "d.description "
5557 						  "FROM pg_class as c "
5558 						  " JOIN pg_attribute a ON (a.attrelid = c.oid) "
5559 						  " JOIN pg_type t ON (a.atttypid = t.oid) "
5560 						  " JOIN pg_namespace n ON (c.relnamespace = n.oid) "
5561 						  " LEFT JOIN pg_description d ON (d.objoid=a.attrelid AND d.objsubid=a.attnum AND c.oid=d.objoid) "
5562 						  "WHERE a.attnum > 0  AND c.relname = '");
5563 	} else {
5564 		smart_str_appends(&querystr,
5565 						  "SELECT a.attname, a.attnum, t.typname, a.attlen, a.attnotnull, a.atthasdef, a.attndims, t.typtype "
5566 						  "FROM pg_class as c "
5567 						  " JOIN pg_attribute a ON (a.attrelid = c.oid) "
5568 						  " JOIN pg_type t ON (a.atttypid = t.oid) "
5569 						  " JOIN pg_namespace n ON (c.relnamespace = n.oid) "
5570 						  "WHERE a.attnum > 0 AND c.relname = '");
5571 	}
5572 	escaped = (char *)safe_emalloc(strlen(tmp_name2), 2, 1);
5573 	new_len = PQescapeStringConn(pg_link, escaped, tmp_name2, strlen(tmp_name2), NULL);
5574 	if (new_len) {
5575 		smart_str_appendl(&querystr, escaped, new_len);
5576 	}
5577 	efree(escaped);
5578 
5579 	smart_str_appends(&querystr, "' AND n.nspname = '");
5580 	escaped = (char *)safe_emalloc(strlen(tmp_name), 2, 1);
5581 	new_len = PQescapeStringConn(pg_link, escaped, tmp_name, strlen(tmp_name), NULL);
5582 	if (new_len) {
5583 		smart_str_appendl(&querystr, escaped, new_len);
5584 	}
5585 	efree(escaped);
5586 
5587 	smart_str_appends(&querystr, "' ORDER BY a.attnum;");
5588 	smart_str_0(&querystr);
5589 	efree(src);
5590 
5591 	pg_result = PQexec(pg_link, ZSTR_VAL(querystr.s));
5592 	if (PQresultStatus(pg_result) != PGRES_TUPLES_OK || (num_rows = PQntuples(pg_result)) == 0) {
5593 		php_error_docref(NULL, E_WARNING, "Table '%s' doesn't exists", table_name);
5594 		smart_str_free(&querystr);
5595 		PQclear(pg_result);
5596 		return FAILURE;
5597 	}
5598 	smart_str_free(&querystr);
5599 
5600 	for (i = 0; i < num_rows; i++) {
5601 		char *name;
5602 		array_init(&elem);
5603 		/* pg_attribute.attnum */
5604 		add_assoc_long_ex(&elem, "num", sizeof("num") - 1, atoi(PQgetvalue(pg_result, i, 1)));
5605 		/* pg_type.typname */
5606 		add_assoc_string_ex(&elem, "type", sizeof("type") - 1, PQgetvalue(pg_result, i, 2));
5607 		/* pg_attribute.attlen */
5608 		add_assoc_long_ex(&elem, "len", sizeof("len") - 1, atoi(PQgetvalue(pg_result,i,3)));
5609 		/* pg_attribute.attnonull */
5610 		add_assoc_bool_ex(&elem, "not null", sizeof("not null") - 1, !strcmp(PQgetvalue(pg_result, i, 4), "t"));
5611 		/* pg_attribute.atthasdef */
5612 		add_assoc_bool_ex(&elem, "has default", sizeof("has default") - 1, !strcmp(PQgetvalue(pg_result,i,5), "t"));
5613 		/* pg_attribute.attndims */
5614 		add_assoc_long_ex(&elem, "array dims", sizeof("array dims") - 1, atoi(PQgetvalue(pg_result, i, 6)));
5615 		/* pg_type.typtype */
5616 		add_assoc_bool_ex(&elem, "is enum", sizeof("is enum") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "e"));
5617 		if (extended) {
5618 			/* pg_type.typtype */
5619 			add_assoc_bool_ex(&elem, "is base", sizeof("is base") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "b"));
5620 			add_assoc_bool_ex(&elem, "is composite", sizeof("is composite") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "c"));
5621 			add_assoc_bool_ex(&elem, "is pesudo", sizeof("is pesudo") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "p"));
5622 			/* pg_description.description */
5623 			add_assoc_string_ex(&elem, "description", sizeof("description") - 1, PQgetvalue(pg_result, i, 8));
5624 		}
5625 		/* pg_attribute.attname */
5626 		name = PQgetvalue(pg_result,i,0);
5627 		add_assoc_zval(meta, name, &elem);
5628 	}
5629 	PQclear(pg_result);
5630 
5631 	return SUCCESS;
5632 }
5633 
5634 /* }}} */
5635 
5636 /* {{{ proto array pg_meta_data(resource db, string table [, bool extended])
5637    Get meta_data */
5638 PHP_FUNCTION(pg_meta_data)
5639 {
5640 	zval *pgsql_link;
5641 	char *table_name;
5642 	size_t table_name_len;
5643 	zend_bool extended=0;
5644 	PGconn *pgsql;
5645 
5646 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs|b",
5647 							  &pgsql_link, &table_name, &table_name_len, &extended) == FAILURE) {
5648 		return;
5649 	}
5650 
5651 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5652 		RETURN_FALSE;
5653 	}
5654 
5655 	array_init(return_value);
5656 	if (php_pgsql_meta_data(pgsql, table_name, return_value, extended) == FAILURE) {
5657 		zend_array_destroy(Z_ARR_P(return_value)); /* destroy array */
5658 		RETURN_FALSE;
5659 	}
5660 }
5661 /* }}} */
5662 
5663 /* {{{ php_pgsql_get_data_type
5664  */
5665 static php_pgsql_data_type php_pgsql_get_data_type(const char *type_name, size_t len)
5666 {
5667 	/* This is stupid way to do. I'll fix it when I decied how to support
5668 	   user defined types. (Yasuo) */
5669 
5670 	/* boolean */
5671 	if (!strcmp(type_name, "bool")|| !strcmp(type_name, "boolean"))
5672 		return PG_BOOL;
5673 	/* object id */
5674 	if (!strcmp(type_name, "oid"))
5675 		return PG_OID;
5676 	/* integer */
5677 	if (!strcmp(type_name, "int2") || !strcmp(type_name, "smallint"))
5678 		return PG_INT2;
5679 	if (!strcmp(type_name, "int4") || !strcmp(type_name, "integer"))
5680 		return PG_INT4;
5681 	if (!strcmp(type_name, "int8") || !strcmp(type_name, "bigint"))
5682 		return PG_INT8;
5683 	/* real and other */
5684 	if (!strcmp(type_name, "float4") || !strcmp(type_name, "real"))
5685 		return PG_FLOAT4;
5686 	if (!strcmp(type_name, "float8") || !strcmp(type_name, "double precision"))
5687 		return PG_FLOAT8;
5688 	if (!strcmp(type_name, "numeric"))
5689 		return PG_NUMERIC;
5690 	if (!strcmp(type_name, "money"))
5691 		return PG_MONEY;
5692 	/* character */
5693 	if (!strcmp(type_name, "text"))
5694 		return PG_TEXT;
5695 	if (!strcmp(type_name, "bpchar") || !strcmp(type_name, "character"))
5696 		return PG_CHAR;
5697 	if (!strcmp(type_name, "varchar") || !strcmp(type_name, "character varying"))
5698 		return PG_VARCHAR;
5699 	/* time and interval */
5700 	if (!strcmp(type_name, "abstime"))
5701 		return PG_UNIX_TIME;
5702 	if (!strcmp(type_name, "reltime"))
5703 		return PG_UNIX_TIME_INTERVAL;
5704 	if (!strcmp(type_name, "tinterval"))
5705 		return PG_UNIX_TIME_INTERVAL;
5706 	if (!strcmp(type_name, "date"))
5707 		return PG_DATE;
5708 	if (!strcmp(type_name, "time"))
5709 		return PG_TIME;
5710 	if (!strcmp(type_name, "time with time zone") || !strcmp(type_name, "timetz"))
5711 		return PG_TIME_WITH_TIMEZONE;
5712 	if (!strcmp(type_name, "timestamp without time zone") || !strcmp(type_name, "timestamp"))
5713 		return PG_TIMESTAMP;
5714 	if (!strcmp(type_name, "timestamp with time zone") || !strcmp(type_name, "timestamptz"))
5715 		return PG_TIMESTAMP_WITH_TIMEZONE;
5716 	if (!strcmp(type_name, "interval"))
5717 		return PG_INTERVAL;
5718 	/* binary */
5719 	if (!strcmp(type_name, "bytea"))
5720 		return PG_BYTEA;
5721 	/* network */
5722 	if (!strcmp(type_name, "cidr"))
5723 		return PG_CIDR;
5724 	if (!strcmp(type_name, "inet"))
5725 		return PG_INET;
5726 	if (!strcmp(type_name, "macaddr"))
5727 		return PG_MACADDR;
5728 	/* bit */
5729 	if (!strcmp(type_name, "bit"))
5730 		return PG_BIT;
5731 	if (!strcmp(type_name, "bit varying"))
5732 		return PG_VARBIT;
5733 	/* geometric */
5734 	if (!strcmp(type_name, "line"))
5735 		return PG_LINE;
5736 	if (!strcmp(type_name, "lseg"))
5737 		return PG_LSEG;
5738 	if (!strcmp(type_name, "box"))
5739 		return PG_BOX;
5740 	if (!strcmp(type_name, "path"))
5741 		return PG_PATH;
5742 	if (!strcmp(type_name, "point"))
5743 		return PG_POINT;
5744 	if (!strcmp(type_name, "polygon"))
5745 		return PG_POLYGON;
5746 	if (!strcmp(type_name, "circle"))
5747 		return PG_CIRCLE;
5748 
5749 	return PG_UNKNOWN;
5750 }
5751 /* }}} */
5752 
5753 /* {{{ php_pgsql_convert_match
5754  * test field value with regular expression specified.
5755  */
5756 static int php_pgsql_convert_match(const char *str, size_t str_len, const char *regex , size_t regex_len, int icase)
5757 {
5758 	pcre2_code *re;
5759 	PCRE2_SIZE           err_offset;
5760 	int res, errnumber;
5761 	uint32_t options = PCRE2_NO_AUTO_CAPTURE;
5762 	size_t i;
5763 	pcre2_match_data *match_data;
5764 
5765 	/* Check invalid chars for POSIX regex */
5766 	for (i = 0; i < str_len; i++) {
5767 		if (str[i] == '\n' ||
5768 			str[i] == '\r' ||
5769 			str[i] == '\0' ) {
5770 			return FAILURE;
5771 		}
5772 	}
5773 
5774 	if (icase) {
5775 		options |= PCRE2_CASELESS;
5776 	}
5777 
5778 	re = pcre2_compile((PCRE2_SPTR)regex, regex_len, options, &errnumber, &err_offset, php_pcre_cctx());
5779 	if (NULL == re) {
5780 		PCRE2_UCHAR err_msg[128];
5781 		pcre2_get_error_message(errnumber, err_msg, sizeof(err_msg));
5782 		php_error_docref(NULL, E_WARNING, "Cannot compile regex: '%s'", err_msg);
5783 		return FAILURE;
5784 	}
5785 
5786 	match_data = php_pcre_create_match_data(0, re);
5787 	if (NULL == match_data) {
5788 		pcre2_code_free(re);
5789 		php_error_docref(NULL, E_WARNING, "Cannot allocate match data");
5790 		return FAILURE;
5791 	}
5792 	res = pcre2_match(re, (PCRE2_SPTR)str, str_len, 0, 0, match_data, php_pcre_mctx());
5793 	php_pcre_free_match_data(match_data);
5794 	pcre2_code_free(re);
5795 
5796 	if (res == PCRE2_ERROR_NOMATCH) {
5797 		return FAILURE;
5798 	} else if (res < 0) {
5799 		php_error_docref(NULL, E_WARNING, "Cannot exec regex");
5800 		return FAILURE;
5801 	}
5802 	return SUCCESS;
5803 }
5804 
5805 /* }}} */
5806 
5807 /* {{{ php_pgsql_add_quote
5808  * add quotes around string.
5809  */
5810 static int php_pgsql_add_quotes(zval *src, zend_bool should_free)
5811 {
5812 	smart_str str = {0};
5813 
5814 	assert(Z_TYPE_P(src) == IS_STRING);
5815 	assert(should_free == 1 || should_free == 0);
5816 
5817 	smart_str_appendc(&str, 'E');
5818 	smart_str_appendc(&str, '\'');
5819 	smart_str_appendl(&str, Z_STRVAL_P(src), Z_STRLEN_P(src));
5820 	smart_str_appendc(&str, '\'');
5821 	smart_str_0(&str);
5822 
5823 	if (should_free) {
5824 		zval_ptr_dtor(src);
5825 	}
5826 	ZVAL_NEW_STR(src, str.s);
5827 
5828 	return SUCCESS;
5829 }
5830 /* }}} */
5831 
5832 #define PGSQL_CONV_CHECK_IGNORE() \
5833 	if (!err && Z_TYPE(new_val) == IS_STRING && !strcmp(Z_STRVAL(new_val), "NULL")) { \
5834 		/* if new_value is string "NULL" and field has default value, remove element to use default value */ \
5835 		if (!(opt & PGSQL_CONV_IGNORE_DEFAULT) && Z_TYPE_P(has_default) == IS_TRUE) { \
5836 			zval_ptr_dtor(&new_val); \
5837 			skip_field = 1; \
5838 		} \
5839 		/* raise error if it's not null and cannot be ignored */ \
5840 		else if (!(opt & PGSQL_CONV_IGNORE_NOT_NULL) && Z_TYPE_P(not_null) == IS_TRUE) { \
5841 			php_error_docref(NULL, E_NOTICE, "Detected NULL for 'NOT NULL' field '%s'", ZSTR_VAL(field)); \
5842 			err = 1; \
5843 		} \
5844 	}
5845 
5846 /* {{{ php_pgsql_convert
5847  * check and convert array values (fieldname=>value pair) for sql
5848  */
5849 PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, const zval *values, zval *result, zend_ulong opt)
5850 {
5851 	zend_string *field = NULL;
5852 	zval meta, *def, *type, *not_null, *has_default, *is_enum, *val, new_val;
5853 	int err = 0, skip_field;
5854 	php_pgsql_data_type data_type;
5855 
5856 	assert(pg_link != NULL);
5857 	assert(Z_TYPE_P(values) == IS_ARRAY);
5858 	assert(Z_TYPE_P(result) == IS_ARRAY);
5859 	assert(!(opt & ~PGSQL_CONV_OPTS));
5860 
5861 	if (!table_name) {
5862 		return FAILURE;
5863 	}
5864 
5865 	array_init(&meta);
5866 /* table_name is escaped by php_pgsql_meta_data */
5867 	if (php_pgsql_meta_data(pg_link, table_name, &meta, 0) == FAILURE) {
5868 		zval_ptr_dtor(&meta);
5869 		return FAILURE;
5870 	}
5871 
5872 	ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(values), field, val) {
5873 		skip_field = 0;
5874 		ZVAL_NULL(&new_val);
5875 
5876 		if (!err && field == NULL) {
5877 			php_error_docref(NULL, E_WARNING, "Accepts only string key for values");
5878 			err = 1;
5879 		}
5880 
5881 		if (!err && (def = zend_hash_find(Z_ARRVAL(meta), field)) == NULL) {
5882 			php_error_docref(NULL, E_NOTICE, "Invalid field name (%s) in values", ZSTR_VAL(field));
5883 			err = 1;
5884 		}
5885 		if (!err && (type = zend_hash_str_find(Z_ARRVAL_P(def), "type", sizeof("type") - 1)) == NULL) {
5886 			php_error_docref(NULL, E_NOTICE, "Detected broken meta data. Missing 'type'");
5887 			err = 1;
5888 		}
5889 		if (!err && (not_null = zend_hash_str_find(Z_ARRVAL_P(def), "not null", sizeof("not null") - 1)) == NULL) {
5890 			php_error_docref(NULL, E_NOTICE, "Detected broken meta data. Missing 'not null'");
5891 			err = 1;
5892 		}
5893 		if (!err && (has_default = zend_hash_str_find(Z_ARRVAL_P(def), "has default", sizeof("has default") - 1)) == NULL) {
5894 			php_error_docref(NULL, E_NOTICE, "Detected broken meta data. Missing 'has default'");
5895 			err = 1;
5896 		}
5897 		if (!err && (is_enum = zend_hash_str_find(Z_ARRVAL_P(def), "is enum", sizeof("is enum") - 1)) == NULL) {
5898 			php_error_docref(NULL, E_NOTICE, "Detected broken meta data. Missing 'is enum'");
5899 			err = 1;
5900 		}
5901 		if (!err && (Z_TYPE_P(val) == IS_ARRAY || Z_TYPE_P(val) == IS_OBJECT)) {
5902 			php_error_docref(NULL, E_NOTICE, "Expects scalar values as field values");
5903 			err = 1;
5904 		}
5905 		if (err) {
5906 			break; /* break out for() */
5907 		}
5908 
5909 		convert_to_boolean(is_enum);
5910 		if (Z_TYPE_P(is_enum) == IS_TRUE) {
5911 			/* enums need to be treated like strings */
5912 			data_type = PG_TEXT;
5913 		} else {
5914 			data_type = php_pgsql_get_data_type(Z_STRVAL_P(type), Z_STRLEN_P(type));
5915 		}
5916 
5917 		switch(data_type)
5918 		{
5919 			case PG_BOOL:
5920 				switch (Z_TYPE_P(val)) {
5921 					case IS_STRING:
5922 						if (Z_STRLEN_P(val) == 0) {
5923 							ZVAL_STRING(&new_val, "NULL");
5924 						}
5925 						else {
5926 							if (!strcmp(Z_STRVAL_P(val), "t") || !strcmp(Z_STRVAL_P(val), "T") ||
5927 								!strcmp(Z_STRVAL_P(val), "y") || !strcmp(Z_STRVAL_P(val), "Y") ||
5928 								!strcmp(Z_STRVAL_P(val), "true") || !strcmp(Z_STRVAL_P(val), "True") ||
5929 								!strcmp(Z_STRVAL_P(val), "yes") || !strcmp(Z_STRVAL_P(val), "Yes") ||
5930 								!strcmp(Z_STRVAL_P(val), "1")) {
5931 								ZVAL_STRINGL(&new_val, "'t'", sizeof("'t'")-1);
5932 							}
5933 							else if (!strcmp(Z_STRVAL_P(val), "f") || !strcmp(Z_STRVAL_P(val), "F") ||
5934 									 !strcmp(Z_STRVAL_P(val), "n") || !strcmp(Z_STRVAL_P(val), "N") ||
5935 									 !strcmp(Z_STRVAL_P(val), "false") ||  !strcmp(Z_STRVAL_P(val), "False") ||
5936 									 !strcmp(Z_STRVAL_P(val), "no") ||  !strcmp(Z_STRVAL_P(val), "No") ||
5937 									 !strcmp(Z_STRVAL_P(val), "0")) {
5938 								ZVAL_STRINGL(&new_val, "'f'", sizeof("'f'")-1);
5939 							}
5940 							else {
5941 								php_error_docref(NULL, E_NOTICE, "Detected invalid value (%s) for PostgreSQL %s field (%s)", Z_STRVAL_P(val), Z_STRVAL_P(type), ZSTR_VAL(field));
5942 								err = 1;
5943 							}
5944 						}
5945 						break;
5946 
5947 					case IS_LONG:
5948 						if (Z_LVAL_P(val)) {
5949 							ZVAL_STRINGL(&new_val, "'t'", sizeof("'t'")-1);
5950 						}
5951 						else {
5952 							ZVAL_STRINGL(&new_val, "'f'", sizeof("'f'")-1);
5953 						}
5954 						break;
5955 
5956 					case IS_TRUE:
5957 						ZVAL_STRINGL(&new_val, "'t'", sizeof("'t'")-1);
5958 						break;
5959 
5960 					case IS_FALSE:
5961 						ZVAL_STRINGL(&new_val, "'f'", sizeof("'f'")-1);
5962 						break;
5963 
5964 					case IS_NULL:
5965 						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
5966 						break;
5967 
5968 					default:
5969 						err = 1;
5970 				}
5971 				PGSQL_CONV_CHECK_IGNORE();
5972 				if (err) {
5973 					php_error_docref(NULL, E_NOTICE, "Expects string, null, long or boolelan value for PostgreSQL '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
5974 				}
5975 				break;
5976 
5977 			case PG_OID:
5978 			case PG_INT2:
5979 			case PG_INT4:
5980 			case PG_INT8:
5981 				switch (Z_TYPE_P(val)) {
5982 					case IS_STRING:
5983 						if (Z_STRLEN_P(val) == 0) {
5984 							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
5985 						}
5986 						else {
5987 							/* FIXME: better regex must be used */
5988 #define REGEX0 "^([+-]{0,1}[0-9]+)$"
5989 							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), REGEX0, sizeof(REGEX0)-1, 0) == FAILURE) {
5990 								err = 1;
5991 							}
5992 							else {
5993 								ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
5994 							}
5995 #undef REGEX0
5996 						}
5997 						break;
5998 
5999 					case IS_DOUBLE:
6000 						ZVAL_DOUBLE(&new_val, Z_DVAL_P(val));
6001 						convert_to_long_ex(&new_val);
6002 						break;
6003 
6004 					case IS_LONG:
6005 						ZVAL_LONG(&new_val, Z_LVAL_P(val));
6006 						break;
6007 
6008 					case IS_NULL:
6009 						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6010 						break;
6011 
6012 					default:
6013 						err = 1;
6014 				}
6015 				PGSQL_CONV_CHECK_IGNORE();
6016 				if (err) {
6017 					php_error_docref(NULL, E_NOTICE, "Expects NULL, string, long or double value for pgsql '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6018 				}
6019 				break;
6020 
6021 			case PG_NUMERIC:
6022 			case PG_MONEY:
6023 			case PG_FLOAT4:
6024 			case PG_FLOAT8:
6025 				switch (Z_TYPE_P(val)) {
6026 					case IS_STRING:
6027 						if (Z_STRLEN_P(val) == 0) {
6028 							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6029 						}
6030 						else {
6031 #define REGEX0 "^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$"
6032 #define REGEX1 "^[+-]{0,1}(inf)(inity){0,1}$"
6033 							/* better regex? */
6034 							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), REGEX0, sizeof(REGEX0)-1, 0) == FAILURE) {
6035 								if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), REGEX1, sizeof(REGEX1)-1, 1) == FAILURE) {
6036 									err = 1;
6037 								} else {
6038 									ZVAL_STRING(&new_val, Z_STRVAL_P(val));
6039 									php_pgsql_add_quotes(&new_val, 1);
6040 								}
6041 							}
6042 							else {
6043 								ZVAL_STRING(&new_val, Z_STRVAL_P(val));
6044 							}
6045 #undef REGEX0
6046 #undef REGEX1
6047 						}
6048 						break;
6049 
6050 					case IS_LONG:
6051 						ZVAL_LONG(&new_val, Z_LVAL_P(val));
6052 						break;
6053 
6054 					case IS_DOUBLE:
6055 						ZVAL_DOUBLE(&new_val, Z_DVAL_P(val));
6056 						break;
6057 
6058 					case IS_NULL:
6059 						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6060 						break;
6061 
6062 					default:
6063 						err = 1;
6064 				}
6065 				PGSQL_CONV_CHECK_IGNORE();
6066 				if (err) {
6067 					php_error_docref(NULL, E_NOTICE, "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6068 				}
6069 				break;
6070 
6071 				/* Exotic types are handled as string also.
6072 				   Please feel free to add more valitions. Invalid query fails
6073 				   at execution anyway. */
6074 			case PG_TEXT:
6075 			case PG_CHAR:
6076 			case PG_VARCHAR:
6077 				/* bit */
6078 			case PG_BIT:
6079 			case PG_VARBIT:
6080 				/* geometric */
6081 			case PG_LINE:
6082 			case PG_LSEG:
6083 			case PG_POINT:
6084 			case PG_BOX:
6085 			case PG_PATH:
6086 			case PG_POLYGON:
6087 			case PG_CIRCLE:
6088 				/* unknown. JSON, Array etc */
6089 			case PG_UNKNOWN:
6090 				switch (Z_TYPE_P(val)) {
6091 					case IS_STRING:
6092 						if (Z_STRLEN_P(val) == 0) {
6093 							if (opt & PGSQL_CONV_FORCE_NULL) {
6094 								ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6095 							} else {
6096 								ZVAL_STRINGL(&new_val, "''", sizeof("''")-1);
6097 							}
6098 						}
6099 						else {
6100 							zend_string *str;
6101 							/* PostgreSQL ignores \0 */
6102 							str = zend_string_alloc(Z_STRLEN_P(val) * 2, 0);
6103 							/* better to use PGSQLescapeLiteral since PGescapeStringConn does not handle special \ */
6104 							ZSTR_LEN(str) = PQescapeStringConn(pg_link, ZSTR_VAL(str), Z_STRVAL_P(val), Z_STRLEN_P(val), NULL);
6105 							str = zend_string_truncate(str, ZSTR_LEN(str), 0);
6106 							ZVAL_NEW_STR(&new_val, str);
6107 							php_pgsql_add_quotes(&new_val, 1);
6108 						}
6109 						break;
6110 
6111 					case IS_LONG:
6112 						ZVAL_LONG(&new_val, Z_LVAL_P(val));
6113 						convert_to_string_ex(&new_val);
6114 						break;
6115 
6116 					case IS_DOUBLE:
6117 						ZVAL_DOUBLE(&new_val, Z_DVAL_P(val));
6118 						convert_to_string_ex(&new_val);
6119 						break;
6120 
6121 					case IS_NULL:
6122 						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6123 						break;
6124 
6125 					default:
6126 						err = 1;
6127 				}
6128 				PGSQL_CONV_CHECK_IGNORE();
6129 				if (err) {
6130 					php_error_docref(NULL, E_NOTICE, "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6131 				}
6132 				break;
6133 
6134 			case PG_UNIX_TIME:
6135 			case PG_UNIX_TIME_INTERVAL:
6136 				/* these are the actallay a integer */
6137 				switch (Z_TYPE_P(val)) {
6138 					case IS_STRING:
6139 						if (Z_STRLEN_P(val) == 0) {
6140 							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6141 						}
6142 						else {
6143 							/* better regex? */
6144 							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), "^[0-9]+$", sizeof("^[0-9]+$")-1, 0) == FAILURE) {
6145 								err = 1;
6146 							}
6147 							else {
6148 								ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
6149 								convert_to_long_ex(&new_val);
6150 							}
6151 						}
6152 						break;
6153 
6154 					case IS_DOUBLE:
6155 						ZVAL_DOUBLE(&new_val, Z_DVAL_P(val));
6156 						convert_to_long_ex(&new_val);
6157 						break;
6158 
6159 					case IS_LONG:
6160 						ZVAL_LONG(&new_val, Z_LVAL_P(val));
6161 						break;
6162 
6163 					case IS_NULL:
6164 						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6165 						break;
6166 
6167 					default:
6168 						err = 1;
6169 				}
6170 				PGSQL_CONV_CHECK_IGNORE();
6171 				if (err) {
6172 					php_error_docref(NULL, E_NOTICE, "Expects NULL, string, long or double value for '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6173 				}
6174 				break;
6175 
6176 			case PG_CIDR:
6177 			case PG_INET:
6178 				switch (Z_TYPE_P(val)) {
6179 					case IS_STRING:
6180 						if (Z_STRLEN_P(val) == 0) {
6181 							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6182 						}
6183 						else {
6184 #define REGEX0 "^((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])(\\/[0-9]{1,3})?$"
6185 #define REGEX1 "^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(\\/[0-9]{1,3})?$"
6186 							/* The inet type holds an IPv4 or IPv6 host address, and optionally its subnet, all in one field. See more in the doc.
6187 							 	The regex might still be not perfect, but catches the most of IP variants. We might decide to remove the regex
6188 								at all though and let the server side to handle it.*/
6189 							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), REGEX0, sizeof(REGEX0)-1, 0) == FAILURE
6190 								&& php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), REGEX1, sizeof(REGEX1)-1, 0) == FAILURE) {
6191 								err = 1;
6192 							}
6193 							else {
6194 								ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
6195 								php_pgsql_add_quotes(&new_val, 1);
6196 							}
6197 #undef REGEX0
6198 #undef REGEX1
6199 						}
6200 						break;
6201 
6202 					case IS_NULL:
6203 						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6204 						break;
6205 
6206 					default:
6207 						err = 1;
6208 				}
6209 				PGSQL_CONV_CHECK_IGNORE();
6210 				if (err) {
6211 					php_error_docref(NULL, E_NOTICE, "Expects NULL or IPv4 or IPv6 address string for '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6212 				}
6213 				break;
6214 
6215 			case PG_TIME_WITH_TIMEZONE:
6216 			case PG_TIMESTAMP:
6217 			case PG_TIMESTAMP_WITH_TIMEZONE:
6218 				switch(Z_TYPE_P(val)) {
6219 					case IS_STRING:
6220 						if (Z_STRLEN_P(val) == 0) {
6221 							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6222 						} else if (!strcasecmp(Z_STRVAL_P(val), "now()")) {
6223 							ZVAL_STRINGL(&new_val, "NOW()", sizeof("NOW()")-1);
6224 						} else {
6225 #define REGEX0 "^([0-9]{4}[/-][0-9]{1,2}[/-][0-9]{1,2})(([ \\t]+|T)(([0-9]{1,2}:[0-9]{1,2}){1}(:[0-9]{1,2}){0,1}(\\.[0-9]+){0,1}([ \\t]*([+-][0-9]{1,4}(:[0-9]{1,2}){0,1}|[-a-zA-Z_/+]{1,50})){0,1})){0,1}$"
6226 							/* better regex? */
6227 							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), REGEX0, sizeof(REGEX0)-1, 1) == FAILURE) {
6228 								err = 1;
6229 							} else {
6230 								ZVAL_STRING(&new_val, Z_STRVAL_P(val));
6231 								php_pgsql_add_quotes(&new_val, 1);
6232 							}
6233 #undef REGEX0
6234 						}
6235 						break;
6236 
6237 					case IS_NULL:
6238 						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6239 						break;
6240 
6241 					default:
6242 						err = 1;
6243 				}
6244 				PGSQL_CONV_CHECK_IGNORE();
6245 				if (err) {
6246 					php_error_docref(NULL, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6247 				}
6248 				break;
6249 
6250 			case PG_DATE:
6251 				switch(Z_TYPE_P(val)) {
6252 					case IS_STRING:
6253 						if (Z_STRLEN_P(val) == 0) {
6254 							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6255 						}
6256 						else {
6257 #define REGEX0 "^([0-9]{4}[/-][0-9]{1,2}[/-][0-9]{1,2})$"
6258 							/* FIXME: better regex must be used */
6259 							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), REGEX0, sizeof(REGEX0)-1, 1) == FAILURE) {
6260 								err = 1;
6261 							}
6262 							else {
6263 								ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
6264 								php_pgsql_add_quotes(&new_val, 1);
6265 							}
6266 #undef REGEX0
6267 						}
6268 						break;
6269 
6270 					case IS_NULL:
6271 						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6272 						break;
6273 
6274 					default:
6275 						err = 1;
6276 				}
6277 				PGSQL_CONV_CHECK_IGNORE();
6278 				if (err) {
6279 					php_error_docref(NULL, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6280 				}
6281 				break;
6282 
6283 			case PG_TIME:
6284 				switch(Z_TYPE_P(val)) {
6285 					case IS_STRING:
6286 						if (Z_STRLEN_P(val) == 0) {
6287 							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6288 						}
6289 						else {
6290 #define REGEX0 "^(([0-9]{1,2}:[0-9]{1,2}){1}(:[0-9]{1,2}){0,1}){0,1}$"
6291 							/* FIXME: better regex must be used */
6292 							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), REGEX0, sizeof(REGEX0)-1, 1) == FAILURE) {
6293 								err = 1;
6294 							}
6295 							else {
6296 								ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
6297 								php_pgsql_add_quotes(&new_val, 1);
6298 							}
6299 #undef REGEX0
6300 						}
6301 						break;
6302 
6303 					case IS_NULL:
6304 						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6305 						break;
6306 
6307 					default:
6308 						err = 1;
6309 				}
6310 				PGSQL_CONV_CHECK_IGNORE();
6311 				if (err) {
6312 					php_error_docref(NULL, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6313 				}
6314 				break;
6315 
6316 			case PG_INTERVAL:
6317 				switch(Z_TYPE_P(val)) {
6318 					case IS_STRING:
6319 						if (Z_STRLEN_P(val) == 0) {
6320 							ZVAL_STRING(&new_val, "NULL");
6321 						}
6322 						else {
6323 
6324 							/* From the Postgres docs:
6325 
6326 							   interval values can be written with the following syntax:
6327 							   [@] quantity unit [quantity unit...] [direction]
6328 
6329 							   Where: quantity is a number (possibly signed); unit is second, minute, hour,
6330 							   day, week, month, year, decade, century, millennium, or abbreviations or
6331 							   plurals of these units [note not *all* abbreviations] ; direction can be
6332 							   ago or empty. The at sign (@) is optional noise.
6333 
6334 							   ...
6335 
6336 							   Quantities of days, hours, minutes, and seconds can be specified without explicit
6337 							   unit markings. For example, '1 12:59:10' is read the same as '1 day 12 hours 59 min 10
6338 							   sec'.
6339 							*/
6340 #define REGEX0 \
6341 	"^(@?[ \\t]+)?(" \
6342 	/* Textual time units and their abbreviations: */ \
6343 	"(([-+]?[ \\t]+)?" \
6344 	"[0-9]+(\\.[0-9]*)?[ \\t]*" \
6345 	"(millenniums|millennia|millennium|mil|mils|" \
6346 	"centuries|century|cent|c|" \
6347 	"decades|decade|dec|decs|" \
6348 	"years|year|y|" \
6349 	"months|month|mon|" \
6350 	"weeks|week|w|" \
6351 	"days|day|d|" \
6352 	"hours|hour|hr|hrs|h|" \
6353 	"minutes|minute|mins|min|m|" \
6354 	"seconds|second|secs|sec|s))+|" \
6355 	/* Textual time units plus (dd)* hh[:mm[:ss]] */ \
6356 	"((([-+]?[ \\t]+)?" \
6357 	"[0-9]+(\\.[0-9]*)?[ \\t]*" \
6358 	"(millenniums|millennia|millennium|mil|mils|" \
6359 	"centuries|century|cent|c|" \
6360 	"decades|decade|dec|decs|" \
6361 	"years|year|y|" \
6362 	"months|month|mon|" \
6363 	"weeks|week|w|" \
6364 	"days|day|d))+" \
6365 	"([-+]?[ \\t]+" \
6366 	"([0-9]+[ \\t]+)+"				 /* dd */ \
6367 	"(([0-9]{1,2}:){0,2}[0-9]{0,2})" /* hh:[mm:[ss]] */ \
6368 	")?))" \
6369 	"([ \\t]+ago)?$"
6370 
6371 							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), REGEX0, sizeof(REGEX0)-1, 1) == FAILURE) {
6372 								err = 1;
6373 							}
6374 							else {
6375 								ZVAL_STRING(&new_val, Z_STRVAL_P(val));
6376 								php_pgsql_add_quotes(&new_val, 1);
6377 							}
6378 #undef REGEX0
6379 						}
6380 						break;
6381 
6382 					case IS_NULL:
6383 						ZVAL_STRING(&new_val, "NULL");
6384 						break;
6385 
6386 					default:
6387 						err = 1;
6388 				}
6389 				PGSQL_CONV_CHECK_IGNORE();
6390 				if (err) {
6391 					php_error_docref(NULL, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6392 				}
6393 				break;
6394 #ifdef HAVE_PQESCAPE
6395 			case PG_BYTEA:
6396 				switch (Z_TYPE_P(val)) {
6397 					case IS_STRING:
6398 						if (Z_STRLEN_P(val) == 0) {
6399 							ZVAL_STRING(&new_val, "NULL");
6400 						}
6401 						else {
6402 							unsigned char *tmp;
6403 							size_t to_len;
6404 							smart_str s = {0};
6405 #ifdef HAVE_PQESCAPE_BYTEA_CONN
6406 							tmp = PQescapeByteaConn(pg_link, (unsigned char *)Z_STRVAL_P(val), Z_STRLEN_P(val), &to_len);
6407 #else
6408 							tmp = PQescapeBytea(Z_STRVAL_P(val), (unsigned char *)Z_STRLEN_P(val), &to_len);
6409 #endif
6410 							ZVAL_STRINGL(&new_val, (char *)tmp, to_len - 1); /* PQescapeBytea's to_len includes additional '\0' */
6411 							PQfreemem(tmp);
6412 							php_pgsql_add_quotes(&new_val, 1);
6413 							smart_str_appendl(&s, Z_STRVAL(new_val), Z_STRLEN(new_val));
6414 							smart_str_0(&s);
6415 							zval_ptr_dtor(&new_val);
6416 							ZVAL_NEW_STR(&new_val, s.s);
6417 						}
6418 						break;
6419 
6420 					case IS_LONG:
6421 						ZVAL_LONG(&new_val, Z_LVAL_P(val));
6422 						convert_to_string_ex(&new_val);
6423 						break;
6424 
6425 					case IS_DOUBLE:
6426 						ZVAL_DOUBLE(&new_val, Z_DVAL_P(val));
6427 						convert_to_string_ex(&new_val);
6428 						break;
6429 
6430 					case IS_NULL:
6431 						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6432 						break;
6433 
6434 					default:
6435 						err = 1;
6436 				}
6437 				PGSQL_CONV_CHECK_IGNORE();
6438 				if (err) {
6439 					php_error_docref(NULL, E_NOTICE, "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6440 				}
6441 				break;
6442 
6443 #endif
6444 			case PG_MACADDR:
6445 				switch(Z_TYPE_P(val)) {
6446 					case IS_STRING:
6447 						if (Z_STRLEN_P(val) == 0) {
6448 							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6449 						}
6450 						else {
6451 #define REGEX0 "^([0-9a-f]{2,2}:){5,5}[0-9a-f]{2,2}$"
6452 							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), REGEX0, sizeof(REGEX0)-1, 1) == FAILURE) {
6453 								err = 1;
6454 							}
6455 							else {
6456 								ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
6457 								php_pgsql_add_quotes(&new_val, 1);
6458 							}
6459 #undef REGEX0
6460 						}
6461 						break;
6462 
6463 					case IS_NULL:
6464 						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6465 						break;
6466 
6467 					default:
6468 						err = 1;
6469 				}
6470 				PGSQL_CONV_CHECK_IGNORE();
6471 				if (err) {
6472 					php_error_docref(NULL, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6473 				}
6474 				break;
6475 
6476 			default:
6477 				/* should not happen */
6478 				php_error_docref(NULL, E_NOTICE, "Unknown or system data type '%s' for '%s'. Report error", Z_STRVAL_P(type), ZSTR_VAL(field));
6479 				err = 1;
6480 				break;
6481 		} /* switch */
6482 
6483 		if (err) {
6484 			zval_ptr_dtor(&new_val);
6485 			break; /* break out for() */
6486 		}
6487 		/* If field is NULL and HAS DEFAULT, should be skipped */
6488 		if (!skip_field) {
6489 			if (_php_pgsql_detect_identifier_escape(ZSTR_VAL(field), ZSTR_LEN(field)) == SUCCESS) {
6490 				zend_hash_update(Z_ARRVAL_P(result), field, &new_val);
6491 			} else {
6492 				char *escaped = PGSQLescapeIdentifier(pg_link, ZSTR_VAL(field), ZSTR_LEN(field));
6493 				add_assoc_zval(result, escaped, &new_val);
6494 				PGSQLfree(escaped);
6495 			}
6496 		}
6497 	} ZEND_HASH_FOREACH_END(); /* for */
6498 
6499 	zval_ptr_dtor(&meta);
6500 
6501 	if (err) {
6502 		/* shouldn't destroy & free zval here */
6503 		return FAILURE;
6504 	}
6505 	return SUCCESS;
6506 }
6507 /* }}} */
6508 
6509 /* {{{ proto array pg_convert(resource db, string table, array values[, int options])
6510    Check and convert values for PostgreSQL SQL statement */
6511 PHP_FUNCTION(pg_convert)
6512 {
6513 	zval *pgsql_link, *values;
6514 	char *table_name;
6515 	size_t table_name_len;
6516 	zend_ulong option = 0;
6517 	PGconn *pg_link;
6518 
6519 	if (zend_parse_parameters(ZEND_NUM_ARGS(),
6520 							  "rsa|l", &pgsql_link, &table_name, &table_name_len, &values, &option) == FAILURE) {
6521 		return;
6522 	}
6523 	if (option & ~PGSQL_CONV_OPTS) {
6524 		php_error_docref(NULL, E_WARNING, "Invalid option is specified");
6525 		RETURN_FALSE;
6526 	}
6527 	if (!table_name_len) {
6528 		php_error_docref(NULL, E_NOTICE, "Table name is invalid");
6529 		RETURN_FALSE;
6530 	}
6531 
6532 	if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
6533 		RETURN_FALSE;
6534 	}
6535 
6536 	if (php_pgsql_flush_query(pg_link)) {
6537 		php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
6538 	}
6539 	array_init(return_value);
6540 	if (php_pgsql_convert(pg_link, table_name, values, return_value, option) == FAILURE) {
6541 		zend_array_destroy(Z_ARR_P(return_value));
6542 		RETURN_FALSE;
6543 	}
6544 }
6545 /* }}} */
6546 
6547 static int do_exec(smart_str *querystr, ExecStatusType expect, PGconn *pg_link, zend_ulong opt) /* {{{ */
6548 {
6549 	if (opt & PGSQL_DML_ASYNC) {
6550 		if (PQsendQuery(pg_link, ZSTR_VAL(querystr->s))) {
6551 			return 0;
6552 		}
6553 	}
6554 	else {
6555 		PGresult *pg_result;
6556 
6557 		pg_result = PQexec(pg_link, ZSTR_VAL(querystr->s));
6558 		if (PQresultStatus(pg_result) == expect) {
6559 			PQclear(pg_result);
6560 			return 0;
6561 		} else {
6562 			php_error_docref(NULL, E_WARNING, "%s", PQresultErrorMessage(pg_result));
6563 			PQclear(pg_result);
6564 		}
6565 	}
6566 
6567 	return -1;
6568 }
6569 /* }}} */
6570 
6571 static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const char *table) /* {{{ */
6572 {
6573 	char *table_copy, *escaped, *tmp;
6574 	const char *token;
6575 	size_t len;
6576 
6577 	/* schame.table should be "schame"."table" */
6578 	table_copy = estrdup(table);
6579 	token = php_strtok_r(table_copy, ".", &tmp);
6580 	if (token == NULL) {
6581 		token = table;
6582 	}
6583 	len = strlen(token);
6584 	if (_php_pgsql_detect_identifier_escape(token, len) == SUCCESS) {
6585 		smart_str_appendl(querystr, token, len);
6586 	} else {
6587 		escaped = PGSQLescapeIdentifier(pg_link, token, len);
6588 		smart_str_appends(querystr, escaped);
6589 		PGSQLfree(escaped);
6590 	}
6591 	if (tmp && *tmp) {
6592 		len = strlen(tmp);
6593 		/* "schema"."table" format */
6594 		if (_php_pgsql_detect_identifier_escape(tmp, len) == SUCCESS) {
6595 			smart_str_appendc(querystr, '.');
6596 			smart_str_appendl(querystr, tmp, len);
6597 		} else {
6598 			escaped = PGSQLescapeIdentifier(pg_link, tmp, len);
6599 			smart_str_appendc(querystr, '.');
6600 			smart_str_appends(querystr, escaped);
6601 			PGSQLfree(escaped);
6602 		}
6603 	}
6604 	efree(table_copy);
6605 }
6606 /* }}} */
6607 
6608 /* {{{ php_pgsql_insert
6609  */
6610 PHP_PGSQL_API int php_pgsql_insert(PGconn *pg_link, const char *table, zval *var_array, zend_ulong opt, zend_string **sql)
6611 {
6612 	zval *val, converted;
6613 	char buf[256];
6614 	char *tmp;
6615 	smart_str querystr = {0};
6616 	int ret = FAILURE;
6617 	zend_string *fld;
6618 
6619 	assert(pg_link != NULL);
6620 	assert(table != NULL);
6621 	assert(Z_TYPE_P(var_array) == IS_ARRAY);
6622 
6623 	ZVAL_UNDEF(&converted);
6624 	if (zend_hash_num_elements(Z_ARRVAL_P(var_array)) == 0) {
6625 		smart_str_appends(&querystr, "INSERT INTO ");
6626 		build_tablename(&querystr, pg_link, table);
6627 		smart_str_appends(&querystr, " DEFAULT VALUES");
6628 
6629 		goto no_values;
6630 	}
6631 
6632 	/* convert input array if needed */
6633 	if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) {
6634 		array_init(&converted);
6635 		if (php_pgsql_convert(pg_link, table, var_array, &converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
6636 			goto cleanup;
6637 		}
6638 		var_array = &converted;
6639 	}
6640 
6641 	smart_str_appends(&querystr, "INSERT INTO ");
6642 	build_tablename(&querystr, pg_link, table);
6643 	smart_str_appends(&querystr, " (");
6644 
6645 	ZEND_HASH_FOREACH_STR_KEY(Z_ARRVAL_P(var_array), fld) {
6646 		if (fld == NULL) {
6647 			php_error_docref(NULL, E_NOTICE, "Expects associative array for values to be inserted");
6648 			goto cleanup;
6649 		}
6650 		if (opt & PGSQL_DML_ESCAPE) {
6651 			tmp = PGSQLescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1);
6652 			smart_str_appends(&querystr, tmp);
6653 			PGSQLfree(tmp);
6654 		} else {
6655 			smart_str_appendl(&querystr, ZSTR_VAL(fld), ZSTR_LEN(fld));
6656 		}
6657 		smart_str_appendc(&querystr, ',');
6658 	} ZEND_HASH_FOREACH_END();
6659 	ZSTR_LEN(querystr.s)--;
6660 	smart_str_appends(&querystr, ") VALUES (");
6661 
6662 	/* make values string */
6663 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(var_array), val) {
6664 		/* we can avoid the key_type check here, because we tested it in the other loop */
6665 		switch (Z_TYPE_P(val)) {
6666 			case IS_STRING:
6667 				if (opt & PGSQL_DML_ESCAPE) {
6668 					size_t new_len;
6669 					char *tmp;
6670 					tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1);
6671 					new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL);
6672 					smart_str_appendc(&querystr, '\'');
6673 					smart_str_appendl(&querystr, tmp, new_len);
6674 					smart_str_appendc(&querystr, '\'');
6675 					efree(tmp);
6676 				} else {
6677 					smart_str_appendl(&querystr, Z_STRVAL_P(val), Z_STRLEN_P(val));
6678 				}
6679 				break;
6680 			case IS_LONG:
6681 				smart_str_append_long(&querystr, Z_LVAL_P(val));
6682 				break;
6683 			case IS_DOUBLE:
6684 				smart_str_appendl(&querystr, buf, snprintf(buf, sizeof(buf), "%F", Z_DVAL_P(val)));
6685 				break;
6686 			case IS_NULL:
6687 				smart_str_appendl(&querystr, "NULL", sizeof("NULL")-1);
6688 				break;
6689 			default:
6690 				php_error_docref(NULL, E_WARNING, "Expects scaler values. type = %d", Z_TYPE_P(val));
6691 				goto cleanup;
6692 				break;
6693 		}
6694 		smart_str_appendc(&querystr, ',');
6695 	} ZEND_HASH_FOREACH_END();
6696 	/* Remove the trailing "," */
6697 	ZSTR_LEN(querystr.s)--;
6698 	smart_str_appends(&querystr, ");");
6699 
6700 no_values:
6701 
6702 	smart_str_0(&querystr);
6703 
6704 	if ((opt & (PGSQL_DML_EXEC|PGSQL_DML_ASYNC)) &&
6705 		do_exec(&querystr, PGRES_COMMAND_OK, pg_link, (opt & PGSQL_CONV_OPTS)) == 0) {
6706 		ret = SUCCESS;
6707 	}
6708 	else if (opt & PGSQL_DML_STRING) {
6709 		ret = SUCCESS;
6710 	}
6711 
6712 cleanup:
6713 	zval_ptr_dtor(&converted);
6714 	if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
6715 		*sql = querystr.s;
6716 	}
6717 	else {
6718 		smart_str_free(&querystr);
6719 	}
6720 	return ret;
6721 }
6722 /* }}} */
6723 
6724 /* {{{ proto mixed pg_insert(resource db, string table, array values[, int options])
6725    Insert values (filed=>value) to table */
6726 PHP_FUNCTION(pg_insert)
6727 {
6728 	zval *pgsql_link, *values;
6729 	char *table;
6730 	size_t table_len;
6731 	zend_ulong option = PGSQL_DML_EXEC, return_sql;
6732 	PGconn *pg_link;
6733 	PGresult *pg_result;
6734 	ExecStatusType status;
6735 	zend_string *sql = NULL;
6736 	int argc = ZEND_NUM_ARGS();
6737 
6738 	if (zend_parse_parameters(argc, "rsa|l",
6739 							  &pgsql_link, &table, &table_len, &values, &option) == FAILURE) {
6740 		return;
6741 	}
6742 	if (option & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
6743 		php_error_docref(NULL, E_WARNING, "Invalid option is specified");
6744 		RETURN_FALSE;
6745 	}
6746 
6747 	if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
6748 		RETURN_FALSE;
6749 	}
6750 
6751 	if (php_pgsql_flush_query(pg_link)) {
6752 		php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
6753 	}
6754 	return_sql = option & PGSQL_DML_STRING;
6755 	if (option & PGSQL_DML_EXEC) {
6756 		/* return resource when executed */
6757 		option = option & ~PGSQL_DML_EXEC;
6758 		if (php_pgsql_insert(pg_link, table, values, option|PGSQL_DML_STRING, &sql) == FAILURE) {
6759 			RETURN_FALSE;
6760 		}
6761 		pg_result = PQexec(pg_link, ZSTR_VAL(sql));
6762 		if ((PGG(auto_reset_persistent) & 2) && PQstatus(pg_link) != CONNECTION_OK) {
6763 			PQclear(pg_result);
6764 			PQreset(pg_link);
6765 			pg_result = PQexec(pg_link, ZSTR_VAL(sql));
6766 		}
6767 		efree(sql);
6768 
6769 		if (pg_result) {
6770 			status = PQresultStatus(pg_result);
6771 		} else {
6772 			status = (ExecStatusType) PQstatus(pg_link);
6773 		}
6774 
6775 		switch (status) {
6776 			case PGRES_EMPTY_QUERY:
6777 			case PGRES_BAD_RESPONSE:
6778 			case PGRES_NONFATAL_ERROR:
6779 			case PGRES_FATAL_ERROR:
6780 				PHP_PQ_ERROR("Query failed: %s", pg_link);
6781 				PQclear(pg_result);
6782 				RETURN_FALSE;
6783 				break;
6784 			case PGRES_COMMAND_OK: /* successful command that did not return rows */
6785 			default:
6786 				if (pg_result) {
6787 					pgsql_result_handle *pgsql_handle = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
6788 					pgsql_handle->conn = pg_link;
6789 					pgsql_handle->result = pg_result;
6790 					pgsql_handle->row = 0;
6791 					RETURN_RES(zend_register_resource(pgsql_handle, le_result));
6792 				} else {
6793 					PQclear(pg_result);
6794 					RETURN_FALSE;
6795 				}
6796 			break;
6797 		}
6798 	} else if (php_pgsql_insert(pg_link, table, values, option, &sql) == FAILURE) {
6799 		RETURN_FALSE;
6800 	}
6801 	if (return_sql) {
6802 		RETURN_STR(sql);
6803 		return;
6804 	}
6805 	RETURN_TRUE;
6806 }
6807 /* }}} */
6808 
6809 static inline int build_assignment_string(PGconn *pg_link, smart_str *querystr, HashTable *ht, int where_cond, const char *pad, int pad_len, zend_ulong opt) /* {{{ */
6810 {
6811 	zend_string *fld;
6812 	zval *val;
6813 
6814 	ZEND_HASH_FOREACH_STR_KEY_VAL(ht, fld, val) {
6815 		if (fld == NULL) {
6816 			php_error_docref(NULL, E_NOTICE, "Expects associative array for values to be inserted");
6817 			return -1;
6818 		}
6819 		if (opt & PGSQL_DML_ESCAPE) {
6820 			char *tmp = PGSQLescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1);
6821 			smart_str_appends(querystr, tmp);
6822 			PGSQLfree(tmp);
6823 		} else {
6824 			smart_str_appendl(querystr, ZSTR_VAL(fld), ZSTR_LEN(fld));
6825 		}
6826 		if (where_cond && (Z_TYPE_P(val) == IS_TRUE || Z_TYPE_P(val) == IS_FALSE || (Z_TYPE_P(val) == IS_STRING && !strcmp(Z_STRVAL_P(val), "NULL")))) {
6827 			smart_str_appends(querystr, " IS ");
6828 		} else {
6829 			smart_str_appendc(querystr, '=');
6830 		}
6831 
6832 		switch (Z_TYPE_P(val)) {
6833 			case IS_STRING:
6834 				if (opt & PGSQL_DML_ESCAPE) {
6835 					char *tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1);
6836 					size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL);
6837 					smart_str_appendc(querystr, '\'');
6838 					smart_str_appendl(querystr, tmp, new_len);
6839 					smart_str_appendc(querystr, '\'');
6840 					efree(tmp);
6841 				} else {
6842 					smart_str_appendl(querystr, Z_STRVAL_P(val), Z_STRLEN_P(val));
6843 				}
6844 				break;
6845 			case IS_LONG:
6846 				smart_str_append_long(querystr, Z_LVAL_P(val));
6847 				break;
6848 			case IS_DOUBLE: {
6849 				char buf[256];
6850 				smart_str_appendl(querystr, buf, MIN(snprintf(buf, sizeof(buf), "%F", Z_DVAL_P(val)), sizeof(buf) - 1));
6851 				}
6852 				break;
6853 			case IS_NULL:
6854 				smart_str_appendl(querystr, "NULL", sizeof("NULL")-1);
6855 				break;
6856 			default:
6857 				php_error_docref(NULL, E_WARNING, "Expects scaler values. type=%d", Z_TYPE_P(val));
6858 				return -1;
6859 		}
6860 		smart_str_appendl(querystr, pad, pad_len);
6861 	} ZEND_HASH_FOREACH_END();
6862 	if (querystr->s) {
6863 		ZSTR_LEN(querystr->s) -= pad_len;
6864 	}
6865 
6866 	return 0;
6867 }
6868 /* }}} */
6869 
6870 /* {{{ php_pgsql_update
6871  */
6872 PHP_PGSQL_API int php_pgsql_update(PGconn *pg_link, const char *table, zval *var_array, zval *ids_array, zend_ulong opt, zend_string **sql)
6873 {
6874 	zval var_converted, ids_converted;
6875 	smart_str querystr = {0};
6876 	int ret = FAILURE;
6877 
6878 	assert(pg_link != NULL);
6879 	assert(table != NULL);
6880 	assert(Z_TYPE_P(var_array) == IS_ARRAY);
6881 	assert(Z_TYPE_P(ids_array) == IS_ARRAY);
6882 	assert(!(opt & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)));
6883 
6884 	if (zend_hash_num_elements(Z_ARRVAL_P(var_array)) == 0
6885 			|| zend_hash_num_elements(Z_ARRVAL_P(ids_array)) == 0) {
6886 		return FAILURE;
6887 	}
6888 
6889 	ZVAL_UNDEF(&var_converted);
6890 	ZVAL_UNDEF(&ids_converted);
6891 	if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) {
6892 		array_init(&var_converted);
6893 		if (php_pgsql_convert(pg_link, table, var_array, &var_converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
6894 			goto cleanup;
6895 		}
6896 		var_array = &var_converted;
6897 		array_init(&ids_converted);
6898 		if (php_pgsql_convert(pg_link, table, ids_array, &ids_converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
6899 			goto cleanup;
6900 		}
6901 		ids_array = &ids_converted;
6902 	}
6903 
6904 	smart_str_appends(&querystr, "UPDATE ");
6905 	build_tablename(&querystr, pg_link, table);
6906 	smart_str_appends(&querystr, " SET ");
6907 
6908 	if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(var_array), 0, ",", 1, opt))
6909 		goto cleanup;
6910 
6911 	smart_str_appends(&querystr, " WHERE ");
6912 
6913 	if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt))
6914 		goto cleanup;
6915 
6916 	smart_str_appendc(&querystr, ';');
6917 	smart_str_0(&querystr);
6918 
6919 	if ((opt & PGSQL_DML_EXEC) && do_exec(&querystr, PGRES_COMMAND_OK, pg_link, opt) == 0) {
6920 		ret = SUCCESS;
6921 	} else if (opt & PGSQL_DML_STRING) {
6922 		ret = SUCCESS;
6923 	}
6924 
6925 cleanup:
6926 	zval_ptr_dtor(&var_converted);
6927 	zval_ptr_dtor(&ids_converted);
6928 	if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
6929 		*sql = querystr.s;
6930 	}
6931 	else {
6932 		smart_str_free(&querystr);
6933 	}
6934 	return ret;
6935 }
6936 /* }}} */
6937 
6938 /* {{{ proto mixed pg_update(resource db, string table, array fields, array ids[, int options])
6939    Update table using values (field=>value) and ids (id=>value) */
6940 PHP_FUNCTION(pg_update)
6941 {
6942 	zval *pgsql_link, *values, *ids;
6943 	char *table;
6944 	size_t table_len;
6945 	zend_ulong option =  PGSQL_DML_EXEC;
6946 	PGconn *pg_link;
6947 	zend_string *sql = NULL;
6948 	int argc = ZEND_NUM_ARGS();
6949 
6950 	if (zend_parse_parameters(argc, "rsaa|l",
6951 							  &pgsql_link, &table, &table_len, &values, &ids, &option) == FAILURE) {
6952 		return;
6953 	}
6954 	if (option & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
6955 		php_error_docref(NULL, E_WARNING, "Invalid option is specified");
6956 		RETURN_FALSE;
6957 	}
6958 
6959 	if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
6960 		RETURN_FALSE;
6961 	}
6962 
6963 	if (php_pgsql_flush_query(pg_link)) {
6964 		php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
6965 	}
6966 	if (php_pgsql_update(pg_link, table, values, ids, option, &sql) == FAILURE) {
6967 		RETURN_FALSE;
6968 	}
6969 	if (option & PGSQL_DML_STRING) {
6970 		RETURN_STR(sql);
6971 	}
6972 	RETURN_TRUE;
6973 }
6974 /* }}} */
6975 
6976 /* {{{ php_pgsql_delete
6977  */
6978 PHP_PGSQL_API int php_pgsql_delete(PGconn *pg_link, const char *table, zval *ids_array, zend_ulong opt, zend_string **sql)
6979 {
6980 	zval ids_converted;
6981 	smart_str querystr = {0};
6982 	int ret = FAILURE;
6983 
6984 	assert(pg_link != NULL);
6985 	assert(table != NULL);
6986 	assert(Z_TYPE_P(ids_array) == IS_ARRAY);
6987 	assert(!(opt & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)));
6988 
6989 	if (zend_hash_num_elements(Z_ARRVAL_P(ids_array)) == 0) {
6990 		return FAILURE;
6991 	}
6992 
6993 	ZVAL_UNDEF(&ids_converted);
6994 	if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) {
6995 		array_init(&ids_converted);
6996 		if (php_pgsql_convert(pg_link, table, ids_array, &ids_converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
6997 			goto cleanup;
6998 		}
6999 		ids_array = &ids_converted;
7000 	}
7001 
7002 	smart_str_appends(&querystr, "DELETE FROM ");
7003 	build_tablename(&querystr, pg_link, table);
7004 	smart_str_appends(&querystr, " WHERE ");
7005 
7006 	if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt))
7007 		goto cleanup;
7008 
7009 	smart_str_appendc(&querystr, ';');
7010 	smart_str_0(&querystr);
7011 
7012 	if ((opt & PGSQL_DML_EXEC) && do_exec(&querystr, PGRES_COMMAND_OK, pg_link, opt) == 0) {
7013 		ret = SUCCESS;
7014 	} else if (opt & PGSQL_DML_STRING) {
7015 		ret = SUCCESS;
7016 	}
7017 
7018 cleanup:
7019 	zval_ptr_dtor(&ids_converted);
7020 	if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
7021 		*sql = querystr.s;
7022 	}
7023 	else {
7024 		smart_str_free(&querystr);
7025 	}
7026 	return ret;
7027 }
7028 /* }}} */
7029 
7030 /* {{{ proto mixed pg_delete(resource db, string table, array ids[, int options])
7031    Delete records has ids (id=>value) */
7032 PHP_FUNCTION(pg_delete)
7033 {
7034 	zval *pgsql_link, *ids;
7035 	char *table;
7036 	size_t table_len;
7037 	zend_ulong option = PGSQL_DML_EXEC;
7038 	PGconn *pg_link;
7039 	zend_string *sql;
7040 	int argc = ZEND_NUM_ARGS();
7041 
7042 	if (zend_parse_parameters(argc, "rsa|l",
7043 							  &pgsql_link, &table, &table_len, &ids, &option) == FAILURE) {
7044 		return;
7045 	}
7046 	if (option & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
7047 		php_error_docref(NULL, E_WARNING, "Invalid option is specified");
7048 		RETURN_FALSE;
7049 	}
7050 
7051 	if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
7052 		RETURN_FALSE;
7053 	}
7054 
7055 	if (php_pgsql_flush_query(pg_link)) {
7056 		php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
7057 	}
7058 	if (php_pgsql_delete(pg_link, table, ids, option, &sql) == FAILURE) {
7059 		RETURN_FALSE;
7060 	}
7061 	if (option & PGSQL_DML_STRING) {
7062 		RETURN_STR(sql);
7063 	}
7064 	RETURN_TRUE;
7065 }
7066 /* }}} */
7067 
7068 /* {{{ php_pgsql_result2array
7069  */
7070 PHP_PGSQL_API int php_pgsql_result2array(PGresult *pg_result, zval *ret_array, long result_type)
7071 {
7072 	zval row;
7073 	char *field_name;
7074 	size_t num_fields;
7075 	int pg_numrows, pg_row;
7076 	uint32_t i;
7077 	assert(Z_TYPE_P(ret_array) == IS_ARRAY);
7078 
7079 	if ((pg_numrows = PQntuples(pg_result)) <= 0) {
7080 		return FAILURE;
7081 	}
7082 	for (pg_row = 0; pg_row < pg_numrows; pg_row++) {
7083 		array_init(&row);
7084 		for (i = 0, num_fields = PQnfields(pg_result); i < num_fields; i++) {
7085 			field_name = PQfname(pg_result, i);
7086 			if (PQgetisnull(pg_result, pg_row, i)) {
7087 				if (result_type & PGSQL_ASSOC) {
7088 					add_assoc_null(&row, field_name);
7089 				}
7090 				if (result_type & PGSQL_NUM) {
7091 					add_next_index_null(&row);
7092 				}
7093 			} else {
7094 				char *element = PQgetvalue(pg_result, pg_row, i);
7095 				if (element) {
7096 					const size_t element_len = strlen(element);
7097 					if (result_type & PGSQL_ASSOC) {
7098 						add_assoc_stringl(&row, field_name, element, element_len);
7099 					}
7100 					if (result_type & PGSQL_NUM) {
7101 						add_next_index_stringl(&row, element, element_len);
7102 					}
7103 				}
7104 			}
7105 		}
7106 		add_index_zval(ret_array, pg_row, &row);
7107 	}
7108 	return SUCCESS;
7109 }
7110 /* }}} */
7111 
7112 /* {{{ php_pgsql_select
7113  */
7114  PHP_PGSQL_API int php_pgsql_select(PGconn *pg_link, const char *table, zval *ids_array, zval *ret_array, zend_ulong opt, long result_type, zend_string **sql)
7115 {
7116 	zval ids_converted;
7117 	smart_str querystr = {0};
7118 	int ret = FAILURE;
7119 	PGresult *pg_result;
7120 
7121 	assert(pg_link != NULL);
7122 	assert(table != NULL);
7123 	assert(Z_TYPE_P(ids_array) == IS_ARRAY);
7124 	assert(Z_TYPE_P(ret_array) == IS_ARRAY);
7125 	assert(!(opt & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)));
7126 
7127 	if (zend_hash_num_elements(Z_ARRVAL_P(ids_array)) == 0) {
7128 		return FAILURE;
7129 	}
7130 
7131 	ZVAL_UNDEF(&ids_converted);
7132 	if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) {
7133 		array_init(&ids_converted);
7134 		if (php_pgsql_convert(pg_link, table, ids_array, &ids_converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
7135 			goto cleanup;
7136 		}
7137 		ids_array = &ids_converted;
7138 	}
7139 
7140 	smart_str_appends(&querystr, "SELECT * FROM ");
7141 	build_tablename(&querystr, pg_link, table);
7142 	smart_str_appends(&querystr, " WHERE ");
7143 
7144 	if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt))
7145 		goto cleanup;
7146 
7147 	smart_str_appendc(&querystr, ';');
7148 	smart_str_0(&querystr);
7149 
7150 	pg_result = PQexec(pg_link, ZSTR_VAL(querystr.s));
7151 	if (PQresultStatus(pg_result) == PGRES_TUPLES_OK) {
7152 		ret = php_pgsql_result2array(pg_result, ret_array, result_type);
7153 	} else {
7154 		php_error_docref(NULL, E_NOTICE, "Failed to execute '%s'", ZSTR_VAL(querystr.s));
7155 	}
7156 	PQclear(pg_result);
7157 
7158 cleanup:
7159 	zval_ptr_dtor(&ids_converted);
7160 	if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
7161 		*sql = querystr.s;
7162 	}
7163 	else {
7164 		smart_str_free(&querystr);
7165 	}
7166 	return ret;
7167 }
7168 /* }}} */
7169 
7170 /* {{{ proto mixed pg_select(resource db, string table, array ids[, int options [, int result_type])
7171    Select records that has ids (id=>value) */
7172 PHP_FUNCTION(pg_select)
7173 {
7174 	zval *pgsql_link, *ids;
7175 	char *table;
7176 	size_t table_len;
7177 	zend_ulong option = PGSQL_DML_EXEC;
7178 	long result_type = PGSQL_ASSOC;
7179 	PGconn *pg_link;
7180 	zend_string *sql = NULL;
7181 	int argc = ZEND_NUM_ARGS();
7182 
7183 	if (zend_parse_parameters(argc, "rsa|l",
7184 							  &pgsql_link, &table, &table_len, &ids, &option, &result_type) == FAILURE) {
7185 		return;
7186 	}
7187 	if (option & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
7188 		php_error_docref(NULL, E_WARNING, "Invalid option is specified");
7189 		RETURN_FALSE;
7190 	}
7191 	if (!(result_type & PGSQL_BOTH)) {
7192 		php_error_docref(NULL, E_WARNING, "Invalid result type");
7193 		RETURN_FALSE;
7194 	}
7195 
7196 	if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
7197 		RETURN_FALSE;
7198 	}
7199 
7200 	if (php_pgsql_flush_query(pg_link)) {
7201 		php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
7202 	}
7203 	array_init(return_value);
7204 	if (php_pgsql_select(pg_link, table, ids, return_value, option, result_type, &sql) == FAILURE) {
7205 		zval_ptr_dtor(return_value);
7206 		RETURN_FALSE;
7207 	}
7208 	if (option & PGSQL_DML_STRING) {
7209 		zval_ptr_dtor(return_value);
7210 		RETURN_STR(sql);
7211 	}
7212 	return;
7213 }
7214 /* }}} */
7215 
7216 #endif
7217 
7218 /*
7219  * Local variables:
7220  * tab-width: 4
7221  * c-basic-offset: 4
7222  * End:
7223  * vim600: sw=4 ts=4 fdm=marker
7224  * vim<600: sw=4 ts=4
7225  */
7226