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