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