xref: /PHP-7.4/ext/pgsql/pgsql.c (revision 55f6895f)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 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 #include "php_pgsql.h"
42 #include "php_globals.h"
43 #include "zend_exceptions.h"
44 
45 #if HAVE_PGSQL
46 
47 #ifndef InvalidOid
48 #define InvalidOid ((Oid) 0)
49 #endif
50 
51 #define PGSQL_ASSOC           1<<0
52 #define PGSQL_NUM             1<<1
53 #define PGSQL_BOTH            (PGSQL_ASSOC|PGSQL_NUM)
54 
55 #define PGSQL_NOTICE_LAST     1  /* Get the last notice */
56 #define PGSQL_NOTICE_ALL      2  /* Get all notices */
57 #define PGSQL_NOTICE_CLEAR    3  /* Remove notices */
58 
59 #define PGSQL_STATUS_LONG     1
60 #define PGSQL_STATUS_STRING   2
61 
62 #define PGSQL_MAX_LENGTH_OF_LONG   30
63 #define PGSQL_MAX_LENGTH_OF_DOUBLE 60
64 
65 #if ZEND_LONG_MAX < UINT_MAX
66 #define PGSQL_RETURN_OID(oid) do { \
67 	if (oid > ZEND_LONG_MAX) { \
68 		smart_str s = {0}; \
69 		smart_str_append_unsigned(&s, oid); \
70 		smart_str_0(&s); \
71 		RETURN_NEW_STR(s.s); \
72 	} \
73 	RETURN_LONG((zend_long)oid); \
74 } while(0)
75 #else
76 #define PGSQL_RETURN_OID(oid) RETURN_LONG((zend_long)oid)
77 #endif
78 
79 #if HAVE_PQSETNONBLOCKING
80 #define PQ_SETNONBLOCKING(pg_link, flag) PQsetnonblocking(pg_link, flag)
81 #else
82 #define PQ_SETNONBLOCKING(pg_link, flag) 0
83 #endif
84 
85 #define CHECK_DEFAULT_LINK(x) if ((x) == NULL) { php_error_docref(NULL, E_WARNING, "No PostgreSQL link opened yet"); RETURN_FALSE; }
86 #define FETCH_DEFAULT_LINK()  PGG(default_link)
87 
88 #ifndef HAVE_PQFREEMEM
89 #define PQfreemem free
90 #endif
91 
92 ZEND_DECLARE_MODULE_GLOBALS(pgsql)
93 static PHP_GINIT_FUNCTION(pgsql);
94 
95 /* {{{ arginfo */
96 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connect, 0, 0, 1)
97 	ZEND_ARG_INFO(0, connection_string)
98 	ZEND_ARG_INFO(0, connect_type)
99 	ZEND_ARG_INFO(0, host)
100 	ZEND_ARG_INFO(0, port)
101 	ZEND_ARG_INFO(0, options)
102 	ZEND_ARG_INFO(0, tty)
103 	ZEND_ARG_INFO(0, database)
104 ZEND_END_ARG_INFO()
105 
106 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_pconnect, 0, 0, 1)
107 	ZEND_ARG_INFO(0, connection_string)
108 	ZEND_ARG_INFO(0, host)
109 	ZEND_ARG_INFO(0, port)
110 	ZEND_ARG_INFO(0, options)
111 	ZEND_ARG_INFO(0, tty)
112 	ZEND_ARG_INFO(0, database)
113 ZEND_END_ARG_INFO()
114 
115 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connect_poll, 0, 0, 0)
116 	ZEND_ARG_INFO(0, connection)
117 ZEND_END_ARG_INFO()
118 
119 #if HAVE_PQPARAMETERSTATUS
120 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_parameter_status, 0, 0, 1)
121 	ZEND_ARG_INFO(0, connection)
122 	ZEND_ARG_INFO(0, param_name)
123 ZEND_END_ARG_INFO()
124 #endif
125 
126 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_close, 0, 0, 0)
127 	ZEND_ARG_INFO(0, connection)
128 ZEND_END_ARG_INFO()
129 
130 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_dbname, 0, 0, 0)
131 	ZEND_ARG_INFO(0, connection)
132 ZEND_END_ARG_INFO()
133 
134 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_last_error, 0, 0, 0)
135 	ZEND_ARG_INFO(0, connection)
136 ZEND_END_ARG_INFO()
137 
138 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_options, 0, 0, 0)
139 	ZEND_ARG_INFO(0, connection)
140 ZEND_END_ARG_INFO()
141 
142 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_port, 0, 0, 0)
143 	ZEND_ARG_INFO(0, connection)
144 ZEND_END_ARG_INFO()
145 
146 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_tty, 0, 0, 0)
147 	ZEND_ARG_INFO(0, connection)
148 ZEND_END_ARG_INFO()
149 
150 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_host, 0, 0, 0)
151 	ZEND_ARG_INFO(0, connection)
152 ZEND_END_ARG_INFO()
153 
154 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_version, 0, 0, 0)
155 	ZEND_ARG_INFO(0, connection)
156 ZEND_END_ARG_INFO()
157 
158 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_ping, 0, 0, 0)
159 	ZEND_ARG_INFO(0, connection)
160 ZEND_END_ARG_INFO()
161 
162 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_query, 0, 0, 0)
163 	ZEND_ARG_INFO(0, connection)
164 	ZEND_ARG_INFO(0, query)
165 ZEND_END_ARG_INFO()
166 
167 #if HAVE_PQEXECPARAMS
168 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_query_params, 0, 0, 0)
169 	ZEND_ARG_INFO(0, connection)
170 	ZEND_ARG_INFO(0, query)
171 	ZEND_ARG_INFO(0, params)
172 ZEND_END_ARG_INFO()
173 #endif
174 
175 #if HAVE_PQPREPARE
176 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_prepare, 0, 0, 0)
177 	ZEND_ARG_INFO(0, connection)
178 	ZEND_ARG_INFO(0, stmtname)
179 	ZEND_ARG_INFO(0, query)
180 ZEND_END_ARG_INFO()
181 #endif
182 
183 #if HAVE_PQEXECPREPARED
184 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_execute, 0, 0, 0)
185 	ZEND_ARG_INFO(0, connection)
186 	ZEND_ARG_INFO(0, stmtname)
187 	ZEND_ARG_INFO(0, params)
188 ZEND_END_ARG_INFO()
189 #endif
190 
191 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_num_rows, 0, 0, 1)
192 	ZEND_ARG_INFO(0, result)
193 ZEND_END_ARG_INFO()
194 
195 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_num_fields, 0, 0, 1)
196 	ZEND_ARG_INFO(0, result)
197 ZEND_END_ARG_INFO()
198 
199 #if HAVE_PQCMDTUPLES
200 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_affected_rows, 0, 0, 1)
201 	ZEND_ARG_INFO(0, result)
202 ZEND_END_ARG_INFO()
203 #endif
204 
205 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_last_notice, 0, 0, 1)
206 	ZEND_ARG_INFO(0, connection)
207 	ZEND_ARG_INFO(0, option)
208 ZEND_END_ARG_INFO()
209 
210 #ifdef HAVE_PQFTABLE
211 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_table, 0, 0, 2)
212 	ZEND_ARG_INFO(0, result)
213 	ZEND_ARG_INFO(0, field_number)
214 	ZEND_ARG_INFO(0, oid_only)
215 ZEND_END_ARG_INFO()
216 #endif
217 
218 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_name, 0, 0, 2)
219 	ZEND_ARG_INFO(0, result)
220 	ZEND_ARG_INFO(0, field_number)
221 ZEND_END_ARG_INFO()
222 
223 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_size, 0, 0, 2)
224 	ZEND_ARG_INFO(0, result)
225 	ZEND_ARG_INFO(0, field_number)
226 ZEND_END_ARG_INFO()
227 
228 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_type, 0, 0, 2)
229 	ZEND_ARG_INFO(0, result)
230 	ZEND_ARG_INFO(0, field_number)
231 ZEND_END_ARG_INFO()
232 
233 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_type_oid, 0, 0, 2)
234 	ZEND_ARG_INFO(0, result)
235 	ZEND_ARG_INFO(0, field_number)
236 ZEND_END_ARG_INFO()
237 
238 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_num, 0, 0, 2)
239 	ZEND_ARG_INFO(0, result)
240 	ZEND_ARG_INFO(0, field_name)
241 ZEND_END_ARG_INFO()
242 
243 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_result, 0, 0, 1)
244 	ZEND_ARG_INFO(0, result)
245 	ZEND_ARG_INFO(0, row_number)
246 	ZEND_ARG_INFO(0, field_name)
247 ZEND_END_ARG_INFO()
248 
249 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_row, 0, 0, 1)
250 	ZEND_ARG_INFO(0, result)
251 	ZEND_ARG_INFO(0, row)
252 	ZEND_ARG_INFO(0, result_type)
253 ZEND_END_ARG_INFO()
254 
255 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_assoc, 0, 0, 1)
256 	ZEND_ARG_INFO(0, result)
257 	ZEND_ARG_INFO(0, row)
258 ZEND_END_ARG_INFO()
259 
260 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_array, 0, 0, 1)
261 	ZEND_ARG_INFO(0, result)
262 	ZEND_ARG_INFO(0, row)
263 	ZEND_ARG_INFO(0, result_type)
264 ZEND_END_ARG_INFO()
265 
266 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_object, 0, 0, 1)
267 	ZEND_ARG_INFO(0, result)
268 	ZEND_ARG_INFO(0, row)
269 	ZEND_ARG_INFO(0, class_name)
270 	ZEND_ARG_INFO(0, l)
271 	ZEND_ARG_INFO(0, ctor_params)
272 ZEND_END_ARG_INFO()
273 
274 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_all, 0, 0, 1)
275 	ZEND_ARG_INFO(0, result)
276 	ZEND_ARG_INFO(0, result_type)
277 ZEND_END_ARG_INFO()
278 
279 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_all_columns, 0, 0, 1)
280 	ZEND_ARG_INFO(0, result)
281 	ZEND_ARG_INFO(0, column_number)
282 ZEND_END_ARG_INFO()
283 
284 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_result_seek, 0, 0, 2)
285 	ZEND_ARG_INFO(0, result)
286 	ZEND_ARG_INFO(0, offset)
287 ZEND_END_ARG_INFO()
288 
289 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_prtlen, 0, 0, 1)
290 	ZEND_ARG_INFO(0, result)
291 	ZEND_ARG_INFO(0, row)
292 	ZEND_ARG_INFO(0, field_name_or_number)
293 ZEND_END_ARG_INFO()
294 
295 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_is_null, 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_free_result, 0, 0, 1)
302 	ZEND_ARG_INFO(0, result)
303 ZEND_END_ARG_INFO()
304 
305 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_last_oid, 0, 0, 1)
306 	ZEND_ARG_INFO(0, result)
307 ZEND_END_ARG_INFO()
308 
309 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_trace, 0, 0, 1)
310 	ZEND_ARG_INFO(0, filename)
311 	ZEND_ARG_INFO(0, mode)
312 	ZEND_ARG_INFO(0, connection)
313 ZEND_END_ARG_INFO()
314 
315 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_untrace, 0, 0, 0)
316 	ZEND_ARG_INFO(0, connection)
317 ZEND_END_ARG_INFO()
318 
319 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_create, 0, 0, 0)
320 	ZEND_ARG_INFO(0, connection)
321 	ZEND_ARG_INFO(0, large_object_id)
322 ZEND_END_ARG_INFO()
323 
324 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_unlink, 0, 0, 0)
325 	ZEND_ARG_INFO(0, connection)
326 	ZEND_ARG_INFO(0, large_object_oid)
327 ZEND_END_ARG_INFO()
328 
329 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_open, 0, 0, 0)
330 	ZEND_ARG_INFO(0, connection)
331 	ZEND_ARG_INFO(0, large_object_oid)
332 	ZEND_ARG_INFO(0, mode)
333 ZEND_END_ARG_INFO()
334 
335 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_close, 0, 0, 1)
336 	ZEND_ARG_INFO(0, large_object)
337 ZEND_END_ARG_INFO()
338 
339 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_read, 0, 0, 1)
340 	ZEND_ARG_INFO(0, large_object)
341 	ZEND_ARG_INFO(0, len)
342 ZEND_END_ARG_INFO()
343 
344 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_write, 0, 0, 2)
345 	ZEND_ARG_INFO(0, large_object)
346 	ZEND_ARG_INFO(0, buf)
347 	ZEND_ARG_INFO(0, len)
348 ZEND_END_ARG_INFO()
349 
350 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_read_all, 0, 0, 1)
351 	ZEND_ARG_INFO(0, large_object)
352 ZEND_END_ARG_INFO()
353 
354 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_import, 0, 0, 0)
355 	ZEND_ARG_INFO(0, connection)
356 	ZEND_ARG_INFO(0, filename)
357 	ZEND_ARG_INFO(0, large_object_oid)
358 ZEND_END_ARG_INFO()
359 
360 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_export, 0, 0, 0)
361 	ZEND_ARG_INFO(0, connection)
362 	ZEND_ARG_INFO(0, objoid)
363 	ZEND_ARG_INFO(0, filename)
364 ZEND_END_ARG_INFO()
365 
366 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_seek, 0, 0, 2)
367 	ZEND_ARG_INFO(0, large_object)
368 	ZEND_ARG_INFO(0, offset)
369 	ZEND_ARG_INFO(0, whence)
370 ZEND_END_ARG_INFO()
371 
372 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_tell, 0, 0, 1)
373 	ZEND_ARG_INFO(0, large_object)
374 ZEND_END_ARG_INFO()
375 
376 #if HAVE_PG_LO_TRUNCATE
377 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_truncate, 0, 0, 1)
378 	ZEND_ARG_INFO(0, large_object)
379 	ZEND_ARG_INFO(0, size)
380 ZEND_END_ARG_INFO()
381 #endif
382 
383 #if HAVE_PQSETERRORVERBOSITY
384 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_set_error_verbosity, 0, 0, 0)
385 	ZEND_ARG_INFO(0, connection)
386 	ZEND_ARG_INFO(0, verbosity)
387 ZEND_END_ARG_INFO()
388 #endif
389 
390 #if HAVE_PQCLIENTENCODING
391 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_set_client_encoding, 0, 0, 0)
392 	ZEND_ARG_INFO(0, connection)
393 	ZEND_ARG_INFO(0, encoding)
394 ZEND_END_ARG_INFO()
395 
396 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_client_encoding, 0, 0, 0)
397 	ZEND_ARG_INFO(0, connection)
398 ZEND_END_ARG_INFO()
399 #endif
400 
401 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_end_copy, 0, 0, 0)
402 	ZEND_ARG_INFO(0, connection)
403 ZEND_END_ARG_INFO()
404 
405 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_put_line, 0, 0, 0)
406 	ZEND_ARG_INFO(0, connection)
407 	ZEND_ARG_INFO(0, query)
408 ZEND_END_ARG_INFO()
409 
410 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_copy_to, 0, 0, 2)
411 	ZEND_ARG_INFO(0, connection)
412 	ZEND_ARG_INFO(0, table_name)
413 	ZEND_ARG_INFO(0, delimiter)
414 	ZEND_ARG_INFO(0, null_as)
415 ZEND_END_ARG_INFO()
416 
417 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_copy_from, 0, 0, 3)
418 	ZEND_ARG_INFO(0, connection)
419 	ZEND_ARG_INFO(0, table_name)
420 	ZEND_ARG_INFO(0, rows)
421 	ZEND_ARG_INFO(0, delimiter)
422 	ZEND_ARG_INFO(0, null_as)
423 ZEND_END_ARG_INFO()
424 
425 #if HAVE_PQESCAPE
426 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape_string, 0, 0, 0)
427 	ZEND_ARG_INFO(0, connection)
428 	ZEND_ARG_INFO(0, data)
429 ZEND_END_ARG_INFO()
430 
431 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape_bytea, 0, 0, 0)
432 	ZEND_ARG_INFO(0, connection)
433 	ZEND_ARG_INFO(0, data)
434 ZEND_END_ARG_INFO()
435 
436 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_unescape_bytea, 0, 0, 1)
437 	ZEND_ARG_INFO(0, data)
438 ZEND_END_ARG_INFO()
439 #endif
440 
441 #if HAVE_PQESCAPE
442 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape_literal, 0, 0, 0)
443 	ZEND_ARG_INFO(0, connection)
444 	ZEND_ARG_INFO(0, data)
445 ZEND_END_ARG_INFO()
446 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape_identifier, 0, 0, 0)
447 	ZEND_ARG_INFO(0, connection)
448 	ZEND_ARG_INFO(0, data)
449 ZEND_END_ARG_INFO()
450 #endif
451 
452 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_result_error, 0, 0, 1)
453 	ZEND_ARG_INFO(0, result)
454 ZEND_END_ARG_INFO()
455 
456 #if HAVE_PQRESULTERRORFIELD
457 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_result_error_field, 0, 0, 2)
458 	ZEND_ARG_INFO(0, result)
459 	ZEND_ARG_INFO(0, fieldcode)
460 ZEND_END_ARG_INFO()
461 #endif
462 
463 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connection_status, 0, 0, 1)
464 	ZEND_ARG_INFO(0, connection)
465 ZEND_END_ARG_INFO()
466 
467 #if HAVE_PGTRANSACTIONSTATUS
468 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_transaction_status, 0, 0, 1)
469 	ZEND_ARG_INFO(0, connection)
470 ZEND_END_ARG_INFO()
471 #endif
472 
473 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connection_reset, 0, 0, 1)
474 	ZEND_ARG_INFO(0, connection)
475 ZEND_END_ARG_INFO()
476 
477 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_cancel_query, 0, 0, 1)
478 	ZEND_ARG_INFO(0, connection)
479 ZEND_END_ARG_INFO()
480 
481 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connection_busy, 0, 0, 1)
482 	ZEND_ARG_INFO(0, connection)
483 ZEND_END_ARG_INFO()
484 
485 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_query, 0, 0, 2)
486 	ZEND_ARG_INFO(0, connection)
487 	ZEND_ARG_INFO(0, query)
488 ZEND_END_ARG_INFO()
489 
490 #if HAVE_PQSENDQUERYPARAMS
491 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_query_params, 0, 0, 3)
492 	ZEND_ARG_INFO(0, connection)
493 	ZEND_ARG_INFO(0, query)
494 	ZEND_ARG_INFO(0, params)
495 ZEND_END_ARG_INFO()
496 #endif
497 
498 #if HAVE_PQSENDPREPARE
499 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_prepare, 0, 0, 3)
500 	ZEND_ARG_INFO(0, connection)
501 	ZEND_ARG_INFO(0, stmtname)
502 	ZEND_ARG_INFO(0, query)
503 ZEND_END_ARG_INFO()
504 #endif
505 
506 #if HAVE_PQSENDQUERYPREPARED
507 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_execute, 0, 0, 3)
508 	ZEND_ARG_INFO(0, connection)
509 	ZEND_ARG_INFO(0, stmtname)
510 	ZEND_ARG_INFO(0, params)
511 ZEND_END_ARG_INFO()
512 #endif
513 
514 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_get_result, 0, 0, 1)
515 	ZEND_ARG_INFO(0, connection)
516 ZEND_END_ARG_INFO()
517 
518 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_result_status, 0, 0, 1)
519 	ZEND_ARG_INFO(0, result)
520 	ZEND_ARG_INFO(0, result_type)
521 ZEND_END_ARG_INFO()
522 
523 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_get_notify, 0, 0, 0)
524 	ZEND_ARG_INFO(0, connection)
525 	ZEND_ARG_INFO(0, e)
526 ZEND_END_ARG_INFO()
527 
528 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_get_pid, 0, 0, 0)
529 	ZEND_ARG_INFO(0, connection)
530 ZEND_END_ARG_INFO()
531 
532 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_socket, 0, 0, 1)
533 	ZEND_ARG_INFO(0, connection)
534 ZEND_END_ARG_INFO()
535 
536 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_consume_input, 0, 0, 1)
537 	ZEND_ARG_INFO(0, connection)
538 ZEND_END_ARG_INFO()
539 
540 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_flush, 0, 0, 1)
541 	ZEND_ARG_INFO(0, connection)
542 ZEND_END_ARG_INFO()
543 
544 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_meta_data, 0, 0, 2)
545 	ZEND_ARG_INFO(0, db)
546 	ZEND_ARG_INFO(0, table)
547 ZEND_END_ARG_INFO()
548 
549 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_convert, 0, 0, 3)
550 	ZEND_ARG_INFO(0, db)
551 	ZEND_ARG_INFO(0, table)
552 	ZEND_ARG_INFO(0, values)
553 	ZEND_ARG_INFO(0, options)
554 ZEND_END_ARG_INFO()
555 
556 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_insert, 0, 0, 3)
557 	ZEND_ARG_INFO(0, db)
558 	ZEND_ARG_INFO(0, table)
559 	ZEND_ARG_INFO(0, values)
560 	ZEND_ARG_INFO(0, options)
561 ZEND_END_ARG_INFO()
562 
563 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_update, 0, 0, 4)
564 	ZEND_ARG_INFO(0, db)
565 	ZEND_ARG_INFO(0, table)
566 	ZEND_ARG_INFO(0, fields)
567 	ZEND_ARG_INFO(0, ids)
568 	ZEND_ARG_INFO(0, options)
569 ZEND_END_ARG_INFO()
570 
571 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_delete, 0, 0, 3)
572 	ZEND_ARG_INFO(0, db)
573 	ZEND_ARG_INFO(0, table)
574 	ZEND_ARG_INFO(0, ids)
575 	ZEND_ARG_INFO(0, options)
576 ZEND_END_ARG_INFO()
577 
578 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_select, 0, 0, 3)
579 	ZEND_ARG_INFO(0, db)
580 	ZEND_ARG_INFO(0, table)
581 	ZEND_ARG_INFO(0, ids)
582 	ZEND_ARG_INFO(0, options)
583 	ZEND_ARG_INFO(0, result_type)
584 ZEND_END_ARG_INFO()
585 /* }}} */
586 
587 /* {{{ pgsql_functions[]
588  */
589 static const zend_function_entry pgsql_functions[] = {
590 	/* connection functions */
591 	PHP_FE(pg_connect,		arginfo_pg_connect)
592 	PHP_FE(pg_pconnect,		arginfo_pg_pconnect)
593 	PHP_FE(pg_connect_poll,	arginfo_pg_connect_poll)
594 	PHP_FE(pg_close,		arginfo_pg_close)
595 	PHP_FE(pg_connection_status,	arginfo_pg_connection_status)
596 	PHP_FE(pg_connection_busy,		arginfo_pg_connection_busy)
597 	PHP_FE(pg_connection_reset,		arginfo_pg_connection_reset)
598 	PHP_FE(pg_host,			arginfo_pg_host)
599 	PHP_FE(pg_dbname,		arginfo_pg_dbname)
600 	PHP_FE(pg_port,			arginfo_pg_port)
601 	PHP_FE(pg_tty,			arginfo_pg_tty)
602 	PHP_FE(pg_options,		arginfo_pg_options)
603 	PHP_FE(pg_version,		arginfo_pg_version)
604 	PHP_FE(pg_ping,			arginfo_pg_ping)
605 #if HAVE_PQPARAMETERSTATUS
606 	PHP_FE(pg_parameter_status, arginfo_pg_parameter_status)
607 #endif
608 #if HAVE_PGTRANSACTIONSTATUS
609 	PHP_FE(pg_transaction_status, arginfo_pg_transaction_status)
610 #endif
611 	/* query functions */
612 	PHP_FE(pg_query,		arginfo_pg_query)
613 #if HAVE_PQEXECPARAMS
614 	PHP_FE(pg_query_params,		arginfo_pg_query_params)
615 #endif
616 #if HAVE_PQPREPARE
617 	PHP_FE(pg_prepare,		arginfo_pg_prepare)
618 #endif
619 #if HAVE_PQEXECPREPARED
620 	PHP_FE(pg_execute,		arginfo_pg_execute)
621 #endif
622 	PHP_FE(pg_send_query,	arginfo_pg_send_query)
623 #if HAVE_PQSENDQUERYPARAMS
624 	PHP_FE(pg_send_query_params,	arginfo_pg_send_query_params)
625 #endif
626 #if HAVE_PQSENDPREPARE
627 	PHP_FE(pg_send_prepare,	arginfo_pg_send_prepare)
628 #endif
629 #if HAVE_PQSENDQUERYPREPARED
630 	PHP_FE(pg_send_execute,	arginfo_pg_send_execute)
631 #endif
632 	PHP_FE(pg_cancel_query, arginfo_pg_cancel_query)
633 	/* result functions */
634 	PHP_FE(pg_fetch_result,	arginfo_pg_fetch_result)
635 	PHP_FE(pg_fetch_row,	arginfo_pg_fetch_row)
636 	PHP_FE(pg_fetch_assoc,	arginfo_pg_fetch_assoc)
637 	PHP_FE(pg_fetch_array,	arginfo_pg_fetch_array)
638 	PHP_FE(pg_fetch_object,	arginfo_pg_fetch_object)
639 	PHP_FE(pg_fetch_all,	arginfo_pg_fetch_all)
640 	PHP_FE(pg_fetch_all_columns,	arginfo_pg_fetch_all_columns)
641 #if HAVE_PQCMDTUPLES
642 	PHP_FE(pg_affected_rows,arginfo_pg_affected_rows)
643 #endif
644 	PHP_FE(pg_get_result,	arginfo_pg_get_result)
645 	PHP_FE(pg_result_seek,	arginfo_pg_result_seek)
646 	PHP_FE(pg_result_status,arginfo_pg_result_status)
647 	PHP_FE(pg_free_result,	arginfo_pg_free_result)
648 	PHP_FE(pg_last_oid,	    arginfo_pg_last_oid)
649 	PHP_FE(pg_num_rows,		arginfo_pg_num_rows)
650 	PHP_FE(pg_num_fields,	arginfo_pg_num_fields)
651 	PHP_FE(pg_field_name,	arginfo_pg_field_name)
652 	PHP_FE(pg_field_num,	arginfo_pg_field_num)
653 	PHP_FE(pg_field_size,	arginfo_pg_field_size)
654 	PHP_FE(pg_field_type,	arginfo_pg_field_type)
655 	PHP_FE(pg_field_type_oid, arginfo_pg_field_type_oid)
656 	PHP_FE(pg_field_prtlen,	arginfo_pg_field_prtlen)
657 	PHP_FE(pg_field_is_null,arginfo_pg_field_is_null)
658 #ifdef HAVE_PQFTABLE
659 	PHP_FE(pg_field_table,  arginfo_pg_field_table)
660 #endif
661 	/* async message function */
662 	PHP_FE(pg_get_notify,   arginfo_pg_get_notify)
663 	PHP_FE(pg_socket,		arginfo_pg_socket)
664 	PHP_FE(pg_consume_input,arginfo_pg_consume_input)
665 	PHP_FE(pg_flush,		arginfo_pg_flush)
666 	PHP_FE(pg_get_pid,      arginfo_pg_get_pid)
667 	/* error message functions */
668 	PHP_FE(pg_result_error, arginfo_pg_result_error)
669 #if HAVE_PQRESULTERRORFIELD
670 	PHP_FE(pg_result_error_field, arginfo_pg_result_error_field)
671 #endif
672 	PHP_FE(pg_last_error,   arginfo_pg_last_error)
673 	PHP_FE(pg_last_notice,  arginfo_pg_last_notice)
674 	/* copy functions */
675 	PHP_FE(pg_put_line,		arginfo_pg_put_line)
676 	PHP_FE(pg_end_copy,		arginfo_pg_end_copy)
677 	PHP_FE(pg_copy_to,      arginfo_pg_copy_to)
678 	PHP_FE(pg_copy_from,    arginfo_pg_copy_from)
679 	/* debug functions */
680 	PHP_FE(pg_trace,		arginfo_pg_trace)
681 	PHP_FE(pg_untrace,		arginfo_pg_untrace)
682 	/* large object functions */
683 	PHP_FE(pg_lo_create,	arginfo_pg_lo_create)
684 	PHP_FE(pg_lo_unlink,	arginfo_pg_lo_unlink)
685 	PHP_FE(pg_lo_open,		arginfo_pg_lo_open)
686 	PHP_FE(pg_lo_close,		arginfo_pg_lo_close)
687 	PHP_FE(pg_lo_read,		arginfo_pg_lo_read)
688 	PHP_FE(pg_lo_write,		arginfo_pg_lo_write)
689 	PHP_FE(pg_lo_read_all,	arginfo_pg_lo_read_all)
690 	PHP_FE(pg_lo_import,	arginfo_pg_lo_import)
691 	PHP_FE(pg_lo_export,	arginfo_pg_lo_export)
692 	PHP_FE(pg_lo_seek,		arginfo_pg_lo_seek)
693 	PHP_FE(pg_lo_tell,		arginfo_pg_lo_tell)
694 #if HAVE_PG_LO_TRUNCATE
695 	PHP_FE(pg_lo_truncate,	arginfo_pg_lo_truncate)
696 #endif
697 	/* utility functions */
698 #if HAVE_PQESCAPE
699 	PHP_FE(pg_escape_string,	arginfo_pg_escape_string)
700 	PHP_FE(pg_escape_bytea, 	arginfo_pg_escape_bytea)
701 	PHP_FE(pg_unescape_bytea, 	arginfo_pg_unescape_bytea)
702 	PHP_FE(pg_escape_literal,	arginfo_pg_escape_literal)
703 	PHP_FE(pg_escape_identifier,	arginfo_pg_escape_identifier)
704 #endif
705 #if HAVE_PQSETERRORVERBOSITY
706 	PHP_FE(pg_set_error_verbosity,	arginfo_pg_set_error_verbosity)
707 #endif
708 #if HAVE_PQCLIENTENCODING
709 	PHP_FE(pg_client_encoding,		arginfo_pg_client_encoding)
710 	PHP_FE(pg_set_client_encoding,	arginfo_pg_set_client_encoding)
711 #endif
712 	/* misc function */
713 	PHP_FE(pg_meta_data,	arginfo_pg_meta_data)
714 	PHP_FE(pg_convert,      arginfo_pg_convert)
715 	PHP_FE(pg_insert,       arginfo_pg_insert)
716 	PHP_FE(pg_update,       arginfo_pg_update)
717 	PHP_FE(pg_delete,       arginfo_pg_delete)
718 	PHP_FE(pg_select,       arginfo_pg_select)
719 	/* aliases for downwards compatibility */
720 	PHP_FALIAS(pg_exec,          pg_query,          arginfo_pg_query)
721 	PHP_FALIAS(pg_getlastoid,    pg_last_oid,       arginfo_pg_last_oid)
722 #if HAVE_PQCMDTUPLES
723 	PHP_FALIAS(pg_cmdtuples,	 pg_affected_rows,  arginfo_pg_affected_rows)
724 #endif
725 	PHP_FALIAS(pg_errormessage,	 pg_last_error,     arginfo_pg_last_error)
726 	PHP_FALIAS(pg_numrows,		 pg_num_rows,       arginfo_pg_num_rows)
727 	PHP_FALIAS(pg_numfields,	 pg_num_fields,     arginfo_pg_num_fields)
728 	PHP_FALIAS(pg_fieldname,	 pg_field_name,     arginfo_pg_field_name)
729 	PHP_FALIAS(pg_fieldsize,     pg_field_size,     arginfo_pg_field_size)
730 	PHP_FALIAS(pg_fieldtype,	 pg_field_type,     arginfo_pg_field_type)
731 	PHP_FALIAS(pg_fieldnum,	     pg_field_num,      arginfo_pg_field_num)
732 	PHP_FALIAS(pg_fieldprtlen,	 pg_field_prtlen,   arginfo_pg_field_prtlen)
733 	PHP_FALIAS(pg_fieldisnull,	 pg_field_is_null,  arginfo_pg_field_is_null)
734 	PHP_FALIAS(pg_freeresult,    pg_free_result,    arginfo_pg_free_result)
735 	PHP_FALIAS(pg_result,	     pg_fetch_result,   arginfo_pg_get_result)
736 	PHP_FALIAS(pg_loreadall,	 pg_lo_read_all,    arginfo_pg_lo_read_all)
737 	PHP_FALIAS(pg_locreate,	     pg_lo_create,      arginfo_pg_lo_create)
738 	PHP_FALIAS(pg_lounlink,	     pg_lo_unlink,      arginfo_pg_lo_unlink)
739 	PHP_FALIAS(pg_loopen,	     pg_lo_open,        arginfo_pg_lo_open)
740 	PHP_FALIAS(pg_loclose,	     pg_lo_close,       arginfo_pg_lo_close)
741 	PHP_FALIAS(pg_loread,	     pg_lo_read,        arginfo_pg_lo_read)
742 	PHP_FALIAS(pg_lowrite,	     pg_lo_write,       arginfo_pg_lo_write)
743 	PHP_FALIAS(pg_loimport,	     pg_lo_import,      arginfo_pg_lo_import)
744 	PHP_FALIAS(pg_loexport,	     pg_lo_export,      arginfo_pg_lo_export)
745 #if HAVE_PQCLIENTENCODING
746 	PHP_FALIAS(pg_clientencoding,		pg_client_encoding,		arginfo_pg_client_encoding)
747 	PHP_FALIAS(pg_setclientencoding,	pg_set_client_encoding,	arginfo_pg_set_client_encoding)
748 #endif
749 	PHP_FE_END
750 };
751 /* }}} */
752 
753 /* {{{ pgsql_module_entry
754  */
755 zend_module_entry pgsql_module_entry = {
756 	STANDARD_MODULE_HEADER,
757 	"pgsql",
758 	pgsql_functions,
759 	PHP_MINIT(pgsql),
760 	PHP_MSHUTDOWN(pgsql),
761 	PHP_RINIT(pgsql),
762 	PHP_RSHUTDOWN(pgsql),
763 	PHP_MINFO(pgsql),
764 	PHP_PGSQL_VERSION,
765 	PHP_MODULE_GLOBALS(pgsql),
766 	PHP_GINIT(pgsql),
767 	NULL,
768 	NULL,
769 	STANDARD_MODULE_PROPERTIES_EX
770 };
771 /* }}} */
772 
773 #ifdef COMPILE_DL_PGSQL
774 #ifdef ZTS
775 ZEND_TSRMLS_CACHE_DEFINE()
776 #endif
777 ZEND_GET_MODULE(pgsql)
778 #endif
779 
780 static int le_link, le_plink, le_result, le_lofp, le_string;
781 
782 /* Compatibility definitions */
783 
784 #ifndef HAVE_PGSQL_WITH_MULTIBYTE_SUPPORT
785 #define pg_encoding_to_char(x) "SQL_ASCII"
786 #endif
787 
788 #if !HAVE_PQESCAPE_CONN
789 #define PQescapeStringConn(conn, to, from, len, error) PQescapeString(to, from, len)
790 #endif
791 
792 #if HAVE_PQESCAPELITERAL
793 #define PGSQLescapeLiteral(conn, str, len) PQescapeLiteral(conn, str, len)
794 #define PGSQLescapeIdentifier(conn, str, len) PQescapeIdentifier(conn, str, len)
795 #define PGSQLfree(a) PQfreemem(a)
796 #else
797 #define PGSQLescapeLiteral(conn, str, len) php_pgsql_PQescapeInternal(conn, str, len, 1, 0)
798 #define PGSQLescapeLiteral2(conn, str, len) php_pgsql_PQescapeInternal(conn, str, len, 1, 1)
799 #define PGSQLescapeIdentifier(conn, str, len) php_pgsql_PQescapeInternal(conn, str, len, 0, 0)
800 #define PGSQLfree(a) efree(a)
801 
802 /* emulate libpq's PQescapeInternal() 9.0 or later */
php_pgsql_PQescapeInternal(PGconn * conn,const char * str,size_t len,int escape_literal,int safe)803 static char *php_pgsql_PQescapeInternal(PGconn *conn, const char *str, size_t len, int escape_literal, int safe) /* {{{ */
804 {
805 	char *result, *rp, *s;
806 
807 	if (!conn) {
808 		return NULL;
809 	}
810 
811 	/* allocate enough memory */
812 	rp = result = (char *)safe_emalloc(len, 2, 5); /* leading " E" needs extra 2 bytes + quote_chars on both end for 2 bytes + NULL */
813 
814 	if (escape_literal) {
815 		if (safe) {
816 			size_t new_len;
817 			char *tmp = (char *)safe_emalloc(len, 2, 1);
818 			*rp++ = '\'';
819 			/* PQescapeString does not escape \, but it handles multibyte chars safely.
820 			   This escape is incompatible with PQescapeLiteral. */
821 			new_len = PQescapeStringConn(conn, tmp, str, len, NULL);
822 			strncpy(rp, tmp, new_len);
823 			efree(tmp);
824 			rp += new_len;
825 		} else {
826 			char *encoding;
827 			size_t tmp_len;
828 			/* This is compatible with PQescapeLiteral, but it cannot handle multbyte chars
829 			   such as SJIS, BIG5. Raise warning and return NULL by checking
830 			   client_encoding. */
831 			encoding = (char *) pg_encoding_to_char(PQclientEncoding(conn));
832 			if (!strncmp(encoding, "SJIS", sizeof("SJIS")-1) ||
833 				!strncmp(encoding, "SHIFT_JIS_2004", sizeof("SHIFT_JIS_2004")-1) ||
834 				!strncmp(encoding, "BIG5", sizeof("BIG5")-1) ||
835 				!strncmp(encoding, "GB18030", sizeof("GB18030")-1) ||
836 				!strncmp(encoding, "GBK", sizeof("GBK")-1) ||
837 				!strncmp(encoding, "JOHAB", sizeof("JOHAB")-1) ||
838 				!strncmp(encoding, "UHC", sizeof("UHC")-1) ) {
839 
840 				php_error_docref(NULL, E_WARNING, "Unsafe encoding is used. Do not use '%s' encoding or use PostgreSQL 9.0 or later libpq.", encoding);
841 			}
842 			/* check backslashes */
843 			tmp_len = strspn(str, "\\");
844 			if (tmp_len != len) {
845 				/* add " E" for escaping slashes */
846 				*rp++ = ' ';
847 				*rp++ = 'E';
848 			}
849 			*rp++ = '\'';
850 			for (s = (char *)str; s - str < len; ++s) {
851 				if (*s == '\'' || *s == '\\') {
852 					*rp++ = *s;
853 					*rp++ = *s;
854 				} else {
855 					*rp++ = *s;
856 				}
857 			}
858 		}
859 		*rp++ = '\'';
860 	} else {
861 		/* Identifier escape. */
862 		*rp++ = '"';
863 		for (s = (char *)str; s - str < len; ++s) {
864 			if (*s == '"') {
865 				*rp++ = '"';
866 				*rp++ = '"';
867 			} else {
868 				*rp++ = *s;
869 			}
870 		}
871 		*rp++ = '"';
872 	}
873 	*rp = '\0';
874 
875 	return result;
876 }
877 /* }}} */
878 #endif
879 
880 /* {{{ _php_pgsql_trim_message */
_php_pgsql_trim_message(const char * message,size_t * len)881 static char * _php_pgsql_trim_message(const char *message, size_t *len)
882 {
883 	register size_t i = strlen(message);
884 
885 	if (i>2 && (message[i-2] == '\r' || message[i-2] == '\n') && message[i-1] == '.') {
886 		--i;
887 	}
888 	while (i>1 && (message[i-1] == '\r' || message[i-1] == '\n')) {
889 		--i;
890 	}
891 	if (len) {
892 		*len = i;
893 	}
894 	return estrndup(message, i);
895 }
896 /* }}} */
897 
898 /* {{{ _php_pgsql_trim_result */
_php_pgsql_trim_result(PGconn * pgsql,char ** buf)899 static inline char * _php_pgsql_trim_result(PGconn * pgsql, char **buf)
900 {
901 	return *buf = _php_pgsql_trim_message(PQerrorMessage(pgsql), NULL);
902 }
903 /* }}} */
904 
905 #define PQErrorMessageTrim(pgsql, buf) _php_pgsql_trim_result(pgsql, buf)
906 
907 #define PHP_PQ_ERROR(text, pgsql) {										\
908 		char *msgbuf = _php_pgsql_trim_message(PQerrorMessage(pgsql), NULL); \
909 		php_error_docref(NULL, E_WARNING, text, msgbuf);		\
910 		efree(msgbuf);													\
911 } \
912 
913 /* {{{ php_pgsql_set_default_link
914  */
php_pgsql_set_default_link(zend_resource * res)915 static void php_pgsql_set_default_link(zend_resource *res)
916 {
917 	GC_ADDREF(res);
918 
919 	if (PGG(default_link) != NULL) {
920 		zend_list_delete(PGG(default_link));
921 	}
922 
923 	PGG(default_link) = res;
924 }
925 /* }}} */
926 
927 /* {{{ _close_pgsql_link
928  */
_close_pgsql_link(zend_resource * rsrc)929 static void _close_pgsql_link(zend_resource *rsrc)
930 {
931 	PGconn *link = (PGconn *)rsrc->ptr;
932 	PGresult *res;
933 	zval *hash;
934 
935 	while ((res = PQgetResult(link))) {
936 		PQclear(res);
937 	}
938 	PQfinish(link);
939 	PGG(num_links)--;
940 
941 	/* Remove connection hash for this link */
942 	hash = zend_hash_index_find(&PGG(hashes), (uintptr_t) link);
943 	if (hash) {
944 		zend_hash_index_del(&PGG(hashes), (uintptr_t) link);
945 		zend_hash_del(&EG(regular_list), Z_STR_P(hash));
946 	}
947 }
948 /* }}} */
949 
950 /* {{{ _close_pgsql_plink
951  */
_close_pgsql_plink(zend_resource * rsrc)952 static void _close_pgsql_plink(zend_resource *rsrc)
953 {
954 	PGconn *link = (PGconn *)rsrc->ptr;
955 	PGresult *res;
956 
957 	while ((res = PQgetResult(link))) {
958 		PQclear(res);
959 	}
960 	PQfinish(link);
961 	PGG(num_persistent)--;
962 	PGG(num_links)--;
963 }
964 /* }}} */
965 
966 /* {{{ _php_pgsql_notice_handler
967  */
_php_pgsql_notice_handler(void * resource_id,const char * message)968 static void _php_pgsql_notice_handler(void *resource_id, const char *message)
969 {
970 	zval *notices;
971 	zval tmp;
972 	char *trimed_message;
973 	size_t trimed_message_len;
974 
975 	if (! PGG(ignore_notices)) {
976 		notices = zend_hash_index_find(&PGG(notices), (zend_ulong)resource_id);
977 		if (!notices) {
978 			array_init(&tmp);
979 			notices = &tmp;
980 			zend_hash_index_update(&PGG(notices), (zend_ulong)resource_id, notices);
981 		}
982 		trimed_message = _php_pgsql_trim_message(message, &trimed_message_len);
983 		if (PGG(log_notices)) {
984 			php_error_docref(NULL, E_NOTICE, "%s", trimed_message);
985 		}
986 		add_next_index_stringl(notices, trimed_message, trimed_message_len);
987 		efree(trimed_message);
988 	}
989 }
990 /* }}} */
991 
992 /* {{{ _rollback_transactions
993  */
_rollback_transactions(zval * el)994 static int _rollback_transactions(zval *el)
995 {
996 	PGconn *link;
997 	PGresult *res;
998 	zend_resource *rsrc = Z_RES_P(el);
999 
1000 	if (rsrc->type != le_plink)
1001 		return 0;
1002 
1003 	link = (PGconn *) rsrc->ptr;
1004 
1005 	if (PQ_SETNONBLOCKING(link, 0)) {
1006 		php_error_docref("ref.pgsql", E_NOTICE, "Cannot set connection to blocking mode");
1007 		return -1;
1008 	}
1009 
1010 	while ((res = PQgetResult(link))) {
1011 		PQclear(res);
1012 	}
1013 #if HAVE_PGTRANSACTIONSTATUS && HAVE_PQPROTOCOLVERSION
1014 	if ((PQprotocolVersion(link) >= 3 && PQtransactionStatus(link) != PQTRANS_IDLE) || PQprotocolVersion(link) < 3)
1015 #endif
1016 	{
1017 		int orig = PGG(ignore_notices);
1018 		PGG(ignore_notices) = 1;
1019 #if HAVE_PGTRANSACTIONSTATUS && HAVE_PQPROTOCOLVERSION
1020 		res = PQexec(link,"ROLLBACK;");
1021 #else
1022 		res = PQexec(link,"BEGIN;");
1023 		PQclear(res);
1024 		res = PQexec(link,"ROLLBACK;");
1025 #endif
1026 		PQclear(res);
1027 		PGG(ignore_notices) = orig;
1028 	}
1029 
1030 	return 0;
1031 }
1032 /* }}} */
1033 
1034 /* {{{ _free_ptr
1035  */
_free_ptr(zend_resource * rsrc)1036 static void _free_ptr(zend_resource *rsrc)
1037 {
1038 	pgLofp *lofp = (pgLofp *)rsrc->ptr;
1039 	efree(lofp);
1040 }
1041 /* }}} */
1042 
1043 /* {{{ _free_result
1044  */
_free_result(zend_resource * rsrc)1045 static void _free_result(zend_resource *rsrc)
1046 {
1047 	pgsql_result_handle *pg_result = (pgsql_result_handle *)rsrc->ptr;
1048 
1049 	PQclear(pg_result->result);
1050 	efree(pg_result);
1051 }
1052 /* }}} */
1053 
_php_pgsql_detect_identifier_escape(const char * identifier,size_t len)1054 static int _php_pgsql_detect_identifier_escape(const char *identifier, size_t len) /* {{{ */
1055 {
1056 	/* Handle edge case. Cannot be a escaped string */
1057 	if (len <= 2) {
1058 		return FAILURE;
1059 	}
1060 	/* Detect double quotes */
1061 	if (identifier[0] == '"' && identifier[len-1] == '"') {
1062 		size_t i;
1063 
1064 		/* Detect wrong format of " inside of escaped string */
1065 		for (i = 1; i < len-1; i++) {
1066 			if (identifier[i] == '"' && (identifier[++i] != '"' || i == len-1)) {
1067 				return FAILURE;
1068 			}
1069 		}
1070 	} else {
1071 		return FAILURE;
1072 	}
1073 	/* Escaped properly */
1074 	return SUCCESS;
1075 }
1076 /* }}} */
1077 
1078 /* {{{ PHP_INI
1079  */
1080 PHP_INI_BEGIN()
1081 STD_PHP_INI_BOOLEAN( "pgsql.allow_persistent",      "1",  PHP_INI_SYSTEM, OnUpdateBool, allow_persistent,      zend_pgsql_globals, pgsql_globals)
1082 STD_PHP_INI_ENTRY_EX("pgsql.max_persistent",       "-1",  PHP_INI_SYSTEM, OnUpdateLong, max_persistent,        zend_pgsql_globals, pgsql_globals, display_link_numbers)
1083 STD_PHP_INI_ENTRY_EX("pgsql.max_links",            "-1",  PHP_INI_SYSTEM, OnUpdateLong, max_links,             zend_pgsql_globals, pgsql_globals, display_link_numbers)
1084 STD_PHP_INI_BOOLEAN( "pgsql.auto_reset_persistent", "0",  PHP_INI_SYSTEM, OnUpdateBool, auto_reset_persistent, zend_pgsql_globals, pgsql_globals)
1085 STD_PHP_INI_BOOLEAN( "pgsql.ignore_notice",         "0",  PHP_INI_ALL,    OnUpdateBool, ignore_notices,        zend_pgsql_globals, pgsql_globals)
1086 STD_PHP_INI_BOOLEAN( "pgsql.log_notice",            "0",  PHP_INI_ALL,    OnUpdateBool, log_notices,           zend_pgsql_globals, pgsql_globals)
PHP_INI_END()1087 PHP_INI_END()
1088 /* }}} */
1089 
1090 /* {{{ PHP_GINIT_FUNCTION
1091  */
1092 static PHP_GINIT_FUNCTION(pgsql)
1093 {
1094 #if defined(COMPILE_DL_PGSQL) && defined(ZTS)
1095 	ZEND_TSRMLS_CACHE_UPDATE();
1096 #endif
1097 	memset(pgsql_globals, 0, sizeof(zend_pgsql_globals));
1098 	/* Initialize notice message hash at MINIT only */
1099 	zend_hash_init_ex(&pgsql_globals->notices, 0, NULL, ZVAL_PTR_DTOR, 1, 0);
1100 	zend_hash_init_ex(&pgsql_globals->hashes, 0, NULL, ZVAL_PTR_DTOR, 1, 0);
1101 }
1102 /* }}} */
1103 
1104 /* {{{ PHP_MINIT_FUNCTION
1105  */
PHP_MINIT_FUNCTION(pgsql)1106 PHP_MINIT_FUNCTION(pgsql)
1107 {
1108 	REGISTER_INI_ENTRIES();
1109 
1110 	le_link = zend_register_list_destructors_ex(_close_pgsql_link, NULL, "pgsql link", module_number);
1111 	le_plink = zend_register_list_destructors_ex(NULL, _close_pgsql_plink, "pgsql link persistent", module_number);
1112 	le_result = zend_register_list_destructors_ex(_free_result, NULL, "pgsql result", module_number);
1113 	le_lofp = zend_register_list_destructors_ex(_free_ptr, NULL, "pgsql large object", module_number);
1114 	le_string = zend_register_list_destructors_ex(_free_ptr, NULL, "pgsql string", module_number);
1115 #if HAVE_PG_CONFIG_H
1116 	/* PG_VERSION - libpq version */
1117 	REGISTER_STRING_CONSTANT("PGSQL_LIBPQ_VERSION", PG_VERSION, CONST_CS | CONST_PERSISTENT);
1118 	REGISTER_STRING_CONSTANT("PGSQL_LIBPQ_VERSION_STR", PG_VERSION_STR, CONST_CS | CONST_PERSISTENT);
1119 #endif
1120 	/* For connection option */
1121 	REGISTER_LONG_CONSTANT("PGSQL_CONNECT_FORCE_NEW", PGSQL_CONNECT_FORCE_NEW, CONST_CS | CONST_PERSISTENT);
1122 	REGISTER_LONG_CONSTANT("PGSQL_CONNECT_ASYNC", PGSQL_CONNECT_ASYNC, CONST_CS | CONST_PERSISTENT);
1123 	/* For pg_fetch_array() */
1124 	REGISTER_LONG_CONSTANT("PGSQL_ASSOC", PGSQL_ASSOC, CONST_CS | CONST_PERSISTENT);
1125 	REGISTER_LONG_CONSTANT("PGSQL_NUM", PGSQL_NUM, CONST_CS | CONST_PERSISTENT);
1126 	REGISTER_LONG_CONSTANT("PGSQL_BOTH", PGSQL_BOTH, CONST_CS | CONST_PERSISTENT);
1127 	/* For pg_last_notice() */
1128 	REGISTER_LONG_CONSTANT("PGSQL_NOTICE_LAST", PGSQL_NOTICE_LAST, CONST_CS | CONST_PERSISTENT);
1129 	REGISTER_LONG_CONSTANT("PGSQL_NOTICE_ALL", PGSQL_NOTICE_ALL, CONST_CS | CONST_PERSISTENT);
1130 	REGISTER_LONG_CONSTANT("PGSQL_NOTICE_CLEAR", PGSQL_NOTICE_CLEAR, CONST_CS | CONST_PERSISTENT);
1131 	/* For pg_connection_status() */
1132 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_BAD", CONNECTION_BAD, CONST_CS | CONST_PERSISTENT);
1133 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_OK", CONNECTION_OK, CONST_CS | CONST_PERSISTENT);
1134 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_STARTED", CONNECTION_STARTED, CONST_CS | CONST_PERSISTENT);
1135 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_MADE", CONNECTION_MADE, CONST_CS | CONST_PERSISTENT);
1136 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_AWAITING_RESPONSE", CONNECTION_AWAITING_RESPONSE, CONST_CS | CONST_PERSISTENT);
1137 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_AUTH_OK", CONNECTION_AUTH_OK, CONST_CS | CONST_PERSISTENT);
1138 #ifdef CONNECTION_SSL_STARTUP
1139 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_SSL_STARTUP", CONNECTION_SSL_STARTUP, CONST_CS | CONST_PERSISTENT);
1140 #endif
1141 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_SETENV", CONNECTION_SETENV, CONST_CS | CONST_PERSISTENT);
1142 	/* For pg_connect_poll() */
1143 	REGISTER_LONG_CONSTANT("PGSQL_POLLING_FAILED", PGRES_POLLING_FAILED, CONST_CS | CONST_PERSISTENT);
1144 	REGISTER_LONG_CONSTANT("PGSQL_POLLING_READING", PGRES_POLLING_READING, CONST_CS | CONST_PERSISTENT);
1145 	REGISTER_LONG_CONSTANT("PGSQL_POLLING_WRITING", PGRES_POLLING_WRITING, CONST_CS | CONST_PERSISTENT);
1146 	REGISTER_LONG_CONSTANT("PGSQL_POLLING_OK", PGRES_POLLING_OK, CONST_CS | CONST_PERSISTENT);
1147 	REGISTER_LONG_CONSTANT("PGSQL_POLLING_ACTIVE", PGRES_POLLING_ACTIVE, CONST_CS | CONST_PERSISTENT);
1148 #if HAVE_PGTRANSACTIONSTATUS
1149 	/* For pg_transaction_status() */
1150 	REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_IDLE", PQTRANS_IDLE, CONST_CS | CONST_PERSISTENT);
1151 	REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_ACTIVE", PQTRANS_ACTIVE, CONST_CS | CONST_PERSISTENT);
1152 	REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_INTRANS", PQTRANS_INTRANS, CONST_CS | CONST_PERSISTENT);
1153 	REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_INERROR", PQTRANS_INERROR, CONST_CS | CONST_PERSISTENT);
1154 	REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_UNKNOWN", PQTRANS_UNKNOWN, CONST_CS | CONST_PERSISTENT);
1155 #endif
1156 #if HAVE_PQSETERRORVERBOSITY
1157 	/* For pg_set_error_verbosity() */
1158 	REGISTER_LONG_CONSTANT("PGSQL_ERRORS_TERSE", PQERRORS_TERSE, CONST_CS | CONST_PERSISTENT);
1159 	REGISTER_LONG_CONSTANT("PGSQL_ERRORS_DEFAULT", PQERRORS_DEFAULT, CONST_CS | CONST_PERSISTENT);
1160 	REGISTER_LONG_CONSTANT("PGSQL_ERRORS_VERBOSE", PQERRORS_VERBOSE, CONST_CS | CONST_PERSISTENT);
1161 #endif
1162 	/* For lo_seek() */
1163 	REGISTER_LONG_CONSTANT("PGSQL_SEEK_SET", SEEK_SET, CONST_CS | CONST_PERSISTENT);
1164 	REGISTER_LONG_CONSTANT("PGSQL_SEEK_CUR", SEEK_CUR, CONST_CS | CONST_PERSISTENT);
1165 	REGISTER_LONG_CONSTANT("PGSQL_SEEK_END", SEEK_END, CONST_CS | CONST_PERSISTENT);
1166 	/* For pg_result_status() return value type */
1167 	REGISTER_LONG_CONSTANT("PGSQL_STATUS_LONG", PGSQL_STATUS_LONG, CONST_CS | CONST_PERSISTENT);
1168 	REGISTER_LONG_CONSTANT("PGSQL_STATUS_STRING", PGSQL_STATUS_STRING, CONST_CS | CONST_PERSISTENT);
1169 	/* For pg_result_status() return value */
1170 	REGISTER_LONG_CONSTANT("PGSQL_EMPTY_QUERY", PGRES_EMPTY_QUERY, CONST_CS | CONST_PERSISTENT);
1171 	REGISTER_LONG_CONSTANT("PGSQL_COMMAND_OK", PGRES_COMMAND_OK, CONST_CS | CONST_PERSISTENT);
1172 	REGISTER_LONG_CONSTANT("PGSQL_TUPLES_OK", PGRES_TUPLES_OK, CONST_CS | CONST_PERSISTENT);
1173 	REGISTER_LONG_CONSTANT("PGSQL_COPY_OUT", PGRES_COPY_OUT, CONST_CS | CONST_PERSISTENT);
1174 	REGISTER_LONG_CONSTANT("PGSQL_COPY_IN", PGRES_COPY_IN, CONST_CS | CONST_PERSISTENT);
1175 	REGISTER_LONG_CONSTANT("PGSQL_BAD_RESPONSE", PGRES_BAD_RESPONSE, CONST_CS | CONST_PERSISTENT);
1176 	REGISTER_LONG_CONSTANT("PGSQL_NONFATAL_ERROR", PGRES_NONFATAL_ERROR, CONST_CS | CONST_PERSISTENT);
1177 	REGISTER_LONG_CONSTANT("PGSQL_FATAL_ERROR", PGRES_FATAL_ERROR, CONST_CS | CONST_PERSISTENT);
1178 #if HAVE_PQRESULTERRORFIELD
1179 	/* For pg_result_error_field() field codes */
1180 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SEVERITY", PG_DIAG_SEVERITY, CONST_CS | CONST_PERSISTENT);
1181 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SQLSTATE", PG_DIAG_SQLSTATE, CONST_CS | CONST_PERSISTENT);
1182 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_MESSAGE_PRIMARY", PG_DIAG_MESSAGE_PRIMARY, CONST_CS | CONST_PERSISTENT);
1183 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_MESSAGE_DETAIL", PG_DIAG_MESSAGE_DETAIL, CONST_CS | CONST_PERSISTENT);
1184 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_MESSAGE_HINT", PG_DIAG_MESSAGE_HINT, CONST_CS | CONST_PERSISTENT);
1185 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_STATEMENT_POSITION", PG_DIAG_STATEMENT_POSITION, CONST_CS | CONST_PERSISTENT);
1186 #ifdef PG_DIAG_INTERNAL_POSITION
1187 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_INTERNAL_POSITION", PG_DIAG_INTERNAL_POSITION, CONST_CS | CONST_PERSISTENT);
1188 #endif
1189 #ifdef PG_DIAG_INTERNAL_QUERY
1190 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_INTERNAL_QUERY", PG_DIAG_INTERNAL_QUERY, CONST_CS | CONST_PERSISTENT);
1191 #endif
1192 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_CONTEXT", PG_DIAG_CONTEXT, CONST_CS | CONST_PERSISTENT);
1193 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SOURCE_FILE", PG_DIAG_SOURCE_FILE, CONST_CS | CONST_PERSISTENT);
1194 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SOURCE_LINE", PG_DIAG_SOURCE_LINE, CONST_CS | CONST_PERSISTENT);
1195 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SOURCE_FUNCTION", PG_DIAG_SOURCE_FUNCTION, CONST_CS | CONST_PERSISTENT);
1196 #ifdef PG_DIAG_SCHEMA_NAME
1197 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SCHEMA_NAME", PG_DIAG_SCHEMA_NAME, CONST_CS | CONST_PERSISTENT);
1198 #endif
1199 #ifdef PG_DIAG_TABLE_NAME
1200 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_TABLE_NAME", PG_DIAG_TABLE_NAME, CONST_CS | CONST_PERSISTENT);
1201 #endif
1202 #ifdef PG_DIAG_COLUMN_NAME
1203 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_COLUMN_NAME", PG_DIAG_COLUMN_NAME, CONST_CS | CONST_PERSISTENT);
1204 #endif
1205 #ifdef PG_DIAG_DATATYPE_NAME
1206 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_DATATYPE_NAME", PG_DIAG_DATATYPE_NAME, CONST_CS | CONST_PERSISTENT);
1207 #endif
1208 #ifdef PG_DIAG_CONSTRAINT_NAME
1209 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_CONSTRAINT_NAME", PG_DIAG_CONSTRAINT_NAME, CONST_CS | CONST_PERSISTENT);
1210 #endif
1211 #ifdef PG_DIAG_SEVERITY_NONLOCALIZED
1212 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SEVERITY_NONLOCALIZED", PG_DIAG_SEVERITY_NONLOCALIZED, CONST_CS | CONST_PERSISTENT);
1213 #endif
1214 #endif
1215 	/* pg_convert options */
1216 	REGISTER_LONG_CONSTANT("PGSQL_CONV_IGNORE_DEFAULT", PGSQL_CONV_IGNORE_DEFAULT, CONST_CS | CONST_PERSISTENT);
1217 	REGISTER_LONG_CONSTANT("PGSQL_CONV_FORCE_NULL", PGSQL_CONV_FORCE_NULL, CONST_CS | CONST_PERSISTENT);
1218 	REGISTER_LONG_CONSTANT("PGSQL_CONV_IGNORE_NOT_NULL", PGSQL_CONV_IGNORE_NOT_NULL, CONST_CS | CONST_PERSISTENT);
1219 	/* pg_insert/update/delete/select options */
1220 	REGISTER_LONG_CONSTANT("PGSQL_DML_ESCAPE", PGSQL_DML_ESCAPE, CONST_CS | CONST_PERSISTENT);
1221 	REGISTER_LONG_CONSTANT("PGSQL_DML_NO_CONV", PGSQL_DML_NO_CONV, CONST_CS | CONST_PERSISTENT);
1222 	REGISTER_LONG_CONSTANT("PGSQL_DML_EXEC", PGSQL_DML_EXEC, CONST_CS | CONST_PERSISTENT);
1223 	REGISTER_LONG_CONSTANT("PGSQL_DML_ASYNC", PGSQL_DML_ASYNC, CONST_CS | CONST_PERSISTENT);
1224 	REGISTER_LONG_CONSTANT("PGSQL_DML_STRING", PGSQL_DML_STRING, CONST_CS | CONST_PERSISTENT);
1225 	return SUCCESS;
1226 }
1227 /* }}} */
1228 
1229 /* {{{ PHP_MSHUTDOWN_FUNCTION
1230  */
PHP_MSHUTDOWN_FUNCTION(pgsql)1231 PHP_MSHUTDOWN_FUNCTION(pgsql)
1232 {
1233 	UNREGISTER_INI_ENTRIES();
1234 	zend_hash_destroy(&PGG(notices));
1235 	zend_hash_destroy(&PGG(hashes));
1236 
1237 	return SUCCESS;
1238 }
1239 /* }}} */
1240 
1241 /* {{{ PHP_RINIT_FUNCTION
1242  */
PHP_RINIT_FUNCTION(pgsql)1243 PHP_RINIT_FUNCTION(pgsql)
1244 {
1245 	PGG(default_link) = NULL;
1246 	PGG(num_links) = PGG(num_persistent);
1247 	return SUCCESS;
1248 }
1249 /* }}} */
1250 
1251 /* {{{ PHP_RSHUTDOWN_FUNCTION
1252  */
PHP_RSHUTDOWN_FUNCTION(pgsql)1253 PHP_RSHUTDOWN_FUNCTION(pgsql)
1254 {
1255 	/* clean up notice messages */
1256 	zend_hash_clean(&PGG(notices));
1257 	zend_hash_clean(&PGG(hashes));
1258 	/* clean up persistent connection */
1259 	zend_hash_apply(&EG(persistent_list), (apply_func_t) _rollback_transactions);
1260 	return SUCCESS;
1261 }
1262 /* }}} */
1263 
1264 /* {{{ PHP_MINFO_FUNCTION
1265  */
PHP_MINFO_FUNCTION(pgsql)1266 PHP_MINFO_FUNCTION(pgsql)
1267 {
1268 	char buf[256];
1269 
1270 	php_info_print_table_start();
1271 	php_info_print_table_header(2, "PostgreSQL Support", "enabled");
1272 #if HAVE_PG_CONFIG_H
1273 	php_info_print_table_row(2, "PostgreSQL(libpq) Version", PG_VERSION);
1274 	php_info_print_table_row(2, "PostgreSQL(libpq) ", PG_VERSION_STR);
1275 #ifdef HAVE_PGSQL_WITH_MULTIBYTE_SUPPORT
1276 	php_info_print_table_row(2, "Multibyte character support", "enabled");
1277 #else
1278 	php_info_print_table_row(2, "Multibyte character support", "disabled");
1279 #endif
1280 #if defined(USE_SSL) || defined(USE_OPENSSL)
1281 	php_info_print_table_row(2, "SSL support", "enabled");
1282 #else
1283 	php_info_print_table_row(2, "SSL support", "disabled");
1284 #endif
1285 #endif /* HAVE_PG_CONFIG_H */
1286 	snprintf(buf, sizeof(buf), ZEND_LONG_FMT, PGG(num_persistent));
1287 	php_info_print_table_row(2, "Active Persistent Links", buf);
1288 	snprintf(buf, sizeof(buf), ZEND_LONG_FMT, PGG(num_links));
1289 	php_info_print_table_row(2, "Active Links", buf);
1290 	php_info_print_table_end();
1291 
1292 	DISPLAY_INI_ENTRIES();
1293 }
1294 /* }}} */
1295 
1296 /* {{{ php_pgsql_do_connect
1297  */
php_pgsql_do_connect(INTERNAL_FUNCTION_PARAMETERS,int persistent)1298 static void php_pgsql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
1299 {
1300 	char *host=NULL,*port=NULL,*options=NULL,*tty=NULL,*dbname=NULL,*connstring=NULL;
1301 	PGconn *pgsql;
1302 	smart_str str = {0};
1303 	zval *args;
1304 	uint32_t i;
1305 	int connect_type = 0;
1306 	PGresult *pg_result;
1307 
1308 	args = (zval *)safe_emalloc(ZEND_NUM_ARGS(), sizeof(zval), 0);
1309 	if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 5
1310 			|| zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) {
1311 		efree(args);
1312 		WRONG_PARAM_COUNT;
1313 	}
1314 
1315 	smart_str_appends(&str, "pgsql");
1316 
1317 	for (i = 0; i < ZEND_NUM_ARGS(); i++) {
1318 		/* make sure that the PGSQL_CONNECT_FORCE_NEW bit is not part of the hash so that subsequent connections
1319 		 * can re-use this connection. Bug #39979
1320 		 */
1321 		if (i == 1 && ZEND_NUM_ARGS() == 2 && Z_TYPE(args[i]) == IS_LONG) {
1322 			if (Z_LVAL(args[1]) == PGSQL_CONNECT_FORCE_NEW) {
1323 				continue;
1324 			} else if (Z_LVAL(args[1]) & PGSQL_CONNECT_FORCE_NEW) {
1325 				smart_str_append_long(&str, Z_LVAL(args[1]) ^ PGSQL_CONNECT_FORCE_NEW);
1326 			}
1327 		}
1328 		ZVAL_STR(&args[i], zval_get_string(&args[i]));
1329 		smart_str_appendc(&str, '_');
1330 		smart_str_appendl(&str, Z_STRVAL(args[i]), Z_STRLEN(args[i]));
1331 	}
1332 
1333 	/* Exception thrown during a string conversion. */
1334 	if (EG(exception)) {
1335 		goto cleanup;
1336 	}
1337 
1338 	smart_str_0(&str);
1339 
1340 	if (ZEND_NUM_ARGS() == 1) { /* new style, using connection string */
1341 		connstring = Z_STRVAL(args[0]);
1342 	} else if (ZEND_NUM_ARGS() == 2 ) { /* Safe to add conntype_option, since 2 args was illegal */
1343 		connstring = Z_STRVAL(args[0]);
1344 		connect_type = (int)zval_get_long(&args[1]);
1345 	} else {
1346 		host = Z_STRVAL(args[0]);
1347 		port = Z_STRVAL(args[1]);
1348 		dbname = Z_STRVAL(args[ZEND_NUM_ARGS()-1]);
1349 
1350 		switch (ZEND_NUM_ARGS()) {
1351 		case 5:
1352 			tty = Z_STRVAL(args[3]);
1353 			/* fall through */
1354 		case 4:
1355 			options = Z_STRVAL(args[2]);
1356 			break;
1357 		}
1358 	}
1359 
1360 	if (persistent && PGG(allow_persistent)) {
1361 		zend_resource *le;
1362 
1363 		/* try to find if we already have this link in our persistent list */
1364 		if ((le = zend_hash_find_ptr(&EG(persistent_list), str.s)) == NULL) {  /* we don't */
1365 			if (PGG(max_links) != -1 && PGG(num_links) >= PGG(max_links)) {
1366 				php_error_docref(NULL, E_WARNING,
1367 								 "Cannot create new link. Too many open links (" ZEND_LONG_FMT ")", PGG(num_links));
1368 				goto err;
1369 			}
1370 			if (PGG(max_persistent) != -1 && PGG(num_persistent) >= PGG(max_persistent)) {
1371 				php_error_docref(NULL, E_WARNING,
1372 								 "Cannot create new link. Too many open persistent links (" ZEND_LONG_FMT ")", PGG(num_persistent));
1373 				goto err;
1374 			}
1375 
1376 			/* create the link */
1377 			if (connstring) {
1378 				pgsql = PQconnectdb(connstring);
1379 			} else {
1380 				pgsql = PQsetdb(host, port, options, tty, dbname);
1381 			}
1382 			if (pgsql == NULL || PQstatus(pgsql) == CONNECTION_BAD) {
1383 				PHP_PQ_ERROR("Unable to connect to PostgreSQL server: %s", pgsql)
1384 				if (pgsql) {
1385 					PQfinish(pgsql);
1386 				}
1387 				goto err;
1388 			}
1389 
1390 			/* hash it up */
1391 			if (zend_register_persistent_resource(ZSTR_VAL(str.s), ZSTR_LEN(str.s), pgsql, le_plink) == NULL) {
1392 				goto err;
1393 			}
1394 			PGG(num_links)++;
1395 			PGG(num_persistent)++;
1396 		} else {  /* we do */
1397 			if (le->type != le_plink) {
1398 				goto err;
1399 			}
1400 			/* ensure that the link did not die */
1401 			if (PGG(auto_reset_persistent) & 1) {
1402 				/* need to send & get something from backend to
1403 				   make sure we catch CONNECTION_BAD every time */
1404 				PGresult *pg_result;
1405 				pg_result = PQexec(le->ptr, "select 1");
1406 				PQclear(pg_result);
1407 			}
1408 			if (PQstatus(le->ptr) == CONNECTION_BAD) { /* the link died */
1409 				if (le->ptr == NULL) {
1410 					if (connstring) {
1411 						le->ptr = PQconnectdb(connstring);
1412 					} else {
1413 						le->ptr = PQsetdb(host,port,options,tty,dbname);
1414 					}
1415 				}
1416 				else {
1417 					PQreset(le->ptr);
1418 				}
1419 				if (le->ptr == NULL || PQstatus(le->ptr) == CONNECTION_BAD) {
1420 					php_error_docref(NULL, E_WARNING,"PostgreSQL link lost, unable to reconnect");
1421 					zend_hash_del(&EG(persistent_list), str.s);
1422 					goto err;
1423 				}
1424 			}
1425 			pgsql = (PGconn *) le->ptr;
1426 #if HAVE_PQPROTOCOLVERSION && HAVE_PQPARAMETERSTATUS
1427 			/* consider to use php_version_compare() here */
1428 			if (PQprotocolVersion(pgsql) >= 3 && zend_strtod(PQparameterStatus(pgsql, "server_version"), NULL) >= 7.2) {
1429 #else
1430 			if (zend_strtod(PG_VERSION, NULL) >= 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, i);
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_init_ex(return_value, ce);
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 			field_offset = PQfnumber(pgsql_result, Z_STRVAL_P(field));
3055 			if (field_offset < 0 || field_offset >= PQnfields(pgsql_result)) {
3056 				php_error_docref(NULL, E_WARNING, "Bad column offset specified");
3057 				RETURN_FALSE;
3058 			}
3059 			break;
3060 		default:
3061 			convert_to_long_ex(field);
3062 			if (Z_LVAL_P(field) < 0 || Z_LVAL_P(field) >= PQnfields(pgsql_result)) {
3063 				php_error_docref(NULL, E_WARNING, "Bad column offset specified");
3064 				RETURN_FALSE;
3065 			}
3066 			field_offset = (int)Z_LVAL_P(field);
3067 			break;
3068 	}
3069 
3070 	switch (entry_type) {
3071 		case PHP_PG_DATA_LENGTH:
3072 			RETVAL_LONG(PQgetlength(pgsql_result, pgsql_row, field_offset));
3073 			break;
3074 		case PHP_PG_DATA_ISNULL:
3075 			RETVAL_LONG(PQgetisnull(pgsql_result, pgsql_row, field_offset));
3076 			break;
3077 	}
3078 }
3079 /* }}} */
3080 
3081 /* {{{ proto int pg_field_prtlen(resource result, [int row,] mixed field_name_or_number)
3082    Returns the printed length */
3083 PHP_FUNCTION(pg_field_prtlen)
3084 {
3085 	php_pgsql_data_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_DATA_LENGTH);
3086 }
3087 /* }}} */
3088 
3089 /* {{{ proto int pg_field_is_null(resource result, [int row,] mixed field_name_or_number)
3090    Test if a field is NULL */
3091 PHP_FUNCTION(pg_field_is_null)
3092 {
3093 	php_pgsql_data_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_DATA_ISNULL);
3094 }
3095 /* }}} */
3096 
3097 /* {{{ proto bool pg_free_result(resource result)
3098    Free result memory */
3099 PHP_FUNCTION(pg_free_result)
3100 {
3101 	zval *result;
3102 	pgsql_result_handle *pg_result;
3103 
3104 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &result) == FAILURE) {
3105 		return;
3106 	}
3107 
3108 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
3109 		RETURN_FALSE;
3110 	}
3111 
3112 	zend_list_close(Z_RES_P(result));
3113 	RETURN_TRUE;
3114 }
3115 /* }}} */
3116 
3117 /* {{{ proto string pg_last_oid(resource result)
3118    Returns the last object identifier */
3119 PHP_FUNCTION(pg_last_oid)
3120 {
3121 	zval *result;
3122 	PGresult *pgsql_result;
3123 	pgsql_result_handle *pg_result;
3124 #ifdef HAVE_PQOIDVALUE
3125 	Oid oid;
3126 #endif
3127 
3128 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &result) == FAILURE) {
3129 		return;
3130 	}
3131 
3132 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
3133 		RETURN_FALSE;
3134 	}
3135 
3136 	pgsql_result = pg_result->result;
3137 #ifdef HAVE_PQOIDVALUE
3138 	oid = PQoidValue(pgsql_result);
3139 	if (oid == InvalidOid) {
3140 		RETURN_FALSE;
3141 	}
3142 	PGSQL_RETURN_OID(oid);
3143 #else
3144 	Z_STRVAL_P(return_value) = (char *) PQoidStatus(pgsql_result);
3145 	if (Z_STRVAL_P(return_value)) {
3146 		RETURN_STRING(Z_STRVAL_P(return_value));
3147 	}
3148 	RETURN_EMPTY_STRING();
3149 #endif
3150 }
3151 /* }}} */
3152 
3153 /* {{{ proto bool pg_trace(string filename [, string mode [, resource connection]])
3154    Enable tracing a PostgreSQL connection */
3155 PHP_FUNCTION(pg_trace)
3156 {
3157 	char *z_filename, *mode = "w";
3158 	size_t z_filename_len, mode_len;
3159 	zval *pgsql_link = NULL;
3160 	int argc = ZEND_NUM_ARGS();
3161 	PGconn *pgsql;
3162 	FILE *fp = NULL;
3163 	php_stream *stream;
3164 	zend_resource *link;
3165 
3166 	if (zend_parse_parameters(argc, "p|sr", &z_filename, &z_filename_len, &mode, &mode_len, &pgsql_link) == FAILURE) {
3167 		return;
3168 	}
3169 
3170 	if (argc < 3) {
3171 		link = FETCH_DEFAULT_LINK();
3172 		CHECK_DEFAULT_LINK(link);
3173 	} else {
3174 		link = Z_RES_P(pgsql_link);
3175 	}
3176 
3177 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3178 		RETURN_FALSE;
3179 	}
3180 
3181 	stream = php_stream_open_wrapper(z_filename, mode, REPORT_ERRORS, NULL);
3182 
3183 	if (!stream) {
3184 		RETURN_FALSE;
3185 	}
3186 
3187 	if (FAILURE == php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void**)&fp, REPORT_ERRORS))	{
3188 		php_stream_close(stream);
3189 		RETURN_FALSE;
3190 	}
3191 	php_stream_auto_cleanup(stream);
3192 	PQtrace(pgsql, fp);
3193 	RETURN_TRUE;
3194 }
3195 /* }}} */
3196 
3197 /* {{{ proto bool pg_untrace([resource connection])
3198    Disable tracing of a PostgreSQL connection */
3199 PHP_FUNCTION(pg_untrace)
3200 {
3201 	zval *pgsql_link = NULL;
3202 	int argc = ZEND_NUM_ARGS();
3203 	PGconn *pgsql;
3204 	zend_resource *link;
3205 
3206 	if (zend_parse_parameters(argc, "|r", &pgsql_link) == FAILURE) {
3207 		return;
3208 	}
3209 
3210 	if (argc == 0) {
3211 		link = FETCH_DEFAULT_LINK();
3212 		CHECK_DEFAULT_LINK(link);
3213 	} else {
3214 		link = Z_RES_P(pgsql_link);
3215 	}
3216 
3217 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3218 		RETURN_FALSE;
3219 	}
3220 
3221 	PQuntrace(pgsql);
3222 	RETURN_TRUE;
3223 }
3224 /* }}} */
3225 
3226 /* {{{ proto mixed pg_lo_create([resource connection],[mixed large_object_oid])
3227    Create a large object */
3228 PHP_FUNCTION(pg_lo_create)
3229 {
3230 	zval *pgsql_link = NULL, *oid = NULL;
3231 	PGconn *pgsql;
3232 	Oid pgsql_oid, wanted_oid = InvalidOid;
3233 	int argc = ZEND_NUM_ARGS();
3234 	zend_resource *link;
3235 
3236 	if (zend_parse_parameters(argc, "|zz", &pgsql_link, &oid) == FAILURE) {
3237 		return;
3238 	}
3239 
3240 	if ((argc == 1) && (Z_TYPE_P(pgsql_link) != IS_RESOURCE)) {
3241 		oid = pgsql_link;
3242 		pgsql_link = NULL;
3243 	}
3244 
3245 	if (pgsql_link == NULL) {
3246 		link = FETCH_DEFAULT_LINK();
3247 		CHECK_DEFAULT_LINK(link);
3248 	} else if ((Z_TYPE_P(pgsql_link) == IS_RESOURCE)) {
3249 		link = Z_RES_P(pgsql_link);
3250 	} else {
3251 		link = NULL;
3252 	}
3253 
3254 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3255 		RETURN_FALSE;
3256 	}
3257 
3258 	if (oid) {
3259 #ifndef HAVE_PG_LO_CREATE
3260 		php_error_docref(NULL, E_NOTICE, "Passing OID value is not supported. Upgrade your PostgreSQL");
3261 #else
3262 		switch (Z_TYPE_P(oid)) {
3263 		case IS_STRING:
3264 			{
3265 				char *end_ptr;
3266 				wanted_oid = (Oid)strtoul(Z_STRVAL_P(oid), &end_ptr, 10);
3267 				if ((Z_STRVAL_P(oid)+Z_STRLEN_P(oid)) != end_ptr) {
3268 				/* wrong integer format */
3269 				php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3270 				RETURN_FALSE;
3271 				}
3272 			}
3273 			break;
3274 		case IS_LONG:
3275 			if (Z_LVAL_P(oid) < (zend_long)InvalidOid) {
3276 				php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3277 				RETURN_FALSE;
3278 			}
3279 			wanted_oid = (Oid)Z_LVAL_P(oid);
3280 			break;
3281 		default:
3282 			php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3283 			RETURN_FALSE;
3284         }
3285 		if ((pgsql_oid = lo_create(pgsql, wanted_oid)) == InvalidOid) {
3286 			php_error_docref(NULL, E_WARNING, "Unable to create PostgreSQL large object");
3287 			RETURN_FALSE;
3288 		}
3289 
3290 		PGSQL_RETURN_OID(pgsql_oid);
3291 #endif
3292 	}
3293 
3294 	if ((pgsql_oid = lo_creat(pgsql, INV_READ|INV_WRITE)) == InvalidOid) {
3295 		php_error_docref(NULL, E_WARNING, "Unable to create PostgreSQL large object");
3296 		RETURN_FALSE;
3297 	}
3298 
3299 	PGSQL_RETURN_OID(pgsql_oid);
3300 }
3301 /* }}} */
3302 
3303 /* {{{ proto bool pg_lo_unlink([resource connection,] string large_object_oid)
3304    Delete a large object */
3305 PHP_FUNCTION(pg_lo_unlink)
3306 {
3307 	zval *pgsql_link = NULL;
3308 	zend_long oid_long;
3309 	char *oid_string, *end_ptr;
3310 	size_t oid_strlen;
3311 	PGconn *pgsql;
3312 	Oid oid;
3313 	zend_resource *link;
3314 	int argc = ZEND_NUM_ARGS();
3315 
3316 	/* accept string type since Oid type is unsigned int */
3317 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3318 								 "rs", &pgsql_link, &oid_string, &oid_strlen) == SUCCESS) {
3319 		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3320 		if ((oid_string+oid_strlen) != end_ptr) {
3321 			/* wrong integer format */
3322 			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3323 			RETURN_FALSE;
3324 		}
3325 		link = Z_RES_P(pgsql_link);
3326 	}
3327 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3328 								 "rl", &pgsql_link, &oid_long) == SUCCESS) {
3329 		if (oid_long <= (zend_long)InvalidOid) {
3330 			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3331 			RETURN_FALSE;
3332 		}
3333 		oid = (Oid)oid_long;
3334 		link = Z_RES_P(pgsql_link);
3335 	}
3336 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3337 								 "s", &oid_string, &oid_strlen) == SUCCESS) {
3338 		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3339 		if ((oid_string+oid_strlen) != end_ptr) {
3340 			/* wrong integer format */
3341 			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3342 			RETURN_FALSE;
3343 		}
3344 		link = FETCH_DEFAULT_LINK();
3345 		CHECK_DEFAULT_LINK(link);
3346 	}
3347 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3348 								 "l", &oid_long) == SUCCESS) {
3349 		if (oid_long <= (zend_long)InvalidOid) {
3350 			php_error_docref(NULL, E_NOTICE, "Invalid OID is specified");
3351 			RETURN_FALSE;
3352 		}
3353 		oid = (Oid)oid_long;
3354 		link = FETCH_DEFAULT_LINK();
3355 		CHECK_DEFAULT_LINK(link);
3356 	}
3357 	else {
3358 		php_error_docref(NULL, E_WARNING, "Requires 1 or 2 arguments");
3359 		RETURN_FALSE;
3360 	}
3361 
3362 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3363 		RETURN_FALSE;
3364 	}
3365 
3366 	if (lo_unlink(pgsql, oid) == -1) {
3367 		php_error_docref(NULL, E_WARNING, "Unable to delete PostgreSQL large object %u", oid);
3368 		RETURN_FALSE;
3369 	}
3370 	RETURN_TRUE;
3371 }
3372 /* }}} */
3373 
3374 /* {{{ proto resource pg_lo_open([resource connection,] int large_object_oid, string mode)
3375    Open a large object and return fd */
3376 PHP_FUNCTION(pg_lo_open)
3377 {
3378 	zval *pgsql_link = NULL;
3379 	zend_long oid_long;
3380 	char *oid_string, *end_ptr, *mode_string;
3381 	size_t oid_strlen, mode_strlen;
3382 	PGconn *pgsql;
3383 	Oid oid;
3384 	int pgsql_mode=0, pgsql_lofd;
3385 	int create = 0;
3386 	pgLofp *pgsql_lofp;
3387 	int argc = ZEND_NUM_ARGS();
3388 	zend_resource *link;
3389 
3390 	/* accept string type since Oid is unsigned int */
3391 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3392 								 "rss", &pgsql_link, &oid_string, &oid_strlen, &mode_string, &mode_strlen) == SUCCESS) {
3393 		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3394 		if ((oid_string+oid_strlen) != end_ptr) {
3395 			/* wrong integer format */
3396 			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3397 			RETURN_FALSE;
3398 		}
3399 		link = Z_RES_P(pgsql_link);
3400 	}
3401 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3402 								 "rls", &pgsql_link, &oid_long, &mode_string, &mode_strlen) == SUCCESS) {
3403 		if (oid_long <= (zend_long)InvalidOid) {
3404 			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3405 			RETURN_FALSE;
3406 		}
3407 		oid = (Oid)oid_long;
3408 		link = Z_RES_P(pgsql_link);
3409 	}
3410 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3411 								 "ss", &oid_string, &oid_strlen, &mode_string, &mode_strlen) == SUCCESS) {
3412 		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3413 		if ((oid_string+oid_strlen) != end_ptr) {
3414 			/* wrong integer format */
3415 			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3416 			RETURN_FALSE;
3417 		}
3418 		link = FETCH_DEFAULT_LINK();
3419 		CHECK_DEFAULT_LINK(link);
3420 	}
3421 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3422 								 "ls", &oid_long, &mode_string, &mode_strlen) == SUCCESS) {
3423 		if (oid_long <= (zend_long)InvalidOid) {
3424 			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3425 			RETURN_FALSE;
3426 		}
3427 		oid = (Oid)oid_long;
3428 		link = FETCH_DEFAULT_LINK();
3429 		CHECK_DEFAULT_LINK(link);
3430 	}
3431 	else {
3432 		php_error_docref(NULL, E_WARNING, "Requires 1 or 2 arguments");
3433 		RETURN_FALSE;
3434 	}
3435 
3436 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3437 		RETURN_FALSE;
3438 	}
3439 
3440 	/* r/w/+ is little bit more PHP-like than INV_READ/INV_WRITE and a lot of
3441 	   faster to type. Unfortunately, doesn't behave the same way as fopen()...
3442 	   (Jouni)
3443 	*/
3444 
3445 	if (strchr(mode_string, 'r') == mode_string) {
3446 		pgsql_mode |= INV_READ;
3447 		if (strchr(mode_string, '+') == mode_string+1) {
3448 			pgsql_mode |= INV_WRITE;
3449 		}
3450 	}
3451 	if (strchr(mode_string, 'w') == mode_string) {
3452 		pgsql_mode |= INV_WRITE;
3453 		create = 1;
3454 		if (strchr(mode_string, '+') == mode_string+1) {
3455 			pgsql_mode |= INV_READ;
3456 		}
3457 	}
3458 
3459 	pgsql_lofp = (pgLofp *) emalloc(sizeof(pgLofp));
3460 
3461 	if ((pgsql_lofd = lo_open(pgsql, oid, pgsql_mode)) == -1) {
3462 		if (create) {
3463 			if ((oid = lo_creat(pgsql, INV_READ|INV_WRITE)) == 0) {
3464 				efree(pgsql_lofp);
3465 				php_error_docref(NULL, E_WARNING, "Unable to create PostgreSQL large object");
3466 				RETURN_FALSE;
3467 			} else {
3468 				if ((pgsql_lofd = lo_open(pgsql, oid, pgsql_mode)) == -1) {
3469 					if (lo_unlink(pgsql, oid) == -1) {
3470 						efree(pgsql_lofp);
3471 						php_error_docref(NULL, E_WARNING, "Something is really messed up! Your database is badly corrupted in a way NOT related to PHP");
3472 						RETURN_FALSE;
3473 					}
3474 					efree(pgsql_lofp);
3475 					php_error_docref(NULL, E_WARNING, "Unable to open PostgreSQL large object");
3476 					RETURN_FALSE;
3477 				} else {
3478 					pgsql_lofp->conn = pgsql;
3479 					pgsql_lofp->lofd = pgsql_lofd;
3480 					RETURN_RES(zend_register_resource(pgsql_lofp, le_lofp));
3481 				}
3482 			}
3483 		} else {
3484 			efree(pgsql_lofp);
3485 			php_error_docref(NULL, E_WARNING, "Unable to open PostgreSQL large object");
3486 			RETURN_FALSE;
3487 		}
3488 	} else {
3489 		pgsql_lofp->conn = pgsql;
3490 		pgsql_lofp->lofd = pgsql_lofd;
3491 		RETURN_RES(zend_register_resource(pgsql_lofp, le_lofp));
3492 	}
3493 }
3494 /* }}} */
3495 
3496 /* {{{ proto bool pg_lo_close(resource large_object)
3497    Close a large object */
3498 PHP_FUNCTION(pg_lo_close)
3499 {
3500 	zval *pgsql_lofp;
3501 	pgLofp *pgsql;
3502 
3503 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pgsql_lofp) == FAILURE) {
3504 		return;
3505 	}
3506 
3507 	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_lofp), "PostgreSQL large object", le_lofp)) == NULL) {
3508 		RETURN_FALSE;
3509 	}
3510 
3511 	if (lo_close((PGconn *)pgsql->conn, pgsql->lofd) < 0) {
3512 		php_error_docref(NULL, E_WARNING, "Unable to close PostgreSQL large object descriptor %d", pgsql->lofd);
3513 		RETVAL_FALSE;
3514 	} else {
3515 		RETVAL_TRUE;
3516 	}
3517 
3518 	zend_list_close(Z_RES_P(pgsql_lofp));
3519 	return;
3520 }
3521 /* }}} */
3522 
3523 #define PGSQL_LO_READ_BUF_SIZE  8192
3524 
3525 /* {{{ proto string pg_lo_read(resource large_object [, int len])
3526    Read a large object */
3527 PHP_FUNCTION(pg_lo_read)
3528 {
3529 	zval *pgsql_id;
3530 	zend_long len;
3531 	size_t buf_len = PGSQL_LO_READ_BUF_SIZE;
3532 	int nbytes, argc = ZEND_NUM_ARGS();
3533 	zend_string *buf;
3534 	pgLofp *pgsql;
3535 
3536 	if (zend_parse_parameters(argc, "r|l", &pgsql_id, &len) == FAILURE) {
3537 		return;
3538 	}
3539 
3540 	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3541 		RETURN_FALSE;
3542 	}
3543 
3544 	if (argc > 1) {
3545 		buf_len = len < 0 ? 0 : len;
3546 	}
3547 
3548 	buf = zend_string_alloc(buf_len, 0);
3549 	if ((nbytes = lo_read((PGconn *)pgsql->conn, pgsql->lofd, ZSTR_VAL(buf), ZSTR_LEN(buf)))<0) {
3550 		zend_string_efree(buf);
3551 		RETURN_FALSE;
3552 	}
3553 
3554 	ZSTR_LEN(buf) = nbytes;
3555 	ZSTR_VAL(buf)[ZSTR_LEN(buf)] = '\0';
3556 	RETURN_NEW_STR(buf);
3557 }
3558 /* }}} */
3559 
3560 /* {{{ proto int pg_lo_write(resource large_object, string buf [, int len])
3561    Write a large object */
3562 PHP_FUNCTION(pg_lo_write)
3563 {
3564   	zval *pgsql_id;
3565   	char *str;
3566   	zend_long z_len;
3567 	size_t str_len, nbytes;
3568 	size_t len;
3569 	pgLofp *pgsql;
3570 	int argc = ZEND_NUM_ARGS();
3571 
3572 	if (zend_parse_parameters(argc, "rs|l", &pgsql_id, &str, &str_len, &z_len) == FAILURE) {
3573 		return;
3574 	}
3575 
3576 	if (argc > 2) {
3577 		if (z_len > (zend_long)str_len) {
3578 			php_error_docref(NULL, E_WARNING, "Cannot write more than buffer size %zu. Tried to write " ZEND_LONG_FMT, str_len, z_len);
3579 			RETURN_FALSE;
3580 		}
3581 		if (z_len < 0) {
3582 			php_error_docref(NULL, E_WARNING, "Buffer size must be larger than 0, but " ZEND_LONG_FMT " was specified", z_len);
3583 			RETURN_FALSE;
3584 		}
3585 		len = z_len;
3586 	}
3587 	else {
3588 		len = str_len;
3589 	}
3590 
3591 	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3592 		RETURN_FALSE;
3593 	}
3594 
3595 	if ((nbytes = lo_write((PGconn *)pgsql->conn, pgsql->lofd, str, len)) == (size_t)-1) {
3596 		RETURN_FALSE;
3597 	}
3598 
3599 	RETURN_LONG(nbytes);
3600 }
3601 /* }}} */
3602 
3603 /* {{{ proto int pg_lo_read_all(resource large_object)
3604    Read a large object and send straight to browser */
3605 PHP_FUNCTION(pg_lo_read_all)
3606 {
3607   	zval *pgsql_id;
3608 	int tbytes;
3609 	volatile int nbytes;
3610 	char buf[PGSQL_LO_READ_BUF_SIZE];
3611 	pgLofp *pgsql;
3612 
3613 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pgsql_id) == FAILURE) {
3614 		return;
3615 	}
3616 
3617 	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3618 		RETURN_FALSE;
3619 	}
3620 
3621 	tbytes = 0;
3622 	while ((nbytes = lo_read((PGconn *)pgsql->conn, pgsql->lofd, buf, PGSQL_LO_READ_BUF_SIZE))>0) {
3623 		PHPWRITE(buf, nbytes);
3624 		tbytes += nbytes;
3625 	}
3626 	RETURN_LONG(tbytes);
3627 }
3628 /* }}} */
3629 
3630 /* {{{ proto int pg_lo_import([resource connection, ] string filename [, mixed oid])
3631    Import large object direct from filesystem */
3632 PHP_FUNCTION(pg_lo_import)
3633 {
3634 	zval *pgsql_link = NULL, *oid = NULL;
3635 	char *file_in;
3636 	size_t name_len;
3637 	int argc = ZEND_NUM_ARGS();
3638 	PGconn *pgsql;
3639 	Oid returned_oid;
3640 	zend_resource *link;
3641 
3642 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3643 								 "rp|z", &pgsql_link, &file_in, &name_len, &oid) == SUCCESS) {
3644 		link = Z_RES_P(pgsql_link);
3645 	}
3646 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3647 									  "p|z", &file_in, &name_len, &oid) == SUCCESS) {
3648 		link = FETCH_DEFAULT_LINK();
3649 		CHECK_DEFAULT_LINK(link);
3650 	}
3651 	/* old calling convention, deprecated since PHP 4.2 */
3652 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3653 									  "pr", &file_in, &name_len, &pgsql_link ) == SUCCESS) {
3654 		php_error_docref(NULL, E_NOTICE, "Old API is used");
3655 		link = Z_RES_P(pgsql_link);
3656 	}
3657 	else {
3658 		WRONG_PARAM_COUNT;
3659 	}
3660 
3661 	if (php_check_open_basedir(file_in)) {
3662 		RETURN_FALSE;
3663 	}
3664 
3665 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3666 		RETURN_FALSE;
3667 	}
3668 
3669 	if (oid) {
3670 #ifndef HAVE_PG_LO_IMPORT_WITH_OID
3671 		php_error_docref(NULL, E_NOTICE, "OID value passing not supported");
3672 #else
3673 		Oid wanted_oid;
3674 		switch (Z_TYPE_P(oid)) {
3675 		case IS_STRING:
3676 			{
3677 				char *end_ptr;
3678 				wanted_oid = (Oid)strtoul(Z_STRVAL_P(oid), &end_ptr, 10);
3679 				if ((Z_STRVAL_P(oid)+Z_STRLEN_P(oid)) != end_ptr) {
3680 				/* wrong integer format */
3681 				php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3682 				RETURN_FALSE;
3683 				}
3684 			}
3685 			break;
3686 		case IS_LONG:
3687 			if (Z_LVAL_P(oid) < (zend_long)InvalidOid) {
3688 				php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3689 				RETURN_FALSE;
3690 			}
3691 			wanted_oid = (Oid)Z_LVAL_P(oid);
3692 			break;
3693 		default:
3694 			php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3695 			RETURN_FALSE;
3696         }
3697 
3698        returned_oid = lo_import_with_oid(pgsql, file_in, wanted_oid);
3699 
3700 	   if (returned_oid == InvalidOid) {
3701 		   RETURN_FALSE;
3702 	   }
3703 
3704 	   PGSQL_RETURN_OID(returned_oid);
3705 #endif
3706 	}
3707 
3708 	returned_oid = lo_import(pgsql, file_in);
3709 
3710 	if (returned_oid == InvalidOid) {
3711 		RETURN_FALSE;
3712 	}
3713 	PGSQL_RETURN_OID(returned_oid);
3714 }
3715 /* }}} */
3716 
3717 /* {{{ proto bool pg_lo_export([resource connection, ] int objoid, string filename)
3718    Export large object direct to filesystem */
3719 PHP_FUNCTION(pg_lo_export)
3720 {
3721 	zval *pgsql_link = NULL;
3722 	char *file_out, *oid_string, *end_ptr;
3723 	size_t oid_strlen;
3724 	size_t name_len;
3725 	zend_long oid_long;
3726 	Oid oid;
3727 	PGconn *pgsql;
3728 	int argc = ZEND_NUM_ARGS();
3729 	zend_resource *link;
3730 
3731 	/* allow string to handle large OID value correctly */
3732 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3733 								 "rlp", &pgsql_link, &oid_long, &file_out, &name_len) == SUCCESS) {
3734 		if (oid_long <= (zend_long)InvalidOid) {
3735 			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3736 			RETURN_FALSE;
3737 		}
3738 		oid = (Oid)oid_long;
3739 		link = Z_RES_P(pgsql_link);
3740 	}
3741 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3742 								 "rss", &pgsql_link, &oid_string, &oid_strlen, &file_out, &name_len) == SUCCESS) {
3743 		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3744 		if ((oid_string+oid_strlen) != end_ptr) {
3745 			/* wrong integer format */
3746 			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3747 			RETURN_FALSE;
3748 		}
3749 		link = Z_RES_P(pgsql_link);
3750 	}
3751 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3752 									  "lp",  &oid_long, &file_out, &name_len) == SUCCESS) {
3753 		if (oid_long <= (zend_long)InvalidOid) {
3754 			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3755 			RETURN_FALSE;
3756 		}
3757 		oid = (Oid)oid_long;
3758 		link = FETCH_DEFAULT_LINK();
3759 		CHECK_DEFAULT_LINK(link);
3760 	}
3761 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3762 								 "sp", &oid_string, &oid_strlen, &file_out, &name_len) == SUCCESS) {
3763 		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3764 		if ((oid_string+oid_strlen) != end_ptr) {
3765 			/* wrong integer format */
3766 			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3767 			RETURN_FALSE;
3768 		}
3769 		link = FETCH_DEFAULT_LINK();
3770 		CHECK_DEFAULT_LINK(link);
3771 	}
3772 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3773 								 "spr", &oid_string, &oid_strlen, &file_out, &name_len, &pgsql_link) == SUCCESS) {
3774 		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3775 		if ((oid_string+oid_strlen) != end_ptr) {
3776 			/* wrong integer format */
3777 			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3778 			RETURN_FALSE;
3779 		}
3780 		link = Z_RES_P(pgsql_link);
3781 	}
3782 	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3783 									  "lpr", &oid_long, &file_out, &name_len, &pgsql_link) == SUCCESS) {
3784 		php_error_docref(NULL, E_NOTICE, "Old API is used");
3785 		if (oid_long <= (zend_long)InvalidOid) {
3786 			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3787 			RETURN_FALSE;
3788 		}
3789 		oid = (Oid)oid_long;
3790 		link = Z_RES_P(pgsql_link);
3791 	}
3792 	else {
3793 		php_error_docref(NULL, E_WARNING, "Requires 2 or 3 arguments");
3794 		RETURN_FALSE;
3795 	}
3796 
3797 	if (php_check_open_basedir(file_out)) {
3798 		RETURN_FALSE;
3799 	}
3800 
3801 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3802 		RETURN_FALSE;
3803 	}
3804 
3805 	if (lo_export(pgsql, oid, file_out) == -1) {
3806 		RETURN_FALSE;
3807 	}
3808 	RETURN_TRUE;
3809 }
3810 /* }}} */
3811 
3812 /* {{{ proto bool pg_lo_seek(resource large_object, int offset [, int whence])
3813    Seeks position of large object */
3814 PHP_FUNCTION(pg_lo_seek)
3815 {
3816 	zval *pgsql_id = NULL;
3817 	zend_long result, offset = 0, whence = SEEK_CUR;
3818 	pgLofp *pgsql;
3819 	int argc = ZEND_NUM_ARGS();
3820 
3821 	if (zend_parse_parameters(argc, "rl|l", &pgsql_id, &offset, &whence) == FAILURE) {
3822 		return;
3823 	}
3824 	if (whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) {
3825 		php_error_docref(NULL, E_WARNING, "Invalid whence parameter");
3826 		return;
3827 	}
3828 
3829 	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3830 		RETURN_FALSE;
3831 	}
3832 
3833 #if HAVE_PG_LO64
3834 	if (PQserverVersion((PGconn *)pgsql->conn) >= 90300) {
3835 		result = lo_lseek64((PGconn *)pgsql->conn, pgsql->lofd, offset, (int)whence);
3836 	} else {
3837 		result = lo_lseek((PGconn *)pgsql->conn, pgsql->lofd, (int)offset, (int)whence);
3838 	}
3839 #else
3840 	result = lo_lseek((PGconn *)pgsql->conn, pgsql->lofd, offset, whence);
3841 #endif
3842 	if (result > -1) {
3843 		RETURN_TRUE;
3844 	} else {
3845 		RETURN_FALSE;
3846 	}
3847 }
3848 /* }}} */
3849 
3850 /* {{{ proto int pg_lo_tell(resource large_object)
3851    Returns current position of large object */
3852 PHP_FUNCTION(pg_lo_tell)
3853 {
3854 	zval *pgsql_id = NULL;
3855 	zend_long offset = 0;
3856 	pgLofp *pgsql;
3857 	int argc = ZEND_NUM_ARGS();
3858 
3859 	if (zend_parse_parameters(argc, "r", &pgsql_id) == FAILURE) {
3860 		return;
3861 	}
3862 
3863 	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3864 		RETURN_FALSE;
3865 	}
3866 
3867 #if HAVE_PG_LO64
3868 	if (PQserverVersion((PGconn *)pgsql->conn) >= 90300) {
3869 		offset = lo_tell64((PGconn *)pgsql->conn, pgsql->lofd);
3870 	} else {
3871 		offset = lo_tell((PGconn *)pgsql->conn, pgsql->lofd);
3872 	}
3873 #else
3874 	offset = lo_tell((PGconn *)pgsql->conn, pgsql->lofd);
3875 #endif
3876 	RETURN_LONG(offset);
3877 }
3878 /* }}} */
3879 
3880 #if HAVE_PG_LO_TRUNCATE
3881 /* {{{ proto bool pg_lo_truncate(resource large_object, int size)
3882    Truncate large object to size */
3883 PHP_FUNCTION(pg_lo_truncate)
3884 {
3885 	zval *pgsql_id = NULL;
3886 	size_t size;
3887 	pgLofp *pgsql;
3888 	int argc = ZEND_NUM_ARGS();
3889 	int result;
3890 
3891 	if (zend_parse_parameters(argc, "rl", &pgsql_id, &size) == FAILURE) {
3892 		return;
3893 	}
3894 
3895 	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3896 		RETURN_FALSE;
3897 	}
3898 
3899 #if HAVE_PG_LO64
3900 	if (PQserverVersion((PGconn *)pgsql->conn) >= 90300) {
3901 		result = lo_truncate64((PGconn *)pgsql->conn, pgsql->lofd, size);
3902 	} else {
3903 		result = lo_truncate((PGconn *)pgsql->conn, pgsql->lofd, size);
3904 	}
3905 #else
3906 	result = lo_truncate((PGconn *)pgsql->conn, pgsql->lofd, size);
3907 #endif
3908 	if (!result) {
3909 		RETURN_TRUE;
3910 	} else {
3911 		RETURN_FALSE;
3912 	}
3913 }
3914 /* }}} */
3915 #endif
3916 
3917 #if HAVE_PQSETERRORVERBOSITY
3918 /* {{{ proto int pg_set_error_verbosity([resource connection,] int verbosity)
3919    Set error verbosity */
3920 PHP_FUNCTION(pg_set_error_verbosity)
3921 {
3922 	zval *pgsql_link = NULL;
3923 	zend_long verbosity;
3924 	int argc = ZEND_NUM_ARGS();
3925 	PGconn *pgsql;
3926 	zend_resource *link;
3927 
3928 	if (argc == 1) {
3929 		if (zend_parse_parameters(argc, "l", &verbosity) == FAILURE) {
3930 			return;
3931 		}
3932 		link = FETCH_DEFAULT_LINK();
3933 		CHECK_DEFAULT_LINK(link);
3934 	} else {
3935 		if (zend_parse_parameters(argc, "rl", &pgsql_link, &verbosity) == FAILURE) {
3936 			return;
3937 		}
3938 		link = Z_RES_P(pgsql_link);
3939 	}
3940 
3941 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3942 		RETURN_FALSE;
3943 	}
3944 
3945 	if (verbosity & (PQERRORS_TERSE|PQERRORS_DEFAULT|PQERRORS_VERBOSE)) {
3946 		RETURN_LONG(PQsetErrorVerbosity(pgsql, verbosity));
3947 	} else {
3948 		RETURN_FALSE;
3949 	}
3950 }
3951 /* }}} */
3952 #endif
3953 
3954 #ifdef HAVE_PQCLIENTENCODING
3955 /* {{{ proto int pg_set_client_encoding([resource connection,] string encoding)
3956    Set client encoding */
3957 PHP_FUNCTION(pg_set_client_encoding)
3958 {
3959 	char *encoding;
3960 	size_t encoding_len;
3961 	zval *pgsql_link = NULL;
3962 	int argc = ZEND_NUM_ARGS();
3963 	PGconn *pgsql;
3964 	zend_resource *link;
3965 
3966 	if (argc == 1) {
3967 		if (zend_parse_parameters(argc, "s", &encoding, &encoding_len) == FAILURE) {
3968 			return;
3969 		}
3970 		link = FETCH_DEFAULT_LINK();
3971 		CHECK_DEFAULT_LINK(link);
3972 	} else {
3973 		if (zend_parse_parameters(argc, "rs", &pgsql_link, &encoding, &encoding_len) == FAILURE) {
3974 			return;
3975 		}
3976 		link = Z_RES_P(pgsql_link);
3977 	}
3978 
3979 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3980 		RETURN_FALSE;
3981 	}
3982 
3983 	RETURN_LONG(PQsetClientEncoding(pgsql, encoding));
3984 }
3985 /* }}} */
3986 
3987 /* {{{ proto string pg_client_encoding([resource connection])
3988    Get the current client encoding */
3989 PHP_FUNCTION(pg_client_encoding)
3990 {
3991 	zval *pgsql_link = NULL;
3992 	int argc = ZEND_NUM_ARGS();
3993 	PGconn *pgsql;
3994 	zend_resource *link;
3995 
3996 	if (zend_parse_parameters(argc, "|r", &pgsql_link) == FAILURE) {
3997 		return;
3998 	}
3999 
4000 	if (argc == 0) {
4001 		link = FETCH_DEFAULT_LINK();
4002 		CHECK_DEFAULT_LINK(link);
4003 	} else {
4004 		link = Z_RES_P(pgsql_link);
4005 	}
4006 
4007 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4008 		RETURN_FALSE;
4009 	}
4010 
4011 	/* Just do the same as found in PostgreSQL sources... */
4012 
4013 	RETURN_STRING((char *) pg_encoding_to_char(PQclientEncoding(pgsql)));
4014 }
4015 /* }}} */
4016 #endif
4017 
4018 #if !HAVE_PQGETCOPYDATA
4019 #define	COPYBUFSIZ	8192
4020 #endif
4021 
4022 /* {{{ proto bool pg_end_copy([resource connection])
4023    Sync with backend. Completes the Copy command */
4024 PHP_FUNCTION(pg_end_copy)
4025 {
4026 	zval *pgsql_link = NULL;
4027 	int argc = ZEND_NUM_ARGS();
4028 	PGconn *pgsql;
4029 	int result = 0;
4030 	zend_resource *link;
4031 
4032 	if (zend_parse_parameters(argc, "|r", &pgsql_link) == FAILURE) {
4033 		return;
4034 	}
4035 
4036 	if (argc == 0) {
4037 		link = FETCH_DEFAULT_LINK();
4038 		CHECK_DEFAULT_LINK(link);
4039 	} else {
4040 		link = Z_RES_P(pgsql_link);
4041 	}
4042 
4043 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4044 		RETURN_FALSE;
4045 	}
4046 
4047 	result = PQendcopy(pgsql);
4048 
4049 	if (result!=0) {
4050 		PHP_PQ_ERROR("Query failed: %s", pgsql);
4051 		RETURN_FALSE;
4052 	}
4053 	RETURN_TRUE;
4054 }
4055 /* }}} */
4056 
4057 /* {{{ proto bool pg_put_line([resource connection,] string query)
4058    Send null-terminated string to backend server*/
4059 PHP_FUNCTION(pg_put_line)
4060 {
4061 	char *query;
4062 	zval *pgsql_link = NULL;
4063 	size_t query_len;
4064 	PGconn *pgsql;
4065 	zend_resource *link;
4066 	int result = 0, argc = ZEND_NUM_ARGS();
4067 
4068 	if (argc == 1) {
4069 		if (zend_parse_parameters(argc, "s", &query, &query_len) == FAILURE) {
4070 			return;
4071 		}
4072 		link = FETCH_DEFAULT_LINK();
4073 		CHECK_DEFAULT_LINK(link);
4074 	} else {
4075 		if (zend_parse_parameters(argc, "rs", &pgsql_link, &query, &query_len) == FAILURE) {
4076 			return;
4077 		}
4078 		link = Z_RES_P(pgsql_link);
4079 	}
4080 
4081 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4082 		RETURN_FALSE;
4083 	}
4084 
4085 	result = PQputline(pgsql, query);
4086 	if (result==EOF) {
4087 		PHP_PQ_ERROR("Query failed: %s", pgsql);
4088 		RETURN_FALSE;
4089 	}
4090 	RETURN_TRUE;
4091 }
4092 /* }}} */
4093 
4094 /* {{{ proto array pg_copy_to(resource connection, string table_name [, string delimiter [, string null_as]])
4095    Copy table to array */
4096 PHP_FUNCTION(pg_copy_to)
4097 {
4098 	zval *pgsql_link;
4099 	char *table_name, *pg_delim = NULL, *pg_null_as = NULL;
4100 	size_t table_name_len, pg_delim_len, pg_null_as_len, free_pg_null = 0;
4101 	char *query;
4102 	PGconn *pgsql;
4103 	PGresult *pgsql_result;
4104 	ExecStatusType status;
4105 	char *csv = (char *)NULL;
4106 	int argc = ZEND_NUM_ARGS();
4107 
4108 	if (zend_parse_parameters(argc, "rs|ss",
4109 							  &pgsql_link, &table_name, &table_name_len,
4110 							  &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len) == FAILURE) {
4111 		return;
4112 	}
4113 
4114 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4115 		RETURN_FALSE;
4116 	}
4117 
4118 	if (!pg_delim) {
4119 		pg_delim = "\t";
4120 	}
4121 	if (!pg_null_as) {
4122 		pg_null_as = estrdup("\\\\N");
4123 		free_pg_null = 1;
4124 	}
4125 
4126 	spprintf(&query, 0, "COPY %s TO STDOUT DELIMITER E'%c' NULL AS E'%s'", table_name, *pg_delim, pg_null_as);
4127 
4128 	while ((pgsql_result = PQgetResult(pgsql))) {
4129 		PQclear(pgsql_result);
4130 	}
4131 	pgsql_result = PQexec(pgsql, query);
4132 	if (free_pg_null) {
4133 		efree(pg_null_as);
4134 	}
4135 	efree(query);
4136 
4137 	if (pgsql_result) {
4138 		status = PQresultStatus(pgsql_result);
4139 	} else {
4140 		status = (ExecStatusType) PQstatus(pgsql);
4141 	}
4142 
4143 	switch (status) {
4144 		case PGRES_COPY_OUT:
4145 			if (pgsql_result) {
4146 				int copydone = 0;
4147 #if !HAVE_PQGETCOPYDATA
4148 				char copybuf[COPYBUFSIZ];
4149 #endif
4150 
4151 				PQclear(pgsql_result);
4152 				array_init(return_value);
4153 #if HAVE_PQGETCOPYDATA
4154 				while (!copydone)
4155 				{
4156 					int ret = PQgetCopyData(pgsql, &csv, 0);
4157 					switch (ret) {
4158 						case -1:
4159 							copydone = 1;
4160 							break;
4161 						case 0:
4162 						case -2:
4163 							PHP_PQ_ERROR("getline failed: %s", pgsql);
4164 							RETURN_FALSE;
4165 							break;
4166 						default:
4167 							add_next_index_string(return_value, csv);
4168 							PQfreemem(csv);
4169 							break;
4170 					}
4171 				}
4172 #else
4173 				while (!copydone)
4174 				{
4175 					if ((ret = PQgetline(pgsql, copybuf, COPYBUFSIZ))) {
4176 						PHP_PQ_ERROR("getline failed: %s", pgsql);
4177 						RETURN_FALSE;
4178 					}
4179 
4180 					if (copybuf[0] == '\\' &&
4181 						copybuf[1] == '.' &&
4182 						copybuf[2] == '\0')
4183 					{
4184 						copydone = 1;
4185 					}
4186 					else
4187 					{
4188 						if (csv == (char *)NULL) {
4189 							csv = estrdup(copybuf);
4190 						} else {
4191 							csv = (char *)erealloc(csv, strlen(csv) + sizeof(char)*(COPYBUFSIZ+1));
4192 							strcat(csv, copybuf);
4193 						}
4194 
4195 						switch (ret)
4196 						{
4197 							case EOF:
4198 								copydone = 1;
4199 							case 0:
4200 								add_next_index_string(return_value, csv);
4201 								efree(csv);
4202 								csv = (char *)NULL;
4203 								break;
4204 							case 1:
4205 								break;
4206 						}
4207 					}
4208 				}
4209 				if (PQendcopy(pgsql)) {
4210 					PHP_PQ_ERROR("endcopy failed: %s", pgsql);
4211 					RETURN_FALSE;
4212 				}
4213 #endif
4214 				while ((pgsql_result = PQgetResult(pgsql))) {
4215 					PQclear(pgsql_result);
4216 				}
4217 			} else {
4218 				PQclear(pgsql_result);
4219 				RETURN_FALSE;
4220 			}
4221 			break;
4222 		default:
4223 			PQclear(pgsql_result);
4224 			PHP_PQ_ERROR("Copy command failed: %s", pgsql);
4225 			RETURN_FALSE;
4226 			break;
4227 	}
4228 }
4229 /* }}} */
4230 
4231 /* {{{ proto bool pg_copy_from(resource connection, string table_name , array rows [, string delimiter [, string null_as]])
4232    Copy table from array */
4233 PHP_FUNCTION(pg_copy_from)
4234 {
4235 	zval *pgsql_link = NULL, *pg_rows;
4236 	zval *value;
4237 	char *table_name, *pg_delim = NULL, *pg_null_as = NULL;
4238 	size_t  table_name_len, pg_delim_len, pg_null_as_len;
4239 	int  pg_null_as_free = 0;
4240 	char *query;
4241 	PGconn *pgsql;
4242 	PGresult *pgsql_result;
4243 	ExecStatusType status;
4244 	int argc = ZEND_NUM_ARGS();
4245 
4246 	if (zend_parse_parameters(argc, "rsa|ss",
4247 							  &pgsql_link, &table_name, &table_name_len, &pg_rows,
4248 							  &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len) == FAILURE) {
4249 		return;
4250 	}
4251 
4252 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4253 		RETURN_FALSE;
4254 	}
4255 
4256 	if (!pg_delim) {
4257 		pg_delim = "\t";
4258 	}
4259 	if (!pg_null_as) {
4260 		pg_null_as = estrdup("\\\\N");
4261 		pg_null_as_free = 1;
4262 	}
4263 
4264 	spprintf(&query, 0, "COPY %s FROM STDIN DELIMITER E'%c' NULL AS E'%s'", table_name, *pg_delim, pg_null_as);
4265 	while ((pgsql_result = PQgetResult(pgsql))) {
4266 		PQclear(pgsql_result);
4267 	}
4268 	pgsql_result = PQexec(pgsql, query);
4269 
4270 	if (pg_null_as_free) {
4271 		efree(pg_null_as);
4272 	}
4273 	efree(query);
4274 
4275 	if (pgsql_result) {
4276 		status = PQresultStatus(pgsql_result);
4277 	} else {
4278 		status = (ExecStatusType) PQstatus(pgsql);
4279 	}
4280 
4281 	switch (status) {
4282 		case PGRES_COPY_IN:
4283 			if (pgsql_result) {
4284 				int command_failed = 0;
4285 				PQclear(pgsql_result);
4286 #if HAVE_PQPUTCOPYDATA
4287 				ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), value) {
4288 					zend_string *tmp = zval_try_get_string(value);
4289 					if (UNEXPECTED(!tmp)) {
4290 						return;
4291 					}
4292 					query = (char *)emalloc(ZSTR_LEN(tmp) + 2);
4293 					strlcpy(query, ZSTR_VAL(tmp), ZSTR_LEN(tmp) + 2);
4294 					if (ZSTR_LEN(tmp) > 0 && *(query + ZSTR_LEN(tmp) - 1) != '\n') {
4295 						strlcat(query, "\n", ZSTR_LEN(tmp) + 2);
4296 					}
4297 					if (PQputCopyData(pgsql, query, (int)strlen(query)) != 1) {
4298 						efree(query);
4299 						zend_string_release(tmp);
4300 						PHP_PQ_ERROR("copy failed: %s", pgsql);
4301 						RETURN_FALSE;
4302 					}
4303 					efree(query);
4304 					zend_string_release(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 					zend_string *tmp = zval_try_get_string(value);
4314 					if (UNEXPECTED(!tmp)) {
4315 						return;
4316 					}
4317 					query = (char *)emalloc(ZSTR_LEN(tmp) + 2);
4318 					strlcpy(query, ZSTR_LVAL(tmp), ZSTR_LEN(tmp) + 2);
4319 					if (ZSTR_LEN(tmp) > 0 && *(query + ZSTR_LEN(tmp) - 1) != '\n') {
4320 						strlcat(query, "\n", ZSTR_LEN(tmp) + 2);
4321 					}
4322 					if (PQputline(pgsql, query)==EOF) {
4323 						efree(query);
4324 						zend_string_release(tmp);
4325 						PHP_PQ_ERROR("copy failed: %s", pgsql);
4326 						RETURN_FALSE;
4327 					}
4328 					efree(query);
4329 					zend_string_release(tmp);
4330 				} ZEND_HASH_FOREACH_END();
4331 
4332 				if (PQputline(pgsql, "\\.\n") == EOF) {
4333 					PHP_PQ_ERROR("putline failed: %s", pgsql);
4334 					RETURN_FALSE;
4335 				}
4336 				if (PQendcopy(pgsql)) {
4337 					PHP_PQ_ERROR("endcopy failed: %s", pgsql);
4338 					RETURN_FALSE;
4339 				}
4340 #endif
4341 				while ((pgsql_result = PQgetResult(pgsql))) {
4342 					if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
4343 						PHP_PQ_ERROR("Copy command failed: %s", pgsql);
4344 						command_failed = 1;
4345 					}
4346 					PQclear(pgsql_result);
4347 				}
4348 				if (command_failed) {
4349 					RETURN_FALSE;
4350 				}
4351 			} else {
4352 				PQclear(pgsql_result);
4353 				RETURN_FALSE;
4354 			}
4355 			RETURN_TRUE;
4356 			break;
4357 		default:
4358 			PQclear(pgsql_result);
4359 			PHP_PQ_ERROR("Copy command failed: %s", pgsql);
4360 			RETURN_FALSE;
4361 			break;
4362 	}
4363 }
4364 /* }}} */
4365 
4366 #ifdef HAVE_PQESCAPE
4367 /* {{{ proto string pg_escape_string([resource connection,] string data)
4368    Escape string for text/char type */
4369 PHP_FUNCTION(pg_escape_string)
4370 {
4371 	zend_string *from = NULL, *to = NULL;
4372 	zval *pgsql_link;
4373 	zend_resource *link;
4374 #ifdef HAVE_PQESCAPE_CONN
4375 	PGconn *pgsql;
4376 #endif
4377 
4378 	switch (ZEND_NUM_ARGS()) {
4379 		case 1:
4380 			if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &from) == FAILURE) {
4381 				return;
4382 			}
4383 			link = FETCH_DEFAULT_LINK();
4384 			break;
4385 		default:
4386 			if (zend_parse_parameters(ZEND_NUM_ARGS(), "rS", &pgsql_link, &from) == FAILURE) {
4387 				return;
4388 			}
4389 			link = Z_RES_P(pgsql_link);
4390 			break;
4391 	}
4392 
4393 	to = zend_string_safe_alloc(ZSTR_LEN(from), 2, 0, 0);
4394 #ifdef HAVE_PQESCAPE_CONN
4395 	if (link) {
4396 		if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4397 			RETURN_FALSE;
4398 		}
4399 		ZSTR_LEN(to) = PQescapeStringConn(pgsql, ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from), NULL);
4400 	} else
4401 #endif
4402 	{
4403 		ZSTR_LEN(to) = PQescapeString(ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from));
4404 	}
4405 
4406 	to = zend_string_truncate(to, ZSTR_LEN(to), 0);
4407 	RETURN_NEW_STR(to);
4408 }
4409 /* }}} */
4410 
4411 /* {{{ proto string pg_escape_bytea([resource connection,] string data)
4412    Escape binary for bytea type  */
4413 PHP_FUNCTION(pg_escape_bytea)
4414 {
4415 	char *from = NULL, *to = NULL;
4416 	size_t to_len;
4417 	size_t from_len;
4418 #ifdef HAVE_PQESCAPE_BYTEA_CONN
4419 	PGconn *pgsql;
4420 #endif
4421 	zval *pgsql_link;
4422 	zend_resource *link;
4423 
4424 	switch (ZEND_NUM_ARGS()) {
4425 		case 1:
4426 			if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &from, &from_len) == FAILURE) {
4427 				return;
4428 			}
4429 			link = FETCH_DEFAULT_LINK();
4430 			break;
4431 		default:
4432 			if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &pgsql_link, &from, &from_len) == FAILURE) {
4433 				return;
4434 			}
4435 			link = Z_RES_P(pgsql_link);
4436 			break;
4437 	}
4438 
4439 #ifdef HAVE_PQESCAPE_BYTEA_CONN
4440 	if (link) {
4441 		if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4442 			RETURN_FALSE;
4443 		}
4444 		to = (char *)PQescapeByteaConn(pgsql, (unsigned char *)from, (size_t)from_len, &to_len);
4445 	} else
4446 #endif
4447 		to = (char *)PQescapeBytea((unsigned char*)from, from_len, &to_len);
4448 
4449 	RETVAL_STRINGL(to, to_len-1); /* to_len includes additional '\0' */
4450 	PQfreemem(to);
4451 }
4452 /* }}} */
4453 
4454 #if !HAVE_PQUNESCAPEBYTEA
4455 /* PQunescapeBytea() from PostgreSQL 7.3 to provide bytea unescape feature to 7.2 users.
4456    Renamed to php_pgsql_unescape_bytea() */
4457 /*
4458  *		PQunescapeBytea - converts the null terminated string representation
4459  *		of a bytea, strtext, into binary, filling a buffer. It returns a
4460  *		pointer to the buffer which is NULL on error, and the size of the
4461  *		buffer in retbuflen. The pointer may subsequently be used as an
4462  *		argument to the function free(3). It is the reverse of PQescapeBytea.
4463  *
4464  *		The following transformations are reversed:
4465  *		'\0' == ASCII  0 == \000
4466  *		'\'' == ASCII 39 == \'
4467  *		'\\' == ASCII 92 == \\
4468  *
4469  *		States:
4470  *		0	normal		0->1->2->3->4
4471  *		1	\			   1->5
4472  *		2	\0			   1->6
4473  *		3	\00
4474  *		4	\000
4475  *		5	\'
4476  *		6	\\
4477  */
4478 static unsigned char * php_pgsql_unescape_bytea(unsigned char *strtext, size_t *retbuflen) /* {{{ */
4479 {
4480 	size_t     buflen;
4481 	unsigned char *buffer,
4482 			   *sp,
4483 			   *bp;
4484 	unsigned int state = 0;
4485 
4486 	if (strtext == NULL)
4487 		return NULL;
4488 	buflen = strlen(strtext);	/* will shrink, also we discover if
4489 								 * strtext */
4490 	buffer = (unsigned char *) emalloc(buflen);	/* isn't NULL terminated */
4491 	for (bp = buffer, sp = strtext; *sp != '\0'; bp++, sp++)
4492 	{
4493 		switch (state)
4494 		{
4495 			case 0:
4496 				if (*sp == '\\')
4497 					state = 1;
4498 				*bp = *sp;
4499 				break;
4500 			case 1:
4501 				if (*sp == '\'')	/* state=5 */
4502 				{				/* replace \' with 39 */
4503 					bp--;
4504 					*bp = '\'';
4505 					buflen--;
4506 					state = 0;
4507 				}
4508 				else if (*sp == '\\')	/* state=6 */
4509 				{				/* replace \\ with 92 */
4510 					bp--;
4511 					*bp = '\\';
4512 					buflen--;
4513 					state = 0;
4514 				}
4515 				else
4516 				{
4517 					if (isdigit(*sp))
4518 						state = 2;
4519 					else
4520 						state = 0;
4521 					*bp = *sp;
4522 				}
4523 				break;
4524 			case 2:
4525 				if (isdigit(*sp))
4526 					state = 3;
4527 				else
4528 					state = 0;
4529 				*bp = *sp;
4530 				break;
4531 			case 3:
4532 				if (isdigit(*sp))		/* state=4 */
4533 				{
4534 					unsigned char *start, *end, buf[4]; /* 000 + '\0' */
4535 
4536 					bp -= 3;
4537 					memcpy(buf, sp-2, 3);
4538 					buf[3] = '\0';
4539 					start = buf;
4540 					*bp = (unsigned char)strtoul(start, (char **)&end, 8);
4541 					buflen -= 3;
4542 					state = 0;
4543 				}
4544 				else
4545 				{
4546 					*bp = *sp;
4547 					state = 0;
4548 				}
4549 				break;
4550 		}
4551 	}
4552 	buffer = erealloc(buffer, buflen+1);
4553 	buffer[buflen] = '\0';
4554 
4555 	*retbuflen = buflen;
4556 	return buffer;
4557 }
4558 /* }}} */
4559 #endif
4560 
4561 /* {{{ proto string pg_unescape_bytea(string data)
4562    Unescape binary for bytea type  */
4563 PHP_FUNCTION(pg_unescape_bytea)
4564 {
4565 	char *from = NULL, *to = NULL, *tmp = NULL;
4566 	size_t to_len;
4567 	size_t from_len;
4568 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
4569 							  &from, &from_len) == FAILURE) {
4570 		return;
4571 	}
4572 
4573 #if HAVE_PQUNESCAPEBYTEA
4574 	tmp = (char *)PQunescapeBytea((unsigned char*)from, &to_len);
4575 	to = estrndup(tmp, to_len);
4576 	PQfreemem(tmp);
4577 #else
4578 	to = (char *)php_pgsql_unescape_bytea((unsigned char*)from, &to_len);
4579 #endif
4580 	if (!to) {
4581 		php_error_docref(NULL, E_WARNING,"Invalid parameter");
4582 		RETURN_FALSE;
4583 	}
4584 	RETVAL_STRINGL(to, to_len);
4585 	efree(to);
4586 }
4587 /* }}} */
4588 #endif
4589 
4590 #ifdef HAVE_PQESCAPE
4591 static void php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAMETERS, int escape_literal) /* {{{ */ {
4592 	char *from = NULL;
4593 	zval *pgsql_link = NULL;
4594 	PGconn *pgsql;
4595 	size_t from_len;
4596 	char *tmp;
4597 	zend_resource *link;
4598 
4599 	switch (ZEND_NUM_ARGS()) {
4600 		case 1:
4601 			if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &from, &from_len) == FAILURE) {
4602 				return;
4603 			}
4604 			link = FETCH_DEFAULT_LINK();
4605 			CHECK_DEFAULT_LINK(link);
4606 			break;
4607 
4608 		default:
4609 			if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &pgsql_link, &from, &from_len) == FAILURE) {
4610 				return;
4611 			}
4612 			link = Z_RES_P(pgsql_link);
4613 			break;
4614 	}
4615 
4616 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4617 		RETURN_FALSE;
4618 	}
4619 
4620 	if (pgsql == NULL) {
4621 		php_error_docref(NULL, E_WARNING,"Cannot get pgsql link");
4622 		RETURN_FALSE;
4623 	}
4624 
4625 	if (escape_literal) {
4626 		tmp = PGSQLescapeLiteral(pgsql, from, (size_t)from_len);
4627 	} else {
4628 		tmp = PGSQLescapeIdentifier(pgsql, from, (size_t)from_len);
4629 	}
4630 	if (!tmp) {
4631 		php_error_docref(NULL, E_WARNING,"Failed to escape");
4632 		RETURN_FALSE;
4633 	}
4634 
4635 	RETVAL_STRING(tmp);
4636 	PGSQLfree(tmp);
4637 }
4638 /* }}} */
4639 
4640 /* {{{ proto string pg_escape_literal([resource connection,] string data)
4641    Escape parameter as string literal (i.e. parameter)	*/
4642 PHP_FUNCTION(pg_escape_literal)
4643 {
4644 	php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4645 }
4646 /* }}} */
4647 
4648 /* {{{ proto string pg_escape_identifier([resource connection,] string data)
4649    Escape identifier (i.e. table name, field name)	*/
4650 PHP_FUNCTION(pg_escape_identifier)
4651 {
4652 	php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4653 }
4654 /* }}} */
4655 #endif
4656 
4657 /* {{{ proto string pg_result_error(resource result)
4658    Get error message associated with result */
4659 PHP_FUNCTION(pg_result_error)
4660 {
4661 	zval *result;
4662 	PGresult *pgsql_result;
4663 	pgsql_result_handle *pg_result;
4664 	char *err = NULL;
4665 
4666 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
4667 								 &result) == FAILURE) {
4668 		RETURN_FALSE;
4669 	}
4670 
4671 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
4672 		RETURN_FALSE;
4673 	}
4674 
4675 	pgsql_result = pg_result->result;
4676 	if (!pgsql_result) {
4677 		RETURN_FALSE;
4678 	}
4679 	err = (char *)PQresultErrorMessage(pgsql_result);
4680 	RETURN_STRING(err);
4681 }
4682 /* }}} */
4683 
4684 #if HAVE_PQRESULTERRORFIELD
4685 /* {{{ proto string pg_result_error_field(resource result, int fieldcode)
4686    Get error message field associated with result */
4687 PHP_FUNCTION(pg_result_error_field)
4688 {
4689 	zval *result;
4690 	zend_long fieldcode;
4691 	PGresult *pgsql_result;
4692 	pgsql_result_handle *pg_result;
4693 	char *field = NULL;
4694 
4695 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "rl",
4696 								 &result, &fieldcode) == FAILURE) {
4697 		RETURN_FALSE;
4698 	}
4699 
4700 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
4701 		RETURN_FALSE;
4702 	}
4703 
4704 	pgsql_result = pg_result->result;
4705 	if (!pgsql_result) {
4706 		RETURN_FALSE;
4707 	}
4708 	if (fieldcode & (PG_DIAG_SEVERITY|PG_DIAG_SQLSTATE|PG_DIAG_MESSAGE_PRIMARY|PG_DIAG_MESSAGE_DETAIL
4709 				|PG_DIAG_MESSAGE_HINT|PG_DIAG_STATEMENT_POSITION
4710 #if PG_DIAG_INTERNAL_POSITION
4711 				|PG_DIAG_INTERNAL_POSITION
4712 #endif
4713 #if PG_DIAG_INTERNAL_QUERY
4714 				|PG_DIAG_INTERNAL_QUERY
4715 #endif
4716 				|PG_DIAG_CONTEXT|PG_DIAG_SOURCE_FILE|PG_DIAG_SOURCE_LINE
4717 				|PG_DIAG_SOURCE_FUNCTION)) {
4718 		field = (char *)PQresultErrorField(pgsql_result, (int)fieldcode);
4719 		if (field == NULL) {
4720 			RETURN_NULL();
4721 		} else {
4722 			RETURN_STRING(field);
4723 		}
4724 	} else {
4725 		RETURN_FALSE;
4726 	}
4727 }
4728 /* }}} */
4729 #endif
4730 
4731 /* {{{ proto int pg_connection_status(resource connection)
4732    Get connection status */
4733 PHP_FUNCTION(pg_connection_status)
4734 {
4735 	zval *pgsql_link = NULL;
4736 	PGconn *pgsql;
4737 
4738 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
4739 								 &pgsql_link) == FAILURE) {
4740 		RETURN_FALSE;
4741 	}
4742 
4743 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4744 		RETURN_FALSE;
4745 	}
4746 
4747 	RETURN_LONG(PQstatus(pgsql));
4748 }
4749 
4750 /* }}} */
4751 
4752 #if HAVE_PGTRANSACTIONSTATUS
4753 /* {{{ proto int pg_transaction_status(resource connection)
4754    Get transaction status */
4755 PHP_FUNCTION(pg_transaction_status)
4756 {
4757 	zval *pgsql_link = NULL;
4758 	PGconn *pgsql;
4759 
4760 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
4761 								 &pgsql_link) == FAILURE) {
4762 		RETURN_FALSE;
4763 	}
4764 
4765 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4766 		RETURN_FALSE;
4767 	}
4768 
4769 	RETURN_LONG(PQtransactionStatus(pgsql));
4770 }
4771 #endif
4772 
4773 /* }}} */
4774 
4775 /* {{{ proto bool pg_connection_reset(resource connection)
4776    Reset connection (reconnect) */
4777 PHP_FUNCTION(pg_connection_reset)
4778 {
4779 	zval *pgsql_link;
4780 	PGconn *pgsql;
4781 
4782 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
4783 								 &pgsql_link) == FAILURE) {
4784 		RETURN_FALSE;
4785 	}
4786 
4787 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4788 		RETURN_FALSE;
4789 	}
4790 
4791 	PQreset(pgsql);
4792 	if (PQstatus(pgsql) == CONNECTION_BAD) {
4793 		RETURN_FALSE;
4794 	}
4795 	RETURN_TRUE;
4796 }
4797 /* }}} */
4798 
4799 #define PHP_PG_ASYNC_IS_BUSY		1
4800 #define PHP_PG_ASYNC_REQUEST_CANCEL 2
4801 
4802 /* {{{ php_pgsql_flush_query
4803  */
4804 static int php_pgsql_flush_query(PGconn *pgsql)
4805 {
4806 	PGresult *res;
4807 	int leftover = 0;
4808 
4809 	if (PQ_SETNONBLOCKING(pgsql, 1)) {
4810 		php_error_docref(NULL, E_NOTICE,"Cannot set connection to nonblocking mode");
4811 		return -1;
4812 	}
4813 	while ((res = PQgetResult(pgsql))) {
4814 		PQclear(res);
4815 		leftover++;
4816 	}
4817 	PQ_SETNONBLOCKING(pgsql, 0);
4818 	return leftover;
4819 }
4820 /* }}} */
4821 
4822 /* {{{ php_pgsql_do_async
4823  */
4824 static void php_pgsql_do_async(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
4825 {
4826 	zval *pgsql_link;
4827 	PGconn *pgsql;
4828 	PGresult *pgsql_result;
4829 
4830 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
4831 								 &pgsql_link) == FAILURE) {
4832 		RETURN_FALSE;
4833 	}
4834 
4835 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4836 		RETURN_FALSE;
4837 	}
4838 
4839 	if (PQ_SETNONBLOCKING(pgsql, 1)) {
4840 		php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
4841 		RETURN_FALSE;
4842 	}
4843 	switch(entry_type) {
4844 		case PHP_PG_ASYNC_IS_BUSY:
4845 			PQconsumeInput(pgsql);
4846 			RETVAL_LONG(PQisBusy(pgsql));
4847 			break;
4848 		case PHP_PG_ASYNC_REQUEST_CANCEL:
4849 			RETVAL_LONG(PQrequestCancel(pgsql));
4850 			while ((pgsql_result = PQgetResult(pgsql))) {
4851 				PQclear(pgsql_result);
4852 			}
4853 			break;
4854 		default:
4855 			php_error_docref(NULL, E_ERROR, "PostgreSQL module error, please report this error");
4856 			break;
4857 	}
4858 	if (PQ_SETNONBLOCKING(pgsql, 0)) {
4859 		php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
4860 	}
4861 	convert_to_boolean_ex(return_value);
4862 }
4863 /* }}} */
4864 
4865 /* {{{ proto bool pg_cancel_query(resource connection)
4866    Cancel request */
4867 PHP_FUNCTION(pg_cancel_query)
4868 {
4869 	php_pgsql_do_async(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_ASYNC_REQUEST_CANCEL);
4870 }
4871 /* }}} */
4872 
4873 /* {{{ proto bool pg_connection_busy(resource connection)
4874    Get connection is busy or not */
4875 PHP_FUNCTION(pg_connection_busy)
4876 {
4877 	php_pgsql_do_async(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_ASYNC_IS_BUSY);
4878 }
4879 /* }}} */
4880 
4881 static int _php_pgsql_link_has_results(PGconn *pgsql) /* {{{ */
4882 {
4883 	PGresult *result;
4884 	while ((result = PQgetResult(pgsql))) {
4885 		PQclear(result);
4886 		return 1;
4887 	}
4888 	return 0;
4889 }
4890 /* }}} */
4891 
4892 /* {{{ proto bool pg_send_query(resource connection, string query)
4893    Send asynchronous query */
4894 PHP_FUNCTION(pg_send_query)
4895 {
4896 	zval *pgsql_link;
4897 	char *query;
4898 	size_t len;
4899 	PGconn *pgsql;
4900 	int is_non_blocking;
4901 	int ret;
4902 
4903 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &pgsql_link, &query, &len) == FAILURE) {
4904 		return;
4905 	}
4906 
4907 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4908 		RETURN_FALSE;
4909 	}
4910 
4911 	is_non_blocking = PQisnonblocking(pgsql);
4912 
4913 	if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) {
4914 		php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
4915 		RETURN_FALSE;
4916 	}
4917 
4918 	if (_php_pgsql_link_has_results(pgsql)) {
4919 		php_error_docref(NULL, E_NOTICE,
4920 			"There are results on this connection. Call pg_get_result() until it returns FALSE");
4921 	}
4922 
4923 	if (is_non_blocking) {
4924 		if (!PQsendQuery(pgsql, query)) {
4925 			RETURN_FALSE;
4926 		}
4927 		ret = PQflush(pgsql);
4928 	} else {
4929 		if (!PQsendQuery(pgsql, query)) {
4930 			if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
4931 				PQreset(pgsql);
4932 			}
4933 			if (!PQsendQuery(pgsql, query)) {
4934 				RETURN_FALSE;
4935 			}
4936 		}
4937 
4938 		/* Wait to finish sending buffer */
4939 		while ((ret = PQflush(pgsql))) {
4940 			if (ret == -1) {
4941 				php_error_docref(NULL, E_NOTICE, "Could not empty PostgreSQL send buffer");
4942 				break;
4943 			}
4944 			usleep(10000);
4945 		}
4946 
4947 		if (PQ_SETNONBLOCKING(pgsql, 0)) {
4948 			php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
4949 		}
4950 	}
4951 
4952 	if (ret == 0) {
4953 		RETURN_TRUE;
4954 	} else if (ret == -1) {
4955 		RETURN_FALSE;
4956 	} else {
4957 		RETURN_LONG(0);
4958 	}
4959 }
4960 /* }}} */
4961 
4962 #if HAVE_PQSENDQUERYPARAMS
4963 /* {{{ proto bool pg_send_query_params(resource connection, string query, array params)
4964    Send asynchronous parameterized query */
4965 PHP_FUNCTION(pg_send_query_params)
4966 {
4967 	zval *pgsql_link, *pv_param_arr, *tmp;
4968 	int num_params = 0;
4969 	char **params = NULL;
4970 	char *query;
4971 	size_t query_len;
4972 	PGconn *pgsql;
4973 	int is_non_blocking;
4974 	int ret;
4975 
4976 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsa", &pgsql_link, &query, &query_len, &pv_param_arr) == FAILURE) {
4977 		return;
4978 	}
4979 
4980 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4981 		RETURN_FALSE;
4982 	}
4983 
4984 	is_non_blocking = PQisnonblocking(pgsql);
4985 
4986 	if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) {
4987 		php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
4988 		RETURN_FALSE;
4989 	}
4990 
4991 	if (_php_pgsql_link_has_results(pgsql)) {
4992 		php_error_docref(NULL, E_NOTICE,
4993 			"There are results on this connection. Call pg_get_result() until it returns FALSE");
4994 	}
4995 
4996 	num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
4997 	if (num_params > 0) {
4998 		int i = 0;
4999 		params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
5000 
5001 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pv_param_arr), tmp) {
5002 
5003 			if (Z_TYPE_P(tmp) == IS_NULL) {
5004 				params[i] = NULL;
5005 			} else {
5006 				zend_string *tmp_str;
5007 				zend_string *str = zval_get_tmp_string(tmp, &tmp_str);
5008 
5009 				params[i] = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
5010 				zend_tmp_string_release(tmp_str);
5011 			}
5012 
5013 			i++;
5014 		} ZEND_HASH_FOREACH_END();
5015 	}
5016 
5017 	if (PQsendQueryParams(pgsql, query, num_params, NULL, (const char * const *)params, NULL, NULL, 0)) {
5018 		_php_pgsql_free_params(params, num_params);
5019 	} else if (is_non_blocking) {
5020 		_php_pgsql_free_params(params, num_params);
5021 		RETURN_FALSE;
5022 	} else {
5023 		if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
5024 			PQreset(pgsql);
5025 		}
5026 		if (!PQsendQueryParams(pgsql, query, num_params, NULL, (const char * const *)params, NULL, NULL, 0)) {
5027 			_php_pgsql_free_params(params, num_params);
5028 			RETURN_FALSE;
5029 		}
5030 	}
5031 
5032 	if (is_non_blocking) {
5033 		ret = PQflush(pgsql);
5034 	} else {
5035 		/* Wait to finish sending buffer */
5036 		while ((ret = PQflush(pgsql))) {
5037 			if (ret == -1) {
5038 				php_error_docref(NULL, E_NOTICE, "Could not empty PostgreSQL send buffer");
5039 				break;
5040 			}
5041 			usleep(10000);
5042 		}
5043 
5044 		if (PQ_SETNONBLOCKING(pgsql, 0) != 0) {
5045 			php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
5046 		}
5047 	}
5048 
5049 	if (ret == 0) {
5050 		RETURN_TRUE;
5051 	} else if (ret == -1) {
5052 		RETURN_FALSE;
5053 	} else {
5054 		RETURN_LONG(0);
5055 	}
5056 }
5057 /* }}} */
5058 #endif
5059 
5060 #if HAVE_PQSENDPREPARE
5061 /* {{{ proto bool pg_send_prepare(resource connection, string stmtname, string query)
5062    Asynchronously prepare a query for future execution */
5063 PHP_FUNCTION(pg_send_prepare)
5064 {
5065 	zval *pgsql_link;
5066 	char *query, *stmtname;
5067 	size_t stmtname_len, query_len;
5068 	PGconn *pgsql;
5069 	int is_non_blocking;
5070 	int ret;
5071 
5072 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rss", &pgsql_link, &stmtname, &stmtname_len, &query, &query_len) == FAILURE) {
5073 		return;
5074 	}
5075 
5076 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5077 		RETURN_FALSE;
5078 	}
5079 
5080 	is_non_blocking = PQisnonblocking(pgsql);
5081 
5082 	if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) {
5083 		php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
5084 		RETURN_FALSE;
5085 	}
5086 
5087 	if (_php_pgsql_link_has_results(pgsql)) {
5088 		php_error_docref(NULL, E_NOTICE,
5089 			"There are results on this connection. Call pg_get_result() until it returns FALSE");
5090 	}
5091 
5092 	if (!PQsendPrepare(pgsql, stmtname, query, 0, NULL)) {
5093 		if (is_non_blocking) {
5094 			RETURN_FALSE;
5095 		} else {
5096 			if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
5097 				PQreset(pgsql);
5098 			}
5099 			if (!PQsendPrepare(pgsql, stmtname, query, 0, NULL)) {
5100 				RETURN_FALSE;
5101 			}
5102 		}
5103 	}
5104 
5105 	if (is_non_blocking) {
5106 		ret = PQflush(pgsql);
5107 	} else {
5108 		/* Wait to finish sending buffer */
5109 		while ((ret = PQflush(pgsql))) {
5110 			if (ret == -1) {
5111 				php_error_docref(NULL, E_NOTICE, "Could not empty PostgreSQL send buffer");
5112 				break;
5113 			}
5114 			usleep(10000);
5115 		}
5116 		if (PQ_SETNONBLOCKING(pgsql, 0) != 0) {
5117 			php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
5118 		}
5119 	}
5120 
5121 	if (ret == 0) {
5122 		RETURN_TRUE;
5123 	} else if (ret == -1) {
5124 		RETURN_FALSE;
5125 	} else {
5126 		RETURN_LONG(0);
5127 	}
5128 }
5129 /* }}} */
5130 #endif
5131 
5132 #if HAVE_PQSENDQUERYPREPARED
5133 /* {{{ proto bool pg_send_execute(resource connection, string stmtname, array params)
5134    Executes prevriously prepared stmtname asynchronously */
5135 PHP_FUNCTION(pg_send_execute)
5136 {
5137 	zval *pgsql_link;
5138 	zval *pv_param_arr, *tmp;
5139 	int num_params = 0;
5140 	char **params = NULL;
5141 	char *stmtname;
5142 	size_t stmtname_len;
5143 	PGconn *pgsql;
5144 	int is_non_blocking;
5145 	int ret;
5146 
5147 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsa", &pgsql_link, &stmtname, &stmtname_len, &pv_param_arr) == FAILURE) {
5148 		return;
5149 	}
5150 
5151 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5152 		RETURN_FALSE;
5153 	}
5154 
5155 	is_non_blocking = PQisnonblocking(pgsql);
5156 
5157 	if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) {
5158 		php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
5159 		RETURN_FALSE;
5160 	}
5161 
5162 	if (_php_pgsql_link_has_results(pgsql)) {
5163 		php_error_docref(NULL, E_NOTICE,
5164 			"There are results on this connection. Call pg_get_result() until it returns FALSE");
5165 	}
5166 
5167 	num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
5168 	if (num_params > 0) {
5169 		int i = 0;
5170 		params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
5171 
5172 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pv_param_arr), tmp) {
5173 
5174 			if (Z_TYPE_P(tmp) == IS_NULL) {
5175 				params[i] = NULL;
5176 			} else {
5177 				zend_string *tmp_str = zval_try_get_string(tmp);
5178 				if (UNEXPECTED(!tmp_str)) {
5179 					_php_pgsql_free_params(params, i);
5180 					return;
5181 				}
5182 				params[i] = estrndup(ZSTR_VAL(tmp_str), ZSTR_LEN(tmp_str));
5183 				zend_string_release(tmp_str);
5184 			}
5185 
5186 			i++;
5187 		} ZEND_HASH_FOREACH_END();
5188 	}
5189 
5190 	if (PQsendQueryPrepared(pgsql, stmtname, num_params, (const char * const *)params, NULL, NULL, 0)) {
5191 		_php_pgsql_free_params(params, num_params);
5192 	} else if (is_non_blocking) {
5193 		_php_pgsql_free_params(params, num_params);
5194 		RETURN_FALSE;
5195 	} else {
5196 		if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
5197 			PQreset(pgsql);
5198 		}
5199 		if (!PQsendQueryPrepared(pgsql, stmtname, num_params, (const char * const *)params, NULL, NULL, 0)) {
5200 			_php_pgsql_free_params(params, num_params);
5201 			RETURN_FALSE;
5202 		}
5203 	}
5204 
5205 	if (is_non_blocking) {
5206 		ret = PQflush(pgsql);
5207 	} else {
5208 		/* Wait to finish sending buffer */
5209 		while ((ret = PQflush(pgsql))) {
5210 			if (ret == -1) {
5211 				php_error_docref(NULL, E_NOTICE, "Could not empty PostgreSQL send buffer");
5212 				break;
5213 			}
5214 			usleep(10000);
5215 		}
5216 		if (PQ_SETNONBLOCKING(pgsql, 0) != 0) {
5217 			php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
5218 		}
5219 	}
5220 
5221 	if (ret == 0) {
5222 		RETURN_TRUE;
5223 	} else if (ret == -1) {
5224 		RETURN_FALSE;
5225 	} else {
5226 		RETURN_LONG(0);
5227 	}
5228 }
5229 /* }}} */
5230 #endif
5231 
5232 /* {{{ proto resource pg_get_result(resource connection)
5233    Get asynchronous query result */
5234 PHP_FUNCTION(pg_get_result)
5235 {
5236 	zval *pgsql_link;
5237 	PGconn *pgsql;
5238 	PGresult *pgsql_result;
5239 	pgsql_result_handle *pg_result;
5240 
5241 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r", &pgsql_link) == FAILURE) {
5242 		RETURN_FALSE;
5243 	}
5244 
5245 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5246 		RETURN_FALSE;
5247 	}
5248 
5249 	pgsql_result = PQgetResult(pgsql);
5250 	if (!pgsql_result) {
5251 		/* no result */
5252 		RETURN_FALSE;
5253 	}
5254 	pg_result = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
5255 	pg_result->conn = pgsql;
5256 	pg_result->result = pgsql_result;
5257 	pg_result->row = 0;
5258 	RETURN_RES(zend_register_resource(pg_result, le_result));
5259 }
5260 /* }}} */
5261 
5262 /* {{{ proto mixed pg_result_status(resource result[, int result_type])
5263    Get status of query result */
5264 PHP_FUNCTION(pg_result_status)
5265 {
5266 	zval *result;
5267 	zend_long result_type = PGSQL_STATUS_LONG;
5268 	ExecStatusType status;
5269 	PGresult *pgsql_result;
5270 	pgsql_result_handle *pg_result;
5271 
5272 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r|l",
5273 								 &result, &result_type) == FAILURE) {
5274 		RETURN_FALSE;
5275 	}
5276 
5277 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
5278 		RETURN_FALSE;
5279 	}
5280 
5281 	pgsql_result = pg_result->result;
5282 	if (result_type == PGSQL_STATUS_LONG) {
5283 		status = PQresultStatus(pgsql_result);
5284 		RETURN_LONG((int)status);
5285 	}
5286 	else if (result_type == PGSQL_STATUS_STRING) {
5287 		RETURN_STRING(PQcmdStatus(pgsql_result));
5288 	}
5289 	else {
5290 		php_error_docref(NULL, E_WARNING, "Optional 2nd parameter should be PGSQL_STATUS_LONG or PGSQL_STATUS_STRING");
5291 		RETURN_FALSE;
5292 	}
5293 }
5294 /* }}} */
5295 
5296 /* {{{ proto mixed pg_get_notify([resource connection[, int result_type]])
5297    Get asynchronous notification */
5298 PHP_FUNCTION(pg_get_notify)
5299 {
5300 	zval *pgsql_link;
5301 	zend_long result_type = PGSQL_ASSOC;
5302 	PGconn *pgsql;
5303 	PGnotify *pgsql_notify;
5304 
5305 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r|l",
5306 								 &pgsql_link, &result_type) == FAILURE) {
5307 		RETURN_FALSE;
5308 	}
5309 
5310 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5311 		RETURN_FALSE;
5312 	}
5313 
5314 	if (!(result_type & PGSQL_BOTH)) {
5315 		php_error_docref(NULL, E_WARNING, "Invalid result type");
5316 		RETURN_FALSE;
5317 	}
5318 
5319 	PQconsumeInput(pgsql);
5320 	pgsql_notify = PQnotifies(pgsql);
5321 	if (!pgsql_notify) {
5322 		/* no notify message */
5323 		RETURN_FALSE;
5324 	}
5325 	array_init(return_value);
5326 	if (result_type & PGSQL_NUM) {
5327 		add_index_string(return_value, 0, pgsql_notify->relname);
5328 		add_index_long(return_value, 1, pgsql_notify->be_pid);
5329 #if HAVE_PQPROTOCOLVERSION && HAVE_PQPARAMETERSTATUS
5330 		/* consider to use php_version_compare() here */
5331 		if (PQprotocolVersion(pgsql) >= 3 && zend_strtod(PQparameterStatus(pgsql, "server_version"), NULL) >= 9.0) {
5332 #else
5333 		if (zend_strtod(PG_VERSION) >= 9.0, NULL) {
5334 #endif
5335 #if HAVE_PQPARAMETERSTATUS
5336 			add_index_string(return_value, 2, pgsql_notify->extra);
5337 #endif
5338 		}
5339 	}
5340 	if (result_type & PGSQL_ASSOC) {
5341 		add_assoc_string(return_value, "message", pgsql_notify->relname);
5342 		add_assoc_long(return_value, "pid", pgsql_notify->be_pid);
5343 #if HAVE_PQPROTOCOLVERSION && HAVE_PQPARAMETERSTATUS
5344 		/* consider to use php_version_compare() here */
5345 		if (PQprotocolVersion(pgsql) >= 3 && zend_strtod(PQparameterStatus(pgsql, "server_version"), NULL) >= 9.0) {
5346 #else
5347 		if (zend_strtod(PG_VERSION, NULL) >= 9.0) {
5348 #endif
5349 #if HAVE_PQPARAMETERSTATUS
5350 			add_assoc_string(return_value, "payload", pgsql_notify->extra);
5351 #endif
5352 		}
5353 	}
5354 	PQfreemem(pgsql_notify);
5355 }
5356 /* }}} */
5357 
5358 /* {{{ proto int pg_get_pid([resource connection)
5359    Get backend(server) pid */
5360 PHP_FUNCTION(pg_get_pid)
5361 {
5362 	zval *pgsql_link;
5363 	PGconn *pgsql;
5364 
5365 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
5366 								 &pgsql_link) == FAILURE) {
5367 		RETURN_FALSE;
5368 	}
5369 
5370 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5371 		RETURN_FALSE;
5372 	}
5373 
5374 	RETURN_LONG(PQbackendPID(pgsql));
5375 }
5376 /* }}} */
5377 
5378 static ssize_t php_pgsql_fd_write(php_stream *stream, const char *buf, size_t count) /* {{{ */
5379 {
5380 	return -1;
5381 }
5382 /* }}} */
5383 
5384 static ssize_t php_pgsql_fd_read(php_stream *stream, char *buf, size_t count) /* {{{ */
5385 {
5386 	return -1;
5387 }
5388 /* }}} */
5389 
5390 static int php_pgsql_fd_close(php_stream *stream, int close_handle) /* {{{ */
5391 {
5392 	return EOF;
5393 }
5394 /* }}} */
5395 
5396 static int php_pgsql_fd_flush(php_stream *stream) /* {{{ */
5397 {
5398 	return FAILURE;
5399 }
5400 /* }}} */
5401 
5402 static int php_pgsql_fd_set_option(php_stream *stream, int option, int value, void *ptrparam) /* {{{ */
5403 {
5404 	PGconn *pgsql = (PGconn *) stream->abstract;
5405 	switch (option) {
5406 		case PHP_STREAM_OPTION_BLOCKING:
5407 			return PQ_SETNONBLOCKING(pgsql, value);
5408 		default:
5409 			return FAILURE;
5410 	}
5411 }
5412 /* }}} */
5413 
5414 static int php_pgsql_fd_cast(php_stream *stream, int cast_as, void **ret) /* {{{ */
5415 {
5416 	PGconn *pgsql = (PGconn *) stream->abstract;
5417 
5418 	switch (cast_as)	{
5419 		case PHP_STREAM_AS_FD_FOR_SELECT:
5420 		case PHP_STREAM_AS_FD:
5421 		case PHP_STREAM_AS_SOCKETD: {
5422 				int fd_number = PQsocket(pgsql);
5423 				if (fd_number == -1) {
5424 					return FAILURE;
5425 				}
5426 
5427 				if (ret) {
5428 					*(php_socket_t *)ret = fd_number;
5429 				}
5430 			}
5431 			return SUCCESS;
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_STR(&new_val, zend_long_to_str(Z_LVAL_P(val)));
6113 						break;
6114 
6115 					case IS_DOUBLE:
6116 						ZVAL_DOUBLE(&new_val, Z_DVAL_P(val));
6117 						convert_to_string_ex(&new_val);
6118 						break;
6119 
6120 					case IS_NULL:
6121 						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6122 						break;
6123 
6124 					default:
6125 						err = 1;
6126 				}
6127 				PGSQL_CONV_CHECK_IGNORE();
6128 				if (err) {
6129 					php_error_docref(NULL, E_NOTICE, "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6130 				}
6131 				break;
6132 
6133 			case PG_UNIX_TIME:
6134 			case PG_UNIX_TIME_INTERVAL:
6135 				/* these are the actallay a integer */
6136 				switch (Z_TYPE_P(val)) {
6137 					case IS_STRING:
6138 						if (Z_STRLEN_P(val) == 0) {
6139 							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6140 						}
6141 						else {
6142 							/* better regex? */
6143 							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), "^[0-9]+$", sizeof("^[0-9]+$")-1, 0) == FAILURE) {
6144 								err = 1;
6145 							}
6146 							else {
6147 								ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
6148 								convert_to_long_ex(&new_val);
6149 							}
6150 						}
6151 						break;
6152 
6153 					case IS_DOUBLE:
6154 						ZVAL_DOUBLE(&new_val, Z_DVAL_P(val));
6155 						convert_to_long_ex(&new_val);
6156 						break;
6157 
6158 					case IS_LONG:
6159 						ZVAL_LONG(&new_val, Z_LVAL_P(val));
6160 						break;
6161 
6162 					case IS_NULL:
6163 						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6164 						break;
6165 
6166 					default:
6167 						err = 1;
6168 				}
6169 				PGSQL_CONV_CHECK_IGNORE();
6170 				if (err) {
6171 					php_error_docref(NULL, E_NOTICE, "Expects NULL, string, long or double value for '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6172 				}
6173 				break;
6174 
6175 			case PG_CIDR:
6176 			case PG_INET:
6177 				switch (Z_TYPE_P(val)) {
6178 					case IS_STRING:
6179 						if (Z_STRLEN_P(val) == 0) {
6180 							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6181 						}
6182 						else {
6183 #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})?$"
6184 #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})?$"
6185 							/* The inet type holds an IPv4 or IPv6 host address, and optionally its subnet, all in one field. See more in the doc.
6186 							 	The regex might still be not perfect, but catches the most of IP variants. We might decide to remove the regex
6187 								at all though and let the server side to handle it.*/
6188 							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), REGEX0, sizeof(REGEX0)-1, 0) == FAILURE
6189 								&& php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), REGEX1, sizeof(REGEX1)-1, 0) == FAILURE) {
6190 								err = 1;
6191 							}
6192 							else {
6193 								ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
6194 								php_pgsql_add_quotes(&new_val, 1);
6195 							}
6196 #undef REGEX0
6197 #undef REGEX1
6198 						}
6199 						break;
6200 
6201 					case IS_NULL:
6202 						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6203 						break;
6204 
6205 					default:
6206 						err = 1;
6207 				}
6208 				PGSQL_CONV_CHECK_IGNORE();
6209 				if (err) {
6210 					php_error_docref(NULL, E_NOTICE, "Expects NULL or IPv4 or IPv6 address string for '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6211 				}
6212 				break;
6213 
6214 			case PG_TIME_WITH_TIMEZONE:
6215 			case PG_TIMESTAMP:
6216 			case PG_TIMESTAMP_WITH_TIMEZONE:
6217 				switch(Z_TYPE_P(val)) {
6218 					case IS_STRING:
6219 						if (Z_STRLEN_P(val) == 0) {
6220 							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6221 						} else if (!strcasecmp(Z_STRVAL_P(val), "now()")) {
6222 							ZVAL_STRINGL(&new_val, "NOW()", sizeof("NOW()")-1);
6223 						} else {
6224 #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}$"
6225 							/* better regex? */
6226 							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), REGEX0, sizeof(REGEX0)-1, 1) == FAILURE) {
6227 								err = 1;
6228 							} else {
6229 								ZVAL_STRING(&new_val, Z_STRVAL_P(val));
6230 								php_pgsql_add_quotes(&new_val, 1);
6231 							}
6232 #undef REGEX0
6233 						}
6234 						break;
6235 
6236 					case IS_NULL:
6237 						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6238 						break;
6239 
6240 					default:
6241 						err = 1;
6242 				}
6243 				PGSQL_CONV_CHECK_IGNORE();
6244 				if (err) {
6245 					php_error_docref(NULL, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6246 				}
6247 				break;
6248 
6249 			case PG_DATE:
6250 				switch(Z_TYPE_P(val)) {
6251 					case IS_STRING:
6252 						if (Z_STRLEN_P(val) == 0) {
6253 							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6254 						}
6255 						else {
6256 #define REGEX0 "^([0-9]{4}[/-][0-9]{1,2}[/-][0-9]{1,2})$"
6257 							/* FIXME: better regex must be used */
6258 							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), REGEX0, sizeof(REGEX0)-1, 1) == FAILURE) {
6259 								err = 1;
6260 							}
6261 							else {
6262 								ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
6263 								php_pgsql_add_quotes(&new_val, 1);
6264 							}
6265 #undef REGEX0
6266 						}
6267 						break;
6268 
6269 					case IS_NULL:
6270 						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6271 						break;
6272 
6273 					default:
6274 						err = 1;
6275 				}
6276 				PGSQL_CONV_CHECK_IGNORE();
6277 				if (err) {
6278 					php_error_docref(NULL, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6279 				}
6280 				break;
6281 
6282 			case PG_TIME:
6283 				switch(Z_TYPE_P(val)) {
6284 					case IS_STRING:
6285 						if (Z_STRLEN_P(val) == 0) {
6286 							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6287 						}
6288 						else {
6289 #define REGEX0 "^(([0-9]{1,2}:[0-9]{1,2}){1}(:[0-9]{1,2}){0,1}){0,1}$"
6290 							/* FIXME: better regex must be used */
6291 							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), REGEX0, sizeof(REGEX0)-1, 1) == FAILURE) {
6292 								err = 1;
6293 							}
6294 							else {
6295 								ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
6296 								php_pgsql_add_quotes(&new_val, 1);
6297 							}
6298 #undef REGEX0
6299 						}
6300 						break;
6301 
6302 					case IS_NULL:
6303 						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6304 						break;
6305 
6306 					default:
6307 						err = 1;
6308 				}
6309 				PGSQL_CONV_CHECK_IGNORE();
6310 				if (err) {
6311 					php_error_docref(NULL, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6312 				}
6313 				break;
6314 
6315 			case PG_INTERVAL:
6316 				switch(Z_TYPE_P(val)) {
6317 					case IS_STRING:
6318 						if (Z_STRLEN_P(val) == 0) {
6319 							ZVAL_STRING(&new_val, "NULL");
6320 						}
6321 						else {
6322 
6323 							/* From the Postgres docs:
6324 
6325 							   interval values can be written with the following syntax:
6326 							   [@] quantity unit [quantity unit...] [direction]
6327 
6328 							   Where: quantity is a number (possibly signed); unit is second, minute, hour,
6329 							   day, week, month, year, decade, century, millennium, or abbreviations or
6330 							   plurals of these units [note not *all* abbreviations] ; direction can be
6331 							   ago or empty. The at sign (@) is optional noise.
6332 
6333 							   ...
6334 
6335 							   Quantities of days, hours, minutes, and seconds can be specified without explicit
6336 							   unit markings. For example, '1 12:59:10' is read the same as '1 day 12 hours 59 min 10
6337 							   sec'.
6338 							*/
6339 #define REGEX0 \
6340 	"^(@?[ \\t]+)?(" \
6341 	/* Textual time units and their abbreviations: */ \
6342 	"(([-+]?[ \\t]+)?" \
6343 	"[0-9]+(\\.[0-9]*)?[ \\t]*" \
6344 	"(millenniums|millennia|millennium|mil|mils|" \
6345 	"centuries|century|cent|c|" \
6346 	"decades|decade|dec|decs|" \
6347 	"years|year|y|" \
6348 	"months|month|mon|" \
6349 	"weeks|week|w|" \
6350 	"days|day|d|" \
6351 	"hours|hour|hr|hrs|h|" \
6352 	"minutes|minute|mins|min|m|" \
6353 	"seconds|second|secs|sec|s))+|" \
6354 	/* Textual time units plus (dd)* hh[:mm[:ss]] */ \
6355 	"((([-+]?[ \\t]+)?" \
6356 	"[0-9]+(\\.[0-9]*)?[ \\t]*" \
6357 	"(millenniums|millennia|millennium|mil|mils|" \
6358 	"centuries|century|cent|c|" \
6359 	"decades|decade|dec|decs|" \
6360 	"years|year|y|" \
6361 	"months|month|mon|" \
6362 	"weeks|week|w|" \
6363 	"days|day|d))+" \
6364 	"([-+]?[ \\t]+" \
6365 	"([0-9]+[ \\t]+)+"				 /* dd */ \
6366 	"(([0-9]{1,2}:){0,2}[0-9]{0,2})" /* hh:[mm:[ss]] */ \
6367 	")?))" \
6368 	"([ \\t]+ago)?$"
6369 
6370 							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), REGEX0, sizeof(REGEX0)-1, 1) == FAILURE) {
6371 								err = 1;
6372 							}
6373 							else {
6374 								ZVAL_STRING(&new_val, Z_STRVAL_P(val));
6375 								php_pgsql_add_quotes(&new_val, 1);
6376 							}
6377 #undef REGEX0
6378 						}
6379 						break;
6380 
6381 					case IS_NULL:
6382 						ZVAL_STRING(&new_val, "NULL");
6383 						break;
6384 
6385 					default:
6386 						err = 1;
6387 				}
6388 				PGSQL_CONV_CHECK_IGNORE();
6389 				if (err) {
6390 					php_error_docref(NULL, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6391 				}
6392 				break;
6393 #ifdef HAVE_PQESCAPE
6394 			case PG_BYTEA:
6395 				switch (Z_TYPE_P(val)) {
6396 					case IS_STRING:
6397 						if (Z_STRLEN_P(val) == 0) {
6398 							ZVAL_STRING(&new_val, "NULL");
6399 						}
6400 						else {
6401 							unsigned char *tmp;
6402 							size_t to_len;
6403 							smart_str s = {0};
6404 #ifdef HAVE_PQESCAPE_BYTEA_CONN
6405 							tmp = PQescapeByteaConn(pg_link, (unsigned char *)Z_STRVAL_P(val), Z_STRLEN_P(val), &to_len);
6406 #else
6407 							tmp = PQescapeBytea(Z_STRVAL_P(val), (unsigned char *)Z_STRLEN_P(val), &to_len);
6408 #endif
6409 							ZVAL_STRINGL(&new_val, (char *)tmp, to_len - 1); /* PQescapeBytea's to_len includes additional '\0' */
6410 							PQfreemem(tmp);
6411 							php_pgsql_add_quotes(&new_val, 1);
6412 							smart_str_appendl(&s, Z_STRVAL(new_val), Z_STRLEN(new_val));
6413 							smart_str_0(&s);
6414 							zval_ptr_dtor(&new_val);
6415 							ZVAL_NEW_STR(&new_val, s.s);
6416 						}
6417 						break;
6418 
6419 					case IS_LONG:
6420 						ZVAL_STR(&new_val, zend_long_to_str(Z_LVAL_P(val)));
6421 						break;
6422 
6423 					case IS_DOUBLE:
6424 						ZVAL_DOUBLE(&new_val, Z_DVAL_P(val));
6425 						convert_to_string_ex(&new_val);
6426 						break;
6427 
6428 					case IS_NULL:
6429 						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6430 						break;
6431 
6432 					default:
6433 						err = 1;
6434 				}
6435 				PGSQL_CONV_CHECK_IGNORE();
6436 				if (err) {
6437 					php_error_docref(NULL, E_NOTICE, "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6438 				}
6439 				break;
6440 
6441 #endif
6442 			case PG_MACADDR:
6443 				switch(Z_TYPE_P(val)) {
6444 					case IS_STRING:
6445 						if (Z_STRLEN_P(val) == 0) {
6446 							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6447 						}
6448 						else {
6449 #define REGEX0 "^([0-9a-f]{2,2}:){5,5}[0-9a-f]{2,2}$"
6450 							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), REGEX0, sizeof(REGEX0)-1, 1) == FAILURE) {
6451 								err = 1;
6452 							}
6453 							else {
6454 								ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
6455 								php_pgsql_add_quotes(&new_val, 1);
6456 							}
6457 #undef REGEX0
6458 						}
6459 						break;
6460 
6461 					case IS_NULL:
6462 						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6463 						break;
6464 
6465 					default:
6466 						err = 1;
6467 				}
6468 				PGSQL_CONV_CHECK_IGNORE();
6469 				if (err) {
6470 					php_error_docref(NULL, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6471 				}
6472 				break;
6473 
6474 			default:
6475 				/* should not happen */
6476 				php_error_docref(NULL, E_NOTICE, "Unknown or system data type '%s' for '%s'. Report error", Z_STRVAL_P(type), ZSTR_VAL(field));
6477 				err = 1;
6478 				break;
6479 		} /* switch */
6480 
6481 		if (err) {
6482 			zval_ptr_dtor(&new_val);
6483 			break; /* break out for() */
6484 		}
6485 		/* If field is NULL and HAS DEFAULT, should be skipped */
6486 		if (!skip_field) {
6487 			if (_php_pgsql_detect_identifier_escape(ZSTR_VAL(field), ZSTR_LEN(field)) == SUCCESS) {
6488 				zend_hash_update(Z_ARRVAL_P(result), field, &new_val);
6489 			} else {
6490 				char *escaped = PGSQLescapeIdentifier(pg_link, ZSTR_VAL(field), ZSTR_LEN(field));
6491 				add_assoc_zval(result, escaped, &new_val);
6492 				PGSQLfree(escaped);
6493 			}
6494 		}
6495 	} ZEND_HASH_FOREACH_END(); /* for */
6496 
6497 	zval_ptr_dtor(&meta);
6498 
6499 	if (err) {
6500 		/* shouldn't destroy & free zval here */
6501 		return FAILURE;
6502 	}
6503 	return SUCCESS;
6504 }
6505 /* }}} */
6506 
6507 /* {{{ proto array pg_convert(resource db, string table, array values[, int options])
6508    Check and convert values for PostgreSQL SQL statement */
6509 PHP_FUNCTION(pg_convert)
6510 {
6511 	zval *pgsql_link, *values;
6512 	char *table_name;
6513 	size_t table_name_len;
6514 	zend_ulong option = 0;
6515 	PGconn *pg_link;
6516 
6517 	if (zend_parse_parameters(ZEND_NUM_ARGS(),
6518 							  "rsa|l", &pgsql_link, &table_name, &table_name_len, &values, &option) == FAILURE) {
6519 		return;
6520 	}
6521 	if (option & ~PGSQL_CONV_OPTS) {
6522 		php_error_docref(NULL, E_WARNING, "Invalid option is specified");
6523 		RETURN_FALSE;
6524 	}
6525 	if (!table_name_len) {
6526 		php_error_docref(NULL, E_NOTICE, "Table name is invalid");
6527 		RETURN_FALSE;
6528 	}
6529 
6530 	if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
6531 		RETURN_FALSE;
6532 	}
6533 
6534 	if (php_pgsql_flush_query(pg_link)) {
6535 		php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
6536 	}
6537 	array_init(return_value);
6538 	if (php_pgsql_convert(pg_link, table_name, values, return_value, option) == FAILURE) {
6539 		zend_array_destroy(Z_ARR_P(return_value));
6540 		RETURN_FALSE;
6541 	}
6542 }
6543 /* }}} */
6544 
6545 static int do_exec(smart_str *querystr, ExecStatusType expect, PGconn *pg_link, zend_ulong opt) /* {{{ */
6546 {
6547 	if (opt & PGSQL_DML_ASYNC) {
6548 		if (PQsendQuery(pg_link, ZSTR_VAL(querystr->s))) {
6549 			return 0;
6550 		}
6551 	}
6552 	else {
6553 		PGresult *pg_result;
6554 
6555 		pg_result = PQexec(pg_link, ZSTR_VAL(querystr->s));
6556 		if (PQresultStatus(pg_result) == expect) {
6557 			PQclear(pg_result);
6558 			return 0;
6559 		} else {
6560 			php_error_docref(NULL, E_WARNING, "%s", PQresultErrorMessage(pg_result));
6561 			PQclear(pg_result);
6562 		}
6563 	}
6564 
6565 	return -1;
6566 }
6567 /* }}} */
6568 
6569 static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const char *table) /* {{{ */
6570 {
6571 	size_t table_len = strlen(table);
6572 
6573 	/* schema.table should be "schema"."table" */
6574 	const char *dot = memchr(table, '.', table_len);
6575 	size_t len = dot ? dot - table : table_len;
6576 	if (_php_pgsql_detect_identifier_escape(table, len) == SUCCESS) {
6577 		smart_str_appendl(querystr, table, len);
6578 	} else {
6579 		char *escaped = PGSQLescapeIdentifier(pg_link, table, len);
6580 		smart_str_appends(querystr, escaped);
6581 		PGSQLfree(escaped);
6582 	}
6583 	if (dot) {
6584 		const char *after_dot = dot + 1;
6585 		len = table_len - len - 1;
6586 		/* "schema"."table" format */
6587 		if (_php_pgsql_detect_identifier_escape(after_dot, len) == SUCCESS) {
6588 			smart_str_appendc(querystr, '.');
6589 			smart_str_appendl(querystr, after_dot, len);
6590 		} else {
6591 			char *escaped = PGSQLescapeIdentifier(pg_link, after_dot, len);
6592 			smart_str_appendc(querystr, '.');
6593 			smart_str_appends(querystr, escaped);
6594 			PGSQLfree(escaped);
6595 		}
6596 	}
6597 }
6598 /* }}} */
6599 
6600 /* {{{ php_pgsql_insert
6601  */
6602 PHP_PGSQL_API int php_pgsql_insert(PGconn *pg_link, const char *table, zval *var_array, zend_ulong opt, zend_string **sql)
6603 {
6604 	zval *val, converted;
6605 	char buf[256];
6606 	char *tmp;
6607 	smart_str querystr = {0};
6608 	int ret = FAILURE;
6609 	zend_string *fld;
6610 
6611 	assert(pg_link != NULL);
6612 	assert(table != NULL);
6613 	assert(Z_TYPE_P(var_array) == IS_ARRAY);
6614 
6615 	ZVAL_UNDEF(&converted);
6616 	if (zend_hash_num_elements(Z_ARRVAL_P(var_array)) == 0) {
6617 		smart_str_appends(&querystr, "INSERT INTO ");
6618 		build_tablename(&querystr, pg_link, table);
6619 		smart_str_appends(&querystr, " DEFAULT VALUES");
6620 
6621 		goto no_values;
6622 	}
6623 
6624 	/* convert input array if needed */
6625 	if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) {
6626 		array_init(&converted);
6627 		if (php_pgsql_convert(pg_link, table, var_array, &converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
6628 			goto cleanup;
6629 		}
6630 		var_array = &converted;
6631 	}
6632 
6633 	smart_str_appends(&querystr, "INSERT INTO ");
6634 	build_tablename(&querystr, pg_link, table);
6635 	smart_str_appends(&querystr, " (");
6636 
6637 	ZEND_HASH_FOREACH_STR_KEY(Z_ARRVAL_P(var_array), fld) {
6638 		if (fld == NULL) {
6639 			php_error_docref(NULL, E_NOTICE, "Expects associative array for values to be inserted");
6640 			goto cleanup;
6641 		}
6642 		if (opt & PGSQL_DML_ESCAPE) {
6643 			tmp = PGSQLescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1);
6644 			smart_str_appends(&querystr, tmp);
6645 			PGSQLfree(tmp);
6646 		} else {
6647 			smart_str_appendl(&querystr, ZSTR_VAL(fld), ZSTR_LEN(fld));
6648 		}
6649 		smart_str_appendc(&querystr, ',');
6650 	} ZEND_HASH_FOREACH_END();
6651 	ZSTR_LEN(querystr.s)--;
6652 	smart_str_appends(&querystr, ") VALUES (");
6653 
6654 	/* make values string */
6655 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(var_array), val) {
6656 		/* we can avoid the key_type check here, because we tested it in the other loop */
6657 		switch (Z_TYPE_P(val)) {
6658 			case IS_STRING:
6659 				if (opt & PGSQL_DML_ESCAPE) {
6660 					size_t new_len;
6661 					char *tmp;
6662 					tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1);
6663 					new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL);
6664 					smart_str_appendc(&querystr, '\'');
6665 					smart_str_appendl(&querystr, tmp, new_len);
6666 					smart_str_appendc(&querystr, '\'');
6667 					efree(tmp);
6668 				} else {
6669 					smart_str_appendl(&querystr, Z_STRVAL_P(val), Z_STRLEN_P(val));
6670 				}
6671 				break;
6672 			case IS_LONG:
6673 				smart_str_append_long(&querystr, Z_LVAL_P(val));
6674 				break;
6675 			case IS_DOUBLE:
6676 				smart_str_appendl(&querystr, buf, snprintf(buf, sizeof(buf), "%F", Z_DVAL_P(val)));
6677 				break;
6678 			case IS_NULL:
6679 				smart_str_appendl(&querystr, "NULL", sizeof("NULL")-1);
6680 				break;
6681 			default:
6682 				php_error_docref(NULL, E_WARNING, "Expects scaler values. type = %d", Z_TYPE_P(val));
6683 				goto cleanup;
6684 				break;
6685 		}
6686 		smart_str_appendc(&querystr, ',');
6687 	} ZEND_HASH_FOREACH_END();
6688 	/* Remove the trailing "," */
6689 	ZSTR_LEN(querystr.s)--;
6690 	smart_str_appends(&querystr, ");");
6691 
6692 no_values:
6693 
6694 	smart_str_0(&querystr);
6695 
6696 	if ((opt & (PGSQL_DML_EXEC|PGSQL_DML_ASYNC)) &&
6697 		do_exec(&querystr, PGRES_COMMAND_OK, pg_link, (opt & PGSQL_CONV_OPTS)) == 0) {
6698 		ret = SUCCESS;
6699 	}
6700 	else if (opt & PGSQL_DML_STRING) {
6701 		ret = SUCCESS;
6702 	}
6703 
6704 cleanup:
6705 	zval_ptr_dtor(&converted);
6706 	if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
6707 		*sql = querystr.s;
6708 	}
6709 	else {
6710 		smart_str_free(&querystr);
6711 	}
6712 	return ret;
6713 }
6714 /* }}} */
6715 
6716 /* {{{ proto mixed pg_insert(resource db, string table, array values[, int options])
6717    Insert values (filed=>value) to table */
6718 PHP_FUNCTION(pg_insert)
6719 {
6720 	zval *pgsql_link, *values;
6721 	char *table;
6722 	size_t table_len;
6723 	zend_ulong option = PGSQL_DML_EXEC, return_sql;
6724 	PGconn *pg_link;
6725 	PGresult *pg_result;
6726 	ExecStatusType status;
6727 	zend_string *sql = NULL;
6728 	int argc = ZEND_NUM_ARGS();
6729 
6730 	if (zend_parse_parameters(argc, "rsa|l",
6731 							  &pgsql_link, &table, &table_len, &values, &option) == FAILURE) {
6732 		return;
6733 	}
6734 	if (option & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
6735 		php_error_docref(NULL, E_WARNING, "Invalid option is specified");
6736 		RETURN_FALSE;
6737 	}
6738 
6739 	if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
6740 		RETURN_FALSE;
6741 	}
6742 
6743 	if (php_pgsql_flush_query(pg_link)) {
6744 		php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
6745 	}
6746 	return_sql = option & PGSQL_DML_STRING;
6747 	if (option & PGSQL_DML_EXEC) {
6748 		/* return resource when executed */
6749 		option = option & ~PGSQL_DML_EXEC;
6750 		if (php_pgsql_insert(pg_link, table, values, option|PGSQL_DML_STRING, &sql) == FAILURE) {
6751 			RETURN_FALSE;
6752 		}
6753 		pg_result = PQexec(pg_link, ZSTR_VAL(sql));
6754 		if ((PGG(auto_reset_persistent) & 2) && PQstatus(pg_link) != CONNECTION_OK) {
6755 			PQclear(pg_result);
6756 			PQreset(pg_link);
6757 			pg_result = PQexec(pg_link, ZSTR_VAL(sql));
6758 		}
6759 		efree(sql);
6760 
6761 		if (pg_result) {
6762 			status = PQresultStatus(pg_result);
6763 		} else {
6764 			status = (ExecStatusType) PQstatus(pg_link);
6765 		}
6766 
6767 		switch (status) {
6768 			case PGRES_EMPTY_QUERY:
6769 			case PGRES_BAD_RESPONSE:
6770 			case PGRES_NONFATAL_ERROR:
6771 			case PGRES_FATAL_ERROR:
6772 				PHP_PQ_ERROR("Query failed: %s", pg_link);
6773 				PQclear(pg_result);
6774 				RETURN_FALSE;
6775 				break;
6776 			case PGRES_COMMAND_OK: /* successful command that did not return rows */
6777 			default:
6778 				if (pg_result) {
6779 					pgsql_result_handle *pgsql_handle = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
6780 					pgsql_handle->conn = pg_link;
6781 					pgsql_handle->result = pg_result;
6782 					pgsql_handle->row = 0;
6783 					RETURN_RES(zend_register_resource(pgsql_handle, le_result));
6784 				} else {
6785 					PQclear(pg_result);
6786 					RETURN_FALSE;
6787 				}
6788 			break;
6789 		}
6790 	} else if (php_pgsql_insert(pg_link, table, values, option, &sql) == FAILURE) {
6791 		RETURN_FALSE;
6792 	}
6793 	if (return_sql) {
6794 		RETURN_STR(sql);
6795 		return;
6796 	}
6797 	RETURN_TRUE;
6798 }
6799 /* }}} */
6800 
6801 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) /* {{{ */
6802 {
6803 	zend_string *fld;
6804 	zval *val;
6805 
6806 	ZEND_HASH_FOREACH_STR_KEY_VAL(ht, fld, val) {
6807 		if (fld == NULL) {
6808 			php_error_docref(NULL, E_NOTICE, "Expects associative array for values to be inserted");
6809 			return -1;
6810 		}
6811 		if (opt & PGSQL_DML_ESCAPE) {
6812 			char *tmp = PGSQLescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1);
6813 			smart_str_appends(querystr, tmp);
6814 			PGSQLfree(tmp);
6815 		} else {
6816 			smart_str_appendl(querystr, ZSTR_VAL(fld), ZSTR_LEN(fld));
6817 		}
6818 		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")))) {
6819 			smart_str_appends(querystr, " IS ");
6820 		} else {
6821 			smart_str_appendc(querystr, '=');
6822 		}
6823 
6824 		switch (Z_TYPE_P(val)) {
6825 			case IS_STRING:
6826 				if (opt & PGSQL_DML_ESCAPE) {
6827 					char *tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1);
6828 					size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL);
6829 					smart_str_appendc(querystr, '\'');
6830 					smart_str_appendl(querystr, tmp, new_len);
6831 					smart_str_appendc(querystr, '\'');
6832 					efree(tmp);
6833 				} else {
6834 					smart_str_appendl(querystr, Z_STRVAL_P(val), Z_STRLEN_P(val));
6835 				}
6836 				break;
6837 			case IS_LONG:
6838 				smart_str_append_long(querystr, Z_LVAL_P(val));
6839 				break;
6840 			case IS_DOUBLE: {
6841 				char buf[256];
6842 				smart_str_appendl(querystr, buf, MIN(snprintf(buf, sizeof(buf), "%F", Z_DVAL_P(val)), sizeof(buf) - 1));
6843 				}
6844 				break;
6845 			case IS_NULL:
6846 				smart_str_appendl(querystr, "NULL", sizeof("NULL")-1);
6847 				break;
6848 			default:
6849 				php_error_docref(NULL, E_WARNING, "Expects scaler values. type=%d", Z_TYPE_P(val));
6850 				return -1;
6851 		}
6852 		smart_str_appendl(querystr, pad, pad_len);
6853 	} ZEND_HASH_FOREACH_END();
6854 	if (querystr->s) {
6855 		ZSTR_LEN(querystr->s) -= pad_len;
6856 	}
6857 
6858 	return 0;
6859 }
6860 /* }}} */
6861 
6862 /* {{{ php_pgsql_update
6863  */
6864 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)
6865 {
6866 	zval var_converted, ids_converted;
6867 	smart_str querystr = {0};
6868 	int ret = FAILURE;
6869 
6870 	assert(pg_link != NULL);
6871 	assert(table != NULL);
6872 	assert(Z_TYPE_P(var_array) == IS_ARRAY);
6873 	assert(Z_TYPE_P(ids_array) == IS_ARRAY);
6874 	assert(!(opt & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)));
6875 
6876 	if (zend_hash_num_elements(Z_ARRVAL_P(var_array)) == 0
6877 			|| zend_hash_num_elements(Z_ARRVAL_P(ids_array)) == 0) {
6878 		return FAILURE;
6879 	}
6880 
6881 	ZVAL_UNDEF(&var_converted);
6882 	ZVAL_UNDEF(&ids_converted);
6883 	if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) {
6884 		array_init(&var_converted);
6885 		if (php_pgsql_convert(pg_link, table, var_array, &var_converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
6886 			goto cleanup;
6887 		}
6888 		var_array = &var_converted;
6889 		array_init(&ids_converted);
6890 		if (php_pgsql_convert(pg_link, table, ids_array, &ids_converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
6891 			goto cleanup;
6892 		}
6893 		ids_array = &ids_converted;
6894 	}
6895 
6896 	smart_str_appends(&querystr, "UPDATE ");
6897 	build_tablename(&querystr, pg_link, table);
6898 	smart_str_appends(&querystr, " SET ");
6899 
6900 	if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(var_array), 0, ",", 1, opt))
6901 		goto cleanup;
6902 
6903 	smart_str_appends(&querystr, " WHERE ");
6904 
6905 	if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt))
6906 		goto cleanup;
6907 
6908 	smart_str_appendc(&querystr, ';');
6909 	smart_str_0(&querystr);
6910 
6911 	if ((opt & PGSQL_DML_EXEC) && do_exec(&querystr, PGRES_COMMAND_OK, pg_link, opt) == 0) {
6912 		ret = SUCCESS;
6913 	} else if (opt & PGSQL_DML_STRING) {
6914 		ret = SUCCESS;
6915 	}
6916 
6917 cleanup:
6918 	zval_ptr_dtor(&var_converted);
6919 	zval_ptr_dtor(&ids_converted);
6920 	if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
6921 		*sql = querystr.s;
6922 	}
6923 	else {
6924 		smart_str_free(&querystr);
6925 	}
6926 	return ret;
6927 }
6928 /* }}} */
6929 
6930 /* {{{ proto mixed pg_update(resource db, string table, array fields, array ids[, int options])
6931    Update table using values (field=>value) and ids (id=>value) */
6932 PHP_FUNCTION(pg_update)
6933 {
6934 	zval *pgsql_link, *values, *ids;
6935 	char *table;
6936 	size_t table_len;
6937 	zend_ulong option =  PGSQL_DML_EXEC;
6938 	PGconn *pg_link;
6939 	zend_string *sql = NULL;
6940 	int argc = ZEND_NUM_ARGS();
6941 
6942 	if (zend_parse_parameters(argc, "rsaa|l",
6943 							  &pgsql_link, &table, &table_len, &values, &ids, &option) == FAILURE) {
6944 		return;
6945 	}
6946 	if (option & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
6947 		php_error_docref(NULL, E_WARNING, "Invalid option is specified");
6948 		RETURN_FALSE;
6949 	}
6950 
6951 	if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
6952 		RETURN_FALSE;
6953 	}
6954 
6955 	if (php_pgsql_flush_query(pg_link)) {
6956 		php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
6957 	}
6958 	if (php_pgsql_update(pg_link, table, values, ids, option, &sql) == FAILURE) {
6959 		RETURN_FALSE;
6960 	}
6961 	if (option & PGSQL_DML_STRING) {
6962 		RETURN_STR(sql);
6963 	}
6964 	RETURN_TRUE;
6965 }
6966 /* }}} */
6967 
6968 /* {{{ php_pgsql_delete
6969  */
6970 PHP_PGSQL_API int php_pgsql_delete(PGconn *pg_link, const char *table, zval *ids_array, zend_ulong opt, zend_string **sql)
6971 {
6972 	zval ids_converted;
6973 	smart_str querystr = {0};
6974 	int ret = FAILURE;
6975 
6976 	assert(pg_link != NULL);
6977 	assert(table != NULL);
6978 	assert(Z_TYPE_P(ids_array) == IS_ARRAY);
6979 	assert(!(opt & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)));
6980 
6981 	if (zend_hash_num_elements(Z_ARRVAL_P(ids_array)) == 0) {
6982 		return FAILURE;
6983 	}
6984 
6985 	ZVAL_UNDEF(&ids_converted);
6986 	if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) {
6987 		array_init(&ids_converted);
6988 		if (php_pgsql_convert(pg_link, table, ids_array, &ids_converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
6989 			goto cleanup;
6990 		}
6991 		ids_array = &ids_converted;
6992 	}
6993 
6994 	smart_str_appends(&querystr, "DELETE FROM ");
6995 	build_tablename(&querystr, pg_link, table);
6996 	smart_str_appends(&querystr, " WHERE ");
6997 
6998 	if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt))
6999 		goto cleanup;
7000 
7001 	smart_str_appendc(&querystr, ';');
7002 	smart_str_0(&querystr);
7003 
7004 	if ((opt & PGSQL_DML_EXEC) && do_exec(&querystr, PGRES_COMMAND_OK, pg_link, opt) == 0) {
7005 		ret = SUCCESS;
7006 	} else if (opt & PGSQL_DML_STRING) {
7007 		ret = SUCCESS;
7008 	}
7009 
7010 cleanup:
7011 	zval_ptr_dtor(&ids_converted);
7012 	if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
7013 		*sql = querystr.s;
7014 	}
7015 	else {
7016 		smart_str_free(&querystr);
7017 	}
7018 	return ret;
7019 }
7020 /* }}} */
7021 
7022 /* {{{ proto mixed pg_delete(resource db, string table, array ids[, int options])
7023    Delete records has ids (id=>value) */
7024 PHP_FUNCTION(pg_delete)
7025 {
7026 	zval *pgsql_link, *ids;
7027 	char *table;
7028 	size_t table_len;
7029 	zend_ulong option = PGSQL_DML_EXEC;
7030 	PGconn *pg_link;
7031 	zend_string *sql;
7032 	int argc = ZEND_NUM_ARGS();
7033 
7034 	if (zend_parse_parameters(argc, "rsa|l",
7035 							  &pgsql_link, &table, &table_len, &ids, &option) == FAILURE) {
7036 		return;
7037 	}
7038 	if (option & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
7039 		php_error_docref(NULL, E_WARNING, "Invalid option is specified");
7040 		RETURN_FALSE;
7041 	}
7042 
7043 	if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
7044 		RETURN_FALSE;
7045 	}
7046 
7047 	if (php_pgsql_flush_query(pg_link)) {
7048 		php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
7049 	}
7050 	if (php_pgsql_delete(pg_link, table, ids, option, &sql) == FAILURE) {
7051 		RETURN_FALSE;
7052 	}
7053 	if (option & PGSQL_DML_STRING) {
7054 		RETURN_STR(sql);
7055 	}
7056 	RETURN_TRUE;
7057 }
7058 /* }}} */
7059 
7060 /* {{{ php_pgsql_result2array
7061  */
7062 PHP_PGSQL_API int php_pgsql_result2array(PGresult *pg_result, zval *ret_array, long result_type)
7063 {
7064 	zval row;
7065 	char *field_name;
7066 	size_t num_fields;
7067 	int pg_numrows, pg_row;
7068 	uint32_t i;
7069 	assert(Z_TYPE_P(ret_array) == IS_ARRAY);
7070 
7071 	if ((pg_numrows = PQntuples(pg_result)) <= 0) {
7072 		return FAILURE;
7073 	}
7074 	for (pg_row = 0; pg_row < pg_numrows; pg_row++) {
7075 		array_init(&row);
7076 		for (i = 0, num_fields = PQnfields(pg_result); i < num_fields; i++) {
7077 			field_name = PQfname(pg_result, i);
7078 			if (PQgetisnull(pg_result, pg_row, i)) {
7079 				if (result_type & PGSQL_ASSOC) {
7080 					add_assoc_null(&row, field_name);
7081 				}
7082 				if (result_type & PGSQL_NUM) {
7083 					add_next_index_null(&row);
7084 				}
7085 			} else {
7086 				char *element = PQgetvalue(pg_result, pg_row, i);
7087 				if (element) {
7088 					const size_t element_len = strlen(element);
7089 					if (result_type & PGSQL_ASSOC) {
7090 						add_assoc_stringl(&row, field_name, element, element_len);
7091 					}
7092 					if (result_type & PGSQL_NUM) {
7093 						add_next_index_stringl(&row, element, element_len);
7094 					}
7095 				}
7096 			}
7097 		}
7098 		add_index_zval(ret_array, pg_row, &row);
7099 	}
7100 	return SUCCESS;
7101 }
7102 /* }}} */
7103 
7104 /* {{{ php_pgsql_select
7105  */
7106  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)
7107 {
7108 	zval ids_converted;
7109 	smart_str querystr = {0};
7110 	int ret = FAILURE;
7111 	PGresult *pg_result;
7112 
7113 	assert(pg_link != NULL);
7114 	assert(table != NULL);
7115 	assert(Z_TYPE_P(ids_array) == IS_ARRAY);
7116 	assert(Z_TYPE_P(ret_array) == IS_ARRAY);
7117 	assert(!(opt & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)));
7118 
7119 	if (zend_hash_num_elements(Z_ARRVAL_P(ids_array)) == 0) {
7120 		return FAILURE;
7121 	}
7122 
7123 	ZVAL_UNDEF(&ids_converted);
7124 	if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) {
7125 		array_init(&ids_converted);
7126 		if (php_pgsql_convert(pg_link, table, ids_array, &ids_converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
7127 			goto cleanup;
7128 		}
7129 		ids_array = &ids_converted;
7130 	}
7131 
7132 	smart_str_appends(&querystr, "SELECT * FROM ");
7133 	build_tablename(&querystr, pg_link, table);
7134 	smart_str_appends(&querystr, " WHERE ");
7135 
7136 	if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt))
7137 		goto cleanup;
7138 
7139 	smart_str_appendc(&querystr, ';');
7140 	smart_str_0(&querystr);
7141 
7142 	pg_result = PQexec(pg_link, ZSTR_VAL(querystr.s));
7143 	if (PQresultStatus(pg_result) == PGRES_TUPLES_OK) {
7144 		ret = php_pgsql_result2array(pg_result, ret_array, result_type);
7145 	} else {
7146 		php_error_docref(NULL, E_NOTICE, "Failed to execute '%s'", ZSTR_VAL(querystr.s));
7147 	}
7148 	PQclear(pg_result);
7149 
7150 cleanup:
7151 	zval_ptr_dtor(&ids_converted);
7152 	if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
7153 		*sql = querystr.s;
7154 	}
7155 	else {
7156 		smart_str_free(&querystr);
7157 	}
7158 	return ret;
7159 }
7160 /* }}} */
7161 
7162 /* {{{ proto mixed pg_select(resource db, string table, array ids[, int options [, int result_type])
7163    Select records that has ids (id=>value) */
7164 PHP_FUNCTION(pg_select)
7165 {
7166 	zval *pgsql_link, *ids;
7167 	char *table;
7168 	size_t table_len;
7169 	zend_ulong option = PGSQL_DML_EXEC;
7170 	long result_type = PGSQL_ASSOC;
7171 	PGconn *pg_link;
7172 	zend_string *sql = NULL;
7173 	int argc = ZEND_NUM_ARGS();
7174 
7175 	if (zend_parse_parameters(argc, "rsa|l",
7176 							  &pgsql_link, &table, &table_len, &ids, &option, &result_type) == FAILURE) {
7177 		return;
7178 	}
7179 	if (option & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
7180 		php_error_docref(NULL, E_WARNING, "Invalid option is specified");
7181 		RETURN_FALSE;
7182 	}
7183 	if (!(result_type & PGSQL_BOTH)) {
7184 		php_error_docref(NULL, E_WARNING, "Invalid result type");
7185 		RETURN_FALSE;
7186 	}
7187 
7188 	if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
7189 		RETURN_FALSE;
7190 	}
7191 
7192 	if (php_pgsql_flush_query(pg_link)) {
7193 		php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
7194 	}
7195 	array_init(return_value);
7196 	if (php_pgsql_select(pg_link, table, ids, return_value, option, result_type, &sql) == FAILURE) {
7197 		zval_ptr_dtor(return_value);
7198 		RETURN_FALSE;
7199 	}
7200 	if (option & PGSQL_DML_STRING) {
7201 		zval_ptr_dtor(return_value);
7202 		RETURN_STR(sql);
7203 	}
7204 	return;
7205 }
7206 /* }}} */
7207 
7208 #endif
7209