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