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