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#ifdef USE_SSL
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
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	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, "Invalid option specified (%d)", option);
2362	}
2363	RETURN_FALSE;
2364}
2365/* }}} */
2366
2367/* {{{ get_field_name
2368 */
2369static char *get_field_name(PGconn *pgsql, Oid oid, HashTable *list)
2370{
2371	PGresult *result;
2372	smart_str str = {0};
2373	zend_resource *field_type;
2374	char *ret=NULL;
2375
2376	/* try to lookup the type in the resource list */
2377	smart_str_appends(&str, "pgsql_oid_");
2378	smart_str_append_unsigned(&str, oid);
2379	smart_str_0(&str);
2380
2381	if ((field_type = zend_hash_find_ptr(list, str.s)) != NULL) {
2382		ret = estrdup((char *)field_type->ptr);
2383	} else { /* hash all oid's */
2384		int i, num_rows;
2385		int oid_offset,name_offset;
2386		char *tmp_oid, *end_ptr, *tmp_name;
2387		zend_resource new_oid_entry;
2388
2389		if ((result = PQexec(pgsql, "select oid,typname from pg_type")) == NULL || PQresultStatus(result) != PGRES_TUPLES_OK) {
2390			if (result) {
2391				PQclear(result);
2392			}
2393			smart_str_free(&str);
2394			return estrndup("", sizeof("") - 1);
2395		}
2396		num_rows = PQntuples(result);
2397		oid_offset = PQfnumber(result,"oid");
2398		name_offset = PQfnumber(result,"typname");
2399
2400		for (i=0; i<num_rows; i++) {
2401			if ((tmp_oid = PQgetvalue(result,i,oid_offset))==NULL) {
2402				continue;
2403			}
2404
2405			smart_str_free(&str);
2406			smart_str_appends(&str, "pgsql_oid_");
2407			smart_str_appends(&str, tmp_oid);
2408			smart_str_0(&str);
2409
2410			if ((tmp_name = PQgetvalue(result,i,name_offset))==NULL) {
2411				continue;
2412			}
2413			new_oid_entry.type = le_string;
2414			new_oid_entry.ptr = estrdup(tmp_name);
2415			zend_hash_update_mem(list, str.s, (void *) &new_oid_entry, sizeof(zend_resource));
2416			if (!ret && strtoul(tmp_oid, &end_ptr, 10)==oid) {
2417				ret = estrdup(tmp_name);
2418			}
2419		}
2420		PQclear(result);
2421	}
2422
2423	smart_str_free(&str);
2424	return ret;
2425}
2426/* }}} */
2427
2428#ifdef HAVE_PQFTABLE
2429/* {{{ proto mixed pg_field_table(resource result, int field_number[, bool oid_only])
2430   Returns the name of the table field belongs to, or table's oid if oid_only is true */
2431PHP_FUNCTION(pg_field_table)
2432{
2433	zval *result;
2434	pgsql_result_handle *pg_result;
2435	zend_long fnum = -1;
2436	zend_bool return_oid = 0;
2437	Oid oid;
2438	smart_str hash_key = {0};
2439	char *table_name;
2440	zend_resource *field_table;
2441
2442	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl|b", &result, &fnum, &return_oid) == FAILURE) {
2443		return;
2444	}
2445
2446	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2447		RETURN_FALSE;
2448	}
2449
2450	if (fnum < 0 || fnum >= PQnfields(pg_result->result)) {
2451		php_error_docref(NULL, E_WARNING, "Bad field offset specified");
2452		RETURN_FALSE;
2453	}
2454
2455	oid = PQftable(pg_result->result, (int)fnum);
2456
2457	if (InvalidOid == oid) {
2458		RETURN_FALSE;
2459	}
2460
2461	if (return_oid) {
2462#if UINT_MAX > ZEND_LONG_MAX /* Oid is unsigned int, we don't need this code, where LONG is wider */
2463		if (oid > ZEND_LONG_MAX) {
2464			smart_str oidstr = {0};
2465			smart_str_append_unsigned(&oidstr, oid);
2466			smart_str_0(&oidstr);
2467			RETURN_NEW_STR(oidstr.s);
2468		} else
2469#endif
2470			RETURN_LONG((zend_long)oid);
2471	}
2472
2473	/* try to lookup the table name in the resource list */
2474	smart_str_appends(&hash_key, "pgsql_table_oid_");
2475	smart_str_append_unsigned(&hash_key, oid);
2476	smart_str_0(&hash_key);
2477
2478	if ((field_table = zend_hash_find_ptr(&EG(regular_list), hash_key.s)) != NULL) {
2479		smart_str_free(&hash_key);
2480		RETURN_STRING((char *)field_table->ptr);
2481	} else { /* Not found, lookup by querying PostgreSQL system tables */
2482		PGresult *tmp_res;
2483		smart_str querystr = {0};
2484		zend_resource new_field_table;
2485
2486		smart_str_appends(&querystr, "select relname from pg_class where oid=");
2487		smart_str_append_unsigned(&querystr, oid);
2488		smart_str_0(&querystr);
2489
2490		if ((tmp_res = PQexec(pg_result->conn, ZSTR_VAL(querystr.s))) == NULL || PQresultStatus(tmp_res) != PGRES_TUPLES_OK) {
2491			if (tmp_res) {
2492				PQclear(tmp_res);
2493			}
2494			smart_str_free(&querystr);
2495			smart_str_free(&hash_key);
2496			RETURN_FALSE;
2497		}
2498
2499		smart_str_free(&querystr);
2500
2501		if ((table_name = PQgetvalue(tmp_res, 0, 0)) == NULL) {
2502			PQclear(tmp_res);
2503			smart_str_free(&hash_key);
2504			RETURN_FALSE;
2505		}
2506
2507		new_field_table.type = le_string;
2508		new_field_table.ptr = estrdup(table_name);
2509		zend_hash_update_mem(&EG(regular_list), hash_key.s, (void *)&new_field_table, sizeof(zend_resource));
2510
2511		smart_str_free(&hash_key);
2512		PQclear(tmp_res);
2513		RETURN_STRING(table_name);
2514	}
2515
2516}
2517/* }}} */
2518#endif
2519
2520#define PHP_PG_FIELD_NAME 1
2521#define PHP_PG_FIELD_SIZE 2
2522#define PHP_PG_FIELD_TYPE 3
2523#define PHP_PG_FIELD_TYPE_OID 4
2524
2525/* {{{ php_pgsql_get_field_info
2526 */
2527static void php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
2528{
2529	zval *result;
2530	zend_long field;
2531	PGresult *pgsql_result;
2532	pgsql_result_handle *pg_result;
2533	Oid oid;
2534
2535	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &result, &field) == FAILURE) {
2536		return;
2537	}
2538
2539	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2540		RETURN_FALSE;
2541	}
2542
2543
2544	pgsql_result = pg_result->result;
2545
2546	if (field < 0 || field >= PQnfields(pgsql_result)) {
2547		php_error_docref(NULL, E_WARNING, "Bad field offset specified");
2548		RETURN_FALSE;
2549	}
2550
2551	switch (entry_type) {
2552		case PHP_PG_FIELD_NAME:
2553			RETURN_STRING(PQfname(pgsql_result, (int)field));
2554			break;
2555		case PHP_PG_FIELD_SIZE:
2556			RETURN_LONG(PQfsize(pgsql_result, (int)field));
2557			break;
2558		case PHP_PG_FIELD_TYPE: {
2559				char *name = get_field_name(pg_result->conn, PQftype(pgsql_result, (int)field), &EG(regular_list));
2560				RETVAL_STRING(name);
2561				efree(name);
2562			}
2563			break;
2564		case PHP_PG_FIELD_TYPE_OID:
2565
2566			oid = PQftype(pgsql_result, (int)field);
2567#if UINT_MAX > ZEND_LONG_MAX
2568			if (oid > ZEND_LONG_MAX) {
2569				smart_str s = {0};
2570				smart_str_append_unsigned(&s, oid);
2571				smart_str_0(&s);
2572				RETURN_NEW_STR(s.s);
2573			} else
2574#endif
2575			{
2576				RETURN_LONG((zend_long)oid);
2577			}
2578			break;
2579		default:
2580			RETURN_FALSE;
2581	}
2582}
2583/* }}} */
2584
2585/* {{{ proto string pg_field_name(resource result, int field_number)
2586   Returns the name of the field */
2587PHP_FUNCTION(pg_field_name)
2588{
2589	php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_NAME);
2590}
2591/* }}} */
2592
2593/* {{{ proto int pg_field_size(resource result, int field_number)
2594   Returns the internal size of the field */
2595PHP_FUNCTION(pg_field_size)
2596{
2597	php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_SIZE);
2598}
2599/* }}} */
2600
2601/* {{{ proto string pg_field_type(resource result, int field_number)
2602   Returns the type name for the given field */
2603PHP_FUNCTION(pg_field_type)
2604{
2605	php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_TYPE);
2606}
2607/* }}} */
2608
2609/* {{{ proto string pg_field_type_oid(resource result, int field_number)
2610   Returns the type oid for the given field */
2611PHP_FUNCTION(pg_field_type_oid)
2612{
2613	php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_TYPE_OID);
2614}
2615/* }}} */
2616
2617/* {{{ proto int pg_field_num(resource result, string field_name)
2618   Returns the field number of the named field */
2619PHP_FUNCTION(pg_field_num)
2620{
2621	zval *result;
2622	char *field;
2623	size_t field_len;
2624	PGresult *pgsql_result;
2625	pgsql_result_handle *pg_result;
2626
2627	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &result, &field, &field_len) == FAILURE) {
2628		return;
2629	}
2630
2631	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2632		RETURN_FALSE;
2633	}
2634
2635	pgsql_result = pg_result->result;
2636
2637	RETURN_LONG(PQfnumber(pgsql_result, field));
2638}
2639/* }}} */
2640
2641/* {{{ proto mixed pg_fetch_result(resource result, [int row_number,] mixed field_name)
2642   Returns values from a result identifier */
2643PHP_FUNCTION(pg_fetch_result)
2644{
2645	zval *result, *field=NULL;
2646	zend_long row;
2647	PGresult *pgsql_result;
2648	pgsql_result_handle *pg_result;
2649	int field_offset, pgsql_row, argc = ZEND_NUM_ARGS();
2650
2651	if (argc == 2) {
2652		if (zend_parse_parameters(argc, "rz", &result, &field) == FAILURE) {
2653			return;
2654		}
2655	} else {
2656		if (zend_parse_parameters(argc, "rlz", &result, &row, &field) == FAILURE) {
2657			return;
2658		}
2659	}
2660
2661	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2662		RETURN_FALSE;
2663	}
2664
2665	pgsql_result = pg_result->result;
2666	if (argc == 2) {
2667		if (pg_result->row < 0) {
2668			pg_result->row = 0;
2669		}
2670		pgsql_row = pg_result->row;
2671		if (pgsql_row >= PQntuples(pgsql_result)) {
2672			RETURN_FALSE;
2673		}
2674	} else {
2675		if (row < 0 || row >= PQntuples(pgsql_result)) {
2676			php_error_docref(NULL, E_WARNING, "Unable to jump to row %pd on PostgreSQL result index %pd",
2677							row, Z_LVAL_P(result));
2678			RETURN_FALSE;
2679		}
2680		pgsql_row = (int)row;
2681	}
2682	switch (Z_TYPE_P(field)) {
2683		case IS_STRING:
2684			field_offset = PQfnumber(pgsql_result, Z_STRVAL_P(field));
2685			if (field_offset < 0 || field_offset >= PQnfields(pgsql_result)) {
2686				php_error_docref(NULL, E_WARNING, "Bad column offset specified");
2687				RETURN_FALSE;
2688			}
2689			break;
2690		default:
2691			convert_to_long_ex(field);
2692			if (Z_LVAL_P(field) < 0 || Z_LVAL_P(field) >= PQnfields(pgsql_result)) {
2693				php_error_docref(NULL, E_WARNING, "Bad column offset specified");
2694				RETURN_FALSE;
2695			}
2696			field_offset = (int)Z_LVAL_P(field);
2697			break;
2698	}
2699
2700	if (PQgetisnull(pgsql_result, pgsql_row, field_offset)) {
2701		RETVAL_NULL();
2702	} else {
2703		RETVAL_STRINGL(PQgetvalue(pgsql_result, pgsql_row, field_offset),
2704				PQgetlength(pgsql_result, pgsql_row, field_offset));
2705	}
2706}
2707/* }}} */
2708
2709/* {{{ void php_pgsql_fetch_hash */
2710static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_type, int into_object)
2711{
2712	zval                *result, *zrow = NULL;
2713	PGresult            *pgsql_result;
2714	pgsql_result_handle *pg_result;
2715	int             i, num_fields, pgsql_row, use_row;
2716	zend_long            row = -1;
2717	char            *field_name;
2718	zval            *ctor_params = NULL;
2719	zend_class_entry *ce = NULL;
2720
2721	if (into_object) {
2722		zend_string *class_name = NULL;
2723
2724		if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|z!Sz", &result, &zrow, &class_name, &ctor_params) == FAILURE) {
2725			return;
2726		}
2727		if (!class_name) {
2728			ce = zend_standard_class_def;
2729		} else {
2730			ce = zend_fetch_class(class_name, ZEND_FETCH_CLASS_AUTO);
2731		}
2732		if (!ce) {
2733			php_error_docref(NULL, E_WARNING, "Could not find class '%s'", ZSTR_VAL(class_name));
2734			return;
2735		}
2736		result_type = PGSQL_ASSOC;
2737	} else {
2738		if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|z!l", &result, &zrow, &result_type) == FAILURE) {
2739			return;
2740		}
2741	}
2742	if (zrow == NULL) {
2743		row = -1;
2744	} else {
2745		convert_to_long(zrow);
2746		row = Z_LVAL_P(zrow);
2747		if (row < 0) {
2748			php_error_docref(NULL, E_WARNING, "The row parameter must be greater or equal to zero");
2749			RETURN_FALSE;
2750		}
2751	}
2752	use_row = ZEND_NUM_ARGS() > 1 && row != -1;
2753
2754	if (!(result_type & PGSQL_BOTH)) {
2755		php_error_docref(NULL, E_WARNING, "Invalid result type");
2756		RETURN_FALSE;
2757	}
2758
2759	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2760		RETURN_FALSE;
2761	}
2762
2763	pgsql_result = pg_result->result;
2764
2765	if (use_row) {
2766		if (row < 0 || row >= PQntuples(pgsql_result)) {
2767			php_error_docref(NULL, E_WARNING, "Unable to jump to row %pd on PostgreSQL result index %pd",
2768							row, Z_LVAL_P(result));
2769			RETURN_FALSE;
2770		}
2771		pgsql_row = (int)row;
2772		pg_result->row = pgsql_row;
2773	} else {
2774		/* If 2nd param is NULL, use internal row counter to access next row */
2775		pgsql_row = pg_result->row;
2776		if (pgsql_row < 0 || pgsql_row >= PQntuples(pgsql_result)) {
2777			RETURN_FALSE;
2778		}
2779		pg_result->row++;
2780	}
2781
2782	array_init(return_value);
2783	for (i = 0, num_fields = PQnfields(pgsql_result); i < num_fields; i++) {
2784		if (PQgetisnull(pgsql_result, pgsql_row, i)) {
2785			if (result_type & PGSQL_NUM) {
2786				add_index_null(return_value, i);
2787			}
2788			if (result_type & PGSQL_ASSOC) {
2789				field_name = PQfname(pgsql_result, i);
2790				add_assoc_null(return_value, field_name);
2791			}
2792		} else {
2793			char *element = PQgetvalue(pgsql_result, pgsql_row, i);
2794			if (element) {
2795				const size_t element_len = strlen(element);
2796
2797				if (result_type & PGSQL_NUM) {
2798					add_index_stringl(return_value, i, element, element_len);
2799				}
2800
2801				if (result_type & PGSQL_ASSOC) {
2802					field_name = PQfname(pgsql_result, i);
2803					add_assoc_stringl(return_value, field_name, element, element_len);
2804				}
2805			}
2806		}
2807	}
2808
2809	if (into_object) {
2810		zval dataset;
2811		zend_fcall_info fci;
2812		zend_fcall_info_cache fcc;
2813		zval retval;
2814
2815		ZVAL_COPY_VALUE(&dataset, return_value);
2816		object_and_properties_init(return_value, ce, NULL);
2817		if (!ce->default_properties_count && !ce->__set) {
2818			Z_OBJ_P(return_value)->properties = Z_ARR(dataset);
2819		} else {
2820			zend_merge_properties(return_value, Z_ARRVAL(dataset));
2821			zval_ptr_dtor(&dataset);
2822		}
2823
2824		if (ce->constructor) {
2825			fci.size = sizeof(fci);
2826			fci.function_table = &ce->function_table;
2827			ZVAL_UNDEF(&fci.function_name);
2828			fci.symbol_table = NULL;
2829			fci.object = Z_OBJ_P(return_value);
2830			fci.retval = &retval;
2831			fci.params = NULL;
2832			fci.param_count = 0;
2833			fci.no_separation = 1;
2834
2835			if (ctor_params && Z_TYPE_P(ctor_params) != IS_NULL) {
2836				if (zend_fcall_info_args(&fci, ctor_params) == FAILURE) {
2837					/* Two problems why we throw exceptions here: PHP is typeless
2838					 * and hence passing one argument that's not an array could be
2839					 * by mistake and the other way round is possible, too. The
2840					 * single value is an array. Also we'd have to make that one
2841					 * argument passed by reference.
2842					 */
2843					zend_throw_exception(zend_ce_exception, "Parameter ctor_params must be an array", 0);
2844					return;
2845				}
2846			}
2847
2848			fcc.initialized = 1;
2849			fcc.function_handler = ce->constructor;
2850			fcc.calling_scope = EG(scope);
2851			fcc.called_scope = Z_OBJCE_P(return_value);
2852			fcc.object = Z_OBJ_P(return_value);
2853
2854			if (zend_call_function(&fci, &fcc) == FAILURE) {
2855				zend_throw_exception_ex(zend_ce_exception, 0, "Could not execute %s::%s()", ce->name, ce->constructor->common.function_name);
2856			} else {
2857				zval_ptr_dtor(&retval);
2858			}
2859			if (fci.params) {
2860				efree(fci.params);
2861			}
2862		} else if (ctor_params) {
2863			zend_throw_exception_ex(zend_ce_exception, 0, "Class %s does not have a constructor hence you cannot use ctor_params", ce->name);
2864		}
2865	}
2866}
2867/* }}} */
2868
2869/* {{{ proto array pg_fetch_row(resource result [, int row [, int result_type]])
2870   Get a row as an enumerated array */
2871PHP_FUNCTION(pg_fetch_row)
2872{
2873	php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_NUM, 0);
2874}
2875/* }}} */
2876
2877/* {{{ proto array pg_fetch_assoc(resource result [, int row])
2878   Fetch a row as an assoc array */
2879PHP_FUNCTION(pg_fetch_assoc)
2880{
2881	/* pg_fetch_assoc() is added from PHP 4.3.0. It should raise error, when
2882	   there is 3rd parameter */
2883	if (ZEND_NUM_ARGS() > 2)
2884		WRONG_PARAM_COUNT;
2885	php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_ASSOC, 0);
2886}
2887/* }}} */
2888
2889/* {{{ proto array pg_fetch_array(resource result [, int row [, int result_type]])
2890   Fetch a row as an array */
2891PHP_FUNCTION(pg_fetch_array)
2892{
2893	php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_BOTH, 0);
2894}
2895/* }}} */
2896
2897/* {{{ proto object pg_fetch_object(resource result [, int row [, string class_name [, NULL|array ctor_params]]])
2898   Fetch a row as an object */
2899PHP_FUNCTION(pg_fetch_object)
2900{
2901	/* pg_fetch_object() allowed result_type used to be. 3rd parameter
2902	   must be allowed for compatibility */
2903	php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_ASSOC, 1);
2904}
2905/* }}} */
2906
2907/* {{{ proto array pg_fetch_all(resource result [, int result_type])
2908   Fetch all rows into array */
2909PHP_FUNCTION(pg_fetch_all)
2910{
2911	zval *result;
2912	long result_type = PGSQL_ASSOC;
2913	PGresult *pgsql_result;
2914	pgsql_result_handle *pg_result;
2915
2916	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &result, &result_type) == FAILURE) {
2917		return;
2918	}
2919
2920	if (!(result_type & PGSQL_BOTH)) {
2921		php_error_docref(NULL, E_WARNING, "Invalid result type");
2922		RETURN_FALSE;
2923	}
2924
2925	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2926		RETURN_FALSE;
2927	}
2928
2929	pgsql_result = pg_result->result;
2930	array_init(return_value);
2931	if (php_pgsql_result2array(pgsql_result, return_value, result_type) == FAILURE) {
2932		zval_dtor(return_value);
2933		RETURN_FALSE;
2934	}
2935}
2936/* }}} */
2937
2938/* {{{ proto array pg_fetch_all_columns(resource result [, int column_number])
2939   Fetch all rows into array */
2940PHP_FUNCTION(pg_fetch_all_columns)
2941{
2942	zval *result;
2943	PGresult *pgsql_result;
2944	pgsql_result_handle *pg_result;
2945	zend_long colno=0;
2946	int pg_numrows, pg_row;
2947	size_t num_fields;
2948
2949	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &result, &colno) == FAILURE) {
2950		RETURN_FALSE;
2951	}
2952
2953	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2954		RETURN_FALSE;
2955	}
2956
2957	pgsql_result = pg_result->result;
2958
2959	num_fields = PQnfields(pgsql_result);
2960	if (colno >= (zend_long)num_fields || colno < 0) {
2961		php_error_docref(NULL, E_WARNING, "Invalid column number '%pd'", colno);
2962		RETURN_FALSE;
2963	}
2964
2965	array_init(return_value);
2966
2967	if ((pg_numrows = PQntuples(pgsql_result)) <= 0) {
2968		return;
2969	}
2970
2971	for (pg_row = 0; pg_row < pg_numrows; pg_row++) {
2972		if (PQgetisnull(pgsql_result, pg_row, (int)colno)) {
2973			add_next_index_null(return_value);
2974		} else {
2975			add_next_index_string(return_value, PQgetvalue(pgsql_result, pg_row, (int)colno));
2976		}
2977	}
2978}
2979/* }}} */
2980
2981/* {{{ proto bool pg_result_seek(resource result, int offset)
2982   Set internal row offset */
2983PHP_FUNCTION(pg_result_seek)
2984{
2985	zval *result;
2986	zend_long row;
2987	pgsql_result_handle *pg_result;
2988
2989	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &result, &row) == FAILURE) {
2990		return;
2991	}
2992
2993	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2994		RETURN_FALSE;
2995	}
2996
2997	if (row < 0 || row >= PQntuples(pg_result->result)) {
2998		RETURN_FALSE;
2999	}
3000
3001	/* seek to offset */
3002	pg_result->row = (int)row;
3003	RETURN_TRUE;
3004}
3005/* }}} */
3006
3007#define PHP_PG_DATA_LENGTH 1
3008#define PHP_PG_DATA_ISNULL 2
3009
3010/* {{{ php_pgsql_data_info
3011 */
3012static void php_pgsql_data_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
3013{
3014	zval *result, *field;
3015	zend_long row;
3016	PGresult *pgsql_result;
3017	pgsql_result_handle *pg_result;
3018	int field_offset, pgsql_row, argc = ZEND_NUM_ARGS();
3019
3020	if (argc == 2) {
3021		if (zend_parse_parameters(argc, "rz", &result, &field) == FAILURE) {
3022			return;
3023		}
3024	} else {
3025		if (zend_parse_parameters(argc, "rlz", &result, &row, &field) == FAILURE) {
3026			return;
3027		}
3028	}
3029
3030	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
3031		RETURN_FALSE;
3032	}
3033
3034	pgsql_result = pg_result->result;
3035	if (argc == 2) {
3036		if (pg_result->row < 0) {
3037			pg_result->row = 0;
3038		}
3039		pgsql_row = pg_result->row;
3040		if (pgsql_row < 0 || pgsql_row >= PQntuples(pgsql_result)) {
3041			RETURN_FALSE;
3042		}
3043	} else {
3044		if (row < 0 || row >= PQntuples(pgsql_result)) {
3045			php_error_docref(NULL, E_WARNING, "Unable to jump to row %pd on PostgreSQL result index %pd",
3046							row, Z_LVAL_P(result));
3047			RETURN_FALSE;
3048		}
3049		pgsql_row = (int)row;
3050	}
3051
3052	switch (Z_TYPE_P(field)) {
3053		case IS_STRING:
3054			convert_to_string_ex(field);
3055			field_offset = PQfnumber(pgsql_result, Z_STRVAL_P(field));
3056			if (field_offset < 0 || field_offset >= PQnfields(pgsql_result)) {
3057				php_error_docref(NULL, E_WARNING, "Bad column offset specified");
3058				RETURN_FALSE;
3059			}
3060			break;
3061		default:
3062			convert_to_long_ex(field);
3063			if (Z_LVAL_P(field) < 0 || Z_LVAL_P(field) >= PQnfields(pgsql_result)) {
3064				php_error_docref(NULL, E_WARNING, "Bad column offset specified");
3065				RETURN_FALSE;
3066			}
3067			field_offset = (int)Z_LVAL_P(field);
3068			break;
3069	}
3070
3071	switch (entry_type) {
3072		case PHP_PG_DATA_LENGTH:
3073			RETVAL_LONG(PQgetlength(pgsql_result, pgsql_row, field_offset));
3074			break;
3075		case PHP_PG_DATA_ISNULL:
3076			RETVAL_LONG(PQgetisnull(pgsql_result, pgsql_row, field_offset))
3077			break;
3078	}
3079}
3080/* }}} */
3081
3082/* {{{ proto int pg_field_prtlen(resource result, [int row,] mixed field_name_or_number)
3083   Returns the printed length */
3084PHP_FUNCTION(pg_field_prtlen)
3085{
3086	php_pgsql_data_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_DATA_LENGTH);
3087}
3088/* }}} */
3089
3090/* {{{ proto int pg_field_is_null(resource result, [int row,] mixed field_name_or_number)
3091   Test if a field is NULL */
3092PHP_FUNCTION(pg_field_is_null)
3093{
3094	php_pgsql_data_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_DATA_ISNULL);
3095}
3096/* }}} */
3097
3098/* {{{ proto bool pg_free_result(resource result)
3099   Free result memory */
3100PHP_FUNCTION(pg_free_result)
3101{
3102	zval *result;
3103	pgsql_result_handle *pg_result;
3104
3105	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &result) == FAILURE) {
3106		return;
3107	}
3108
3109	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
3110		RETURN_FALSE;
3111	}
3112
3113	zend_list_close(Z_RES_P(result));
3114	RETURN_TRUE;
3115}
3116/* }}} */
3117
3118/* {{{ proto string pg_last_oid(resource result)
3119   Returns the last object identifier */
3120PHP_FUNCTION(pg_last_oid)
3121{
3122	zval *result;
3123	PGresult *pgsql_result;
3124	pgsql_result_handle *pg_result;
3125#ifdef HAVE_PQOIDVALUE
3126	Oid oid;
3127#endif
3128
3129	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &result) == FAILURE) {
3130		return;
3131	}
3132
3133	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
3134		RETURN_FALSE;
3135	}
3136
3137	pgsql_result = pg_result->result;
3138#ifdef HAVE_PQOIDVALUE
3139	oid = PQoidValue(pgsql_result);
3140	if (oid == InvalidOid) {
3141		RETURN_FALSE;
3142	}
3143	PGSQL_RETURN_OID(oid);
3144#else
3145	Z_STRVAL_P(return_value) = (char *) PQoidStatus(pgsql_result);
3146	if (Z_STRVAL_P(return_value)) {
3147		RETURN_STRING(Z_STRVAL_P(return_value));
3148	}
3149	RETURN_EMPTY_STRING();
3150#endif
3151}
3152/* }}} */
3153
3154/* {{{ proto bool pg_trace(string filename [, string mode [, resource connection]])
3155   Enable tracing a PostgreSQL connection */
3156PHP_FUNCTION(pg_trace)
3157{
3158	char *z_filename, *mode = "w";
3159	size_t z_filename_len, mode_len;
3160	zval *pgsql_link = NULL;
3161	int argc = ZEND_NUM_ARGS();
3162	PGconn *pgsql;
3163	FILE *fp = NULL;
3164	php_stream *stream;
3165	zend_resource *link;
3166
3167	if (zend_parse_parameters(argc, "p|sr", &z_filename, &z_filename_len, &mode, &mode_len, &pgsql_link) == FAILURE) {
3168		return;
3169	}
3170
3171	if (argc < 3) {
3172		link = FETCH_DEFAULT_LINK();
3173		CHECK_DEFAULT_LINK(link);
3174	} else {
3175		link = Z_RES_P(pgsql_link);
3176	}
3177
3178	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3179		RETURN_FALSE;
3180	}
3181
3182	stream = php_stream_open_wrapper(z_filename, mode, REPORT_ERRORS, NULL);
3183
3184	if (!stream) {
3185		RETURN_FALSE;
3186	}
3187
3188	if (FAILURE == php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void**)&fp, REPORT_ERRORS))	{
3189		php_stream_close(stream);
3190		RETURN_FALSE;
3191	}
3192	php_stream_auto_cleanup(stream);
3193	PQtrace(pgsql, fp);
3194	RETURN_TRUE;
3195}
3196/* }}} */
3197
3198/* {{{ proto bool pg_untrace([resource connection])
3199   Disable tracing of a PostgreSQL connection */
3200PHP_FUNCTION(pg_untrace)
3201{
3202	zval *pgsql_link = NULL;
3203	int argc = ZEND_NUM_ARGS();
3204	PGconn *pgsql;
3205	zend_resource *link;
3206
3207	if (zend_parse_parameters(argc, "|r", &pgsql_link) == FAILURE) {
3208		return;
3209	}
3210
3211	if (argc == 0) {
3212		link = FETCH_DEFAULT_LINK();
3213		CHECK_DEFAULT_LINK(link);
3214	} else {
3215		link = Z_RES_P(pgsql_link);
3216	}
3217
3218	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3219		RETURN_FALSE;
3220	}
3221
3222	PQuntrace(pgsql);
3223	RETURN_TRUE;
3224}
3225/* }}} */
3226
3227/* {{{ proto mixed pg_lo_create([resource connection],[mixed large_object_oid])
3228   Create a large object */
3229PHP_FUNCTION(pg_lo_create)
3230{
3231	zval *pgsql_link = NULL, *oid = NULL;
3232	PGconn *pgsql;
3233	Oid pgsql_oid, wanted_oid = InvalidOid;
3234	int argc = ZEND_NUM_ARGS();
3235	zend_resource *link;
3236
3237	if (zend_parse_parameters(argc, "|zz", &pgsql_link, &oid) == FAILURE) {
3238		return;
3239	}
3240
3241	if ((argc == 1) && (Z_TYPE_P(pgsql_link) != IS_RESOURCE)) {
3242		oid = pgsql_link;
3243		pgsql_link = NULL;
3244	}
3245
3246	if (pgsql_link == NULL) {
3247		link = FETCH_DEFAULT_LINK();
3248		CHECK_DEFAULT_LINK(link);
3249	} else {
3250		link = Z_RES_P(pgsql_link);
3251	}
3252
3253	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3254		RETURN_FALSE;
3255	}
3256
3257	if (oid) {
3258#ifndef HAVE_PG_LO_CREATE
3259		php_error_docref(NULL, E_NOTICE, "Passing OID value is not supported. Upgrade your PostgreSQL");
3260#else
3261		switch (Z_TYPE_P(oid)) {
3262		case IS_STRING:
3263			{
3264				char *end_ptr;
3265				wanted_oid = (Oid)strtoul(Z_STRVAL_P(oid), &end_ptr, 10);
3266				if ((Z_STRVAL_P(oid)+Z_STRLEN_P(oid)) != end_ptr) {
3267				/* wrong integer format */
3268				php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3269				RETURN_FALSE;
3270				}
3271			}
3272			break;
3273		case IS_LONG:
3274			if (Z_LVAL_P(oid) < (zend_long)InvalidOid) {
3275				php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3276				RETURN_FALSE;
3277			}
3278			wanted_oid = (Oid)Z_LVAL_P(oid);
3279			break;
3280		default:
3281			php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3282			RETURN_FALSE;
3283        }
3284		if ((pgsql_oid = lo_create(pgsql, wanted_oid)) == InvalidOid) {
3285			php_error_docref(NULL, E_WARNING, "Unable to create PostgreSQL large object");
3286			RETURN_FALSE;
3287		}
3288
3289		PGSQL_RETURN_OID(pgsql_oid);
3290#endif
3291	}
3292
3293	if ((pgsql_oid = lo_creat(pgsql, INV_READ|INV_WRITE)) == InvalidOid) {
3294		php_error_docref(NULL, E_WARNING, "Unable to create PostgreSQL large object");
3295		RETURN_FALSE;
3296	}
3297
3298	PGSQL_RETURN_OID(pgsql_oid);
3299}
3300/* }}} */
3301
3302/* {{{ proto bool pg_lo_unlink([resource connection,] string large_object_oid)
3303   Delete a large object */
3304PHP_FUNCTION(pg_lo_unlink)
3305{
3306	zval *pgsql_link = NULL;
3307	zend_long oid_long;
3308	char *oid_string, *end_ptr;
3309	size_t oid_strlen;
3310	PGconn *pgsql;
3311	Oid oid;
3312	zend_resource *link;
3313	int argc = ZEND_NUM_ARGS();
3314
3315	/* accept string type since Oid type is unsigned int */
3316	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3317								 "rs", &pgsql_link, &oid_string, &oid_strlen) == SUCCESS) {
3318		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3319		if ((oid_string+oid_strlen) != end_ptr) {
3320			/* wrong integer format */
3321			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3322			RETURN_FALSE;
3323		}
3324		link = Z_RES_P(pgsql_link);
3325	}
3326	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3327								 "rl", &pgsql_link, &oid_long) == SUCCESS) {
3328		if (oid_long <= InvalidOid) {
3329			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3330			RETURN_FALSE;
3331		}
3332		oid = (Oid)oid_long;
3333		link = Z_RES_P(pgsql_link);
3334	}
3335	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3336								 "s", &oid_string, &oid_strlen) == SUCCESS) {
3337		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3338		if ((oid_string+oid_strlen) != end_ptr) {
3339			/* wrong integer format */
3340			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3341			RETURN_FALSE;
3342		}
3343		link = FETCH_DEFAULT_LINK();
3344		CHECK_DEFAULT_LINK(link);
3345	}
3346	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3347								 "l", &oid_long) == SUCCESS) {
3348		if (oid_long <= InvalidOid) {
3349			php_error_docref(NULL, E_NOTICE, "Invalid OID is specified");
3350			RETURN_FALSE;
3351		}
3352		oid = (Oid)oid_long;
3353		link = FETCH_DEFAULT_LINK();
3354		CHECK_DEFAULT_LINK(link);
3355	}
3356	else {
3357		php_error_docref(NULL, E_WARNING, "Requires 1 or 2 arguments");
3358		RETURN_FALSE;
3359	}
3360
3361	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3362		RETURN_FALSE;
3363	}
3364
3365	if (lo_unlink(pgsql, oid) == -1) {
3366		php_error_docref(NULL, E_WARNING, "Unable to delete PostgreSQL large object %u", oid);
3367		RETURN_FALSE;
3368	}
3369	RETURN_TRUE;
3370}
3371/* }}} */
3372
3373/* {{{ proto resource pg_lo_open([resource connection,] int large_object_oid, string mode)
3374   Open a large object and return fd */
3375PHP_FUNCTION(pg_lo_open)
3376{
3377	zval *pgsql_link = NULL;
3378	zend_long oid_long;
3379	char *oid_string, *end_ptr, *mode_string;
3380	size_t oid_strlen, mode_strlen;
3381	PGconn *pgsql;
3382	Oid oid;
3383	int pgsql_mode=0, pgsql_lofd;
3384	int create = 0;
3385	pgLofp *pgsql_lofp;
3386	int argc = ZEND_NUM_ARGS();
3387	zend_resource *link;
3388
3389	/* accept string type since Oid is unsigned int */
3390	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3391								 "rss", &pgsql_link, &oid_string, &oid_strlen, &mode_string, &mode_strlen) == SUCCESS) {
3392		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3393		if ((oid_string+oid_strlen) != end_ptr) {
3394			/* wrong integer format */
3395			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3396			RETURN_FALSE;
3397		}
3398		link = Z_RES_P(pgsql_link);
3399	}
3400	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3401								 "rls", &pgsql_link, &oid_long, &mode_string, &mode_strlen) == SUCCESS) {
3402		if (oid_long <= InvalidOid) {
3403			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3404			RETURN_FALSE;
3405		}
3406		oid = (Oid)oid_long;
3407		link = Z_RES_P(pgsql_link);
3408	}
3409	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3410								 "ss", &oid_string, &oid_strlen, &mode_string, &mode_strlen) == SUCCESS) {
3411		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3412		if ((oid_string+oid_strlen) != end_ptr) {
3413			/* wrong integer format */
3414			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3415			RETURN_FALSE;
3416		}
3417		link = FETCH_DEFAULT_LINK();
3418		CHECK_DEFAULT_LINK(link);
3419	}
3420	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3421								 "ls", &oid_long, &mode_string, &mode_strlen) == SUCCESS) {
3422		if (oid_long <= InvalidOid) {
3423			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3424			RETURN_FALSE;
3425		}
3426		oid = (Oid)oid_long;
3427		link = FETCH_DEFAULT_LINK();
3428		CHECK_DEFAULT_LINK(link);
3429	}
3430	else {
3431		php_error_docref(NULL, E_WARNING, "Requires 1 or 2 arguments");
3432		RETURN_FALSE;
3433	}
3434
3435	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3436		RETURN_FALSE;
3437	}
3438
3439	/* r/w/+ is little bit more PHP-like than INV_READ/INV_WRITE and a lot of
3440	   faster to type. Unfortunately, doesn't behave the same way as fopen()...
3441	   (Jouni)
3442	*/
3443
3444	if (strchr(mode_string, 'r') == mode_string) {
3445		pgsql_mode |= INV_READ;
3446		if (strchr(mode_string, '+') == mode_string+1) {
3447			pgsql_mode |= INV_WRITE;
3448		}
3449	}
3450	if (strchr(mode_string, 'w') == mode_string) {
3451		pgsql_mode |= INV_WRITE;
3452		create = 1;
3453		if (strchr(mode_string, '+') == mode_string+1) {
3454			pgsql_mode |= INV_READ;
3455		}
3456	}
3457
3458	pgsql_lofp = (pgLofp *) emalloc(sizeof(pgLofp));
3459
3460	if ((pgsql_lofd = lo_open(pgsql, oid, pgsql_mode)) == -1) {
3461		if (create) {
3462			if ((oid = lo_creat(pgsql, INV_READ|INV_WRITE)) == 0) {
3463				efree(pgsql_lofp);
3464				php_error_docref(NULL, E_WARNING, "Unable to create PostgreSQL large object");
3465				RETURN_FALSE;
3466			} else {
3467				if ((pgsql_lofd = lo_open(pgsql, oid, pgsql_mode)) == -1) {
3468					if (lo_unlink(pgsql, oid) == -1) {
3469						efree(pgsql_lofp);
3470						php_error_docref(NULL, E_WARNING, "Something is really messed up! Your database is badly corrupted in a way NOT related to PHP");
3471						RETURN_FALSE;
3472					}
3473					efree(pgsql_lofp);
3474					php_error_docref(NULL, E_WARNING, "Unable to open PostgreSQL large object");
3475					RETURN_FALSE;
3476				} else {
3477					pgsql_lofp->conn = pgsql;
3478					pgsql_lofp->lofd = pgsql_lofd;
3479					RETURN_RES(zend_register_resource(pgsql_lofp, le_lofp));
3480				}
3481			}
3482		} else {
3483			efree(pgsql_lofp);
3484			php_error_docref(NULL, E_WARNING, "Unable to open PostgreSQL large object");
3485			RETURN_FALSE;
3486		}
3487	} else {
3488		pgsql_lofp->conn = pgsql;
3489		pgsql_lofp->lofd = pgsql_lofd;
3490		RETURN_RES(zend_register_resource(pgsql_lofp, le_lofp));
3491	}
3492}
3493/* }}} */
3494
3495/* {{{ proto bool pg_lo_close(resource large_object)
3496   Close a large object */
3497PHP_FUNCTION(pg_lo_close)
3498{
3499	zval *pgsql_lofp;
3500	pgLofp *pgsql;
3501
3502	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pgsql_lofp) == FAILURE) {
3503		return;
3504	}
3505
3506	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_lofp), "PostgreSQL large object", le_lofp)) == NULL) {
3507		RETURN_FALSE;
3508	}
3509
3510	if (lo_close((PGconn *)pgsql->conn, pgsql->lofd) < 0) {
3511		php_error_docref(NULL, E_WARNING, "Unable to close PostgreSQL large object descriptor %d", pgsql->lofd);
3512		RETVAL_FALSE;
3513	} else {
3514		RETVAL_TRUE;
3515	}
3516
3517	zend_list_close(Z_RES_P(pgsql_lofp));
3518	return;
3519}
3520/* }}} */
3521
3522#define PGSQL_LO_READ_BUF_SIZE  8192
3523
3524/* {{{ proto string pg_lo_read(resource large_object [, int len])
3525   Read a large object */
3526PHP_FUNCTION(pg_lo_read)
3527{
3528	zval *pgsql_id;
3529	zend_long len;
3530	size_t buf_len = PGSQL_LO_READ_BUF_SIZE;
3531	int nbytes, argc = ZEND_NUM_ARGS();
3532	zend_string *buf;
3533	pgLofp *pgsql;
3534
3535	if (zend_parse_parameters(argc, "r|l", &pgsql_id, &len) == FAILURE) {
3536		return;
3537	}
3538
3539	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3540		RETURN_FALSE;
3541	}
3542
3543	if (argc > 1) {
3544		buf_len = len < 0 ? 0 : len;
3545	}
3546
3547	buf = zend_string_alloc(buf_len, 0);
3548	if ((nbytes = lo_read((PGconn *)pgsql->conn, pgsql->lofd, ZSTR_VAL(buf), ZSTR_LEN(buf)))<0) {
3549		zend_string_free(buf);
3550		RETURN_FALSE;
3551	}
3552
3553	ZSTR_LEN(buf) = nbytes;
3554	ZSTR_VAL(buf)[ZSTR_LEN(buf)] = '\0';
3555	RETURN_NEW_STR(buf);
3556}
3557/* }}} */
3558
3559/* {{{ proto int pg_lo_write(resource large_object, string buf [, int len])
3560   Write a large object */
3561PHP_FUNCTION(pg_lo_write)
3562{
3563  	zval *pgsql_id;
3564  	char *str;
3565  	zend_long z_len;
3566	size_t str_len, nbytes;
3567	size_t len;
3568	pgLofp *pgsql;
3569	int argc = ZEND_NUM_ARGS();
3570
3571	if (zend_parse_parameters(argc, "rs|l", &pgsql_id, &str, &str_len, &z_len) == FAILURE) {
3572		return;
3573	}
3574
3575	if (argc > 2) {
3576		if (z_len > (zend_long)str_len) {
3577			php_error_docref(NULL, E_WARNING, "Cannot write more than buffer size %d. Tried to write %pd", str_len, z_len);
3578			RETURN_FALSE;
3579		}
3580		if (z_len < 0) {
3581			php_error_docref(NULL, E_WARNING, "Buffer size must be larger than 0, but %pd was specified", z_len);
3582			RETURN_FALSE;
3583		}
3584		len = z_len;
3585	}
3586	else {
3587		len = str_len;
3588	}
3589
3590	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3591		RETURN_FALSE;
3592	}
3593
3594	if ((nbytes = lo_write((PGconn *)pgsql->conn, pgsql->lofd, str, len)) == -1) {
3595		RETURN_FALSE;
3596	}
3597
3598	RETURN_LONG(nbytes);
3599}
3600/* }}} */
3601
3602/* {{{ proto int pg_lo_read_all(resource large_object)
3603   Read a large object and send straight to browser */
3604PHP_FUNCTION(pg_lo_read_all)
3605{
3606  	zval *pgsql_id;
3607	int tbytes;
3608	volatile int nbytes;
3609	char buf[PGSQL_LO_READ_BUF_SIZE];
3610	pgLofp *pgsql;
3611
3612	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pgsql_id) == FAILURE) {
3613		return;
3614	}
3615
3616	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3617		RETURN_FALSE;
3618	}
3619
3620	tbytes = 0;
3621	while ((nbytes = lo_read((PGconn *)pgsql->conn, pgsql->lofd, buf, PGSQL_LO_READ_BUF_SIZE))>0) {
3622		PHPWRITE(buf, nbytes);
3623		tbytes += nbytes;
3624	}
3625	RETURN_LONG(tbytes);
3626}
3627/* }}} */
3628
3629/* {{{ proto int pg_lo_import([resource connection, ] string filename [, mixed oid])
3630   Import large object direct from filesystem */
3631PHP_FUNCTION(pg_lo_import)
3632{
3633	zval *pgsql_link = NULL, *oid = NULL;
3634	char *file_in;
3635	size_t name_len;
3636	int argc = ZEND_NUM_ARGS();
3637	PGconn *pgsql;
3638	Oid returned_oid;
3639	zend_resource *link;
3640
3641	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3642								 "rp|z", &pgsql_link, &file_in, &name_len, &oid) == SUCCESS) {
3643		link = Z_RES_P(pgsql_link);
3644	}
3645	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3646									  "p|z", &file_in, &name_len, &oid) == SUCCESS) {
3647		link = FETCH_DEFAULT_LINK();
3648		CHECK_DEFAULT_LINK(link);
3649	}
3650	/* old calling convention, deprecated since PHP 4.2 */
3651	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3652									  "pr", &file_in, &name_len, &pgsql_link ) == SUCCESS) {
3653		php_error_docref(NULL, E_NOTICE, "Old API is used");
3654		link = Z_RES_P(pgsql_link);
3655	}
3656	else {
3657		WRONG_PARAM_COUNT;
3658	}
3659
3660	if (php_check_open_basedir(file_in)) {
3661		RETURN_FALSE;
3662	}
3663
3664	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3665		RETURN_FALSE;
3666	}
3667
3668	if (oid) {
3669#ifndef HAVE_PG_LO_IMPORT_WITH_OID
3670		php_error_docref(NULL, E_NOTICE, "OID value passing not supported");
3671#else
3672		Oid wanted_oid;
3673		switch (Z_TYPE_P(oid)) {
3674		case IS_STRING:
3675			{
3676				char *end_ptr;
3677				wanted_oid = (Oid)strtoul(Z_STRVAL_P(oid), &end_ptr, 10);
3678				if ((Z_STRVAL_P(oid)+Z_STRLEN_P(oid)) != end_ptr) {
3679				/* wrong integer format */
3680				php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3681				RETURN_FALSE;
3682				}
3683			}
3684			break;
3685		case IS_LONG:
3686			if (Z_LVAL_P(oid) < (zend_long)InvalidOid) {
3687				php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3688				RETURN_FALSE;
3689			}
3690			wanted_oid = (Oid)Z_LVAL_P(oid);
3691			break;
3692		default:
3693			php_error_docref(NULL, E_NOTICE, "invalid OID value passed");
3694			RETURN_FALSE;
3695        }
3696
3697       returned_oid = lo_import_with_oid(pgsql, file_in, wanted_oid);
3698
3699	   if (returned_oid == InvalidOid) {
3700		   RETURN_FALSE;
3701	   }
3702
3703	   PGSQL_RETURN_OID(returned_oid);
3704#endif
3705	}
3706
3707	returned_oid = lo_import(pgsql, file_in);
3708
3709	if (returned_oid == InvalidOid) {
3710		RETURN_FALSE;
3711	}
3712	PGSQL_RETURN_OID(returned_oid);
3713}
3714/* }}} */
3715
3716/* {{{ proto bool pg_lo_export([resource connection, ] int objoid, string filename)
3717   Export large object direct to filesystem */
3718PHP_FUNCTION(pg_lo_export)
3719{
3720	zval *pgsql_link = NULL;
3721	char *file_out, *oid_string, *end_ptr;
3722	size_t oid_strlen;
3723	size_t name_len;
3724	zend_long oid_long;
3725	Oid oid;
3726	PGconn *pgsql;
3727	int argc = ZEND_NUM_ARGS();
3728	zend_resource *link;
3729
3730	/* allow string to handle large OID value correctly */
3731	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3732								 "rlp", &pgsql_link, &oid_long, &file_out, &name_len) == SUCCESS) {
3733		if (oid_long <= InvalidOid) {
3734			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3735			RETURN_FALSE;
3736		}
3737		oid = (Oid)oid_long;
3738		link = Z_RES_P(pgsql_link);
3739	}
3740	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3741								 "rss", &pgsql_link, &oid_string, &oid_strlen, &file_out, &name_len) == SUCCESS) {
3742		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3743		if ((oid_string+oid_strlen) != end_ptr) {
3744			/* wrong integer format */
3745			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3746			RETURN_FALSE;
3747		}
3748		link = Z_RES_P(pgsql_link);
3749	}
3750	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3751									  "lp",  &oid_long, &file_out, &name_len) == SUCCESS) {
3752		if (oid_long <= InvalidOid) {
3753			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3754			RETURN_FALSE;
3755		}
3756		oid = (Oid)oid_long;
3757		link = FETCH_DEFAULT_LINK();
3758		CHECK_DEFAULT_LINK(link);
3759	}
3760	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3761								 "sp", &oid_string, &oid_strlen, &file_out, &name_len) == SUCCESS) {
3762		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3763		if ((oid_string+oid_strlen) != end_ptr) {
3764			/* wrong integer format */
3765			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3766			RETURN_FALSE;
3767		}
3768		link = FETCH_DEFAULT_LINK();
3769		CHECK_DEFAULT_LINK(link);
3770	}
3771	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3772								 "spr", &oid_string, &oid_strlen, &file_out, &name_len, &pgsql_link) == SUCCESS) {
3773		oid = (Oid)strtoul(oid_string, &end_ptr, 10);
3774		if ((oid_string+oid_strlen) != end_ptr) {
3775			/* wrong integer format */
3776			php_error_docref(NULL, E_NOTICE, "Wrong OID value passed");
3777			RETURN_FALSE;
3778		}
3779		link = Z_RES_P(pgsql_link);
3780	}
3781	else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc,
3782									  "lpr", &oid_long, &file_out, &name_len, &pgsql_link) == SUCCESS) {
3783		php_error_docref(NULL, E_NOTICE, "Old API is used");
3784		if (oid_long <= InvalidOid) {
3785			php_error_docref(NULL, E_NOTICE, "Invalid OID specified");
3786			RETURN_FALSE;
3787		}
3788		oid = (Oid)oid_long;
3789		link = Z_RES_P(pgsql_link);
3790	}
3791	else {
3792		php_error_docref(NULL, E_WARNING, "Requires 2 or 3 arguments");
3793		RETURN_FALSE;
3794	}
3795
3796	if (php_check_open_basedir(file_out)) {
3797		RETURN_FALSE;
3798	}
3799
3800	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3801		RETURN_FALSE;
3802	}
3803
3804	if (lo_export(pgsql, oid, file_out) == -1) {
3805		RETURN_FALSE;
3806	}
3807	RETURN_TRUE;
3808}
3809/* }}} */
3810
3811/* {{{ proto bool pg_lo_seek(resource large_object, int offset [, int whence])
3812   Seeks position of large object */
3813PHP_FUNCTION(pg_lo_seek)
3814{
3815	zval *pgsql_id = NULL;
3816	zend_long result, offset = 0, whence = SEEK_CUR;
3817	pgLofp *pgsql;
3818	int argc = ZEND_NUM_ARGS();
3819
3820	if (zend_parse_parameters(argc, "rl|l", &pgsql_id, &offset, &whence) == FAILURE) {
3821		return;
3822	}
3823	if (whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) {
3824		php_error_docref(NULL, E_WARNING, "Invalid whence parameter");
3825		return;
3826	}
3827
3828	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3829		RETURN_FALSE;
3830	}
3831
3832#if HAVE_PG_LO64
3833	if (PQserverVersion((PGconn *)pgsql->conn) >= 90300) {
3834		result = lo_lseek64((PGconn *)pgsql->conn, pgsql->lofd, offset, (int)whence);
3835	} else {
3836		result = lo_lseek((PGconn *)pgsql->conn, pgsql->lofd, (int)offset, (int)whence);
3837	}
3838#else
3839	result = lo_lseek((PGconn *)pgsql->conn, pgsql->lofd, offset, whence);
3840#endif
3841	if (result > -1) {
3842		RETURN_TRUE;
3843	} else {
3844		RETURN_FALSE;
3845	}
3846}
3847/* }}} */
3848
3849/* {{{ proto int pg_lo_tell(resource large_object)
3850   Returns current position of large object */
3851PHP_FUNCTION(pg_lo_tell)
3852{
3853	zval *pgsql_id = NULL;
3854	zend_long offset = 0;
3855	pgLofp *pgsql;
3856	int argc = ZEND_NUM_ARGS();
3857
3858	if (zend_parse_parameters(argc, "r", &pgsql_id) == FAILURE) {
3859		return;
3860	}
3861
3862	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3863		RETURN_FALSE;
3864	}
3865
3866#if HAVE_PG_LO64
3867	if (PQserverVersion((PGconn *)pgsql->conn) >= 90300) {
3868		offset = lo_tell64((PGconn *)pgsql->conn, pgsql->lofd);
3869	} else {
3870		offset = lo_tell((PGconn *)pgsql->conn, pgsql->lofd);
3871	}
3872#else
3873	offset = lo_tell((PGconn *)pgsql->conn, pgsql->lofd);
3874#endif
3875	RETURN_LONG(offset);
3876}
3877/* }}} */
3878
3879#if HAVE_PG_LO_TRUNCATE
3880/* {{{ proto bool pg_lo_truncate(resource large_object, int size)
3881   Truncate large object to size */
3882PHP_FUNCTION(pg_lo_truncate)
3883{
3884	zval *pgsql_id = NULL;
3885	size_t size;
3886	pgLofp *pgsql;
3887	int argc = ZEND_NUM_ARGS();
3888	int result;
3889
3890	if (zend_parse_parameters(argc, "rl", &pgsql_id, &size) == FAILURE) {
3891		return;
3892	}
3893
3894	if ((pgsql = (pgLofp *)zend_fetch_resource(Z_RES_P(pgsql_id), "PostgreSQL large object", le_lofp)) == NULL) {
3895		RETURN_FALSE;
3896	}
3897
3898#if HAVE_PG_LO64
3899	if (PQserverVersion((PGconn *)pgsql->conn) >= 90300) {
3900		result = lo_truncate64((PGconn *)pgsql->conn, pgsql->lofd, size);
3901	} else {
3902		result = lo_truncate((PGconn *)pgsql->conn, pgsql->lofd, size);
3903	}
3904#else
3905	result = lo_truncate((PGconn *)pgsql->conn, pgsql->lofd, size);
3906#endif
3907	if (!result) {
3908		RETURN_TRUE;
3909	} else {
3910		RETURN_FALSE;
3911	}
3912}
3913/* }}} */
3914#endif
3915
3916#if HAVE_PQSETERRORVERBOSITY
3917/* {{{ proto int pg_set_error_verbosity([resource connection,] int verbosity)
3918   Set error verbosity */
3919PHP_FUNCTION(pg_set_error_verbosity)
3920{
3921	zval *pgsql_link = NULL;
3922	zend_long verbosity;
3923	int argc = ZEND_NUM_ARGS();
3924	PGconn *pgsql;
3925	zend_resource *link;
3926
3927	if (argc == 1) {
3928		if (zend_parse_parameters(argc, "l", &verbosity) == FAILURE) {
3929			return;
3930		}
3931		link = FETCH_DEFAULT_LINK();
3932		CHECK_DEFAULT_LINK(link);
3933	} else {
3934		if (zend_parse_parameters(argc, "rl", &pgsql_link, &verbosity) == FAILURE) {
3935			return;
3936		}
3937		link = Z_RES_P(pgsql_link);
3938	}
3939
3940	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3941		RETURN_FALSE;
3942	}
3943
3944	if (verbosity & (PQERRORS_TERSE|PQERRORS_DEFAULT|PQERRORS_VERBOSE)) {
3945		RETURN_LONG(PQsetErrorVerbosity(pgsql, verbosity));
3946	} else {
3947		RETURN_FALSE;
3948	}
3949}
3950/* }}} */
3951#endif
3952
3953#ifdef HAVE_PQCLIENTENCODING
3954/* {{{ proto int pg_set_client_encoding([resource connection,] string encoding)
3955   Set client encoding */
3956PHP_FUNCTION(pg_set_client_encoding)
3957{
3958	char *encoding;
3959	size_t encoding_len;
3960	zval *pgsql_link = NULL;
3961	int argc = ZEND_NUM_ARGS();
3962	PGconn *pgsql;
3963	zend_resource *link;
3964
3965	if (argc == 1) {
3966		if (zend_parse_parameters(argc, "s", &encoding, &encoding_len) == FAILURE) {
3967			return;
3968		}
3969		link = FETCH_DEFAULT_LINK();
3970		CHECK_DEFAULT_LINK(link);
3971	} else {
3972		if (zend_parse_parameters(argc, "rs", &pgsql_link, &encoding, &encoding_len) == FAILURE) {
3973			return;
3974		}
3975		link = Z_RES_P(pgsql_link);
3976	}
3977
3978	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3979		RETURN_FALSE;
3980	}
3981
3982	RETURN_LONG(PQsetClientEncoding(pgsql, encoding));
3983}
3984/* }}} */
3985
3986/* {{{ proto string pg_client_encoding([resource connection])
3987   Get the current client encoding */
3988PHP_FUNCTION(pg_client_encoding)
3989{
3990	zval *pgsql_link = NULL;
3991	int argc = ZEND_NUM_ARGS();
3992	PGconn *pgsql;
3993	zend_resource *link;
3994
3995	if (zend_parse_parameters(argc, "|r", &pgsql_link) == FAILURE) {
3996		return;
3997	}
3998
3999	if (argc == 0) {
4000		link = FETCH_DEFAULT_LINK();
4001		CHECK_DEFAULT_LINK(link);
4002	} else {
4003		link = Z_RES_P(pgsql_link);
4004	}
4005
4006	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4007		RETURN_FALSE;
4008	}
4009
4010	/* Just do the same as found in PostgreSQL sources... */
4011
4012	RETURN_STRING((char *) pg_encoding_to_char(PQclientEncoding(pgsql)));
4013}
4014/* }}} */
4015#endif
4016
4017#if !HAVE_PQGETCOPYDATA
4018#define	COPYBUFSIZ	8192
4019#endif
4020
4021/* {{{ proto bool pg_end_copy([resource connection])
4022   Sync with backend. Completes the Copy command */
4023PHP_FUNCTION(pg_end_copy)
4024{
4025	zval *pgsql_link = NULL;
4026	int argc = ZEND_NUM_ARGS();
4027	PGconn *pgsql;
4028	int result = 0;
4029	zend_resource *link;
4030
4031	if (zend_parse_parameters(argc, "|r", &pgsql_link) == FAILURE) {
4032		return;
4033	}
4034
4035	if (argc == 0) {
4036		link = FETCH_DEFAULT_LINK();
4037		CHECK_DEFAULT_LINK(link);
4038	} else {
4039		link = Z_RES_P(pgsql_link);
4040	}
4041
4042	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4043		RETURN_FALSE;
4044	}
4045
4046	result = PQendcopy(pgsql);
4047
4048	if (result!=0) {
4049		PHP_PQ_ERROR("Query failed: %s", pgsql);
4050		RETURN_FALSE;
4051	}
4052	RETURN_TRUE;
4053}
4054/* }}} */
4055
4056/* {{{ proto bool pg_put_line([resource connection,] string query)
4057   Send null-terminated string to backend server*/
4058PHP_FUNCTION(pg_put_line)
4059{
4060	char *query;
4061	zval *pgsql_link = NULL;
4062	size_t query_len;
4063	PGconn *pgsql;
4064	zend_resource *link;
4065	int result = 0, argc = ZEND_NUM_ARGS();
4066
4067	if (argc == 1) {
4068		if (zend_parse_parameters(argc, "s", &query, &query_len) == FAILURE) {
4069			return;
4070		}
4071		link = FETCH_DEFAULT_LINK();
4072		CHECK_DEFAULT_LINK(link);
4073	} else {
4074		if (zend_parse_parameters(argc, "rs", &pgsql_link, &query, &query_len) == FAILURE) {
4075			return;
4076		}
4077		link = Z_RES_P(pgsql_link);
4078	}
4079
4080	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4081		RETURN_FALSE;
4082	}
4083
4084	result = PQputline(pgsql, query);
4085	if (result==EOF) {
4086		PHP_PQ_ERROR("Query failed: %s", pgsql);
4087		RETURN_FALSE;
4088	}
4089	RETURN_TRUE;
4090}
4091/* }}} */
4092
4093/* {{{ proto array pg_copy_to(resource connection, string table_name [, string delimiter [, string null_as]])
4094   Copy table to array */
4095PHP_FUNCTION(pg_copy_to)
4096{
4097	zval *pgsql_link;
4098	char *table_name, *pg_delim = NULL, *pg_null_as = NULL;
4099	size_t table_name_len, pg_delim_len, pg_null_as_len, free_pg_null = 0;
4100	char *query;
4101	PGconn *pgsql;
4102	PGresult *pgsql_result;
4103	ExecStatusType status;
4104	int copydone = 0;
4105#if !HAVE_PQGETCOPYDATA
4106	char copybuf[COPYBUFSIZ];
4107#endif
4108	char *csv = (char *)NULL;
4109	int ret;
4110	int argc = ZEND_NUM_ARGS();
4111
4112	if (zend_parse_parameters(argc, "rs|ss",
4113							  &pgsql_link, &table_name, &table_name_len,
4114							  &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len) == FAILURE) {
4115		return;
4116	}
4117	if (!pg_delim) {
4118		pg_delim = "\t";
4119	}
4120
4121	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4122		RETURN_FALSE;
4123	}
4124
4125	if (!pg_null_as) {
4126		pg_null_as = estrdup("\\\\N");
4127		free_pg_null = 1;
4128	}
4129
4130	spprintf(&query, 0, "COPY %s TO STDOUT DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, *pg_delim, pg_null_as);
4131
4132	while ((pgsql_result = PQgetResult(pgsql))) {
4133		PQclear(pgsql_result);
4134	}
4135	pgsql_result = PQexec(pgsql, query);
4136	if (free_pg_null) {
4137		efree(pg_null_as);
4138	}
4139	efree(query);
4140
4141	if (pgsql_result) {
4142		status = PQresultStatus(pgsql_result);
4143	} else {
4144		status = (ExecStatusType) PQstatus(pgsql);
4145	}
4146
4147	switch (status) {
4148		case PGRES_COPY_OUT:
4149			if (pgsql_result) {
4150				PQclear(pgsql_result);
4151				array_init(return_value);
4152#if HAVE_PQGETCOPYDATA
4153				while (!copydone)
4154				{
4155					ret = PQgetCopyData(pgsql, &csv, 0);
4156					switch (ret) {
4157						case -1:
4158							copydone = 1;
4159							break;
4160						case 0:
4161						case -2:
4162							PHP_PQ_ERROR("getline failed: %s", pgsql);
4163							RETURN_FALSE;
4164							break;
4165						default:
4166							add_next_index_string(return_value, csv);
4167							PQfreemem(csv);
4168							break;
4169					}
4170				}
4171#else
4172				while (!copydone)
4173				{
4174					if ((ret = PQgetline(pgsql, copybuf, COPYBUFSIZ))) {
4175						PHP_PQ_ERROR("getline failed: %s", pgsql);
4176						RETURN_FALSE;
4177					}
4178
4179					if (copybuf[0] == '\\' &&
4180						copybuf[1] == '.' &&
4181						copybuf[2] == '\0')
4182					{
4183						copydone = 1;
4184					}
4185					else
4186					{
4187						if (csv == (char *)NULL) {
4188							csv = estrdup(copybuf);
4189						} else {
4190							csv = (char *)erealloc(csv, strlen(csv) + sizeof(char)*(COPYBUFSIZ+1));
4191							strcat(csv, copybuf);
4192						}
4193
4194						switch (ret)
4195						{
4196							case EOF:
4197								copydone = 1;
4198							case 0:
4199								add_next_index_string(return_value, csv);
4200								efree(csv);
4201								csv = (char *)NULL;
4202								break;
4203							case 1:
4204								break;
4205						}
4206					}
4207				}
4208				if (PQendcopy(pgsql)) {
4209					PHP_PQ_ERROR("endcopy failed: %s", pgsql);
4210					RETURN_FALSE;
4211				}
4212#endif
4213				while ((pgsql_result = PQgetResult(pgsql))) {
4214					PQclear(pgsql_result);
4215				}
4216			} else {
4217				PQclear(pgsql_result);
4218				RETURN_FALSE;
4219			}
4220			break;
4221		default:
4222			PQclear(pgsql_result);
4223			PHP_PQ_ERROR("Copy command failed: %s", pgsql);
4224			RETURN_FALSE;
4225			break;
4226	}
4227}
4228/* }}} */
4229
4230/* {{{ proto bool pg_copy_from(resource connection, string table_name , array rows [, string delimiter [, string null_as]])
4231   Copy table from array */
4232PHP_FUNCTION(pg_copy_from)
4233{
4234	zval *pgsql_link = NULL, *pg_rows;
4235	zval *value;
4236	char *table_name, *pg_delim = NULL, *pg_null_as = NULL;
4237	size_t  table_name_len, pg_delim_len, pg_null_as_len;
4238	int  pg_null_as_free = 0;
4239	char *query;
4240	PGconn *pgsql;
4241	PGresult *pgsql_result;
4242	ExecStatusType status;
4243	int argc = ZEND_NUM_ARGS();
4244
4245	if (zend_parse_parameters(argc, "rsa|ss",
4246							  &pgsql_link, &table_name, &table_name_len, &pg_rows,
4247							  &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len) == FAILURE) {
4248		return;
4249	}
4250	if (!pg_delim) {
4251		pg_delim = "\t";
4252	}
4253	if (!pg_null_as) {
4254		pg_null_as = estrdup("\\\\N");
4255		pg_null_as_free = 1;
4256	}
4257
4258	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4259		RETURN_FALSE;
4260	}
4261
4262	spprintf(&query, 0, "COPY %s FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, *pg_delim, pg_null_as);
4263	while ((pgsql_result = PQgetResult(pgsql))) {
4264		PQclear(pgsql_result);
4265	}
4266	pgsql_result = PQexec(pgsql, query);
4267
4268	if (pg_null_as_free) {
4269		efree(pg_null_as);
4270	}
4271	efree(query);
4272
4273	if (pgsql_result) {
4274		status = PQresultStatus(pgsql_result);
4275	} else {
4276		status = (ExecStatusType) PQstatus(pgsql);
4277	}
4278
4279	switch (status) {
4280		case PGRES_COPY_IN:
4281			if (pgsql_result) {
4282				int command_failed = 0;
4283				PQclear(pgsql_result);
4284#if HAVE_PQPUTCOPYDATA
4285				ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), value) {
4286					zval tmp;
4287					ZVAL_COPY(&tmp, value);
4288					convert_to_string_ex(&tmp);
4289					query = (char *)emalloc(Z_STRLEN(tmp) + 2);
4290					strlcpy(query, Z_STRVAL(tmp), Z_STRLEN(tmp) + 2);
4291					if(Z_STRLEN(tmp) > 0 && *(query + Z_STRLEN(tmp) - 1) != '\n') {
4292						strlcat(query, "\n", Z_STRLEN(tmp) + 2);
4293					}
4294					if (PQputCopyData(pgsql, query, (int)strlen(query)) != 1) {
4295						efree(query);
4296						zval_dtor(&tmp);
4297						PHP_PQ_ERROR("copy failed: %s", pgsql);
4298						RETURN_FALSE;
4299					}
4300					efree(query);
4301					zval_dtor(&tmp);
4302				} ZEND_HASH_FOREACH_END();
4303
4304				if (PQputCopyEnd(pgsql, NULL) != 1) {
4305					PHP_PQ_ERROR("putcopyend failed: %s", pgsql);
4306					RETURN_FALSE;
4307				}
4308#else
4309				ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), value) {
4310					zval tmp;
4311					ZVAL_COPY(&tmp, value);
4312					convert_to_string_ex(&tmp);
4313					query = (char *)emalloc(Z_STRLEN(tmp) + 2);
4314					strlcpy(query, Z_STRVAL(tmp), Z_STRLEN(tmp) + 2);
4315					if(Z_STRLEN(tmp) > 0 && *(query + Z_STRLEN(tmp) - 1) != '\n') {
4316						strlcat(query, "\n", Z_STRLEN(tmp) + 2);
4317					}
4318					if (PQputline(pgsql, query)==EOF) {
4319						efree(query);
4320						zval_dtor(&tmp);
4321						PHP_PQ_ERROR("copy failed: %s", pgsql);
4322						RETURN_FALSE;
4323					}
4324					efree(query);
4325					zval_dtor(&tmp);
4326				} ZEND_HASH_FOREACH_END();
4327
4328				if (PQputline(pgsql, "\\.\n") == EOF) {
4329					PHP_PQ_ERROR("putline failed: %s", pgsql);
4330					RETURN_FALSE;
4331				}
4332				if (PQendcopy(pgsql)) {
4333					PHP_PQ_ERROR("endcopy failed: %s", pgsql);
4334					RETURN_FALSE;
4335				}
4336#endif
4337				while ((pgsql_result = PQgetResult(pgsql))) {
4338					if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
4339						PHP_PQ_ERROR("Copy command failed: %s", pgsql);
4340						command_failed = 1;
4341					}
4342					PQclear(pgsql_result);
4343				}
4344				if (command_failed) {
4345					RETURN_FALSE;
4346				}
4347			} else {
4348				PQclear(pgsql_result);
4349				RETURN_FALSE;
4350			}
4351			RETURN_TRUE;
4352			break;
4353		default:
4354			PQclear(pgsql_result);
4355			PHP_PQ_ERROR("Copy command failed: %s", pgsql);
4356			RETURN_FALSE;
4357			break;
4358	}
4359}
4360/* }}} */
4361
4362#ifdef HAVE_PQESCAPE
4363/* {{{ proto string pg_escape_string([resource connection,] string data)
4364   Escape string for text/char type */
4365PHP_FUNCTION(pg_escape_string)
4366{
4367	zend_string *from = NULL, *to = NULL;
4368	zval *pgsql_link;
4369	zend_resource *link;
4370#ifdef HAVE_PQESCAPE_CONN
4371	PGconn *pgsql;
4372#endif
4373
4374	switch (ZEND_NUM_ARGS()) {
4375		case 1:
4376			if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &from) == FAILURE) {
4377				return;
4378			}
4379			link = FETCH_DEFAULT_LINK();
4380			break;
4381		default:
4382			if (zend_parse_parameters(ZEND_NUM_ARGS(), "rS", &pgsql_link, &from) == FAILURE) {
4383				return;
4384			}
4385			link = Z_RES_P(pgsql_link);
4386			break;
4387	}
4388
4389	to = zend_string_alloc(ZSTR_LEN(from) * 2, 0);
4390#ifdef HAVE_PQESCAPE_CONN
4391	if (link) {
4392		if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4393			RETURN_FALSE;
4394		}
4395		ZSTR_LEN(to) = PQescapeStringConn(pgsql, ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from), NULL);
4396	} else
4397#endif
4398	{
4399		ZSTR_LEN(to) = PQescapeString(ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from));
4400	}
4401
4402	to = zend_string_truncate(to, ZSTR_LEN(to), 0);
4403	RETURN_NEW_STR(to);
4404}
4405/* }}} */
4406
4407/* {{{ proto string pg_escape_bytea([resource connection,] string data)
4408   Escape binary for bytea type  */
4409PHP_FUNCTION(pg_escape_bytea)
4410{
4411	char *from = NULL, *to = NULL;
4412	size_t to_len;
4413	size_t from_len;
4414#ifdef HAVE_PQESCAPE_BYTEA_CONN
4415	PGconn *pgsql;
4416#endif
4417	zval *pgsql_link;
4418	zend_resource *link;
4419
4420	switch (ZEND_NUM_ARGS()) {
4421		case 1:
4422			if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &from, &from_len) == FAILURE) {
4423				return;
4424			}
4425			link = FETCH_DEFAULT_LINK();
4426			break;
4427		default:
4428			if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &pgsql_link, &from, &from_len) == FAILURE) {
4429				return;
4430			}
4431			link = Z_RES_P(pgsql_link);
4432			break;
4433	}
4434
4435#ifdef HAVE_PQESCAPE_BYTEA_CONN
4436	if (link) {
4437		if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4438			RETURN_FALSE;
4439		}
4440		to = (char *)PQescapeByteaConn(pgsql, (unsigned char *)from, (size_t)from_len, &to_len);
4441	} else
4442#endif
4443		to = (char *)PQescapeBytea((unsigned char*)from, from_len, &to_len);
4444
4445	RETVAL_STRINGL(to, to_len-1); /* to_len includes additional '\0' */
4446}
4447/* }}} */
4448
4449#if !HAVE_PQUNESCAPEBYTEA
4450/* PQunescapeBytea() from PostgreSQL 7.3 to provide bytea unescape feature to 7.2 users.
4451   Renamed to php_pgsql_unescape_bytea() */
4452/*
4453 *		PQunescapeBytea - converts the null terminated string representation
4454 *		of a bytea, strtext, into binary, filling a buffer. It returns a
4455 *		pointer to the buffer which is NULL on error, and the size of the
4456 *		buffer in retbuflen. The pointer may subsequently be used as an
4457 *		argument to the function free(3). It is the reverse of PQescapeBytea.
4458 *
4459 *		The following transformations are reversed:
4460 *		'\0' == ASCII  0 == \000
4461 *		'\'' == ASCII 39 == \'
4462 *		'\\' == ASCII 92 == \\
4463 *
4464 *		States:
4465 *		0	normal		0->1->2->3->4
4466 *		1	\			   1->5
4467 *		2	\0			   1->6
4468 *		3	\00
4469 *		4	\000
4470 *		5	\'
4471 *		6	\\
4472 */
4473static unsigned char * php_pgsql_unescape_bytea(unsigned char *strtext, size_t *retbuflen) /* {{{ */
4474{
4475	size_t     buflen;
4476	unsigned char *buffer,
4477			   *sp,
4478			   *bp;
4479	unsigned int state = 0;
4480
4481	if (strtext == NULL)
4482		return NULL;
4483	buflen = strlen(strtext);	/* will shrink, also we discover if
4484								 * strtext */
4485	buffer = (unsigned char *) emalloc(buflen);	/* isn't NULL terminated */
4486	for (bp = buffer, sp = strtext; *sp != '\0'; bp++, sp++)
4487	{
4488		switch (state)
4489		{
4490			case 0:
4491				if (*sp == '\\')
4492					state = 1;
4493				*bp = *sp;
4494				break;
4495			case 1:
4496				if (*sp == '\'')	/* state=5 */
4497				{				/* replace \' with 39 */
4498					bp--;
4499					*bp = '\'';
4500					buflen--;
4501					state = 0;
4502				}
4503				else if (*sp == '\\')	/* state=6 */
4504				{				/* replace \\ with 92 */
4505					bp--;
4506					*bp = '\\';
4507					buflen--;
4508					state = 0;
4509				}
4510				else
4511				{
4512					if (isdigit(*sp))
4513						state = 2;
4514					else
4515						state = 0;
4516					*bp = *sp;
4517				}
4518				break;
4519			case 2:
4520				if (isdigit(*sp))
4521					state = 3;
4522				else
4523					state = 0;
4524				*bp = *sp;
4525				break;
4526			case 3:
4527				if (isdigit(*sp))		/* state=4 */
4528				{
4529					unsigned char *start, *end, buf[4]; /* 000 + '\0' */
4530
4531					bp -= 3;
4532					memcpy(buf, sp-2, 3);
4533					buf[3] = '\0';
4534					start = buf;
4535					*bp = (unsigned char)strtoul(start, (char **)&end, 8);
4536					buflen -= 3;
4537					state = 0;
4538				}
4539				else
4540				{
4541					*bp = *sp;
4542					state = 0;
4543				}
4544				break;
4545		}
4546	}
4547	buffer = erealloc(buffer, buflen+1);
4548	buffer[buflen] = '\0';
4549
4550	*retbuflen = buflen;
4551	return buffer;
4552}
4553/* }}} */
4554#endif
4555
4556/* {{{ proto string pg_unescape_bytea(string data)
4557   Unescape binary for bytea type  */
4558PHP_FUNCTION(pg_unescape_bytea)
4559{
4560	char *from = NULL, *to = NULL, *tmp = NULL;
4561	size_t to_len;
4562	size_t from_len;
4563	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
4564							  &from, &from_len) == FAILURE) {
4565		return;
4566	}
4567
4568#if HAVE_PQUNESCAPEBYTEA
4569	tmp = (char *)PQunescapeBytea((unsigned char*)from, &to_len);
4570	to = estrndup(tmp, to_len);
4571	PQfreemem(tmp);
4572#else
4573	to = (char *)php_pgsql_unescape_bytea((unsigned char*)from, &to_len);
4574#endif
4575	if (!to) {
4576		php_error_docref(NULL, E_WARNING,"Invalid parameter");
4577		RETURN_FALSE;
4578	}
4579	RETVAL_STRINGL(to, to_len);
4580	efree(to);
4581}
4582/* }}} */
4583#endif
4584
4585#ifdef HAVE_PQESCAPE
4586static void php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAMETERS, int escape_literal) /* {{{ */ {
4587	char *from = NULL;
4588	zval *pgsql_link = NULL;
4589	PGconn *pgsql;
4590	size_t from_len;
4591	char *tmp;
4592	zend_resource *link;
4593
4594	switch (ZEND_NUM_ARGS()) {
4595		case 1:
4596			if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &from, &from_len) == FAILURE) {
4597				return;
4598			}
4599			link = FETCH_DEFAULT_LINK();
4600			CHECK_DEFAULT_LINK(link);
4601			break;
4602
4603		default:
4604			if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &pgsql_link, &from, &from_len) == FAILURE) {
4605				return;
4606			}
4607			link = Z_RES_P(pgsql_link);
4608			break;
4609	}
4610
4611	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
4612		RETURN_FALSE;
4613	}
4614
4615	if (pgsql == NULL) {
4616		php_error_docref(NULL, E_WARNING,"Cannot get pgsql link");
4617		RETURN_FALSE;
4618	}
4619
4620	if (escape_literal) {
4621		tmp = PGSQLescapeLiteral(pgsql, from, (size_t)from_len);
4622	} else {
4623		tmp = PGSQLescapeIdentifier(pgsql, from, (size_t)from_len);
4624	}
4625	if (!tmp) {
4626		php_error_docref(NULL, E_WARNING,"Failed to escape");
4627		RETURN_FALSE;
4628	}
4629
4630	RETVAL_STRING(tmp);
4631	PGSQLfree(tmp);
4632}
4633/* }}} */
4634
4635/* {{{ proto string pg_escape_literal([resource connection,] string data)
4636   Escape parameter as string literal (i.e. parameter)	*/
4637PHP_FUNCTION(pg_escape_literal)
4638{
4639	php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4640}
4641/* }}} */
4642
4643/* {{{ proto string pg_escape_identifier([resource connection,] string data)
4644   Escape identifier (i.e. table name, field name)	*/
4645PHP_FUNCTION(pg_escape_identifier)
4646{
4647	php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4648}
4649/* }}} */
4650#endif
4651
4652/* {{{ proto string pg_result_error(resource result)
4653   Get error message associated with result */
4654PHP_FUNCTION(pg_result_error)
4655{
4656	zval *result;
4657	PGresult *pgsql_result;
4658	pgsql_result_handle *pg_result;
4659	char *err = NULL;
4660
4661	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
4662								 &result) == FAILURE) {
4663		RETURN_FALSE;
4664	}
4665
4666	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
4667		RETURN_FALSE;
4668	}
4669
4670	pgsql_result = pg_result->result;
4671	if (!pgsql_result) {
4672		RETURN_FALSE;
4673	}
4674	err = (char *)PQresultErrorMessage(pgsql_result);
4675	RETURN_STRING(err);
4676}
4677/* }}} */
4678
4679#if HAVE_PQRESULTERRORFIELD
4680/* {{{ proto string pg_result_error_field(resource result, int fieldcode)
4681   Get error message field associated with result */
4682PHP_FUNCTION(pg_result_error_field)
4683{
4684	zval *result;
4685	zend_long fieldcode;
4686	PGresult *pgsql_result;
4687	pgsql_result_handle *pg_result;
4688	char *field = NULL;
4689
4690	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "rl",
4691								 &result, &fieldcode) == FAILURE) {
4692		RETURN_FALSE;
4693	}
4694
4695	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
4696		RETURN_FALSE;
4697	}
4698
4699	pgsql_result = pg_result->result;
4700	if (!pgsql_result) {
4701		RETURN_FALSE;
4702	}
4703	if (fieldcode & (PG_DIAG_SEVERITY|PG_DIAG_SQLSTATE|PG_DIAG_MESSAGE_PRIMARY|PG_DIAG_MESSAGE_DETAIL
4704				|PG_DIAG_MESSAGE_HINT|PG_DIAG_STATEMENT_POSITION
4705#if PG_DIAG_INTERNAL_POSITION
4706				|PG_DIAG_INTERNAL_POSITION
4707#endif
4708#if PG_DIAG_INTERNAL_QUERY
4709				|PG_DIAG_INTERNAL_QUERY
4710#endif
4711				|PG_DIAG_CONTEXT|PG_DIAG_SOURCE_FILE|PG_DIAG_SOURCE_LINE
4712				|PG_DIAG_SOURCE_FUNCTION)) {
4713		field = (char *)PQresultErrorField(pgsql_result, (int)fieldcode);
4714		if (field == NULL) {
4715			RETURN_NULL();
4716		} else {
4717			RETURN_STRING(field);
4718		}
4719	} else {
4720		RETURN_FALSE;
4721	}
4722}
4723/* }}} */
4724#endif
4725
4726/* {{{ proto int pg_connection_status(resource connection)
4727   Get connection status */
4728PHP_FUNCTION(pg_connection_status)
4729{
4730	zval *pgsql_link = NULL;
4731	PGconn *pgsql;
4732
4733	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
4734								 &pgsql_link) == FAILURE) {
4735		RETURN_FALSE;
4736	}
4737
4738	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4739		RETURN_FALSE;
4740	}
4741
4742	RETURN_LONG(PQstatus(pgsql));
4743}
4744
4745/* }}} */
4746
4747#if HAVE_PGTRANSACTIONSTATUS
4748/* {{{ proto int pg_transaction_status(resource connection)
4749   Get transaction status */
4750PHP_FUNCTION(pg_transaction_status)
4751{
4752	zval *pgsql_link = NULL;
4753	PGconn *pgsql;
4754
4755	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
4756								 &pgsql_link) == FAILURE) {
4757		RETURN_FALSE;
4758	}
4759
4760	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4761		RETURN_FALSE;
4762	}
4763
4764	RETURN_LONG(PQtransactionStatus(pgsql));
4765}
4766#endif
4767
4768/* }}} */
4769
4770/* {{{ proto bool pg_connection_reset(resource connection)
4771   Reset connection (reconnect) */
4772PHP_FUNCTION(pg_connection_reset)
4773{
4774	zval *pgsql_link;
4775	PGconn *pgsql;
4776
4777	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
4778								 &pgsql_link) == FAILURE) {
4779		RETURN_FALSE;
4780	}
4781
4782	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4783		RETURN_FALSE;
4784	}
4785
4786	PQreset(pgsql);
4787	if (PQstatus(pgsql) == CONNECTION_BAD) {
4788		RETURN_FALSE;
4789	}
4790	RETURN_TRUE;
4791}
4792/* }}} */
4793
4794#define PHP_PG_ASYNC_IS_BUSY		1
4795#define PHP_PG_ASYNC_REQUEST_CANCEL 2
4796
4797/* {{{ php_pgsql_flush_query
4798 */
4799static int php_pgsql_flush_query(PGconn *pgsql)
4800{
4801	PGresult *res;
4802	int leftover = 0;
4803
4804	if (PQ_SETNONBLOCKING(pgsql, 1)) {
4805		php_error_docref(NULL, E_NOTICE,"Cannot set connection to nonblocking mode");
4806		return -1;
4807	}
4808	while ((res = PQgetResult(pgsql))) {
4809		PQclear(res);
4810		leftover++;
4811	}
4812	PQ_SETNONBLOCKING(pgsql, 0);
4813	return leftover;
4814}
4815/* }}} */
4816
4817/* {{{ php_pgsql_do_async
4818 */
4819static void php_pgsql_do_async(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
4820{
4821	zval *pgsql_link;
4822	PGconn *pgsql;
4823	PGresult *pgsql_result;
4824
4825	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
4826								 &pgsql_link) == FAILURE) {
4827		RETURN_FALSE;
4828	}
4829
4830	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4831		RETURN_FALSE;
4832	}
4833
4834	if (PQ_SETNONBLOCKING(pgsql, 1)) {
4835		php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
4836		RETURN_FALSE;
4837	}
4838	switch(entry_type) {
4839		case PHP_PG_ASYNC_IS_BUSY:
4840			PQconsumeInput(pgsql);
4841			RETVAL_LONG(PQisBusy(pgsql));
4842			break;
4843		case PHP_PG_ASYNC_REQUEST_CANCEL:
4844			RETVAL_LONG(PQrequestCancel(pgsql));
4845			while ((pgsql_result = PQgetResult(pgsql))) {
4846				PQclear(pgsql_result);
4847			}
4848			break;
4849		default:
4850			php_error_docref(NULL, E_ERROR, "PostgreSQL module error, please report this error");
4851			break;
4852	}
4853	if (PQ_SETNONBLOCKING(pgsql, 0)) {
4854		php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
4855	}
4856	convert_to_boolean_ex(return_value);
4857}
4858/* }}} */
4859
4860/* {{{ proto bool pg_cancel_query(resource connection)
4861   Cancel request */
4862PHP_FUNCTION(pg_cancel_query)
4863{
4864	php_pgsql_do_async(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_ASYNC_REQUEST_CANCEL);
4865}
4866/* }}} */
4867
4868/* {{{ proto bool pg_connection_busy(resource connection)
4869   Get connection is busy or not */
4870PHP_FUNCTION(pg_connection_busy)
4871{
4872	php_pgsql_do_async(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_ASYNC_IS_BUSY);
4873}
4874/* }}} */
4875
4876static int _php_pgsql_link_has_results(PGconn *pgsql) /* {{{ */
4877{
4878	PGresult *result;
4879	while ((result = PQgetResult(pgsql))) {
4880		PQclear(result);
4881		return 1;
4882	}
4883	return 0;
4884}
4885/* }}} */
4886
4887/* {{{ proto bool pg_send_query(resource connection, string query)
4888   Send asynchronous query */
4889PHP_FUNCTION(pg_send_query)
4890{
4891	zval *pgsql_link;
4892	char *query;
4893	size_t len;
4894	PGconn *pgsql;
4895	int is_non_blocking;
4896	int ret;
4897
4898	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &pgsql_link, &query, &len) == FAILURE) {
4899		return;
4900	}
4901
4902	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4903		RETURN_FALSE;
4904	}
4905
4906	is_non_blocking = PQisnonblocking(pgsql);
4907
4908	if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) {
4909		php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
4910		RETURN_FALSE;
4911	}
4912
4913	if (_php_pgsql_link_has_results(pgsql)) {
4914		php_error_docref(NULL, E_NOTICE,
4915			"There are results on this connection. Call pg_get_result() until it returns FALSE");
4916	}
4917
4918	if (is_non_blocking) {
4919		if (!PQsendQuery(pgsql, query)) {
4920			RETURN_FALSE;
4921		}
4922		ret = PQflush(pgsql);
4923	} else {
4924		if (!PQsendQuery(pgsql, query)) {
4925			if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
4926				PQreset(pgsql);
4927			}
4928			if (!PQsendQuery(pgsql, query)) {
4929				RETURN_FALSE;
4930			}
4931		}
4932
4933		/* Wait to finish sending buffer */
4934		while ((ret = PQflush(pgsql))) {
4935			if (ret == -1) {
4936				php_error_docref(NULL, E_NOTICE, "Could not empty PostgreSQL send buffer");
4937				break;
4938			}
4939			usleep(10000);
4940		}
4941
4942		if (PQ_SETNONBLOCKING(pgsql, 0)) {
4943			php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
4944		}
4945	}
4946
4947	if (ret == 0) {
4948		RETURN_TRUE;
4949	} else if (ret == -1) {
4950		RETURN_FALSE;
4951	} else {
4952		RETURN_LONG(0);
4953	}
4954}
4955/* }}} */
4956
4957#if HAVE_PQSENDQUERYPARAMS
4958/* {{{ proto bool pg_send_query_params(resource connection, string query, array params)
4959   Send asynchronous parameterized query */
4960PHP_FUNCTION(pg_send_query_params)
4961{
4962	zval *pgsql_link, *pv_param_arr, *tmp;
4963	int num_params = 0;
4964	char **params = NULL;
4965	char *query;
4966	size_t query_len;
4967	PGconn *pgsql;
4968	int is_non_blocking;
4969	int ret;
4970
4971	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsa/", &pgsql_link, &query, &query_len, &pv_param_arr) == FAILURE) {
4972		return;
4973	}
4974
4975	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
4976		RETURN_FALSE;
4977	}
4978
4979	is_non_blocking = PQisnonblocking(pgsql);
4980
4981	if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) {
4982		php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
4983		RETURN_FALSE;
4984	}
4985
4986	if (_php_pgsql_link_has_results(pgsql)) {
4987		php_error_docref(NULL, E_NOTICE,
4988			"There are results on this connection. Call pg_get_result() until it returns FALSE");
4989	}
4990
4991	num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
4992	if (num_params > 0) {
4993		int i = 0;
4994		params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
4995
4996		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pv_param_arr), tmp) {
4997
4998			if (Z_TYPE_P(tmp) == IS_NULL) {
4999				params[i] = NULL;
5000			} else {
5001				zval tmp_val;
5002				ZVAL_COPY(&tmp_val, tmp);
5003				convert_to_string(&tmp_val);
5004				if (Z_TYPE(tmp_val) != IS_STRING) {
5005					php_error_docref(NULL, E_WARNING,"Error converting parameter");
5006					zval_ptr_dtor(&tmp_val);
5007					_php_pgsql_free_params(params, num_params);
5008					RETURN_FALSE;
5009				}
5010				params[i] = estrndup(Z_STRVAL(tmp_val), Z_STRLEN(tmp_val));
5011				zval_ptr_dtor(&tmp_val);
5012			}
5013
5014			i++;
5015		} ZEND_HASH_FOREACH_END();
5016	}
5017
5018	if (PQsendQueryParams(pgsql, query, num_params, NULL, (const char * const *)params, NULL, NULL, 0)) {
5019		_php_pgsql_free_params(params, num_params);
5020	} else if (is_non_blocking) {
5021		_php_pgsql_free_params(params, num_params);
5022		RETURN_FALSE;
5023	} else {
5024		if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
5025			PQreset(pgsql);
5026		}
5027		if (!PQsendQueryParams(pgsql, query, num_params, NULL, (const char * const *)params, NULL, NULL, 0)) {
5028			_php_pgsql_free_params(params, num_params);
5029			RETURN_FALSE;
5030		}
5031	}
5032
5033	if (is_non_blocking) {
5034		ret = PQflush(pgsql);
5035	} else {
5036		/* Wait to finish sending buffer */
5037		while ((ret = PQflush(pgsql))) {
5038			if (ret == -1) {
5039				php_error_docref(NULL, E_NOTICE, "Could not empty PostgreSQL send buffer");
5040				break;
5041			}
5042			usleep(10000);
5043		}
5044
5045		if (PQ_SETNONBLOCKING(pgsql, 0) != 0) {
5046			php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
5047		}
5048	}
5049
5050	if (ret == 0) {
5051		RETURN_TRUE;
5052	} else if (ret == -1) {
5053		RETURN_FALSE;
5054	} else {
5055		RETURN_LONG(0);
5056	}
5057}
5058/* }}} */
5059#endif
5060
5061#if HAVE_PQSENDPREPARE
5062/* {{{ proto bool pg_send_prepare(resource connection, string stmtname, string query)
5063   Asynchronously prepare a query for future execution */
5064PHP_FUNCTION(pg_send_prepare)
5065{
5066	zval *pgsql_link;
5067	char *query, *stmtname;
5068	size_t stmtname_len, query_len;
5069	PGconn *pgsql;
5070	int is_non_blocking;
5071	int ret;
5072
5073	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rss", &pgsql_link, &stmtname, &stmtname_len, &query, &query_len) == FAILURE) {
5074		return;
5075	}
5076
5077	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5078		RETURN_FALSE;
5079	}
5080
5081	is_non_blocking = PQisnonblocking(pgsql);
5082
5083	if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) {
5084		php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
5085		RETURN_FALSE;
5086	}
5087
5088	if (_php_pgsql_link_has_results(pgsql)) {
5089		php_error_docref(NULL, E_NOTICE,
5090			"There are results on this connection. Call pg_get_result() until it returns FALSE");
5091	}
5092
5093	if (!PQsendPrepare(pgsql, stmtname, query, 0, NULL)) {
5094		if (is_non_blocking) {
5095			RETURN_FALSE;
5096		} else {
5097			if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
5098				PQreset(pgsql);
5099			}
5100			if (!PQsendPrepare(pgsql, stmtname, query, 0, NULL)) {
5101				RETURN_FALSE;
5102			}
5103		}
5104	}
5105
5106	if (is_non_blocking) {
5107		ret = PQflush(pgsql);
5108	} else {
5109		/* Wait to finish sending buffer */
5110		while ((ret = PQflush(pgsql))) {
5111			if (ret == -1) {
5112				php_error_docref(NULL, E_NOTICE, "Could not empty PostgreSQL send buffer");
5113				break;
5114			}
5115			usleep(10000);
5116		}
5117		if (PQ_SETNONBLOCKING(pgsql, 0) != 0) {
5118			php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
5119		}
5120	}
5121
5122	if (ret == 0) {
5123		RETURN_TRUE;
5124	} else if (ret == -1) {
5125		RETURN_FALSE;
5126	} else {
5127		RETURN_LONG(0);
5128	}
5129}
5130/* }}} */
5131#endif
5132
5133#if HAVE_PQSENDQUERYPREPARED
5134/* {{{ proto bool pg_send_execute(resource connection, string stmtname, array params)
5135   Executes prevriously prepared stmtname asynchronously */
5136PHP_FUNCTION(pg_send_execute)
5137{
5138	zval *pgsql_link;
5139	zval *pv_param_arr, *tmp;
5140	int num_params = 0;
5141	char **params = NULL;
5142	char *stmtname;
5143	size_t stmtname_len;
5144	PGconn *pgsql;
5145	int is_non_blocking;
5146	int ret;
5147
5148	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsa", &pgsql_link, &stmtname, &stmtname_len, &pv_param_arr) == FAILURE) {
5149		return;
5150	}
5151
5152	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5153		RETURN_FALSE;
5154	}
5155
5156	is_non_blocking = PQisnonblocking(pgsql);
5157
5158	if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) {
5159		php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
5160		RETURN_FALSE;
5161	}
5162
5163	if (_php_pgsql_link_has_results(pgsql)) {
5164		php_error_docref(NULL, E_NOTICE,
5165			"There are results on this connection. Call pg_get_result() until it returns FALSE");
5166	}
5167
5168	num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
5169	if (num_params > 0) {
5170		int i = 0;
5171		params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
5172
5173		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pv_param_arr), tmp) {
5174
5175			if (Z_TYPE_P(tmp) == IS_NULL) {
5176				params[i] = NULL;
5177			} else {
5178				zval tmp_val;
5179				ZVAL_COPY(&tmp_val, tmp);
5180				convert_to_string(&tmp_val);
5181				if (Z_TYPE(tmp_val) != IS_STRING) {
5182					php_error_docref(NULL, E_WARNING,"Error converting parameter");
5183					zval_ptr_dtor(&tmp_val);
5184					_php_pgsql_free_params(params, num_params);
5185					RETURN_FALSE;
5186				}
5187				params[i] = estrndup(Z_STRVAL(tmp_val), Z_STRLEN(tmp_val));
5188				zval_ptr_dtor(&tmp_val);
5189			}
5190
5191			i++;
5192		} ZEND_HASH_FOREACH_END();
5193	}
5194
5195	if (PQsendQueryPrepared(pgsql, stmtname, num_params, (const char * const *)params, NULL, NULL, 0)) {
5196		_php_pgsql_free_params(params, num_params);
5197	} else if (is_non_blocking) {
5198		_php_pgsql_free_params(params, num_params);
5199		RETURN_FALSE;
5200	} else {
5201		if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
5202			PQreset(pgsql);
5203		}
5204		if (!PQsendQueryPrepared(pgsql, stmtname, num_params, (const char * const *)params, NULL, NULL, 0)) {
5205			_php_pgsql_free_params(params, num_params);
5206			RETURN_FALSE;
5207		}
5208	}
5209
5210	if (is_non_blocking) {
5211		ret = PQflush(pgsql);
5212	} else {
5213		/* Wait to finish sending buffer */
5214		while ((ret = PQflush(pgsql))) {
5215			if (ret == -1) {
5216				php_error_docref(NULL, E_NOTICE, "Could not empty PostgreSQL send buffer");
5217				break;
5218			}
5219			usleep(10000);
5220		}
5221		if (PQ_SETNONBLOCKING(pgsql, 0) != 0) {
5222			php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
5223		}
5224	}
5225
5226	if (ret == 0) {
5227		RETURN_TRUE;
5228	} else if (ret == -1) {
5229		RETURN_FALSE;
5230	} else {
5231		RETURN_LONG(0);
5232	}
5233}
5234/* }}} */
5235#endif
5236
5237/* {{{ proto resource pg_get_result(resource connection)
5238   Get asynchronous query result */
5239PHP_FUNCTION(pg_get_result)
5240{
5241	zval *pgsql_link;
5242	PGconn *pgsql;
5243	PGresult *pgsql_result;
5244	pgsql_result_handle *pg_result;
5245
5246	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r", &pgsql_link) == FAILURE) {
5247		RETURN_FALSE;
5248	}
5249
5250	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5251		RETURN_FALSE;
5252	}
5253
5254	pgsql_result = PQgetResult(pgsql);
5255	if (!pgsql_result) {
5256		/* no result */
5257		RETURN_FALSE;
5258	}
5259	pg_result = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
5260	pg_result->conn = pgsql;
5261	pg_result->result = pgsql_result;
5262	pg_result->row = 0;
5263	RETURN_RES(zend_register_resource(pg_result, le_result));
5264}
5265/* }}} */
5266
5267/* {{{ proto mixed pg_result_status(resource result[, long result_type])
5268   Get status of query result */
5269PHP_FUNCTION(pg_result_status)
5270{
5271	zval *result;
5272	zend_long result_type = PGSQL_STATUS_LONG;
5273	ExecStatusType status;
5274	PGresult *pgsql_result;
5275	pgsql_result_handle *pg_result;
5276
5277	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r|l",
5278								 &result, &result_type) == FAILURE) {
5279		RETURN_FALSE;
5280	}
5281
5282	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
5283		RETURN_FALSE;
5284	}
5285
5286	pgsql_result = pg_result->result;
5287	if (result_type == PGSQL_STATUS_LONG) {
5288		status = PQresultStatus(pgsql_result);
5289		RETURN_LONG((int)status);
5290	}
5291	else if (result_type == PGSQL_STATUS_STRING) {
5292		RETURN_STRING(PQcmdStatus(pgsql_result));
5293	}
5294	else {
5295		php_error_docref(NULL, E_WARNING, "Optional 2nd parameter should be PGSQL_STATUS_LONG or PGSQL_STATUS_STRING");
5296		RETURN_FALSE;
5297	}
5298}
5299/* }}} */
5300
5301/* {{{ proto mixed pg_get_notify([resource connection[, int result_type]])
5302   Get asynchronous notification */
5303PHP_FUNCTION(pg_get_notify)
5304{
5305	zval *pgsql_link;
5306	zend_long result_type = PGSQL_ASSOC;
5307	PGconn *pgsql;
5308	PGnotify *pgsql_notify;
5309
5310	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r|l",
5311								 &pgsql_link, &result_type) == FAILURE) {
5312		RETURN_FALSE;
5313	}
5314
5315	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5316		RETURN_FALSE;
5317	}
5318
5319	if (!(result_type & PGSQL_BOTH)) {
5320		php_error_docref(NULL, E_WARNING, "Invalid result type");
5321		RETURN_FALSE;
5322	}
5323
5324	PQconsumeInput(pgsql);
5325	pgsql_notify = PQnotifies(pgsql);
5326	if (!pgsql_notify) {
5327		/* no notify message */
5328		RETURN_FALSE;
5329	}
5330	array_init(return_value);
5331	if (result_type & PGSQL_NUM) {
5332		add_index_string(return_value, 0, pgsql_notify->relname);
5333		add_index_long(return_value, 1, pgsql_notify->be_pid);
5334#if HAVE_PQPROTOCOLVERSION && HAVE_PQPARAMETERSTATUS
5335		if (PQprotocolVersion(pgsql) >= 3 && atof(PQparameterStatus(pgsql, "server_version")) >= 9.0) {
5336#else
5337		if (atof(PG_VERSION) >= 9.0) {
5338#endif
5339#if HAVE_PQPARAMETERSTATUS
5340			add_index_string(return_value, 2, pgsql_notify->extra);
5341#endif
5342		}
5343	}
5344	if (result_type & PGSQL_ASSOC) {
5345		add_assoc_string(return_value, "message", pgsql_notify->relname);
5346		add_assoc_long(return_value, "pid", pgsql_notify->be_pid);
5347#if HAVE_PQPROTOCOLVERSION && HAVE_PQPARAMETERSTATUS
5348		if (PQprotocolVersion(pgsql) >= 3 && atof(PQparameterStatus(pgsql, "server_version")) >= 9.0) {
5349#else
5350		if (atof(PG_VERSION) >= 9.0) {
5351#endif
5352#if HAVE_PQPARAMETERSTATUS
5353			add_assoc_string(return_value, "payload", pgsql_notify->extra);
5354#endif
5355		}
5356	}
5357	PQfreemem(pgsql_notify);
5358}
5359/* }}} */
5360
5361/* {{{ proto int pg_get_pid([resource connection)
5362   Get backend(server) pid */
5363PHP_FUNCTION(pg_get_pid)
5364{
5365	zval *pgsql_link;
5366	PGconn *pgsql;
5367
5368	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r",
5369								 &pgsql_link) == FAILURE) {
5370		RETURN_FALSE;
5371	}
5372
5373	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5374		RETURN_FALSE;
5375	}
5376
5377	RETURN_LONG(PQbackendPID(pgsql));
5378}
5379/* }}} */
5380
5381static size_t php_pgsql_fd_write(php_stream *stream, const char *buf, size_t count) /* {{{ */
5382{
5383	return 0;
5384}
5385/* }}} */
5386
5387static size_t php_pgsql_fd_read(php_stream *stream, char *buf, size_t count) /* {{{ */
5388{
5389	return 0;
5390}
5391/* }}} */
5392
5393static int php_pgsql_fd_close(php_stream *stream, int close_handle) /* {{{ */
5394{
5395	return EOF;
5396}
5397/* }}} */
5398
5399static int php_pgsql_fd_flush(php_stream *stream) /* {{{ */
5400{
5401	return FAILURE;
5402}
5403/* }}} */
5404
5405static int php_pgsql_fd_set_option(php_stream *stream, int option, int value, void *ptrparam) /* {{{ */
5406{
5407	PGconn *pgsql = (PGconn *) stream->abstract;
5408	switch (option) {
5409		case PHP_STREAM_OPTION_BLOCKING:
5410			return PQ_SETNONBLOCKING(pgsql, value);
5411		default:
5412			return FAILURE;
5413	}
5414}
5415/* }}} */
5416
5417static int php_pgsql_fd_cast(php_stream *stream, int cast_as, void **ret) /* {{{ */
5418{
5419	PGconn *pgsql = (PGconn *) stream->abstract;
5420	int fd_number;
5421
5422	switch (cast_as)	{
5423		case PHP_STREAM_AS_FD_FOR_SELECT:
5424		case PHP_STREAM_AS_FD:
5425		case PHP_STREAM_AS_SOCKETD:
5426			if (ret) {
5427				fd_number = PQsocket(pgsql);
5428				if (fd_number == -1) {
5429					return FAILURE;
5430				}
5431
5432				*(php_socket_t *)ret = fd_number;
5433				return SUCCESS;
5434			}
5435		default:
5436			return FAILURE;
5437	}
5438}
5439/* }}} */
5440
5441/* {{{ proto resource pg_socket(resource connection)
5442   Get a read-only handle to the socket underlying the pgsql connection */
5443PHP_FUNCTION(pg_socket)
5444{
5445	zval *pgsql_link;
5446	php_stream *stream;
5447	PGconn *pgsql;
5448
5449	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pgsql_link) == FAILURE) {
5450		return;
5451	}
5452
5453	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5454		RETURN_FALSE;
5455	}
5456
5457	stream = php_stream_alloc(&php_stream_pgsql_fd_ops, pgsql, NULL, "r");
5458
5459	if (stream) {
5460		php_stream_to_zval(stream, return_value);
5461		return;
5462	}
5463
5464	RETURN_FALSE;
5465}
5466/* }}} */
5467
5468/* {{{ proto bool pg_consume_input(resource connection)
5469   Reads input on the connection */
5470PHP_FUNCTION(pg_consume_input)
5471{
5472	zval *pgsql_link;
5473	PGconn *pgsql;
5474
5475	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pgsql_link) == FAILURE) {
5476		return;
5477	}
5478
5479	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5480		RETURN_FALSE;
5481	}
5482
5483	RETURN_BOOL(PQconsumeInput(pgsql));
5484}
5485/* }}} */
5486
5487/* {{{ proto mixed pg_flush(resource connection)
5488   Flush outbound query data on the connection */
5489PHP_FUNCTION(pg_flush)
5490{
5491	zval *pgsql_link;
5492	PGconn *pgsql;
5493	int ret;
5494	int is_non_blocking;
5495
5496	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pgsql_link) == FAILURE) {
5497		return;
5498	}
5499
5500	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5501		RETURN_FALSE;
5502	}
5503
5504	is_non_blocking = PQisnonblocking(pgsql);
5505
5506	if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 1) == -1) {
5507		php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
5508		RETURN_FALSE;
5509	}
5510
5511	ret = PQflush(pgsql);
5512
5513	if (is_non_blocking == 0 && PQ_SETNONBLOCKING(pgsql, 0) == -1) {
5514		php_error_docref(NULL, E_NOTICE, "Failed resetting connection to blocking mode");
5515	}
5516
5517	switch (ret) {
5518		case 0: RETURN_TRUE; break;
5519		case 1: RETURN_LONG(0); break;
5520		default: RETURN_FALSE;
5521	}
5522}
5523/* }}} */
5524
5525/* {{{ php_pgsql_meta_data
5526 * TODO: Add meta_data cache for better performance
5527 */
5528PHP_PGSQL_API int php_pgsql_meta_data(PGconn *pg_link, const char *table_name, zval *meta, zend_bool extended)
5529{
5530	PGresult *pg_result;
5531	char *src, *tmp_name, *tmp_name2 = NULL;
5532	char *escaped;
5533	smart_str querystr = {0};
5534	size_t new_len;
5535	int i, num_rows;
5536	zval elem;
5537
5538	if (!*table_name) {
5539		php_error_docref(NULL, E_WARNING, "The table name must be specified");
5540		return FAILURE;
5541	}
5542
5543	src = estrdup(table_name);
5544	tmp_name = php_strtok_r(src, ".", &tmp_name2);
5545	if (!tmp_name) {
5546		efree(src);
5547		php_error_docref(NULL, E_WARNING, "The table name must be specified");
5548		return FAILURE;
5549	}
5550	if (!tmp_name2 || !*tmp_name2) {
5551		/* Default schema */
5552		tmp_name2 = tmp_name;
5553		tmp_name = "public";
5554	}
5555
5556	if (extended) {
5557		smart_str_appends(&querystr,
5558						  "SELECT a.attname, a.attnum, t.typname, a.attlen, a.attnotNULL, a.atthasdef, a.attndims, t.typtype, "
5559						  "d.description "
5560						  "FROM pg_class as c "
5561						  " JOIN pg_attribute a ON (a.attrelid = c.oid) "
5562						  " JOIN pg_type t ON (a.atttypid = t.oid) "
5563						  " JOIN pg_namespace n ON (c.relnamespace = n.oid) "
5564						  " LEFT JOIN pg_description d ON (d.objoid=a.attrelid AND d.objsubid=a.attnum AND c.oid=d.objoid) "
5565						  "WHERE a.attnum > 0  AND c.relname = '");
5566	} else {
5567		smart_str_appends(&querystr,
5568						  "SELECT a.attname, a.attnum, t.typname, a.attlen, a.attnotnull, a.atthasdef, a.attndims, t.typtype "
5569						  "FROM pg_class as c "
5570						  " JOIN pg_attribute a ON (a.attrelid = c.oid) "
5571						  " JOIN pg_type t ON (a.atttypid = t.oid) "
5572						  " JOIN pg_namespace n ON (c.relnamespace = n.oid) "
5573						  "WHERE a.attnum > 0 AND c.relname = '");
5574	}
5575	escaped = (char *)safe_emalloc(strlen(tmp_name2), 2, 1);
5576	new_len = PQescapeStringConn(pg_link, escaped, tmp_name2, strlen(tmp_name2), NULL);
5577	if (new_len) {
5578		smart_str_appendl(&querystr, escaped, new_len);
5579	}
5580	efree(escaped);
5581
5582	smart_str_appends(&querystr, "' AND n.nspname = '");
5583	escaped = (char *)safe_emalloc(strlen(tmp_name), 2, 1);
5584	new_len = PQescapeStringConn(pg_link, escaped, tmp_name, strlen(tmp_name), NULL);
5585	if (new_len) {
5586		smart_str_appendl(&querystr, escaped, new_len);
5587	}
5588	efree(escaped);
5589
5590	smart_str_appends(&querystr, "' ORDER BY a.attnum;");
5591	smart_str_0(&querystr);
5592	efree(src);
5593
5594	pg_result = PQexec(pg_link, ZSTR_VAL(querystr.s));
5595	if (PQresultStatus(pg_result) != PGRES_TUPLES_OK || (num_rows = PQntuples(pg_result)) == 0) {
5596		php_error_docref(NULL, E_WARNING, "Table '%s' doesn't exists", table_name);
5597		smart_str_free(&querystr);
5598		PQclear(pg_result);
5599		return FAILURE;
5600	}
5601	smart_str_free(&querystr);
5602
5603	for (i = 0; i < num_rows; i++) {
5604		char *name;
5605		array_init(&elem);
5606		/* pg_attribute.attnum */
5607		add_assoc_long_ex(&elem, "num", sizeof("num") - 1, atoi(PQgetvalue(pg_result, i, 1)));
5608		/* pg_type.typname */
5609		add_assoc_string_ex(&elem, "type", sizeof("type") - 1, PQgetvalue(pg_result, i, 2));
5610		/* pg_attribute.attlen */
5611		add_assoc_long_ex(&elem, "len", sizeof("len") - 1, atoi(PQgetvalue(pg_result,i,3)));
5612		/* pg_attribute.attnonull */
5613		add_assoc_bool_ex(&elem, "not null", sizeof("not null") - 1, !strcmp(PQgetvalue(pg_result, i, 4), "t"));
5614		/* pg_attribute.atthasdef */
5615		add_assoc_bool_ex(&elem, "has default", sizeof("has default") - 1, !strcmp(PQgetvalue(pg_result,i,5), "t"));
5616		/* pg_attribute.attndims */
5617		add_assoc_long_ex(&elem, "array dims", sizeof("array dims") - 1, atoi(PQgetvalue(pg_result, i, 6)));
5618		/* pg_type.typtype */
5619		add_assoc_bool_ex(&elem, "is enum", sizeof("is enum") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "e"));
5620		if (extended) {
5621			/* pg_type.typtype */
5622			add_assoc_bool_ex(&elem, "is base", sizeof("is base") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "b"));
5623			add_assoc_bool_ex(&elem, "is composite", sizeof("is composite") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "c"));
5624			add_assoc_bool_ex(&elem, "is pesudo", sizeof("is pesudo") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "p"));
5625			/* pg_description.description */
5626			add_assoc_string_ex(&elem, "description", sizeof("description") - 1, PQgetvalue(pg_result, i, 8));
5627		}
5628		/* pg_attribute.attname */
5629		name = PQgetvalue(pg_result,i,0);
5630		add_assoc_zval(meta, name, &elem);
5631	}
5632	PQclear(pg_result);
5633
5634	return SUCCESS;
5635}
5636
5637/* }}} */
5638
5639/* {{{ proto array pg_meta_data(resource db, string table [, bool extended])
5640   Get meta_data */
5641PHP_FUNCTION(pg_meta_data)
5642{
5643	zval *pgsql_link;
5644	char *table_name;
5645	size_t table_name_len;
5646	zend_bool extended=0;
5647	PGconn *pgsql;
5648
5649	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs|b",
5650							  &pgsql_link, &table_name, &table_name_len, &extended) == FAILURE) {
5651		return;
5652	}
5653
5654	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
5655		RETURN_FALSE;
5656	}
5657
5658	array_init(return_value);
5659	if (php_pgsql_meta_data(pgsql, table_name, return_value, extended) == FAILURE) {
5660		zval_dtor(return_value); /* destroy array */
5661		RETURN_FALSE;
5662	}
5663}
5664/* }}} */
5665
5666/* {{{ php_pgsql_get_data_type
5667 */
5668static php_pgsql_data_type php_pgsql_get_data_type(const char *type_name, size_t len)
5669{
5670	/* This is stupid way to do. I'll fix it when I decied how to support
5671	   user defined types. (Yasuo) */
5672
5673	/* boolean */
5674	if (!strcmp(type_name, "bool")|| !strcmp(type_name, "boolean"))
5675		return PG_BOOL;
5676	/* object id */
5677	if (!strcmp(type_name, "oid"))
5678		return PG_OID;
5679	/* integer */
5680	if (!strcmp(type_name, "int2") || !strcmp(type_name, "smallint"))
5681		return PG_INT2;
5682	if (!strcmp(type_name, "int4") || !strcmp(type_name, "integer"))
5683		return PG_INT4;
5684	if (!strcmp(type_name, "int8") || !strcmp(type_name, "bigint"))
5685		return PG_INT8;
5686	/* real and other */
5687	if (!strcmp(type_name, "float4") || !strcmp(type_name, "real"))
5688		return PG_FLOAT4;
5689	if (!strcmp(type_name, "float8") || !strcmp(type_name, "double precision"))
5690		return PG_FLOAT8;
5691	if (!strcmp(type_name, "numeric"))
5692		return PG_NUMERIC;
5693	if (!strcmp(type_name, "money"))
5694		return PG_MONEY;
5695	/* character */
5696	if (!strcmp(type_name, "text"))
5697		return PG_TEXT;
5698	if (!strcmp(type_name, "bpchar") || !strcmp(type_name, "character"))
5699		return PG_CHAR;
5700	if (!strcmp(type_name, "varchar") || !strcmp(type_name, "character varying"))
5701		return PG_VARCHAR;
5702	/* time and interval */
5703	if (!strcmp(type_name, "abstime"))
5704		return PG_UNIX_TIME;
5705	if (!strcmp(type_name, "reltime"))
5706		return PG_UNIX_TIME_INTERVAL;
5707	if (!strcmp(type_name, "tinterval"))
5708		return PG_UNIX_TIME_INTERVAL;
5709	if (!strcmp(type_name, "date"))
5710		return PG_DATE;
5711	if (!strcmp(type_name, "time"))
5712		return PG_TIME;
5713	if (!strcmp(type_name, "time with time zone") || !strcmp(type_name, "timetz"))
5714		return PG_TIME_WITH_TIMEZONE;
5715	if (!strcmp(type_name, "timestamp without time zone") || !strcmp(type_name, "timestamp"))
5716		return PG_TIMESTAMP;
5717	if (!strcmp(type_name, "timestamp with time zone") || !strcmp(type_name, "timestamptz"))
5718		return PG_TIMESTAMP_WITH_TIMEZONE;
5719	if (!strcmp(type_name, "interval"))
5720		return PG_INTERVAL;
5721	/* binary */
5722	if (!strcmp(type_name, "bytea"))
5723		return PG_BYTEA;
5724	/* network */
5725	if (!strcmp(type_name, "cidr"))
5726		return PG_CIDR;
5727	if (!strcmp(type_name, "inet"))
5728		return PG_INET;
5729	if (!strcmp(type_name, "macaddr"))
5730		return PG_MACADDR;
5731	/* bit */
5732	if (!strcmp(type_name, "bit"))
5733		return PG_BIT;
5734	if (!strcmp(type_name, "bit varying"))
5735		return PG_VARBIT;
5736	/* geometric */
5737	if (!strcmp(type_name, "line"))
5738		return PG_LINE;
5739	if (!strcmp(type_name, "lseg"))
5740		return PG_LSEG;
5741	if (!strcmp(type_name, "box"))
5742		return PG_BOX;
5743	if (!strcmp(type_name, "path"))
5744		return PG_PATH;
5745	if (!strcmp(type_name, "point"))
5746		return PG_POINT;
5747	if (!strcmp(type_name, "polygon"))
5748		return PG_POLYGON;
5749	if (!strcmp(type_name, "circle"))
5750		return PG_CIRCLE;
5751
5752	return PG_UNKNOWN;
5753}
5754/* }}} */
5755
5756/* {{{ php_pgsql_convert_match
5757 * test field value with regular expression specified.
5758 */
5759static int php_pgsql_convert_match(const char *str, size_t str_len, const char *regex , int icase)
5760{
5761	pcre *re;
5762	const char *err_msg;
5763	int err_offset;
5764	int options = PCRE_NO_AUTO_CAPTURE, res;
5765	size_t i;
5766
5767	/* Check invalid chars for POSIX regex */
5768	for (i = 0; i < str_len; i++) {
5769		if (str[i] == '\n' ||
5770			str[i] == '\r' ||
5771			str[i] == '\0' ) {
5772			return FAILURE;
5773		}
5774	}
5775
5776	if (icase) {
5777		options |= PCRE_CASELESS;
5778	}
5779
5780	if ((re = pcre_compile(regex, options, &err_msg, &err_offset, NULL)) == NULL) {
5781		php_error_docref(NULL, E_WARNING, "Cannot compile regex");
5782		return FAILURE;
5783	}
5784
5785	res = pcre_exec(re, NULL, str, str_len, 0, 0, NULL, 0);
5786	pcre_free(re);
5787
5788	if (res == PCRE_ERROR_NOMATCH) {
5789		return FAILURE;
5790	} else if (res) {
5791		php_error_docref(NULL, E_WARNING, "Cannot exec regex");
5792		return FAILURE;
5793	}
5794	return SUCCESS;
5795}
5796
5797/* }}} */
5798
5799/* {{{ php_pgsql_add_quote
5800 * add quotes around string.
5801 */
5802static int php_pgsql_add_quotes(zval *src, zend_bool should_free)
5803{
5804	smart_str str = {0};
5805
5806	assert(Z_TYPE_P(src) == IS_STRING);
5807	assert(should_free == 1 || should_free == 0);
5808
5809	smart_str_appendc(&str, 'E');
5810	smart_str_appendc(&str, '\'');
5811	smart_str_appendl(&str, Z_STRVAL_P(src), Z_STRLEN_P(src));
5812	smart_str_appendc(&str, '\'');
5813	smart_str_0(&str);
5814
5815	if (should_free) {
5816		zval_ptr_dtor(src);
5817	}
5818	ZVAL_NEW_STR(src, str.s);
5819
5820	return SUCCESS;
5821}
5822/* }}} */
5823
5824#define PGSQL_CONV_CHECK_IGNORE() \
5825	if (!err && Z_TYPE(new_val) == IS_STRING && !strcmp(Z_STRVAL(new_val), "NULL")) { \
5826		/* if new_value is string "NULL" and field has default value, remove element to use default value */ \
5827		if (!(opt & PGSQL_CONV_IGNORE_DEFAULT) && Z_TYPE_P(has_default) == IS_TRUE) { \
5828			zval_ptr_dtor(&new_val); \
5829			skip_field = 1; \
5830		} \
5831		/* raise error if it's not null and cannot be ignored */ \
5832		else if (!(opt & PGSQL_CONV_IGNORE_NOT_NULL) && Z_TYPE_P(not_null) == IS_TRUE) { \
5833			php_error_docref(NULL, E_NOTICE, "Detected NULL for 'NOT NULL' field '%s'", ZSTR_VAL(field)); \
5834			err = 1; \
5835		} \
5836	}
5837
5838/* {{{ php_pgsql_convert
5839 * check and convert array values (fieldname=>vlaue pair) for sql
5840 */
5841PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, const zval *values, zval *result, zend_ulong opt)
5842{
5843	zend_string *field = NULL;
5844	zval meta, *def, *type, *not_null, *has_default, *is_enum, *val, new_val;
5845	int err = 0, skip_field;
5846	php_pgsql_data_type data_type;
5847
5848	assert(pg_link != NULL);
5849	assert(Z_TYPE_P(values) == IS_ARRAY);
5850	assert(Z_TYPE_P(result) == IS_ARRAY);
5851	assert(!(opt & ~PGSQL_CONV_OPTS));
5852
5853	if (!table_name) {
5854		return FAILURE;
5855	}
5856
5857	array_init(&meta);
5858/* table_name is escaped by php_pgsql_meta_data */
5859	if (php_pgsql_meta_data(pg_link, table_name, &meta, 0) == FAILURE) {
5860		zval_ptr_dtor(&meta);
5861		return FAILURE;
5862	}
5863
5864	ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(values), field, val) {
5865		skip_field = 0;
5866		ZVAL_NULL(&new_val);
5867
5868		if (!err && field == NULL) {
5869			php_error_docref(NULL, E_WARNING, "Accepts only string key for values");
5870			err = 1;
5871		}
5872
5873		if (!err && (def = zend_hash_find(Z_ARRVAL(meta), field)) == NULL) {
5874			php_error_docref(NULL, E_NOTICE, "Invalid field name (%s) in values", ZSTR_VAL(field));
5875			err = 1;
5876		}
5877		if (!err && (type = zend_hash_str_find(Z_ARRVAL_P(def), "type", sizeof("type") - 1)) == NULL) {
5878			php_error_docref(NULL, E_NOTICE, "Detected broken meta data. Missing 'type'");
5879			err = 1;
5880		}
5881		if (!err && (not_null = zend_hash_str_find(Z_ARRVAL_P(def), "not null", sizeof("not null") - 1)) == NULL) {
5882			php_error_docref(NULL, E_NOTICE, "Detected broken meta data. Missing 'not null'");
5883			err = 1;
5884		}
5885		if (!err && (has_default = zend_hash_str_find(Z_ARRVAL_P(def), "has default", sizeof("has default") - 1)) == NULL) {
5886			php_error_docref(NULL, E_NOTICE, "Detected broken meta data. Missing 'has default'");
5887			err = 1;
5888		}
5889		if (!err && (is_enum = zend_hash_str_find(Z_ARRVAL_P(def), "is enum", sizeof("is enum") - 1)) == NULL) {
5890			php_error_docref(NULL, E_NOTICE, "Detected broken meta data. Missing 'is enum'");
5891			err = 1;
5892		}
5893		if (!err && (Z_TYPE_P(val) == IS_ARRAY || Z_TYPE_P(val) == IS_OBJECT)) {
5894			php_error_docref(NULL, E_NOTICE, "Expects scalar values as field values");
5895			err = 1;
5896		}
5897		if (err) {
5898			break; /* break out for() */
5899		}
5900
5901		convert_to_boolean(is_enum);
5902		if (Z_TYPE_P(is_enum) == IS_TRUE) {
5903			/* enums need to be treated like strings */
5904			data_type = PG_TEXT;
5905		} else {
5906			data_type = php_pgsql_get_data_type(Z_STRVAL_P(type), Z_STRLEN_P(type));
5907		}
5908
5909		switch(data_type)
5910		{
5911			case PG_BOOL:
5912				switch (Z_TYPE_P(val)) {
5913					case IS_STRING:
5914						if (Z_STRLEN_P(val) == 0) {
5915							ZVAL_STRING(&new_val, "NULL");
5916						}
5917						else {
5918							if (!strcmp(Z_STRVAL_P(val), "t") || !strcmp(Z_STRVAL_P(val), "T") ||
5919								!strcmp(Z_STRVAL_P(val), "y") || !strcmp(Z_STRVAL_P(val), "Y") ||
5920								!strcmp(Z_STRVAL_P(val), "true") || !strcmp(Z_STRVAL_P(val), "True") ||
5921								!strcmp(Z_STRVAL_P(val), "yes") || !strcmp(Z_STRVAL_P(val), "Yes") ||
5922								!strcmp(Z_STRVAL_P(val), "1")) {
5923								ZVAL_STRINGL(&new_val, "'t'", sizeof("'t'")-1);
5924							}
5925							else if (!strcmp(Z_STRVAL_P(val), "f") || !strcmp(Z_STRVAL_P(val), "F") ||
5926									 !strcmp(Z_STRVAL_P(val), "n") || !strcmp(Z_STRVAL_P(val), "N") ||
5927									 !strcmp(Z_STRVAL_P(val), "false") ||  !strcmp(Z_STRVAL_P(val), "False") ||
5928									 !strcmp(Z_STRVAL_P(val), "no") ||  !strcmp(Z_STRVAL_P(val), "No") ||
5929									 !strcmp(Z_STRVAL_P(val), "0")) {
5930								ZVAL_STRINGL(&new_val, "'f'", sizeof("'f'")-1);
5931							}
5932							else {
5933								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));
5934								err = 1;
5935							}
5936						}
5937						break;
5938
5939					case IS_LONG:
5940						if (Z_LVAL_P(val)) {
5941							ZVAL_STRINGL(&new_val, "'t'", sizeof("'t'")-1);
5942						}
5943						else {
5944							ZVAL_STRINGL(&new_val, "'f'", sizeof("'f'")-1);
5945						}
5946						break;
5947
5948					case IS_TRUE:
5949						ZVAL_STRINGL(&new_val, "'t'", sizeof("'t'")-1);
5950						break;
5951
5952					case IS_FALSE:
5953						ZVAL_STRINGL(&new_val, "'f'", sizeof("'f'")-1);
5954						break;
5955
5956					case IS_NULL:
5957						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
5958						break;
5959
5960					default:
5961						err = 1;
5962				}
5963				PGSQL_CONV_CHECK_IGNORE();
5964				if (err) {
5965					php_error_docref(NULL, E_NOTICE, "Expects string, null, long or boolelan value for PostgreSQL '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
5966				}
5967				break;
5968
5969			case PG_OID:
5970			case PG_INT2:
5971			case PG_INT4:
5972			case PG_INT8:
5973				switch (Z_TYPE_P(val)) {
5974					case IS_STRING:
5975						if (Z_STRLEN_P(val) == 0) {
5976							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
5977						}
5978						else {
5979							/* FIXME: better regex must be used */
5980							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), "^([+-]{0,1}[0-9]+)$", 0) == FAILURE) {
5981								err = 1;
5982							}
5983							else {
5984								ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
5985							}
5986						}
5987						break;
5988
5989					case IS_DOUBLE:
5990						ZVAL_DOUBLE(&new_val, Z_DVAL_P(val));
5991						convert_to_long_ex(&new_val);
5992						break;
5993
5994					case IS_LONG:
5995						ZVAL_LONG(&new_val, Z_LVAL_P(val));
5996						break;
5997
5998					case IS_NULL:
5999						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6000						break;
6001
6002					default:
6003						err = 1;
6004				}
6005				PGSQL_CONV_CHECK_IGNORE();
6006				if (err) {
6007					php_error_docref(NULL, E_NOTICE, "Expects NULL, string, long or double value for pgsql '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6008				}
6009				break;
6010
6011			case PG_NUMERIC:
6012			case PG_MONEY:
6013			case PG_FLOAT4:
6014			case PG_FLOAT8:
6015				switch (Z_TYPE_P(val)) {
6016					case IS_STRING:
6017						if (Z_STRLEN_P(val) == 0) {
6018							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6019						}
6020						else {
6021							/* better regex? */
6022							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), "^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$", 0) == FAILURE) {
6023								if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), "^[+-]{0,1}(inf)(inity){0,1}$", 1) == FAILURE) {
6024									err = 1;
6025								} else {
6026									ZVAL_STRING(&new_val, Z_STRVAL_P(val));
6027									php_pgsql_add_quotes(&new_val, 1);
6028								}
6029							}
6030							else {
6031								ZVAL_STRING(&new_val, Z_STRVAL_P(val));
6032							}
6033						}
6034						break;
6035
6036					case IS_LONG:
6037						ZVAL_LONG(&new_val, Z_LVAL_P(val));
6038						break;
6039
6040					case IS_DOUBLE:
6041						ZVAL_DOUBLE(&new_val, Z_DVAL_P(val));
6042						break;
6043
6044					case IS_NULL:
6045						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6046						break;
6047
6048					default:
6049						err = 1;
6050				}
6051				PGSQL_CONV_CHECK_IGNORE();
6052				if (err) {
6053					php_error_docref(NULL, E_NOTICE, "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6054				}
6055				break;
6056
6057				/* Exotic types are handled as string also.
6058				   Please feel free to add more valitions. Invalid query fails
6059				   at execution anyway. */
6060			case PG_TEXT:
6061			case PG_CHAR:
6062			case PG_VARCHAR:
6063				/* bit */
6064			case PG_BIT:
6065			case PG_VARBIT:
6066				/* geometric */
6067			case PG_LINE:
6068			case PG_LSEG:
6069			case PG_POINT:
6070			case PG_BOX:
6071			case PG_PATH:
6072			case PG_POLYGON:
6073			case PG_CIRCLE:
6074				/* unknown. JSON, Array etc */
6075			case PG_UNKNOWN:
6076				switch (Z_TYPE_P(val)) {
6077					case IS_STRING:
6078						if (Z_STRLEN_P(val) == 0) {
6079							if (opt & PGSQL_CONV_FORCE_NULL) {
6080								ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6081							} else {
6082								ZVAL_STRINGL(&new_val, "''", sizeof("''")-1);
6083							}
6084						}
6085						else {
6086							zend_string *str;
6087							/* PostgreSQL ignores \0 */
6088							str = zend_string_alloc(Z_STRLEN_P(val) * 2, 0);
6089							/* better to use PGSQLescapeLiteral since PGescapeStringConn does not handle special \ */
6090							ZSTR_LEN(str) = PQescapeStringConn(pg_link, ZSTR_VAL(str), Z_STRVAL_P(val), Z_STRLEN_P(val), NULL);
6091							str = zend_string_truncate(str, ZSTR_LEN(str), 0);
6092							ZVAL_NEW_STR(&new_val, str);
6093							php_pgsql_add_quotes(&new_val, 1);
6094						}
6095						break;
6096
6097					case IS_LONG:
6098						ZVAL_LONG(&new_val, Z_LVAL_P(val));
6099						convert_to_string_ex(&new_val);
6100						break;
6101
6102					case IS_DOUBLE:
6103						ZVAL_DOUBLE(&new_val, Z_DVAL_P(val));
6104						convert_to_string_ex(&new_val);
6105						break;
6106
6107					case IS_NULL:
6108						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6109						break;
6110
6111					default:
6112						err = 1;
6113				}
6114				PGSQL_CONV_CHECK_IGNORE();
6115				if (err) {
6116					php_error_docref(NULL, E_NOTICE, "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6117				}
6118				break;
6119
6120			case PG_UNIX_TIME:
6121			case PG_UNIX_TIME_INTERVAL:
6122				/* these are the actallay a integer */
6123				switch (Z_TYPE_P(val)) {
6124					case IS_STRING:
6125						if (Z_STRLEN_P(val) == 0) {
6126							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6127						}
6128						else {
6129							/* better regex? */
6130							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), "^[0-9]+$", 0) == FAILURE) {
6131								err = 1;
6132							}
6133							else {
6134								ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
6135								convert_to_long_ex(&new_val);
6136							}
6137						}
6138						break;
6139
6140					case IS_DOUBLE:
6141						ZVAL_DOUBLE(&new_val, Z_DVAL_P(val));
6142						convert_to_long_ex(&new_val);
6143						break;
6144
6145					case IS_LONG:
6146						ZVAL_LONG(&new_val, Z_LVAL_P(val));
6147						break;
6148
6149					case IS_NULL:
6150						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6151						break;
6152
6153					default:
6154						err = 1;
6155				}
6156				PGSQL_CONV_CHECK_IGNORE();
6157				if (err) {
6158					php_error_docref(NULL, E_NOTICE, "Expects NULL, string, long or double value for '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6159				}
6160				break;
6161
6162			case PG_CIDR:
6163			case PG_INET:
6164				switch (Z_TYPE_P(val)) {
6165					case IS_STRING:
6166						if (Z_STRLEN_P(val) == 0) {
6167							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6168						}
6169						else {
6170							/* better regex? IPV6 and IPV4 */
6171							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), "^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$", 0) == FAILURE) {
6172								err = 1;
6173							}
6174							else {
6175								ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
6176								php_pgsql_add_quotes(&new_val, 1);
6177							}
6178						}
6179						break;
6180
6181					case IS_NULL:
6182						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6183						break;
6184
6185					default:
6186						err = 1;
6187				}
6188				PGSQL_CONV_CHECK_IGNORE();
6189				if (err) {
6190					php_error_docref(NULL, E_NOTICE, "Expects NULL or string for '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6191				}
6192				break;
6193
6194			case PG_TIME_WITH_TIMEZONE:
6195			case PG_TIMESTAMP:
6196			case PG_TIMESTAMP_WITH_TIMEZONE:
6197				switch(Z_TYPE_P(val)) {
6198					case IS_STRING:
6199						if (Z_STRLEN_P(val) == 0) {
6200							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6201						} else if (!strcasecmp(Z_STRVAL_P(val), "now()")) {
6202							ZVAL_STRINGL(&new_val, "NOW()", sizeof("NOW()")-1);
6203						} else {
6204							/* better regex? */
6205							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val), "^([0-9]{4}[/-][0-9]{1,2}[/-][0-9]{1,2})([ \\t]+(([0-9]{1,2}:[0-9]{1,2}){1}(:[0-9]{1,2}){0,1}(\\.[0-9]+){0,1}([ \\t]*([+-][0-9]{1,4}(:[0-9]{1,2}){0,1}|[-a-zA-Z_/+]{1,50})){0,1})){0,1}$", 1) == FAILURE) {
6206								err = 1;
6207							} else {
6208								ZVAL_STRING(&new_val, Z_STRVAL_P(val));
6209								php_pgsql_add_quotes(&new_val, 1);
6210							}
6211						}
6212						break;
6213
6214					case IS_NULL:
6215						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6216						break;
6217
6218					default:
6219						err = 1;
6220				}
6221				PGSQL_CONV_CHECK_IGNORE();
6222				if (err) {
6223					php_error_docref(NULL, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6224				}
6225				break;
6226
6227			case PG_DATE:
6228				switch(Z_TYPE_P(val)) {
6229					case IS_STRING:
6230						if (Z_STRLEN_P(val) == 0) {
6231							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6232						}
6233						else {
6234							/* FIXME: better regex must be used */
6235							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) {
6236								err = 1;
6237							}
6238							else {
6239								ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
6240								php_pgsql_add_quotes(&new_val, 1);
6241							}
6242						}
6243						break;
6244
6245					case IS_NULL:
6246						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6247						break;
6248
6249					default:
6250						err = 1;
6251				}
6252				PGSQL_CONV_CHECK_IGNORE();
6253				if (err) {
6254					php_error_docref(NULL, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6255				}
6256				break;
6257
6258			case PG_TIME:
6259				switch(Z_TYPE_P(val)) {
6260					case IS_STRING:
6261						if (Z_STRLEN_P(val) == 0) {
6262							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6263						}
6264						else {
6265							/* FIXME: better regex must be used */
6266							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) {
6267								err = 1;
6268							}
6269							else {
6270								ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
6271								php_pgsql_add_quotes(&new_val, 1);
6272							}
6273						}
6274						break;
6275
6276					case IS_NULL:
6277						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6278						break;
6279
6280					default:
6281						err = 1;
6282				}
6283				PGSQL_CONV_CHECK_IGNORE();
6284				if (err) {
6285					php_error_docref(NULL, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6286				}
6287				break;
6288
6289			case PG_INTERVAL:
6290				switch(Z_TYPE_P(val)) {
6291					case IS_STRING:
6292						if (Z_STRLEN_P(val) == 0) {
6293							ZVAL_STRING(&new_val, "NULL");
6294						}
6295						else {
6296
6297							/* From the Postgres docs:
6298
6299							   interval values can be written with the following syntax:
6300							   [@] quantity unit [quantity unit...] [direction]
6301
6302							   Where: quantity is a number (possibly signed); unit is second, minute, hour,
6303							   day, week, month, year, decade, century, millennium, or abbreviations or
6304							   plurals of these units [note not *all* abbreviations] ; direction can be
6305							   ago or empty. The at sign (@) is optional noise.
6306
6307							   ...
6308
6309							   Quantities of days, hours, minutes, and seconds can be specified without explicit
6310							   unit markings. For example, '1 12:59:10' is read the same as '1 day 12 hours 59 min 10
6311							   sec'.
6312							*/
6313							if (php_pgsql_convert_match(Z_STRVAL_P(val), Z_STRLEN_P(val),
6314														"^(@?[ \\t]+)?("
6315
6316														/* Textual time units and their abbreviations: */
6317														"(([-+]?[ \\t]+)?"
6318														"[0-9]+(\\.[0-9]*)?[ \\t]*"
6319														"(millenniums|millennia|millennium|mil|mils|"
6320														"centuries|century|cent|c|"
6321														"decades|decade|dec|decs|"
6322														"years|year|y|"
6323														"months|month|mon|"
6324														"weeks|week|w|"
6325														"days|day|d|"
6326														"hours|hour|hr|hrs|h|"
6327														"minutes|minute|mins|min|m|"
6328														"seconds|second|secs|sec|s))+|"
6329
6330														/* Textual time units plus (dd)* hh[:mm[:ss]] */
6331														"((([-+]?[ \\t]+)?"
6332														"[0-9]+(\\.[0-9]*)?[ \\t]*"
6333														"(millenniums|millennia|millennium|mil|mils|"
6334														"centuries|century|cent|c|"
6335														"decades|decade|dec|decs|"
6336														"years|year|y|"
6337														"months|month|mon|"
6338														"weeks|week|w|"
6339														"days|day|d))+"
6340														"([-+]?[ \\t]+"
6341														"([0-9]+[ \\t]+)+"				 /* dd */
6342														"(([0-9]{1,2}:){0,2}[0-9]{0,2})" /* hh:[mm:[ss]] */
6343														")?))"
6344														"([ \\t]+ago)?$",
6345														1) == FAILURE) {
6346								err = 1;
6347							}
6348							else {
6349								ZVAL_STRING(&new_val, Z_STRVAL_P(val));
6350								php_pgsql_add_quotes(&new_val, 1);
6351							}
6352						}
6353						break;
6354
6355					case IS_NULL:
6356						ZVAL_STRING(&new_val, "NULL");
6357						break;
6358
6359					default:
6360						err = 1;
6361				}
6362				PGSQL_CONV_CHECK_IGNORE();
6363				if (err) {
6364					php_error_docref(NULL, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6365				}
6366				break;
6367#ifdef HAVE_PQESCAPE
6368			case PG_BYTEA:
6369				switch (Z_TYPE_P(val)) {
6370					case IS_STRING:
6371						if (Z_STRLEN_P(val) == 0) {
6372							ZVAL_STRING(&new_val, "NULL");
6373						}
6374						else {
6375							unsigned char *tmp;
6376							size_t to_len;
6377							smart_str s = {0};
6378#ifdef HAVE_PQESCAPE_BYTEA_CONN
6379							tmp = PQescapeByteaConn(pg_link, (unsigned char *)Z_STRVAL_P(val), Z_STRLEN_P(val), &to_len);
6380#else
6381							tmp = PQescapeBytea(Z_STRVAL_P(val), (unsigned char *)Z_STRLEN_P(val), &to_len);
6382#endif
6383							ZVAL_STRINGL(&new_val, (char *)tmp, to_len - 1); /* PQescapeBytea's to_len includes additional '\0' */
6384							PQfreemem(tmp);
6385							php_pgsql_add_quotes(&new_val, 1);
6386							smart_str_appendl(&s, Z_STRVAL(new_val), Z_STRLEN(new_val));
6387							smart_str_0(&s);
6388							zval_ptr_dtor(&new_val);
6389							ZVAL_NEW_STR(&new_val, s.s);
6390						}
6391						break;
6392
6393					case IS_LONG:
6394						ZVAL_LONG(&new_val, Z_LVAL_P(val));
6395						convert_to_string_ex(&new_val);
6396						break;
6397
6398					case IS_DOUBLE:
6399						ZVAL_DOUBLE(&new_val, Z_DVAL_P(val));
6400						convert_to_string_ex(&new_val);
6401						break;
6402
6403					case IS_NULL:
6404						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6405						break;
6406
6407					default:
6408						err = 1;
6409				}
6410				PGSQL_CONV_CHECK_IGNORE();
6411				if (err) {
6412					php_error_docref(NULL, E_NOTICE, "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6413				}
6414				break;
6415
6416#endif
6417			case PG_MACADDR:
6418				switch(Z_TYPE_P(val)) {
6419					case IS_STRING:
6420						if (Z_STRLEN_P(val) == 0) {
6421							ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6422						}
6423						else {
6424							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) {
6425								err = 1;
6426							}
6427							else {
6428								ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
6429								php_pgsql_add_quotes(&new_val, 1);
6430							}
6431						}
6432						break;
6433
6434					case IS_NULL:
6435						ZVAL_STRINGL(&new_val, "NULL", sizeof("NULL")-1);
6436						break;
6437
6438					default:
6439						err = 1;
6440				}
6441				PGSQL_CONV_CHECK_IGNORE();
6442				if (err) {
6443					php_error_docref(NULL, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field));
6444				}
6445				break;
6446
6447			default:
6448				/* should not happen */
6449				php_error_docref(NULL, E_NOTICE, "Unknown or system data type '%s' for '%s'. Report error", Z_STRVAL_P(type), ZSTR_VAL(field));
6450				err = 1;
6451				break;
6452		} /* switch */
6453
6454		if (err) {
6455			zval_ptr_dtor(&new_val);
6456			break; /* break out for() */
6457		}
6458		/* If field is NULL and HAS DEFAULT, should be skipped */
6459		if (!skip_field) {
6460			char *escaped;
6461
6462			if (_php_pgsql_detect_identifier_escape(ZSTR_VAL(field), ZSTR_LEN(field)) == SUCCESS) {
6463				zend_hash_update(Z_ARRVAL_P(result), field, &new_val);
6464			} else {
6465				escaped = PGSQLescapeIdentifier(pg_link, ZSTR_VAL(field), ZSTR_LEN(field));
6466				add_assoc_zval(result, escaped, &new_val);
6467				PGSQLfree(escaped);
6468			}
6469		}
6470	} ZEND_HASH_FOREACH_END(); /* for */
6471
6472	zval_ptr_dtor(&meta);
6473
6474	if (err) {
6475		/* shouldn't destroy & free zval here */
6476		return FAILURE;
6477	}
6478	return SUCCESS;
6479}
6480/* }}} */
6481
6482/* {{{ proto array pg_convert(resource db, string table, array values[, int options])
6483   Check and convert values for PostgreSQL SQL statement */
6484PHP_FUNCTION(pg_convert)
6485{
6486	zval *pgsql_link, *values;
6487	char *table_name;
6488	size_t table_name_len;
6489	zend_ulong option = 0;
6490	PGconn *pg_link;
6491
6492	if (zend_parse_parameters(ZEND_NUM_ARGS(),
6493							  "rsa|l", &pgsql_link, &table_name, &table_name_len, &values, &option) == FAILURE) {
6494		return;
6495	}
6496	if (option & ~PGSQL_CONV_OPTS) {
6497		php_error_docref(NULL, E_WARNING, "Invalid option is specified");
6498		RETURN_FALSE;
6499	}
6500	if (!table_name_len) {
6501		php_error_docref(NULL, E_NOTICE, "Table name is invalid");
6502		RETURN_FALSE;
6503	}
6504
6505	if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
6506		RETURN_FALSE;
6507	}
6508
6509	if (php_pgsql_flush_query(pg_link)) {
6510		php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
6511	}
6512	array_init(return_value);
6513	if (php_pgsql_convert(pg_link, table_name, values, return_value, option) == FAILURE) {
6514		zval_dtor(return_value);
6515		RETURN_FALSE;
6516	}
6517}
6518/* }}} */
6519
6520static int do_exec(smart_str *querystr, int expect, PGconn *pg_link, zend_ulong opt) /* {{{ */
6521{
6522	if (opt & PGSQL_DML_ASYNC) {
6523		if (PQsendQuery(pg_link, ZSTR_VAL(querystr->s))) {
6524			return 0;
6525		}
6526	}
6527	else {
6528		PGresult *pg_result;
6529
6530		pg_result = PQexec(pg_link, ZSTR_VAL(querystr->s));
6531		if (PQresultStatus(pg_result) == expect) {
6532			PQclear(pg_result);
6533			return 0;
6534		} else {
6535			php_error_docref(NULL, E_WARNING, "%s", PQresultErrorMessage(pg_result));
6536			PQclear(pg_result);
6537		}
6538	}
6539
6540	return -1;
6541}
6542/* }}} */
6543
6544static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const char *table) /* {{{ */
6545{
6546	char *table_copy, *escaped, *tmp;
6547	const char *token;
6548	size_t len;
6549
6550	/* schame.table should be "schame"."table" */
6551	table_copy = estrdup(table);
6552	token = php_strtok_r(table_copy, ".", &tmp);
6553	if (token == NULL) {
6554		token = table;
6555	}
6556	len = strlen(token);
6557	if (_php_pgsql_detect_identifier_escape(token, len) == SUCCESS) {
6558		smart_str_appendl(querystr, token, len);
6559	} else {
6560		escaped = PGSQLescapeIdentifier(pg_link, token, len);
6561		smart_str_appends(querystr, escaped);
6562		PGSQLfree(escaped);
6563	}
6564	if (tmp && *tmp) {
6565		len = strlen(tmp);
6566		/* "schema"."table" format */
6567		if (_php_pgsql_detect_identifier_escape(tmp, len) == SUCCESS) {
6568			smart_str_appendc(querystr, '.');
6569			smart_str_appendl(querystr, tmp, len);
6570		} else {
6571			escaped = PGSQLescapeIdentifier(pg_link, tmp, len);
6572			smart_str_appendc(querystr, '.');
6573			smart_str_appends(querystr, escaped);
6574			PGSQLfree(escaped);
6575		}
6576	}
6577	efree(table_copy);
6578}
6579/* }}} */
6580
6581/* {{{ php_pgsql_insert
6582 */
6583PHP_PGSQL_API int php_pgsql_insert(PGconn *pg_link, const char *table, zval *var_array, zend_ulong opt, zend_string **sql)
6584{
6585	zval *val, converted;
6586	char buf[256];
6587	char *tmp;
6588	smart_str querystr = {0};
6589	int ret = FAILURE;
6590	zend_string *fld;
6591
6592	assert(pg_link != NULL);
6593	assert(table != NULL);
6594	assert(Z_TYPE_P(var_array) == IS_ARRAY);
6595
6596	ZVAL_UNDEF(&converted);
6597	if (zend_hash_num_elements(Z_ARRVAL_P(var_array)) == 0) {
6598		smart_str_appends(&querystr, "INSERT INTO ");
6599		build_tablename(&querystr, pg_link, table);
6600		smart_str_appends(&querystr, " DEFAULT VALUES");
6601
6602		goto no_values;
6603	}
6604
6605	/* convert input array if needed */
6606	if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) {
6607		array_init(&converted);
6608		if (php_pgsql_convert(pg_link, table, var_array, &converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
6609			goto cleanup;
6610		}
6611		var_array = &converted;
6612	}
6613
6614	smart_str_appends(&querystr, "INSERT INTO ");
6615	build_tablename(&querystr, pg_link, table);
6616	smart_str_appends(&querystr, " (");
6617
6618	ZEND_HASH_FOREACH_STR_KEY(Z_ARRVAL_P(var_array), fld) {
6619		if (fld == NULL) {
6620			php_error_docref(NULL, E_NOTICE, "Expects associative array for values to be inserted");
6621			goto cleanup;
6622		}
6623		if (opt & PGSQL_DML_ESCAPE) {
6624			tmp = PGSQLescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1);
6625			smart_str_appends(&querystr, tmp);
6626			PGSQLfree(tmp);
6627		} else {
6628			smart_str_appendl(&querystr, ZSTR_VAL(fld), ZSTR_LEN(fld));
6629		}
6630		smart_str_appendc(&querystr, ',');
6631	} ZEND_HASH_FOREACH_END();
6632	ZSTR_LEN(querystr.s)--;
6633	smart_str_appends(&querystr, ") VALUES (");
6634
6635	/* make values string */
6636	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(var_array), val) {
6637		/* we can avoid the key_type check here, because we tested it in the other loop */
6638		switch (Z_TYPE_P(val)) {
6639			case IS_STRING:
6640				if (opt & PGSQL_DML_ESCAPE) {
6641					size_t new_len;
6642					char *tmp;
6643					tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1);
6644					new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL);
6645					smart_str_appendc(&querystr, '\'');
6646					smart_str_appendl(&querystr, tmp, new_len);
6647					smart_str_appendc(&querystr, '\'');
6648					efree(tmp);
6649				} else {
6650					smart_str_appendl(&querystr, Z_STRVAL_P(val), Z_STRLEN_P(val));
6651				}
6652				break;
6653			case IS_LONG:
6654				smart_str_append_long(&querystr, Z_LVAL_P(val));
6655				break;
6656			case IS_DOUBLE:
6657				smart_str_appendl(&querystr, buf, snprintf(buf, sizeof(buf), "%F", Z_DVAL_P(val)));
6658				break;
6659			case IS_NULL:
6660				smart_str_appendl(&querystr, "NULL", sizeof("NULL")-1);
6661				break;
6662			default:
6663				php_error_docref(NULL, E_WARNING, "Expects scaler values. type = %d", Z_TYPE_P(val));
6664				goto cleanup;
6665				break;
6666		}
6667		smart_str_appendc(&querystr, ',');
6668	} ZEND_HASH_FOREACH_END();
6669	/* Remove the trailing "," */
6670	ZSTR_LEN(querystr.s)--;
6671	smart_str_appends(&querystr, ");");
6672
6673no_values:
6674
6675	smart_str_0(&querystr);
6676
6677	if ((opt & (PGSQL_DML_EXEC|PGSQL_DML_ASYNC)) &&
6678		do_exec(&querystr, PGRES_COMMAND_OK, pg_link, (opt & PGSQL_CONV_OPTS)) == 0) {
6679		ret = SUCCESS;
6680	}
6681	else if (opt & PGSQL_DML_STRING) {
6682		ret = SUCCESS;
6683	}
6684
6685cleanup:
6686	zval_ptr_dtor(&converted);
6687	if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
6688		*sql = querystr.s;
6689	}
6690	else {
6691		smart_str_free(&querystr);
6692	}
6693	return ret;
6694}
6695/* }}} */
6696
6697/* {{{ proto mixed pg_insert(resource db, string table, array values[, int options])
6698   Insert values (filed=>value) to table */
6699PHP_FUNCTION(pg_insert)
6700{
6701	zval *pgsql_link, *values;
6702	char *table;
6703	size_t table_len;
6704	zend_ulong option = PGSQL_DML_EXEC, return_sql;
6705	PGconn *pg_link;
6706	PGresult *pg_result;
6707	ExecStatusType status;
6708	pgsql_result_handle *pgsql_handle;
6709	zend_string *sql = NULL;
6710	int argc = ZEND_NUM_ARGS();
6711
6712	if (zend_parse_parameters(argc, "rsa|l",
6713							  &pgsql_link, &table, &table_len, &values, &option) == FAILURE) {
6714		return;
6715	}
6716	if (option & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
6717		php_error_docref(NULL, E_WARNING, "Invalid option is specified");
6718		RETURN_FALSE;
6719	}
6720
6721	if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
6722		RETURN_FALSE;
6723	}
6724
6725	if (php_pgsql_flush_query(pg_link)) {
6726		php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
6727	}
6728	return_sql = option & PGSQL_DML_STRING;
6729	if (option & PGSQL_DML_EXEC) {
6730		/* return resource when executed */
6731		option = option & ~PGSQL_DML_EXEC;
6732		if (php_pgsql_insert(pg_link, table, values, option|PGSQL_DML_STRING, &sql) == FAILURE) {
6733			RETURN_FALSE;
6734		}
6735		pg_result = PQexec(pg_link, ZSTR_VAL(sql));
6736		if ((PGG(auto_reset_persistent) & 2) && PQstatus(pg_link) != CONNECTION_OK) {
6737			PQclear(pg_result);
6738			PQreset(pg_link);
6739			pg_result = PQexec(pg_link, ZSTR_VAL(sql));
6740		}
6741		efree(sql);
6742
6743		if (pg_result) {
6744			status = PQresultStatus(pg_result);
6745		} else {
6746			status = (ExecStatusType) PQstatus(pg_link);
6747		}
6748
6749		switch (status) {
6750			case PGRES_EMPTY_QUERY:
6751			case PGRES_BAD_RESPONSE:
6752			case PGRES_NONFATAL_ERROR:
6753			case PGRES_FATAL_ERROR:
6754				PHP_PQ_ERROR("Query failed: %s", pg_link);
6755				PQclear(pg_result);
6756				RETURN_FALSE;
6757				break;
6758			case PGRES_COMMAND_OK: /* successful command that did not return rows */
6759			default:
6760				if (pg_result) {
6761					pgsql_handle = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
6762					pgsql_handle->conn = pg_link;
6763					pgsql_handle->result = pg_result;
6764					pgsql_handle->row = 0;
6765					RETURN_RES(zend_register_resource(pgsql_handle, le_result));
6766				} else {
6767					PQclear(pg_result);
6768					RETURN_FALSE;
6769				}
6770			break;
6771		}
6772	} else if (php_pgsql_insert(pg_link, table, values, option, &sql) == FAILURE) {
6773		RETURN_FALSE;
6774	}
6775	if (return_sql) {
6776		RETURN_STR(sql);
6777		return;
6778	}
6779	RETURN_TRUE;
6780}
6781/* }}} */
6782
6783static 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) /* {{{ */
6784{
6785	char *tmp;
6786	char buf[256];
6787	zend_string *fld;
6788	zval *val;
6789
6790	ZEND_HASH_FOREACH_STR_KEY_VAL(ht, fld, val) {
6791		if (fld == NULL) {
6792			php_error_docref(NULL, E_NOTICE, "Expects associative array for values to be inserted");
6793			return -1;
6794		}
6795		if (opt & PGSQL_DML_ESCAPE) {
6796			tmp = PGSQLescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1);
6797			smart_str_appends(querystr, tmp);
6798			PGSQLfree(tmp);
6799		} else {
6800			smart_str_appendl(querystr, ZSTR_VAL(fld), ZSTR_LEN(fld));
6801		}
6802		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")))) {
6803			smart_str_appends(querystr, " IS ");
6804		} else {
6805			smart_str_appendc(querystr, '=');
6806		}
6807
6808		switch (Z_TYPE_P(val)) {
6809			case IS_STRING:
6810				if (opt & PGSQL_DML_ESCAPE) {
6811					size_t new_len;
6812					tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1);
6813					new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL);
6814					smart_str_appendc(querystr, '\'');
6815					smart_str_appendl(querystr, tmp, new_len);
6816					smart_str_appendc(querystr, '\'');
6817					efree(tmp);
6818				} else {
6819					smart_str_appendl(querystr, Z_STRVAL_P(val), Z_STRLEN_P(val));
6820				}
6821				break;
6822			case IS_LONG:
6823				smart_str_append_long(querystr, Z_LVAL_P(val));
6824				break;
6825			case IS_DOUBLE:
6826				smart_str_appendl(querystr, buf, MIN(snprintf(buf, sizeof(buf), "%F", Z_DVAL_P(val)), sizeof(buf)-1));
6827				break;
6828			case IS_NULL:
6829				smart_str_appendl(querystr, "NULL", sizeof("NULL")-1);
6830				break;
6831			default:
6832				php_error_docref(NULL, E_WARNING, "Expects scaler values. type=%d", Z_TYPE_P(val));
6833				return -1;
6834		}
6835		smart_str_appendl(querystr, pad, pad_len);
6836	} ZEND_HASH_FOREACH_END();
6837	if (querystr->s) {
6838		ZSTR_LEN(querystr->s) -= pad_len;
6839	}
6840
6841	return 0;
6842}
6843/* }}} */
6844
6845/* {{{ php_pgsql_update
6846 */
6847PHP_PGSQL_API int php_pgsql_update(PGconn *pg_link, const char *table, zval *var_array, zval *ids_array, zend_ulong opt, zend_string **sql)
6848{
6849	zval var_converted, ids_converted;
6850	smart_str querystr = {0};
6851	int ret = FAILURE;
6852
6853	assert(pg_link != NULL);
6854	assert(table != NULL);
6855	assert(Z_TYPE_P(var_array) == IS_ARRAY);
6856	assert(Z_TYPE_P(ids_array) == IS_ARRAY);
6857	assert(!(opt & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)));
6858
6859	if (zend_hash_num_elements(Z_ARRVAL_P(var_array)) == 0
6860			|| zend_hash_num_elements(Z_ARRVAL_P(ids_array)) == 0) {
6861		return FAILURE;
6862	}
6863
6864	ZVAL_UNDEF(&var_converted);
6865	ZVAL_UNDEF(&ids_converted);
6866	if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) {
6867		array_init(&var_converted);
6868		if (php_pgsql_convert(pg_link, table, var_array, &var_converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
6869			goto cleanup;
6870		}
6871		var_array = &var_converted;
6872		array_init(&ids_converted);
6873		if (php_pgsql_convert(pg_link, table, ids_array, &ids_converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
6874			goto cleanup;
6875		}
6876		ids_array = &ids_converted;
6877	}
6878
6879	smart_str_appends(&querystr, "UPDATE ");
6880	build_tablename(&querystr, pg_link, table);
6881	smart_str_appends(&querystr, " SET ");
6882
6883	if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(var_array), 0, ",", 1, opt))
6884		goto cleanup;
6885
6886	smart_str_appends(&querystr, " WHERE ");
6887
6888	if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt))
6889		goto cleanup;
6890
6891	smart_str_appendc(&querystr, ';');
6892	smart_str_0(&querystr);
6893
6894	if ((opt & PGSQL_DML_EXEC) && do_exec(&querystr, PGRES_COMMAND_OK, pg_link, opt) == 0) {
6895		ret = SUCCESS;
6896	} else if (opt & PGSQL_DML_STRING) {
6897		ret = SUCCESS;
6898	}
6899
6900cleanup:
6901	zval_ptr_dtor(&var_converted);
6902	zval_ptr_dtor(&ids_converted);
6903	if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
6904		*sql = querystr.s;
6905	}
6906	else {
6907		smart_str_free(&querystr);
6908	}
6909	return ret;
6910}
6911/* }}} */
6912
6913/* {{{ proto mixed pg_update(resource db, string table, array fields, array ids[, int options])
6914   Update table using values (field=>value) and ids (id=>value) */
6915PHP_FUNCTION(pg_update)
6916{
6917	zval *pgsql_link, *values, *ids;
6918	char *table;
6919	size_t table_len;
6920	zend_ulong option =  PGSQL_DML_EXEC;
6921	PGconn *pg_link;
6922	zend_string *sql = NULL;
6923	int argc = ZEND_NUM_ARGS();
6924
6925	if (zend_parse_parameters(argc, "rsaa|l",
6926							  &pgsql_link, &table, &table_len, &values, &ids, &option) == FAILURE) {
6927		return;
6928	}
6929	if (option & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
6930		php_error_docref(NULL, E_WARNING, "Invalid option is specified");
6931		RETURN_FALSE;
6932	}
6933
6934	if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
6935		RETURN_FALSE;
6936	}
6937
6938	if (php_pgsql_flush_query(pg_link)) {
6939		php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
6940	}
6941	if (php_pgsql_update(pg_link, table, values, ids, option, &sql) == FAILURE) {
6942		RETURN_FALSE;
6943	}
6944	if (option & PGSQL_DML_STRING) {
6945		RETURN_STR(sql);
6946	}
6947	RETURN_TRUE;
6948}
6949/* }}} */
6950
6951/* {{{ php_pgsql_delete
6952 */
6953PHP_PGSQL_API int php_pgsql_delete(PGconn *pg_link, const char *table, zval *ids_array, zend_ulong opt, zend_string **sql)
6954{
6955	zval ids_converted;
6956	smart_str querystr = {0};
6957	int ret = FAILURE;
6958
6959	assert(pg_link != NULL);
6960	assert(table != NULL);
6961	assert(Z_TYPE_P(ids_array) == IS_ARRAY);
6962	assert(!(opt & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)));
6963
6964	if (zend_hash_num_elements(Z_ARRVAL_P(ids_array)) == 0) {
6965		return FAILURE;
6966	}
6967
6968	ZVAL_UNDEF(&ids_converted);
6969	if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) {
6970		array_init(&ids_converted);
6971		if (php_pgsql_convert(pg_link, table, ids_array, &ids_converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
6972			goto cleanup;
6973		}
6974		ids_array = &ids_converted;
6975	}
6976
6977	smart_str_appends(&querystr, "DELETE FROM ");
6978	build_tablename(&querystr, pg_link, table);
6979	smart_str_appends(&querystr, " WHERE ");
6980
6981	if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt))
6982		goto cleanup;
6983
6984	smart_str_appendc(&querystr, ';');
6985	smart_str_0(&querystr);
6986
6987	if ((opt & PGSQL_DML_EXEC) && do_exec(&querystr, PGRES_COMMAND_OK, pg_link, opt) == 0) {
6988		ret = SUCCESS;
6989	} else if (opt & PGSQL_DML_STRING) {
6990		ret = SUCCESS;
6991	}
6992
6993cleanup:
6994	zval_ptr_dtor(&ids_converted);
6995	if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
6996		*sql = querystr.s;
6997	}
6998	else {
6999		smart_str_free(&querystr);
7000	}
7001	return ret;
7002}
7003/* }}} */
7004
7005/* {{{ proto mixed pg_delete(resource db, string table, array ids[, int options])
7006   Delete records has ids (id=>value) */
7007PHP_FUNCTION(pg_delete)
7008{
7009	zval *pgsql_link, *ids;
7010	char *table;
7011	size_t table_len;
7012	zend_ulong option = PGSQL_DML_EXEC;
7013	PGconn *pg_link;
7014	zend_string *sql;
7015	int argc = ZEND_NUM_ARGS();
7016
7017	if (zend_parse_parameters(argc, "rsa|l",
7018							  &pgsql_link, &table, &table_len, &ids, &option) == FAILURE) {
7019		return;
7020	}
7021	if (option & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
7022		php_error_docref(NULL, E_WARNING, "Invalid option is specified");
7023		RETURN_FALSE;
7024	}
7025
7026	if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
7027		RETURN_FALSE;
7028	}
7029
7030	if (php_pgsql_flush_query(pg_link)) {
7031		php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
7032	}
7033	if (php_pgsql_delete(pg_link, table, ids, option, &sql) == FAILURE) {
7034		RETURN_FALSE;
7035	}
7036	if (option & PGSQL_DML_STRING) {
7037		RETURN_STR(sql);
7038	}
7039	RETURN_TRUE;
7040}
7041/* }}} */
7042
7043/* {{{ php_pgsql_result2array
7044 */
7045PHP_PGSQL_API int php_pgsql_result2array(PGresult *pg_result, zval *ret_array, long result_type)
7046{
7047	zval row;
7048	char *field_name;
7049	size_t num_fields;
7050	int pg_numrows, pg_row;
7051	uint i;
7052	assert(Z_TYPE_P(ret_array) == IS_ARRAY);
7053
7054	if ((pg_numrows = PQntuples(pg_result)) <= 0) {
7055		return FAILURE;
7056	}
7057	for (pg_row = 0; pg_row < pg_numrows; pg_row++) {
7058		array_init(&row);
7059		for (i = 0, num_fields = PQnfields(pg_result); i < num_fields; i++) {
7060			field_name = PQfname(pg_result, i);
7061			if (PQgetisnull(pg_result, pg_row, i)) {
7062				if (result_type & PGSQL_ASSOC) {
7063					add_assoc_null(&row, field_name);
7064				}
7065				if (result_type & PGSQL_NUM) {
7066					add_next_index_null(&row);
7067				}
7068			} else {
7069				char *element = PQgetvalue(pg_result, pg_row, i);
7070				if (element) {
7071					const size_t element_len = strlen(element);
7072					if (result_type & PGSQL_ASSOC) {
7073						add_assoc_stringl(&row, field_name, element, element_len);
7074					}
7075					if (result_type & PGSQL_NUM) {
7076						add_next_index_stringl(&row, element, element_len);
7077					}
7078				}
7079			}
7080		}
7081		add_index_zval(ret_array, pg_row, &row);
7082	}
7083	return SUCCESS;
7084}
7085/* }}} */
7086
7087/* {{{ php_pgsql_select
7088 */
7089 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)
7090{
7091	zval ids_converted;
7092	smart_str querystr = {0};
7093	int ret = FAILURE;
7094	PGresult *pg_result;
7095
7096	assert(pg_link != NULL);
7097	assert(table != NULL);
7098	assert(Z_TYPE_P(ids_array) == IS_ARRAY);
7099	assert(Z_TYPE_P(ret_array) == IS_ARRAY);
7100	assert(!(opt & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)));
7101
7102	if (zend_hash_num_elements(Z_ARRVAL_P(ids_array)) == 0) {
7103		return FAILURE;
7104	}
7105
7106	ZVAL_UNDEF(&ids_converted);
7107	if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) {
7108		array_init(&ids_converted);
7109		if (php_pgsql_convert(pg_link, table, ids_array, &ids_converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
7110			goto cleanup;
7111		}
7112		ids_array = &ids_converted;
7113	}
7114
7115	smart_str_appends(&querystr, "SELECT * FROM ");
7116	build_tablename(&querystr, pg_link, table);
7117	smart_str_appends(&querystr, " WHERE ");
7118
7119	if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt))
7120		goto cleanup;
7121
7122	smart_str_appendc(&querystr, ';');
7123	smart_str_0(&querystr);
7124
7125	pg_result = PQexec(pg_link, ZSTR_VAL(querystr.s));
7126	if (PQresultStatus(pg_result) == PGRES_TUPLES_OK) {
7127		ret = php_pgsql_result2array(pg_result, ret_array, result_type);
7128	} else {
7129		php_error_docref(NULL, E_NOTICE, "Failed to execute '%s'", ZSTR_VAL(querystr.s));
7130	}
7131	PQclear(pg_result);
7132
7133cleanup:
7134	zval_ptr_dtor(&ids_converted);
7135	if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
7136		*sql = querystr.s;
7137	}
7138	else {
7139		smart_str_free(&querystr);
7140	}
7141	return ret;
7142}
7143/* }}} */
7144
7145/* {{{ proto mixed pg_select(resource db, string table, array ids[, int options [, int result_type])
7146   Select records that has ids (id=>value) */
7147PHP_FUNCTION(pg_select)
7148{
7149	zval *pgsql_link, *ids;
7150	char *table;
7151	size_t table_len;
7152	zend_ulong option = PGSQL_DML_EXEC;
7153	long result_type = PGSQL_ASSOC;
7154	PGconn *pg_link;
7155	zend_string *sql = NULL;
7156	int argc = ZEND_NUM_ARGS();
7157
7158	if (zend_parse_parameters(argc, "rsa|l",
7159							  &pgsql_link, &table, &table_len, &ids, &option, &result_type) == FAILURE) {
7160		return;
7161	}
7162	if (option & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
7163		php_error_docref(NULL, E_WARNING, "Invalid option is specified");
7164		RETURN_FALSE;
7165	}
7166	if (!(result_type & PGSQL_BOTH)) {
7167		php_error_docref(NULL, E_WARNING, "Invalid result type");
7168		RETURN_FALSE;
7169	}
7170
7171	if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
7172		RETURN_FALSE;
7173	}
7174
7175	if (php_pgsql_flush_query(pg_link)) {
7176		php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
7177	}
7178	array_init(return_value);
7179	if (php_pgsql_select(pg_link, table, ids, return_value, option, result_type, &sql) == FAILURE) {
7180		zval_ptr_dtor(return_value);
7181		RETURN_FALSE;
7182	}
7183	if (option & PGSQL_DML_STRING) {
7184		zval_ptr_dtor(return_value);
7185		RETURN_STR(sql);
7186	}
7187	return;
7188}
7189/* }}} */
7190
7191#endif
7192
7193/*
7194 * Local variables:
7195 * tab-width: 4
7196 * c-basic-offset: 4
7197 * End:
7198 * vim600: sw=4 ts=4 fdm=marker
7199 * vim<600: sw=4 ts=4
7200 */
7201