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