1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 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@php.net>                                 |
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 #include <stdlib.h>
24 
25 #define PHP_PGSQL_PRIVATE 1
26 
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30 
31 #define SMART_STR_PREALLOC 512
32 
33 #include "php.h"
34 #include "php_ini.h"
35 #include "ext/standard/php_standard.h"
36 #include "zend_smart_str.h"
37 #include "ext/pcre/php_pcre.h"
38 #ifdef PHP_WIN32
39 # include "win32/time.h"
40 #endif
41 #include "php_pgsql.h"
42 #include "php_globals.h"
43 #include "zend_exceptions.h"
44 
45 #if HAVE_PGSQL
46 
47 #ifndef InvalidOid
48 #define InvalidOid ((Oid) 0)
49 #endif
50 
51 #define PGSQL_ASSOC           1<<0
52 #define PGSQL_NUM             1<<1
53 #define PGSQL_BOTH            (PGSQL_ASSOC|PGSQL_NUM)
54 
55 #define PGSQL_NOTICE_LAST     1  /* Get the last notice */
56 #define PGSQL_NOTICE_ALL      2  /* Get all notices */
57 #define PGSQL_NOTICE_CLEAR    3  /* Remove notices */
58 
59 #define PGSQL_STATUS_LONG     1
60 #define PGSQL_STATUS_STRING   2
61 
62 #define PGSQL_MAX_LENGTH_OF_LONG   30
63 #define PGSQL_MAX_LENGTH_OF_DOUBLE 60
64 
65 #if ZEND_LONG_MAX < UINT_MAX
66 #define PGSQL_RETURN_OID(oid) do { \
67 	if (oid > ZEND_LONG_MAX) { \
68 		smart_str s = {0}; \
69 		smart_str_append_unsigned(&s, oid); \
70 		smart_str_0(&s); \
71 		RETURN_NEW_STR(s.s); \
72 	} \
73 	RETURN_LONG((zend_long)oid); \
74 } while(0)
75 #else
76 #define PGSQL_RETURN_OID(oid) RETURN_LONG((zend_long)oid)
77 #endif
78 
79 #if HAVE_PQSETNONBLOCKING
80 #define PQ_SETNONBLOCKING(pg_link, flag) PQsetnonblocking(pg_link, flag)
81 #else
82 #define PQ_SETNONBLOCKING(pg_link, flag) 0
83 #endif
84 
85 #define CHECK_DEFAULT_LINK(x) if ((x) == NULL) { php_error_docref(NULL, E_WARNING, "No PostgreSQL link opened yet"); RETURN_FALSE; }
86 #define FETCH_DEFAULT_LINK()  PGG(default_link)
87 
88 #ifndef HAVE_PQFREEMEM
89 #define PQfreemem free
90 #endif
91 
92 ZEND_DECLARE_MODULE_GLOBALS(pgsql)
93 static PHP_GINIT_FUNCTION(pgsql);
94 
95 /* {{{ arginfo */
96 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connect, 0, 0, 1)
97 	ZEND_ARG_INFO(0, connection_string)
98 	ZEND_ARG_INFO(0, connect_type)
99 	ZEND_ARG_INFO(0, host)
100 	ZEND_ARG_INFO(0, port)
101 	ZEND_ARG_INFO(0, options)
102 	ZEND_ARG_INFO(0, tty)
103 	ZEND_ARG_INFO(0, database)
104 ZEND_END_ARG_INFO()
105 
106 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_pconnect, 0, 0, 1)
107 	ZEND_ARG_INFO(0, connection_string)
108 	ZEND_ARG_INFO(0, host)
109 	ZEND_ARG_INFO(0, port)
110 	ZEND_ARG_INFO(0, options)
111 	ZEND_ARG_INFO(0, tty)
112 	ZEND_ARG_INFO(0, database)
113 ZEND_END_ARG_INFO()
114 
115 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connect_poll, 0, 0, 0)
116 	ZEND_ARG_INFO(0, connection)
117 ZEND_END_ARG_INFO()
118 
119 #if HAVE_PQPARAMETERSTATUS
120 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_parameter_status, 0, 0, 1)
121 	ZEND_ARG_INFO(0, connection)
122 	ZEND_ARG_INFO(0, param_name)
123 ZEND_END_ARG_INFO()
124 #endif
125 
126 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_close, 0, 0, 0)
127 	ZEND_ARG_INFO(0, connection)
128 ZEND_END_ARG_INFO()
129 
130 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_dbname, 0, 0, 0)
131 	ZEND_ARG_INFO(0, connection)
132 ZEND_END_ARG_INFO()
133 
134 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_last_error, 0, 0, 0)
135 	ZEND_ARG_INFO(0, connection)
136 ZEND_END_ARG_INFO()
137 
138 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_options, 0, 0, 0)
139 	ZEND_ARG_INFO(0, connection)
140 ZEND_END_ARG_INFO()
141 
142 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_port, 0, 0, 0)
143 	ZEND_ARG_INFO(0, connection)
144 ZEND_END_ARG_INFO()
145 
146 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_tty, 0, 0, 0)
147 	ZEND_ARG_INFO(0, connection)
148 ZEND_END_ARG_INFO()
149 
150 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_host, 0, 0, 0)
151 	ZEND_ARG_INFO(0, connection)
152 ZEND_END_ARG_INFO()
153 
154 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_version, 0, 0, 0)
155 	ZEND_ARG_INFO(0, connection)
156 ZEND_END_ARG_INFO()
157 
158 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_ping, 0, 0, 0)
159 	ZEND_ARG_INFO(0, connection)
160 ZEND_END_ARG_INFO()
161 
162 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_query, 0, 0, 0)
163 	ZEND_ARG_INFO(0, connection)
164 	ZEND_ARG_INFO(0, query)
165 ZEND_END_ARG_INFO()
166 
167 #if HAVE_PQEXECPARAMS
168 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_query_params, 0, 0, 0)
169 	ZEND_ARG_INFO(0, connection)
170 	ZEND_ARG_INFO(0, query)
171 	ZEND_ARG_INFO(0, params)
172 ZEND_END_ARG_INFO()
173 #endif
174 
175 #if HAVE_PQPREPARE
176 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_prepare, 0, 0, 0)
177 	ZEND_ARG_INFO(0, connection)
178 	ZEND_ARG_INFO(0, stmtname)
179 	ZEND_ARG_INFO(0, query)
180 ZEND_END_ARG_INFO()
181 #endif
182 
183 #if HAVE_PQEXECPREPARED
184 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_execute, 0, 0, 0)
185 	ZEND_ARG_INFO(0, connection)
186 	ZEND_ARG_INFO(0, stmtname)
187 	ZEND_ARG_INFO(0, params)
188 ZEND_END_ARG_INFO()
189 #endif
190 
191 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_num_rows, 0, 0, 1)
192 	ZEND_ARG_INFO(0, result)
193 ZEND_END_ARG_INFO()
194 
195 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_num_fields, 0, 0, 1)
196 	ZEND_ARG_INFO(0, result)
197 ZEND_END_ARG_INFO()
198 
199 #if HAVE_PQCMDTUPLES
200 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_affected_rows, 0, 0, 1)
201 	ZEND_ARG_INFO(0, result)
202 ZEND_END_ARG_INFO()
203 #endif
204 
205 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_last_notice, 0, 0, 1)
206 	ZEND_ARG_INFO(0, connection)
207 	ZEND_ARG_INFO(0, option)
208 ZEND_END_ARG_INFO()
209 
210 #ifdef HAVE_PQFTABLE
211 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_table, 0, 0, 2)
212 	ZEND_ARG_INFO(0, result)
213 	ZEND_ARG_INFO(0, field_number)
214 	ZEND_ARG_INFO(0, oid_only)
215 ZEND_END_ARG_INFO()
216 #endif
217 
218 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_name, 0, 0, 2)
219 	ZEND_ARG_INFO(0, result)
220 	ZEND_ARG_INFO(0, field_number)
221 ZEND_END_ARG_INFO()
222 
223 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_size, 0, 0, 2)
224 	ZEND_ARG_INFO(0, result)
225 	ZEND_ARG_INFO(0, field_number)
226 ZEND_END_ARG_INFO()
227 
228 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_type, 0, 0, 2)
229 	ZEND_ARG_INFO(0, result)
230 	ZEND_ARG_INFO(0, field_number)
231 ZEND_END_ARG_INFO()
232 
233 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_type_oid, 0, 0, 2)
234 	ZEND_ARG_INFO(0, result)
235 	ZEND_ARG_INFO(0, field_number)
236 ZEND_END_ARG_INFO()
237 
238 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_num, 0, 0, 2)
239 	ZEND_ARG_INFO(0, result)
240 	ZEND_ARG_INFO(0, field_name)
241 ZEND_END_ARG_INFO()
242 
243 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_result, 0, 0, 1)
244 	ZEND_ARG_INFO(0, result)
245 	ZEND_ARG_INFO(0, row_number)
246 	ZEND_ARG_INFO(0, field_name)
247 ZEND_END_ARG_INFO()
248 
249 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_row, 0, 0, 1)
250 	ZEND_ARG_INFO(0, result)
251 	ZEND_ARG_INFO(0, row)
252 	ZEND_ARG_INFO(0, result_type)
253 ZEND_END_ARG_INFO()
254 
255 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_assoc, 0, 0, 1)
256 	ZEND_ARG_INFO(0, result)
257 	ZEND_ARG_INFO(0, row)
258 ZEND_END_ARG_INFO()
259 
260 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_array, 0, 0, 1)
261 	ZEND_ARG_INFO(0, result)
262 	ZEND_ARG_INFO(0, row)
263 	ZEND_ARG_INFO(0, result_type)
264 ZEND_END_ARG_INFO()
265 
266 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_object, 0, 0, 1)
267 	ZEND_ARG_INFO(0, result)
268 	ZEND_ARG_INFO(0, row)
269 	ZEND_ARG_INFO(0, class_name)
270 	ZEND_ARG_INFO(0, l)
271 	ZEND_ARG_INFO(0, ctor_params)
272 ZEND_END_ARG_INFO()
273 
274 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_all, 0, 0, 1)
275 	ZEND_ARG_INFO(0, result)
276 	ZEND_ARG_INFO(0, result_type)
277 ZEND_END_ARG_INFO()
278 
279 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_all_columns, 0, 0, 1)
280 	ZEND_ARG_INFO(0, result)
281 	ZEND_ARG_INFO(0, column_number)
282 ZEND_END_ARG_INFO()
283 
284 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_result_seek, 0, 0, 2)
285 	ZEND_ARG_INFO(0, result)
286 	ZEND_ARG_INFO(0, offset)
287 ZEND_END_ARG_INFO()
288 
289 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_prtlen, 0, 0, 1)
290 	ZEND_ARG_INFO(0, result)
291 	ZEND_ARG_INFO(0, row)
292 	ZEND_ARG_INFO(0, field_name_or_number)
293 ZEND_END_ARG_INFO()
294 
295 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_is_null, 0, 0, 1)
296 	ZEND_ARG_INFO(0, result)
297 	ZEND_ARG_INFO(0, row)
298 	ZEND_ARG_INFO(0, field_name_or_number)
299 ZEND_END_ARG_INFO()
300 
301 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_free_result, 0, 0, 1)
302 	ZEND_ARG_INFO(0, result)
303 ZEND_END_ARG_INFO()
304 
305 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_last_oid, 0, 0, 1)
306 	ZEND_ARG_INFO(0, result)
307 ZEND_END_ARG_INFO()
308 
309 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_trace, 0, 0, 1)
310 	ZEND_ARG_INFO(0, filename)
311 	ZEND_ARG_INFO(0, mode)
312 	ZEND_ARG_INFO(0, connection)
313 ZEND_END_ARG_INFO()
314 
315 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_untrace, 0, 0, 0)
316 	ZEND_ARG_INFO(0, connection)
317 ZEND_END_ARG_INFO()
318 
319 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_create, 0, 0, 0)
320 	ZEND_ARG_INFO(0, connection)
321 	ZEND_ARG_INFO(0, large_object_id)
322 ZEND_END_ARG_INFO()
323 
324 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_unlink, 0, 0, 0)
325 	ZEND_ARG_INFO(0, connection)
326 	ZEND_ARG_INFO(0, large_object_oid)
327 ZEND_END_ARG_INFO()
328 
329 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_open, 0, 0, 0)
330 	ZEND_ARG_INFO(0, connection)
331 	ZEND_ARG_INFO(0, large_object_oid)
332 	ZEND_ARG_INFO(0, mode)
333 ZEND_END_ARG_INFO()
334 
335 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_close, 0, 0, 1)
336 	ZEND_ARG_INFO(0, large_object)
337 ZEND_END_ARG_INFO()
338 
339 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_read, 0, 0, 1)
340 	ZEND_ARG_INFO(0, large_object)
341 	ZEND_ARG_INFO(0, len)
342 ZEND_END_ARG_INFO()
343 
344 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_write, 0, 0, 2)
345 	ZEND_ARG_INFO(0, large_object)
346 	ZEND_ARG_INFO(0, buf)
347 	ZEND_ARG_INFO(0, len)
348 ZEND_END_ARG_INFO()
349 
350 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_read_all, 0, 0, 1)
351 	ZEND_ARG_INFO(0, large_object)
352 ZEND_END_ARG_INFO()
353 
354 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_import, 0, 0, 0)
355 	ZEND_ARG_INFO(0, connection)
356 	ZEND_ARG_INFO(0, filename)
357 	ZEND_ARG_INFO(0, large_object_oid)
358 ZEND_END_ARG_INFO()
359 
360 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_export, 0, 0, 0)
361 	ZEND_ARG_INFO(0, connection)
362 	ZEND_ARG_INFO(0, objoid)
363 	ZEND_ARG_INFO(0, filename)
364 ZEND_END_ARG_INFO()
365 
366 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_seek, 0, 0, 2)
367 	ZEND_ARG_INFO(0, large_object)
368 	ZEND_ARG_INFO(0, offset)
369 	ZEND_ARG_INFO(0, whence)
370 ZEND_END_ARG_INFO()
371 
372 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_tell, 0, 0, 1)
373 	ZEND_ARG_INFO(0, large_object)
374 ZEND_END_ARG_INFO()
375 
376 #if HAVE_PG_LO_TRUNCATE
377 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_truncate, 0, 0, 1)
378 	ZEND_ARG_INFO(0, large_object)
379 	ZEND_ARG_INFO(0, size)
380 ZEND_END_ARG_INFO()
381 #endif
382 
383 #if HAVE_PQSETERRORVERBOSITY
384 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_set_error_verbosity, 0, 0, 0)
385 	ZEND_ARG_INFO(0, connection)
386 	ZEND_ARG_INFO(0, verbosity)
387 ZEND_END_ARG_INFO()
388 #endif
389 
390 #if HAVE_PQCLIENTENCODING
391 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_set_client_encoding, 0, 0, 0)
392 	ZEND_ARG_INFO(0, connection)
393 	ZEND_ARG_INFO(0, encoding)
394 ZEND_END_ARG_INFO()
395 
396 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_client_encoding, 0, 0, 0)
397 	ZEND_ARG_INFO(0, connection)
398 ZEND_END_ARG_INFO()
399 #endif
400 
401 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_end_copy, 0, 0, 0)
402 	ZEND_ARG_INFO(0, connection)
403 ZEND_END_ARG_INFO()
404 
405 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_put_line, 0, 0, 0)
406 	ZEND_ARG_INFO(0, connection)
407 	ZEND_ARG_INFO(0, query)
408 ZEND_END_ARG_INFO()
409 
410 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_copy_to, 0, 0, 2)
411 	ZEND_ARG_INFO(0, connection)
412 	ZEND_ARG_INFO(0, table_name)
413 	ZEND_ARG_INFO(0, delimiter)
414 	ZEND_ARG_INFO(0, null_as)
415 ZEND_END_ARG_INFO()
416 
417 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_copy_from, 0, 0, 3)
418 	ZEND_ARG_INFO(0, connection)
419 	ZEND_ARG_INFO(0, table_name)
420 	ZEND_ARG_INFO(0, rows)
421 	ZEND_ARG_INFO(0, delimiter)
422 	ZEND_ARG_INFO(0, null_as)
423 ZEND_END_ARG_INFO()
424 
425 #if HAVE_PQESCAPE
426 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape_string, 0, 0, 0)
427 	ZEND_ARG_INFO(0, connection)
428 	ZEND_ARG_INFO(0, data)
429 ZEND_END_ARG_INFO()
430 
431 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape_bytea, 0, 0, 0)
432 	ZEND_ARG_INFO(0, connection)
433 	ZEND_ARG_INFO(0, data)
434 ZEND_END_ARG_INFO()
435 
436 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_unescape_bytea, 0, 0, 1)
437 	ZEND_ARG_INFO(0, data)
438 ZEND_END_ARG_INFO()
439 #endif
440 
441 #if HAVE_PQESCAPE
442 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape_literal, 0, 0, 0)
443 	ZEND_ARG_INFO(0, connection)
444 	ZEND_ARG_INFO(0, data)
445 ZEND_END_ARG_INFO()
446 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape_identifier, 0, 0, 0)
447 	ZEND_ARG_INFO(0, connection)
448 	ZEND_ARG_INFO(0, data)
449 ZEND_END_ARG_INFO()
450 #endif
451 
452 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_result_error, 0, 0, 1)
453 	ZEND_ARG_INFO(0, result)
454 ZEND_END_ARG_INFO()
455 
456 #if HAVE_PQRESULTERRORFIELD
457 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_result_error_field, 0, 0, 2)
458 	ZEND_ARG_INFO(0, result)
459 	ZEND_ARG_INFO(0, fieldcode)
460 ZEND_END_ARG_INFO()
461 #endif
462 
463 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connection_status, 0, 0, 1)
464 	ZEND_ARG_INFO(0, connection)
465 ZEND_END_ARG_INFO()
466 
467 #if HAVE_PGTRANSACTIONSTATUS
468 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_transaction_status, 0, 0, 1)
469 	ZEND_ARG_INFO(0, connection)
470 ZEND_END_ARG_INFO()
471 #endif
472 
473 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connection_reset, 0, 0, 1)
474 	ZEND_ARG_INFO(0, connection)
475 ZEND_END_ARG_INFO()
476 
477 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_cancel_query, 0, 0, 1)
478 	ZEND_ARG_INFO(0, connection)
479 ZEND_END_ARG_INFO()
480 
481 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connection_busy, 0, 0, 1)
482 	ZEND_ARG_INFO(0, connection)
483 ZEND_END_ARG_INFO()
484 
485 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_query, 0, 0, 2)
486 	ZEND_ARG_INFO(0, connection)
487 	ZEND_ARG_INFO(0, query)
488 ZEND_END_ARG_INFO()
489 
490 #if HAVE_PQSENDQUERYPARAMS
491 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_query_params, 0, 0, 3)
492 	ZEND_ARG_INFO(0, connection)
493 	ZEND_ARG_INFO(0, query)
494 	ZEND_ARG_INFO(0, params)
495 ZEND_END_ARG_INFO()
496 #endif
497 
498 #if HAVE_PQSENDPREPARE
499 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_prepare, 0, 0, 3)
500 	ZEND_ARG_INFO(0, connection)
501 	ZEND_ARG_INFO(0, stmtname)
502 	ZEND_ARG_INFO(0, query)
503 ZEND_END_ARG_INFO()
504 #endif
505 
506 #if HAVE_PQSENDQUERYPREPARED
507 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_execute, 0, 0, 3)
508 	ZEND_ARG_INFO(0, connection)
509 	ZEND_ARG_INFO(0, stmtname)
510 	ZEND_ARG_INFO(0, params)
511 ZEND_END_ARG_INFO()
512 #endif
513 
514 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_get_result, 0, 0, 1)
515 	ZEND_ARG_INFO(0, connection)
516 ZEND_END_ARG_INFO()
517 
518 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_result_status, 0, 0, 1)
519 	ZEND_ARG_INFO(0, result)
520 	ZEND_ARG_INFO(0, result_type)
521 ZEND_END_ARG_INFO()
522 
523 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_get_notify, 0, 0, 0)
524 	ZEND_ARG_INFO(0, connection)
525 	ZEND_ARG_INFO(0, e)
526 ZEND_END_ARG_INFO()
527 
528 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_get_pid, 0, 0, 0)
529 	ZEND_ARG_INFO(0, connection)
530 ZEND_END_ARG_INFO()
531 
532 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_socket, 0, 0, 1)
533 	ZEND_ARG_INFO(0, connection)
534 ZEND_END_ARG_INFO()
535 
536 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_consume_input, 0, 0, 1)
537 	ZEND_ARG_INFO(0, connection)
538 ZEND_END_ARG_INFO()
539 
540 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_flush, 0, 0, 1)
541 	ZEND_ARG_INFO(0, connection)
542 ZEND_END_ARG_INFO()
543 
544 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_meta_data, 0, 0, 2)
545 	ZEND_ARG_INFO(0, db)
546 	ZEND_ARG_INFO(0, table)
547 ZEND_END_ARG_INFO()
548 
549 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_convert, 0, 0, 3)
550 	ZEND_ARG_INFO(0, db)
551 	ZEND_ARG_INFO(0, table)
552 	ZEND_ARG_INFO(0, values)
553 	ZEND_ARG_INFO(0, options)
554 ZEND_END_ARG_INFO()
555 
556 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_insert, 0, 0, 3)
557 	ZEND_ARG_INFO(0, db)
558 	ZEND_ARG_INFO(0, table)
559 	ZEND_ARG_INFO(0, values)
560 	ZEND_ARG_INFO(0, options)
561 ZEND_END_ARG_INFO()
562 
563 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_update, 0, 0, 4)
564 	ZEND_ARG_INFO(0, db)
565 	ZEND_ARG_INFO(0, table)
566 	ZEND_ARG_INFO(0, fields)
567 	ZEND_ARG_INFO(0, ids)
568 	ZEND_ARG_INFO(0, options)
569 ZEND_END_ARG_INFO()
570 
571 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_delete, 0, 0, 3)
572 	ZEND_ARG_INFO(0, db)
573 	ZEND_ARG_INFO(0, table)
574 	ZEND_ARG_INFO(0, ids)
575 	ZEND_ARG_INFO(0, options)
576 ZEND_END_ARG_INFO()
577 
578 ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_select, 0, 0, 3)
579 	ZEND_ARG_INFO(0, db)
580 	ZEND_ARG_INFO(0, table)
581 	ZEND_ARG_INFO(0, ids)
582 	ZEND_ARG_INFO(0, options)
583 	ZEND_ARG_INFO(0, result_type)
584 ZEND_END_ARG_INFO()
585 /* }}} */
586 
587 /* {{{ pgsql_functions[]
588  */
589 static const zend_function_entry pgsql_functions[] = {
590 	/* connection functions */
591 	PHP_FE(pg_connect,		arginfo_pg_connect)
592 	PHP_FE(pg_pconnect,		arginfo_pg_pconnect)
593 	PHP_FE(pg_connect_poll,	arginfo_pg_connect_poll)
594 	PHP_FE(pg_close,		arginfo_pg_close)
595 	PHP_FE(pg_connection_status,	arginfo_pg_connection_status)
596 	PHP_FE(pg_connection_busy,		arginfo_pg_connection_busy)
597 	PHP_FE(pg_connection_reset,		arginfo_pg_connection_reset)
598 	PHP_FE(pg_host,			arginfo_pg_host)
599 	PHP_FE(pg_dbname,		arginfo_pg_dbname)
600 	PHP_FE(pg_port,			arginfo_pg_port)
601 	PHP_FE(pg_tty,			arginfo_pg_tty)
602 	PHP_FE(pg_options,		arginfo_pg_options)
603 	PHP_FE(pg_version,		arginfo_pg_version)
604 	PHP_FE(pg_ping,			arginfo_pg_ping)
605 #if HAVE_PQPARAMETERSTATUS
606 	PHP_FE(pg_parameter_status, arginfo_pg_parameter_status)
607 #endif
608 #if HAVE_PGTRANSACTIONSTATUS
609 	PHP_FE(pg_transaction_status, arginfo_pg_transaction_status)
610 #endif
611 	/* query functions */
612 	PHP_FE(pg_query,		arginfo_pg_query)
613 #if HAVE_PQEXECPARAMS
614 	PHP_FE(pg_query_params,		arginfo_pg_query_params)
615 #endif
616 #if HAVE_PQPREPARE
617 	PHP_FE(pg_prepare,		arginfo_pg_prepare)
618 #endif
619 #if HAVE_PQEXECPREPARED
620 	PHP_FE(pg_execute,		arginfo_pg_execute)
621 #endif
622 	PHP_FE(pg_send_query,	arginfo_pg_send_query)
623 #if HAVE_PQSENDQUERYPARAMS
624 	PHP_FE(pg_send_query_params,	arginfo_pg_send_query_params)
625 #endif
626 #if HAVE_PQSENDPREPARE
627 	PHP_FE(pg_send_prepare,	arginfo_pg_send_prepare)
628 #endif
629 #if HAVE_PQSENDQUERYPREPARED
630 	PHP_FE(pg_send_execute,	arginfo_pg_send_execute)
631 #endif
632 	PHP_FE(pg_cancel_query, arginfo_pg_cancel_query)
633 	/* result functions */
634 	PHP_FE(pg_fetch_result,	arginfo_pg_fetch_result)
635 	PHP_FE(pg_fetch_row,	arginfo_pg_fetch_row)
636 	PHP_FE(pg_fetch_assoc,	arginfo_pg_fetch_assoc)
637 	PHP_FE(pg_fetch_array,	arginfo_pg_fetch_array)
638 	PHP_FE(pg_fetch_object,	arginfo_pg_fetch_object)
639 	PHP_FE(pg_fetch_all,	arginfo_pg_fetch_all)
640 	PHP_FE(pg_fetch_all_columns,	arginfo_pg_fetch_all_columns)
641 #if HAVE_PQCMDTUPLES
642 	PHP_FE(pg_affected_rows,arginfo_pg_affected_rows)
643 #endif
644 	PHP_FE(pg_get_result,	arginfo_pg_get_result)
645 	PHP_FE(pg_result_seek,	arginfo_pg_result_seek)
646 	PHP_FE(pg_result_status,arginfo_pg_result_status)
647 	PHP_FE(pg_free_result,	arginfo_pg_free_result)
648 	PHP_FE(pg_last_oid,	    arginfo_pg_last_oid)
649 	PHP_FE(pg_num_rows,		arginfo_pg_num_rows)
650 	PHP_FE(pg_num_fields,	arginfo_pg_num_fields)
651 	PHP_FE(pg_field_name,	arginfo_pg_field_name)
652 	PHP_FE(pg_field_num,	arginfo_pg_field_num)
653 	PHP_FE(pg_field_size,	arginfo_pg_field_size)
654 	PHP_FE(pg_field_type,	arginfo_pg_field_type)
655 	PHP_FE(pg_field_type_oid, arginfo_pg_field_type_oid)
656 	PHP_FE(pg_field_prtlen,	arginfo_pg_field_prtlen)
657 	PHP_FE(pg_field_is_null,arginfo_pg_field_is_null)
658 #ifdef HAVE_PQFTABLE
659 	PHP_FE(pg_field_table,  arginfo_pg_field_table)
660 #endif
661 	/* async message function */
662 	PHP_FE(pg_get_notify,   arginfo_pg_get_notify)
663 	PHP_FE(pg_socket,		arginfo_pg_socket)
664 	PHP_FE(pg_consume_input,arginfo_pg_consume_input)
665 	PHP_FE(pg_flush,		arginfo_pg_flush)
666 	PHP_FE(pg_get_pid,      arginfo_pg_get_pid)
667 	/* error message functions */
668 	PHP_FE(pg_result_error, arginfo_pg_result_error)
669 #if HAVE_PQRESULTERRORFIELD
670 	PHP_FE(pg_result_error_field, arginfo_pg_result_error_field)
671 #endif
672 	PHP_FE(pg_last_error,   arginfo_pg_last_error)
673 	PHP_FE(pg_last_notice,  arginfo_pg_last_notice)
674 	/* copy functions */
675 	PHP_FE(pg_put_line,		arginfo_pg_put_line)
676 	PHP_FE(pg_end_copy,		arginfo_pg_end_copy)
677 	PHP_FE(pg_copy_to,      arginfo_pg_copy_to)
678 	PHP_FE(pg_copy_from,    arginfo_pg_copy_from)
679 	/* debug functions */
680 	PHP_FE(pg_trace,		arginfo_pg_trace)
681 	PHP_FE(pg_untrace,		arginfo_pg_untrace)
682 	/* large object functions */
683 	PHP_FE(pg_lo_create,	arginfo_pg_lo_create)
684 	PHP_FE(pg_lo_unlink,	arginfo_pg_lo_unlink)
685 	PHP_FE(pg_lo_open,		arginfo_pg_lo_open)
686 	PHP_FE(pg_lo_close,		arginfo_pg_lo_close)
687 	PHP_FE(pg_lo_read,		arginfo_pg_lo_read)
688 	PHP_FE(pg_lo_write,		arginfo_pg_lo_write)
689 	PHP_FE(pg_lo_read_all,	arginfo_pg_lo_read_all)
690 	PHP_FE(pg_lo_import,	arginfo_pg_lo_import)
691 	PHP_FE(pg_lo_export,	arginfo_pg_lo_export)
692 	PHP_FE(pg_lo_seek,		arginfo_pg_lo_seek)
693 	PHP_FE(pg_lo_tell,		arginfo_pg_lo_tell)
694 #if HAVE_PG_LO_TRUNCATE
695 	PHP_FE(pg_lo_truncate,	arginfo_pg_lo_truncate)
696 #endif
697 	/* utility functions */
698 #if HAVE_PQESCAPE
699 	PHP_FE(pg_escape_string,	arginfo_pg_escape_string)
700 	PHP_FE(pg_escape_bytea, 	arginfo_pg_escape_bytea)
701 	PHP_FE(pg_unescape_bytea, 	arginfo_pg_unescape_bytea)
702 	PHP_FE(pg_escape_literal,	arginfo_pg_escape_literal)
703 	PHP_FE(pg_escape_identifier,	arginfo_pg_escape_identifier)
704 #endif
705 #if HAVE_PQSETERRORVERBOSITY
706 	PHP_FE(pg_set_error_verbosity,	arginfo_pg_set_error_verbosity)
707 #endif
708 #if HAVE_PQCLIENTENCODING
709 	PHP_FE(pg_client_encoding,		arginfo_pg_client_encoding)
710 	PHP_FE(pg_set_client_encoding,	arginfo_pg_set_client_encoding)
711 #endif
712 	/* misc function */
713 	PHP_FE(pg_meta_data,	arginfo_pg_meta_data)
714 	PHP_FE(pg_convert,      arginfo_pg_convert)
715 	PHP_FE(pg_insert,       arginfo_pg_insert)
716 	PHP_FE(pg_update,       arginfo_pg_update)
717 	PHP_FE(pg_delete,       arginfo_pg_delete)
718 	PHP_FE(pg_select,       arginfo_pg_select)
719 	/* aliases for downwards compatibility */
720 	PHP_FALIAS(pg_exec,          pg_query,          arginfo_pg_query)
721 	PHP_FALIAS(pg_getlastoid,    pg_last_oid,       arginfo_pg_last_oid)
722 #if HAVE_PQCMDTUPLES
723 	PHP_FALIAS(pg_cmdtuples,	 pg_affected_rows,  arginfo_pg_affected_rows)
724 #endif
725 	PHP_FALIAS(pg_errormessage,	 pg_last_error,     arginfo_pg_last_error)
726 	PHP_FALIAS(pg_numrows,		 pg_num_rows,       arginfo_pg_num_rows)
727 	PHP_FALIAS(pg_numfields,	 pg_num_fields,     arginfo_pg_num_fields)
728 	PHP_FALIAS(pg_fieldname,	 pg_field_name,     arginfo_pg_field_name)
729 	PHP_FALIAS(pg_fieldsize,     pg_field_size,     arginfo_pg_field_size)
730 	PHP_FALIAS(pg_fieldtype,	 pg_field_type,     arginfo_pg_field_type)
731 	PHP_FALIAS(pg_fieldnum,	     pg_field_num,      arginfo_pg_field_num)
732 	PHP_FALIAS(pg_fieldprtlen,	 pg_field_prtlen,   arginfo_pg_field_prtlen)
733 	PHP_FALIAS(pg_fieldisnull,	 pg_field_is_null,  arginfo_pg_field_is_null)
734 	PHP_FALIAS(pg_freeresult,    pg_free_result,    arginfo_pg_free_result)
735 	PHP_FALIAS(pg_result,	     pg_fetch_result,   arginfo_pg_get_result)
736 	PHP_FALIAS(pg_loreadall,	 pg_lo_read_all,    arginfo_pg_lo_read_all)
737 	PHP_FALIAS(pg_locreate,	     pg_lo_create,      arginfo_pg_lo_create)
738 	PHP_FALIAS(pg_lounlink,	     pg_lo_unlink,      arginfo_pg_lo_unlink)
739 	PHP_FALIAS(pg_loopen,	     pg_lo_open,        arginfo_pg_lo_open)
740 	PHP_FALIAS(pg_loclose,	     pg_lo_close,       arginfo_pg_lo_close)
741 	PHP_FALIAS(pg_loread,	     pg_lo_read,        arginfo_pg_lo_read)
742 	PHP_FALIAS(pg_lowrite,	     pg_lo_write,       arginfo_pg_lo_write)
743 	PHP_FALIAS(pg_loimport,	     pg_lo_import,      arginfo_pg_lo_import)
744 	PHP_FALIAS(pg_loexport,	     pg_lo_export,      arginfo_pg_lo_export)
745 #if HAVE_PQCLIENTENCODING
746 	PHP_FALIAS(pg_clientencoding,		pg_client_encoding,		arginfo_pg_client_encoding)
747 	PHP_FALIAS(pg_setclientencoding,	pg_set_client_encoding,	arginfo_pg_set_client_encoding)
748 #endif
749 	PHP_FE_END
750 };
751 /* }}} */
752 
753 /* {{{ pgsql_module_entry
754  */
755 zend_module_entry pgsql_module_entry = {
756 	STANDARD_MODULE_HEADER,
757 	"pgsql",
758 	pgsql_functions,
759 	PHP_MINIT(pgsql),
760 	PHP_MSHUTDOWN(pgsql),
761 	PHP_RINIT(pgsql),
762 	PHP_RSHUTDOWN(pgsql),
763 	PHP_MINFO(pgsql),
764 	PHP_PGSQL_VERSION,
765 	PHP_MODULE_GLOBALS(pgsql),
766 	PHP_GINIT(pgsql),
767 	NULL,
768 	NULL,
769 	STANDARD_MODULE_PROPERTIES_EX
770 };
771 /* }}} */
772 
773 #ifdef COMPILE_DL_PGSQL
774 #ifdef ZTS
775 ZEND_TSRMLS_CACHE_DEFINE()
776 #endif
777 ZEND_GET_MODULE(pgsql)
778 #endif
779 
780 static int le_link, le_plink, le_result, le_lofp, le_string;
781 
782 /* Compatibility definitions */
783 
784 #ifndef HAVE_PGSQL_WITH_MULTIBYTE_SUPPORT
785 #define pg_encoding_to_char(x) "SQL_ASCII"
786 #endif
787 
788 #if !HAVE_PQESCAPE_CONN
789 #define PQescapeStringConn(conn, to, from, len, error) PQescapeString(to, from, len)
790 #endif
791 
792 #if HAVE_PQESCAPELITERAL
793 #define PGSQLescapeLiteral(conn, str, len) PQescapeLiteral(conn, str, len)
794 #define PGSQLescapeIdentifier(conn, str, len) PQescapeIdentifier(conn, str, len)
795 #define PGSQLfree(a) PQfreemem(a)
796 #else
797 #define PGSQLescapeLiteral(conn, str, len) php_pgsql_PQescapeInternal(conn, str, len, 1, 0)
798 #define PGSQLescapeLiteral2(conn, str, len) php_pgsql_PQescapeInternal(conn, str, len, 1, 1)
799 #define PGSQLescapeIdentifier(conn, str, len) php_pgsql_PQescapeInternal(conn, str, len, 0, 0)
800 #define PGSQLfree(a) efree(a)
801 
802 /* emulate libpq's PQescapeInternal() 9.0 or later */
php_pgsql_PQescapeInternal(PGconn *conn, const char *str, size_t len, int escape_literal, int safe)803 static char *php_pgsql_PQescapeInternal(PGconn *conn, const char *str, size_t len, int escape_literal, int safe) /* {{{ */
804 {
805 	char *result, *rp, *s;
806 
807 	if (!conn) {
808 		return NULL;
809 	}
810 
811 	/* allocate enough memory */
812 	rp = result = (char *)safe_emalloc(len, 2, 5); /* leading " E" needs extra 2 bytes + quote_chars on both end for 2 bytes + NULL */
813 
814 	if (escape_literal) {
815 		if (safe) {
816 			size_t new_len;
817 			char *tmp = (char *)safe_emalloc(len, 2, 1);
818 			*rp++ = '\'';
819 			/* PQescapeString does not escape \, but it handles multibyte chars safely.
820 			   This escape is incompatible with PQescapeLiteral. */
821 			new_len = PQescapeStringConn(conn, tmp, str, len, NULL);
822 			strncpy(rp, tmp, new_len);
823 			efree(tmp);
824 			rp += new_len;
825 		} else {
826 			char *encoding;
827 			size_t tmp_len;
828 			/* This is compatible with PQescapeLiteral, but it cannot handle multbyte chars
829 			   such as SJIS, BIG5. Raise warning and return NULL by checking
830 			   client_encoding. */
831 			encoding = (char *) pg_encoding_to_char(PQclientEncoding(conn));
832 			if (!strncmp(encoding, "SJIS", sizeof("SJIS")-1) ||
833 				!strncmp(encoding, "SHIFT_JIS_2004", sizeof("SHIFT_JIS_2004")-1) ||
834 				!strncmp(encoding, "BIG5", sizeof("BIG5")-1) ||
835 				!strncmp(encoding, "GB18030", sizeof("GB18030")-1) ||
836 				!strncmp(encoding, "GBK", sizeof("GBK")-1) ||
837 				!strncmp(encoding, "JOHAB", sizeof("JOHAB")-1) ||
838 				!strncmp(encoding, "UHC", sizeof("UHC")-1) ) {
839 
840 				php_error_docref(NULL, E_WARNING, "Unsafe encoding is used. Do not use '%s' encoding or use PostgreSQL 9.0 or later libpq.", encoding);
841 			}
842 			/* check backslashes */
843 			tmp_len = strspn(str, "\\");
844 			if (tmp_len != len) {
845 				/* add " E" for escaping slashes */
846 				*rp++ = ' ';
847 				*rp++ = 'E';
848 			}
849 			*rp++ = '\'';
850 			for (s = (char *)str; s - str < len; ++s) {
851 				if (*s == '\'' || *s == '\\') {
852 					*rp++ = *s;
853 					*rp++ = *s;
854 				} else {
855 					*rp++ = *s;
856 				}
857 			}
858 		}
859 		*rp++ = '\'';
860 	} else {
861 		/* Identifier escape. */
862 		*rp++ = '"';
863 		for (s = (char *)str; s - str < len; ++s) {
864 			if (*s == '"') {
865 				*rp++ = '"';
866 				*rp++ = '"';
867 			} else {
868 				*rp++ = *s;
869 			}
870 		}
871 		*rp++ = '"';
872 	}
873 	*rp = '\0';
874 
875 	return result;
876 }
877 /* }}} */
878 #endif
879 
880 /* {{{ _php_pgsql_trim_message */
_php_pgsql_trim_message(const char *message, size_t *len)881 static char * _php_pgsql_trim_message(const char *message, size_t *len)
882 {
883 	register size_t i = strlen(message);
884 
885 	if (i>2 && (message[i-2] == '\r' || message[i-2] == '\n') && message[i-1] == '.') {
886 		--i;
887 	}
888 	while (i>1 && (message[i-1] == '\r' || message[i-1] == '\n')) {
889 		--i;
890 	}
891 	if (len) {
892 		*len = i;
893 	}
894 	return estrndup(message, i);
895 }
896 /* }}} */
897 
898 /* {{{ _php_pgsql_trim_result */
_php_pgsql_trim_result(PGconn * pgsql, char **buf)899 static inline char * _php_pgsql_trim_result(PGconn * pgsql, char **buf)
900 {
901 	return *buf = _php_pgsql_trim_message(PQerrorMessage(pgsql), NULL);
902 }
903 /* }}} */
904 
905 #define PQErrorMessageTrim(pgsql, buf) _php_pgsql_trim_result(pgsql, buf)
906 
907 #define PHP_PQ_ERROR(text, pgsql) {										\
908 		char *msgbuf = _php_pgsql_trim_message(PQerrorMessage(pgsql), NULL); \
909 		php_error_docref(NULL, E_WARNING, text, msgbuf);		\
910 		efree(msgbuf);													\
911 } \
912 
913 /* {{{ php_pgsql_set_default_link
914  */
php_pgsql_set_default_link(zend_resource *res)915 static void php_pgsql_set_default_link(zend_resource *res)
916 {
917 	GC_ADDREF(res);
918 
919 	if (PGG(default_link) != NULL) {
920 		zend_list_delete(PGG(default_link));
921 	}
922 
923 	PGG(default_link) = res;
924 }
925 /* }}} */
926 
927 /* {{{ _close_pgsql_link
928  */
_close_pgsql_link(zend_resource *rsrc)929 static void _close_pgsql_link(zend_resource *rsrc)
930 {
931 	PGconn *link = (PGconn *)rsrc->ptr;
932 	PGresult *res;
933 	zval *hash;
934 
935 	while ((res = PQgetResult(link))) {
936 		PQclear(res);
937 	}
938 	PQfinish(link);
939 	PGG(num_links)--;
940 
941 	/* Remove connection hash for this link */
942 	hash = zend_hash_index_find(&PGG(hashes), (uintptr_t) link);
943 	if (hash) {
944 		zend_hash_index_del(&PGG(hashes), (uintptr_t) link);
945 		zend_hash_del(&EG(regular_list), Z_STR_P(hash));
946 	}
947 }
948 /* }}} */
949 
950 /* {{{ _close_pgsql_plink
951  */
_close_pgsql_plink(zend_resource *rsrc)952 static void _close_pgsql_plink(zend_resource *rsrc)
953 {
954 	PGconn *link = (PGconn *)rsrc->ptr;
955 	PGresult *res;
956 
957 	while ((res = PQgetResult(link))) {
958 		PQclear(res);
959 	}
960 	PQfinish(link);
961 	PGG(num_persistent)--;
962 	PGG(num_links)--;
963 }
964 /* }}} */
965 
966 /* {{{ _php_pgsql_notice_handler
967  */
_php_pgsql_notice_handler(void *resource_id, const char *message)968 static void _php_pgsql_notice_handler(void *resource_id, const char *message)
969 {
970 	zval *notices;
971 	zval tmp;
972 	char *trimed_message;
973 	size_t trimed_message_len;
974 
975 	if (! PGG(ignore_notices)) {
976 		notices = zend_hash_index_find(&PGG(notices), (zend_ulong)resource_id);
977 		if (!notices) {
978 			array_init(&tmp);
979 			notices = &tmp;
980 			zend_hash_index_update(&PGG(notices), (zend_ulong)resource_id, notices);
981 		}
982 		trimed_message = _php_pgsql_trim_message(message, &trimed_message_len);
983 		if (PGG(log_notices)) {
984 			php_error_docref(NULL, E_NOTICE, "%s", trimed_message);
985 		}
986 		add_next_index_stringl(notices, trimed_message, trimed_message_len);
987 		efree(trimed_message);
988 	}
989 }
990 /* }}} */
991 
992 /* {{{ _rollback_transactions
993  */
_rollback_transactions(zval *el)994 static int _rollback_transactions(zval *el)
995 {
996 	PGconn *link;
997 	PGresult *res;
998 	zend_resource *rsrc = Z_RES_P(el);
999 
1000 	if (rsrc->type != le_plink)
1001 		return 0;
1002 
1003 	link = (PGconn *) rsrc->ptr;
1004 
1005 	if (PQ_SETNONBLOCKING(link, 0)) {
1006 		php_error_docref("ref.pgsql", E_NOTICE, "Cannot set connection to blocking mode");
1007 		return -1;
1008 	}
1009 
1010 	while ((res = PQgetResult(link))) {
1011 		PQclear(res);
1012 	}
1013 #if HAVE_PGTRANSACTIONSTATUS && HAVE_PQPROTOCOLVERSION
1014 	if ((PQprotocolVersion(link) >= 3 && PQtransactionStatus(link) != PQTRANS_IDLE) || PQprotocolVersion(link) < 3)
1015 #endif
1016 	{
1017 		int orig = PGG(ignore_notices);
1018 		PGG(ignore_notices) = 1;
1019 #if HAVE_PGTRANSACTIONSTATUS && HAVE_PQPROTOCOLVERSION
1020 		res = PQexec(link,"ROLLBACK;");
1021 #else
1022 		res = PQexec(link,"BEGIN;");
1023 		PQclear(res);
1024 		res = PQexec(link,"ROLLBACK;");
1025 #endif
1026 		PQclear(res);
1027 		PGG(ignore_notices) = orig;
1028 	}
1029 
1030 	return 0;
1031 }
1032 /* }}} */
1033 
1034 /* {{{ _free_ptr
1035  */
_free_ptr(zend_resource *rsrc)1036 static void _free_ptr(zend_resource *rsrc)
1037 {
1038 	pgLofp *lofp = (pgLofp *)rsrc->ptr;
1039 	efree(lofp);
1040 }
1041 /* }}} */
1042 
1043 /* {{{ _free_result
1044  */
_free_result(zend_resource *rsrc)1045 static void _free_result(zend_resource *rsrc)
1046 {
1047 	pgsql_result_handle *pg_result = (pgsql_result_handle *)rsrc->ptr;
1048 
1049 	PQclear(pg_result->result);
1050 	efree(pg_result);
1051 }
1052 /* }}} */
1053 
_php_pgsql_detect_identifier_escape(const char *identifier, size_t len)1054 static int _php_pgsql_detect_identifier_escape(const char *identifier, size_t len) /* {{{ */
1055 {
1056 	/* Handle edge case. Cannot be a escaped string */
1057 	if (len <= 2) {
1058 		return FAILURE;
1059 	}
1060 	/* Detect double quotes */
1061 	if (identifier[0] == '"' && identifier[len-1] == '"') {
1062 		size_t i;
1063 
1064 		/* Detect wrong format of " inside of escaped string */
1065 		for (i = 1; i < len-1; i++) {
1066 			if (identifier[i] == '"' && (identifier[++i] != '"' || i == len-1)) {
1067 				return FAILURE;
1068 			}
1069 		}
1070 	} else {
1071 		return FAILURE;
1072 	}
1073 	/* Escaped properly */
1074 	return SUCCESS;
1075 }
1076 /* }}} */
1077 
1078 /* {{{ PHP_INI
1079  */
1080 PHP_INI_BEGIN()
1081 STD_PHP_INI_BOOLEAN( "pgsql.allow_persistent",      "1",  PHP_INI_SYSTEM, OnUpdateBool, allow_persistent,      zend_pgsql_globals, pgsql_globals)
1082 STD_PHP_INI_ENTRY_EX("pgsql.max_persistent",       "-1",  PHP_INI_SYSTEM, OnUpdateLong, max_persistent,        zend_pgsql_globals, pgsql_globals, display_link_numbers)
1083 STD_PHP_INI_ENTRY_EX("pgsql.max_links",            "-1",  PHP_INI_SYSTEM, OnUpdateLong, max_links,             zend_pgsql_globals, pgsql_globals, display_link_numbers)
1084 STD_PHP_INI_BOOLEAN( "pgsql.auto_reset_persistent", "0",  PHP_INI_SYSTEM, OnUpdateBool, auto_reset_persistent, zend_pgsql_globals, pgsql_globals)
1085 STD_PHP_INI_BOOLEAN( "pgsql.ignore_notice",         "0",  PHP_INI_ALL,    OnUpdateBool, ignore_notices,        zend_pgsql_globals, pgsql_globals)
1086 STD_PHP_INI_BOOLEAN( "pgsql.log_notice",            "0",  PHP_INI_ALL,    OnUpdateBool, log_notices,           zend_pgsql_globals, pgsql_globals)
1087 PHP_INI_END()
1088 /* }}} */
1089 
1090 /* {{{ PHP_GINIT_FUNCTION
1091  */
PHP_GINIT_FUNCTIONnull1092 static PHP_GINIT_FUNCTION(pgsql)
1093 {
1094 #if defined(COMPILE_DL_PGSQL) && defined(ZTS)
1095 	ZEND_TSRMLS_CACHE_UPDATE();
1096 #endif
1097 	memset(pgsql_globals, 0, sizeof(zend_pgsql_globals));
1098 	/* Initialize notice message hash at MINIT only */
1099 	zend_hash_init_ex(&pgsql_globals->notices, 0, NULL, ZVAL_PTR_DTOR, 1, 0);
1100 	zend_hash_init_ex(&pgsql_globals->hashes, 0, NULL, ZVAL_PTR_DTOR, 1, 0);
1101 }
1102 /* }}} */
1103 
1104 /* {{{ PHP_MINIT_FUNCTION
1105  */
PHP_MINIT_FUNCTIONnull1106 PHP_MINIT_FUNCTION(pgsql)
1107 {
1108 	REGISTER_INI_ENTRIES();
1109 
1110 	le_link = zend_register_list_destructors_ex(_close_pgsql_link, NULL, "pgsql link", module_number);
1111 	le_plink = zend_register_list_destructors_ex(NULL, _close_pgsql_plink, "pgsql link persistent", module_number);
1112 	le_result = zend_register_list_destructors_ex(_free_result, NULL, "pgsql result", module_number);
1113 	le_lofp = zend_register_list_destructors_ex(_free_ptr, NULL, "pgsql large object", module_number);
1114 	le_string = zend_register_list_destructors_ex(_free_ptr, NULL, "pgsql string", module_number);
1115 #if HAVE_PG_CONFIG_H
1116 	/* PG_VERSION - libpq version */
1117 	REGISTER_STRING_CONSTANT("PGSQL_LIBPQ_VERSION", PG_VERSION, CONST_CS | CONST_PERSISTENT);
1118 	REGISTER_STRING_CONSTANT("PGSQL_LIBPQ_VERSION_STR", PG_VERSION_STR, CONST_CS | CONST_PERSISTENT);
1119 #endif
1120 	/* For connection option */
1121 	REGISTER_LONG_CONSTANT("PGSQL_CONNECT_FORCE_NEW", PGSQL_CONNECT_FORCE_NEW, CONST_CS | CONST_PERSISTENT);
1122 	REGISTER_LONG_CONSTANT("PGSQL_CONNECT_ASYNC", PGSQL_CONNECT_ASYNC, CONST_CS | CONST_PERSISTENT);
1123 	/* For pg_fetch_array() */
1124 	REGISTER_LONG_CONSTANT("PGSQL_ASSOC", PGSQL_ASSOC, CONST_CS | CONST_PERSISTENT);
1125 	REGISTER_LONG_CONSTANT("PGSQL_NUM", PGSQL_NUM, CONST_CS | CONST_PERSISTENT);
1126 	REGISTER_LONG_CONSTANT("PGSQL_BOTH", PGSQL_BOTH, CONST_CS | CONST_PERSISTENT);
1127 	/* For pg_last_notice() */
1128 	REGISTER_LONG_CONSTANT("PGSQL_NOTICE_LAST", PGSQL_NOTICE_LAST, CONST_CS | CONST_PERSISTENT);
1129 	REGISTER_LONG_CONSTANT("PGSQL_NOTICE_ALL", PGSQL_NOTICE_ALL, CONST_CS | CONST_PERSISTENT);
1130 	REGISTER_LONG_CONSTANT("PGSQL_NOTICE_CLEAR", PGSQL_NOTICE_CLEAR, CONST_CS | CONST_PERSISTENT);
1131 	/* For pg_connection_status() */
1132 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_BAD", CONNECTION_BAD, CONST_CS | CONST_PERSISTENT);
1133 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_OK", CONNECTION_OK, CONST_CS | CONST_PERSISTENT);
1134 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_STARTED", CONNECTION_STARTED, CONST_CS | CONST_PERSISTENT);
1135 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_MADE", CONNECTION_MADE, CONST_CS | CONST_PERSISTENT);
1136 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_AWAITING_RESPONSE", CONNECTION_AWAITING_RESPONSE, CONST_CS | CONST_PERSISTENT);
1137 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_AUTH_OK", CONNECTION_AUTH_OK, CONST_CS | CONST_PERSISTENT);
1138 #ifdef CONNECTION_SSL_STARTUP
1139 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_SSL_STARTUP", CONNECTION_SSL_STARTUP, CONST_CS | CONST_PERSISTENT);
1140 #endif
1141 	REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_SETENV", CONNECTION_SETENV, CONST_CS | CONST_PERSISTENT);
1142 	/* For pg_connect_poll() */
1143 	REGISTER_LONG_CONSTANT("PGSQL_POLLING_FAILED", PGRES_POLLING_FAILED, CONST_CS | CONST_PERSISTENT);
1144 	REGISTER_LONG_CONSTANT("PGSQL_POLLING_READING", PGRES_POLLING_READING, CONST_CS | CONST_PERSISTENT);
1145 	REGISTER_LONG_CONSTANT("PGSQL_POLLING_WRITING", PGRES_POLLING_WRITING, CONST_CS | CONST_PERSISTENT);
1146 	REGISTER_LONG_CONSTANT("PGSQL_POLLING_OK", PGRES_POLLING_OK, CONST_CS | CONST_PERSISTENT);
1147 	REGISTER_LONG_CONSTANT("PGSQL_POLLING_ACTIVE", PGRES_POLLING_ACTIVE, CONST_CS | CONST_PERSISTENT);
1148 #if HAVE_PGTRANSACTIONSTATUS
1149 	/* For pg_transaction_status() */
1150 	REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_IDLE", PQTRANS_IDLE, CONST_CS | CONST_PERSISTENT);
1151 	REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_ACTIVE", PQTRANS_ACTIVE, CONST_CS | CONST_PERSISTENT);
1152 	REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_INTRANS", PQTRANS_INTRANS, CONST_CS | CONST_PERSISTENT);
1153 	REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_INERROR", PQTRANS_INERROR, CONST_CS | CONST_PERSISTENT);
1154 	REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_UNKNOWN", PQTRANS_UNKNOWN, CONST_CS | CONST_PERSISTENT);
1155 #endif
1156 #if HAVE_PQSETERRORVERBOSITY
1157 	/* For pg_set_error_verbosity() */
1158 	REGISTER_LONG_CONSTANT("PGSQL_ERRORS_TERSE", PQERRORS_TERSE, CONST_CS | CONST_PERSISTENT);
1159 	REGISTER_LONG_CONSTANT("PGSQL_ERRORS_DEFAULT", PQERRORS_DEFAULT, CONST_CS | CONST_PERSISTENT);
1160 	REGISTER_LONG_CONSTANT("PGSQL_ERRORS_VERBOSE", PQERRORS_VERBOSE, CONST_CS | CONST_PERSISTENT);
1161 #endif
1162 	/* For lo_seek() */
1163 	REGISTER_LONG_CONSTANT("PGSQL_SEEK_SET", SEEK_SET, CONST_CS | CONST_PERSISTENT);
1164 	REGISTER_LONG_CONSTANT("PGSQL_SEEK_CUR", SEEK_CUR, CONST_CS | CONST_PERSISTENT);
1165 	REGISTER_LONG_CONSTANT("PGSQL_SEEK_END", SEEK_END, CONST_CS | CONST_PERSISTENT);
1166 	/* For pg_result_status() return value type */
1167 	REGISTER_LONG_CONSTANT("PGSQL_STATUS_LONG", PGSQL_STATUS_LONG, CONST_CS | CONST_PERSISTENT);
1168 	REGISTER_LONG_CONSTANT("PGSQL_STATUS_STRING", PGSQL_STATUS_STRING, CONST_CS | CONST_PERSISTENT);
1169 	/* For pg_result_status() return value */
1170 	REGISTER_LONG_CONSTANT("PGSQL_EMPTY_QUERY", PGRES_EMPTY_QUERY, CONST_CS | CONST_PERSISTENT);
1171 	REGISTER_LONG_CONSTANT("PGSQL_COMMAND_OK", PGRES_COMMAND_OK, CONST_CS | CONST_PERSISTENT);
1172 	REGISTER_LONG_CONSTANT("PGSQL_TUPLES_OK", PGRES_TUPLES_OK, CONST_CS | CONST_PERSISTENT);
1173 	REGISTER_LONG_CONSTANT("PGSQL_COPY_OUT", PGRES_COPY_OUT, CONST_CS | CONST_PERSISTENT);
1174 	REGISTER_LONG_CONSTANT("PGSQL_COPY_IN", PGRES_COPY_IN, CONST_CS | CONST_PERSISTENT);
1175 	REGISTER_LONG_CONSTANT("PGSQL_BAD_RESPONSE", PGRES_BAD_RESPONSE, CONST_CS | CONST_PERSISTENT);
1176 	REGISTER_LONG_CONSTANT("PGSQL_NONFATAL_ERROR", PGRES_NONFATAL_ERROR, CONST_CS | CONST_PERSISTENT);
1177 	REGISTER_LONG_CONSTANT("PGSQL_FATAL_ERROR", PGRES_FATAL_ERROR, CONST_CS | CONST_PERSISTENT);
1178 #if HAVE_PQRESULTERRORFIELD
1179 	/* For pg_result_error_field() field codes */
1180 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SEVERITY", PG_DIAG_SEVERITY, CONST_CS | CONST_PERSISTENT);
1181 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SQLSTATE", PG_DIAG_SQLSTATE, CONST_CS | CONST_PERSISTENT);
1182 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_MESSAGE_PRIMARY", PG_DIAG_MESSAGE_PRIMARY, CONST_CS | CONST_PERSISTENT);
1183 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_MESSAGE_DETAIL", PG_DIAG_MESSAGE_DETAIL, CONST_CS | CONST_PERSISTENT);
1184 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_MESSAGE_HINT", PG_DIAG_MESSAGE_HINT, CONST_CS | CONST_PERSISTENT);
1185 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_STATEMENT_POSITION", PG_DIAG_STATEMENT_POSITION, CONST_CS | CONST_PERSISTENT);
1186 #ifdef PG_DIAG_INTERNAL_POSITION
1187 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_INTERNAL_POSITION", PG_DIAG_INTERNAL_POSITION, CONST_CS | CONST_PERSISTENT);
1188 #endif
1189 #ifdef PG_DIAG_INTERNAL_QUERY
1190 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_INTERNAL_QUERY", PG_DIAG_INTERNAL_QUERY, CONST_CS | CONST_PERSISTENT);
1191 #endif
1192 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_CONTEXT", PG_DIAG_CONTEXT, CONST_CS | CONST_PERSISTENT);
1193 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SOURCE_FILE", PG_DIAG_SOURCE_FILE, CONST_CS | CONST_PERSISTENT);
1194 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SOURCE_LINE", PG_DIAG_SOURCE_LINE, CONST_CS | CONST_PERSISTENT);
1195 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SOURCE_FUNCTION", PG_DIAG_SOURCE_FUNCTION, CONST_CS | CONST_PERSISTENT);
1196 #ifdef PG_DIAG_SCHEMA_NAME
1197 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SCHEMA_NAME", PG_DIAG_SCHEMA_NAME, CONST_CS | CONST_PERSISTENT);
1198 #endif
1199 #ifdef PG_DIAG_TABLE_NAME
1200 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_TABLE_NAME", PG_DIAG_TABLE_NAME, CONST_CS | CONST_PERSISTENT);
1201 #endif
1202 #ifdef PG_DIAG_COLUMN_NAME
1203 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_COLUMN_NAME", PG_DIAG_COLUMN_NAME, CONST_CS | CONST_PERSISTENT);
1204 #endif
1205 #ifdef PG_DIAG_DATATYPE_NAME
1206 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_DATATYPE_NAME", PG_DIAG_DATATYPE_NAME, CONST_CS | CONST_PERSISTENT);
1207 #endif
1208 #ifdef PG_DIAG_CONSTRAINT_NAME
1209 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_CONSTRAINT_NAME", PG_DIAG_CONSTRAINT_NAME, CONST_CS | CONST_PERSISTENT);
1210 #endif
1211 #ifdef PG_DIAG_SEVERITY_NONLOCALIZED
1212 	REGISTER_LONG_CONSTANT("PGSQL_DIAG_SEVERITY_NONLOCALIZED", PG_DIAG_SEVERITY_NONLOCALIZED, CONST_CS | CONST_PERSISTENT);
1213 #endif
1214 #endif
1215 	/* pg_convert options */
1216 	REGISTER_LONG_CONSTANT("PGSQL_CONV_IGNORE_DEFAULT", PGSQL_CONV_IGNORE_DEFAULT, CONST_CS | CONST_PERSISTENT);
1217 	REGISTER_LONG_CONSTANT("PGSQL_CONV_FORCE_NULL", PGSQL_CONV_FORCE_NULL, CONST_CS | CONST_PERSISTENT);
1218 	REGISTER_LONG_CONSTANT("PGSQL_CONV_IGNORE_NOT_NULL", PGSQL_CONV_IGNORE_NOT_NULL, CONST_CS | CONST_PERSISTENT);
1219 	/* pg_insert/update/delete/select options */
1220 	REGISTER_LONG_CONSTANT("PGSQL_DML_ESCAPE", PGSQL_DML_ESCAPE, CONST_CS | CONST_PERSISTENT);
1221 	REGISTER_LONG_CONSTANT("PGSQL_DML_NO_CONV", PGSQL_DML_NO_CONV, CONST_CS | CONST_PERSISTENT);
1222 	REGISTER_LONG_CONSTANT("PGSQL_DML_EXEC", PGSQL_DML_EXEC, CONST_CS | CONST_PERSISTENT);
1223 	REGISTER_LONG_CONSTANT("PGSQL_DML_ASYNC", PGSQL_DML_ASYNC, CONST_CS | CONST_PERSISTENT);
1224 	REGISTER_LONG_CONSTANT("PGSQL_DML_STRING", PGSQL_DML_STRING, CONST_CS | CONST_PERSISTENT);
1225 	return SUCCESS;
1226 }
1227 /* }}} */
1228 
1229 /* {{{ PHP_MSHUTDOWN_FUNCTION
1230  */
PHP_MSHUTDOWN_FUNCTIONnull1231 PHP_MSHUTDOWN_FUNCTION(pgsql)
1232 {
1233 	UNREGISTER_INI_ENTRIES();
1234 	zend_hash_destroy(&PGG(notices));
1235 	zend_hash_destroy(&PGG(hashes));
1236 
1237 	return SUCCESS;
1238 }
1239 /* }}} */
1240 
1241 /* {{{ PHP_RINIT_FUNCTION
1242  */
PHP_RINIT_FUNCTIONnull1243 PHP_RINIT_FUNCTION(pgsql)
1244 {
1245 	PGG(default_link) = NULL;
1246 	PGG(num_links) = PGG(num_persistent);
1247 	return SUCCESS;
1248 }
1249 /* }}} */
1250 
1251 /* {{{ PHP_RSHUTDOWN_FUNCTION
1252  */
PHP_RSHUTDOWN_FUNCTIONnull1253 PHP_RSHUTDOWN_FUNCTION(pgsql)
1254 {
1255 	/* clean up notice messages */
1256 	zend_hash_clean(&PGG(notices));
1257 	zend_hash_clean(&PGG(hashes));
1258 	/* clean up persistent connection */
1259 	zend_hash_apply(&EG(persistent_list), (apply_func_t) _rollback_transactions);
1260 	return SUCCESS;
1261 }
1262 /* }}} */
1263 
1264 /* {{{ PHP_MINFO_FUNCTION
1265  */
PHP_MINFO_FUNCTIONnull1266 PHP_MINFO_FUNCTION(pgsql)
1267 {
1268 	char buf[256];
1269 
1270 	php_info_print_table_start();
1271 	php_info_print_table_header(2, "PostgreSQL Support", "enabled");
1272 #if HAVE_PG_CONFIG_H
1273 	php_info_print_table_row(2, "PostgreSQL(libpq) Version", PG_VERSION);
1274 	php_info_print_table_row(2, "PostgreSQL(libpq) ", PG_VERSION_STR);
1275 #ifdef HAVE_PGSQL_WITH_MULTIBYTE_SUPPORT
1276 	php_info_print_table_row(2, "Multibyte character support", "enabled");
1277 #else
1278 	php_info_print_table_row(2, "Multibyte character support", "disabled");
1279 #endif
1280 #if defined(USE_SSL) || defined(USE_OPENSSL)
1281 	php_info_print_table_row(2, "SSL support", "enabled");
1282 #else
1283 	php_info_print_table_row(2, "SSL support", "disabled");
1284 #endif
1285 #endif /* HAVE_PG_CONFIG_H */
1286 	snprintf(buf, sizeof(buf), ZEND_LONG_FMT, PGG(num_persistent));
1287 	php_info_print_table_row(2, "Active Persistent Links", buf);
1288 	snprintf(buf, sizeof(buf), ZEND_LONG_FMT, PGG(num_links));
1289 	php_info_print_table_row(2, "Active Links", buf);
1290 	php_info_print_table_end();
1291 
1292 	DISPLAY_INI_ENTRIES();
1293 }
1294 /* }}} */
1295 
1296 /* {{{ php_pgsql_do_connect
1297  */
php_pgsql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)1298 static void php_pgsql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
1299 {
1300 	char *host=NULL,*port=NULL,*options=NULL,*tty=NULL,*dbname=NULL,*connstring=NULL;
1301 	PGconn *pgsql;
1302 	smart_str str = {0};
1303 	zval *args;
1304 	uint32_t i;
1305 	int connect_type = 0;
1306 	PGresult *pg_result;
1307 
1308 	args = (zval *)safe_emalloc(ZEND_NUM_ARGS(), sizeof(zval), 0);
1309 	if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 5
1310 			|| zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) {
1311 		efree(args);
1312 		WRONG_PARAM_COUNT;
1313 	}
1314 
1315 	smart_str_appends(&str, "pgsql");
1316 
1317 	for (i = 0; i < ZEND_NUM_ARGS(); i++) {
1318 		/* make sure that the PGSQL_CONNECT_FORCE_NEW bit is not part of the hash so that subsequent connections
1319 		 * can re-use this connection. Bug #39979
1320 		 */
1321 		if (i == 1 && ZEND_NUM_ARGS() == 2 && Z_TYPE(args[i]) == IS_LONG) {
1322 			if (Z_LVAL(args[1]) == PGSQL_CONNECT_FORCE_NEW) {
1323 				continue;
1324 			} else if (Z_LVAL(args[1]) & PGSQL_CONNECT_FORCE_NEW) {
1325 				smart_str_append_long(&str, Z_LVAL(args[1]) ^ PGSQL_CONNECT_FORCE_NEW);
1326 			}
1327 		}
1328 		ZVAL_STR(&args[i], zval_get_string(&args[i]));
1329 		smart_str_appendc(&str, '_');
1330 		smart_str_appendl(&str, Z_STRVAL(args[i]), Z_STRLEN(args[i]));
1331 	}
1332 
1333 	/* Exception thrown during a string conversion. */
1334 	if (EG(exception)) {
1335 		goto cleanup;
1336 	}
1337 
1338 	smart_str_0(&str);
1339 
1340 	if (ZEND_NUM_ARGS() == 1) { /* new style, using connection string */
1341 		connstring = Z_STRVAL(args[0]);
1342 	} else if (ZEND_NUM_ARGS() == 2 ) { /* Safe to add conntype_option, since 2 args was illegal */
1343 		connstring = Z_STRVAL(args[0]);
1344 		connect_type = (int)zval_get_long(&args[1]);
1345 	} else {
1346 		host = Z_STRVAL(args[0]);
1347 		port = Z_STRVAL(args[1]);
1348 		dbname = Z_STRVAL(args[ZEND_NUM_ARGS()-1]);
1349 
1350 		switch (ZEND_NUM_ARGS()) {
1351 		case 5:
1352 			tty = Z_STRVAL(args[3]);
1353 			/* fall through */
1354 		case 4:
1355 			options = Z_STRVAL(args[2]);
1356 			break;
1357 		}
1358 	}
1359 
1360 	if (persistent && PGG(allow_persistent)) {
1361 		zend_resource *le;
1362 
1363 		/* try to find if we already have this link in our persistent list */
1364 		if ((le = zend_hash_find_ptr(&EG(persistent_list), str.s)) == NULL) {  /* we don't */
1365 			if (PGG(max_links) != -1 && PGG(num_links) >= PGG(max_links)) {
1366 				php_error_docref(NULL, E_WARNING,
1367 								 "Cannot create new link. Too many open links (" ZEND_LONG_FMT ")", PGG(num_links));
1368 				goto err;
1369 			}
1370 			if (PGG(max_persistent) != -1 && PGG(num_persistent) >= PGG(max_persistent)) {
1371 				php_error_docref(NULL, E_WARNING,
1372 								 "Cannot create new link. Too many open persistent links (" ZEND_LONG_FMT ")", PGG(num_persistent));
1373 				goto err;
1374 			}
1375 
1376 			/* create the link */
1377 			if (connstring) {
1378 				pgsql = PQconnectdb(connstring);
1379 			} else {
1380 				pgsql = PQsetdb(host, port, options, tty, dbname);
1381 			}
1382 			if (pgsql == NULL || PQstatus(pgsql) == CONNECTION_BAD) {
1383 				PHP_PQ_ERROR("Unable to connect to PostgreSQL server: %s", pgsql)
1384 				if (pgsql) {
1385 					PQfinish(pgsql);
1386 				}
1387 				goto err;
1388 			}
1389 
1390 			/* hash it up */
1391 			if (zend_register_persistent_resource(ZSTR_VAL(str.s), ZSTR_LEN(str.s), pgsql, le_plink) == NULL) {
1392 				goto err;
1393 			}
1394 			PGG(num_links)++;
1395 			PGG(num_persistent)++;
1396 		} else {  /* we do */
1397 			if (le->type != le_plink) {
1398 				goto err;
1399 			}
1400 			/* ensure that the link did not die */
1401 			if (PGG(auto_reset_persistent) & 1) {
1402 				/* need to send & get something from backend to
1403 				   make sure we catch CONNECTION_BAD every time */
1404 				PGresult *pg_result;
1405 				pg_result = PQexec(le->ptr, "select 1");
1406 				PQclear(pg_result);
1407 			}
1408 			if (PQstatus(le->ptr) == CONNECTION_BAD) { /* the link died */
1409 				if (le->ptr == NULL) {
1410 					if (connstring) {
1411 						le->ptr = PQconnectdb(connstring);
1412 					} else {
1413 						le->ptr = PQsetdb(host,port,options,tty,dbname);
1414 					}
1415 				}
1416 				else {
1417 					PQreset(le->ptr);
1418 				}
1419 				if (le->ptr == NULL || PQstatus(le->ptr) == CONNECTION_BAD) {
1420 					php_error_docref(NULL, E_WARNING,"PostgreSQL link lost, unable to reconnect");
1421 					zend_hash_del(&EG(persistent_list), str.s);
1422 					goto err;
1423 				}
1424 			}
1425 			pgsql = (PGconn *) le->ptr;
1426 #if HAVE_PQPROTOCOLVERSION && HAVE_PQPARAMETERSTATUS
1427 			if (PQprotocolVersion(pgsql) >= 3 && atof(PQparameterStatus(pgsql, "server_version")) >= 7.2) {
1428 #else
1429 			if (atof(PG_VERSION) >= 7.2) {
1430 #endif
1431 				pg_result = PQexec(pgsql, "RESET ALL;");
1432 				PQclear(pg_result);
1433 			}
1434 		}
1435 		RETVAL_RES(zend_register_resource(pgsql, le_plink));
1436 	} else { /* Non persistent connection */
1437 		zend_resource *index_ptr, new_index_ptr;
1438 
1439 		/* first we check the hash for the hashed_details key.  if it exists,
1440 		 * it should point us to the right offset where the actual pgsql link sits.
1441 		 * if it doesn't, open a new pgsql link, add it to the resource list,
1442 		 * and add a pointer to it with hashed_details as the key.
1443 		 */
1444 		if (!(connect_type & PGSQL_CONNECT_FORCE_NEW)
1445 			&& (index_ptr = zend_hash_find_ptr(&EG(regular_list), str.s)) != NULL) {
1446 			zend_resource *link;
1447 
1448 			if (index_ptr->type != le_index_ptr) {
1449 				goto err;
1450 			}
1451 
1452 			link = (zend_resource *)index_ptr->ptr;
1453 			ZEND_ASSERT(link->ptr && (link->type == le_link || link->type == le_plink));
1454 			php_pgsql_set_default_link(link);
1455 			GC_ADDREF(link);
1456 			RETVAL_RES(link);
1457 			goto cleanup;
1458 		}
1459 		if (PGG(max_links) != -1 && PGG(num_links) >= PGG(max_links)) {
1460 			php_error_docref(NULL, E_WARNING, "Cannot create new link. Too many open links (" ZEND_LONG_FMT ")", PGG(num_links));
1461 			goto err;
1462 		}
1463 
1464 		/* Non-blocking connect */
1465 		if (connect_type & PGSQL_CONNECT_ASYNC) {
1466 			if (connstring) {
1467 				pgsql = PQconnectStart(connstring);
1468 				if (pgsql==NULL || PQstatus(pgsql)==CONNECTION_BAD) {
1469 					PHP_PQ_ERROR("Unable to connect to PostgreSQL server: %s", pgsql);
1470 					if (pgsql) {
1471 						PQfinish(pgsql);
1472 					}
1473 					goto err;
1474 				}
1475 			} else {
1476 				php_error_docref(NULL, E_WARNING, "Connection string required for async connections");
1477 				goto err;
1478 			}
1479 		} else {
1480 			if (connstring) {
1481 				pgsql = PQconnectdb(connstring);
1482 			} else {
1483 				pgsql = PQsetdb(host,port,options,tty,dbname);
1484 			}
1485 			if (pgsql==NULL || PQstatus(pgsql)==CONNECTION_BAD) {
1486 				PHP_PQ_ERROR("Unable to connect to PostgreSQL server: %s", pgsql);
1487 				if (pgsql) {
1488 					PQfinish(pgsql);
1489 				}
1490 				goto err;
1491 			}
1492 		}
1493 
1494 		/* add it to the list */
1495 		RETVAL_RES(zend_register_resource(pgsql, le_link));
1496 
1497 		/* add it to the hash */
1498 		new_index_ptr.ptr = (void *) Z_RES_P(return_value);
1499 		new_index_ptr.type = le_index_ptr;
1500 		zend_hash_update_mem(&EG(regular_list), str.s, (void *) &new_index_ptr, sizeof(zend_resource));
1501 
1502 		/* Keep track of link => hash mapping, so we can remove the hash entry from regular_list
1503 		 * when the connection is closed. This uses the address of the connection rather than the
1504 		 * zend_resource, because the resource destructor is passed a stack copy of the resource
1505 		 * structure. */
1506 		{
1507 			zval tmp;
1508 			ZVAL_STR_COPY(&tmp, str.s);
1509 			zend_hash_index_update(&PGG(hashes), (uintptr_t) pgsql, &tmp);
1510 		}
1511 		PGG(num_links)++;
1512 	}
1513 	/* set notice processor */
1514 	if (! PGG(ignore_notices) && Z_TYPE_P(return_value) == IS_RESOURCE) {
1515 		PQsetNoticeProcessor(pgsql, _php_pgsql_notice_handler, (void*)(zend_uintptr_t)Z_RES_HANDLE_P(return_value));
1516 	}
1517 	php_pgsql_set_default_link(Z_RES_P(return_value));
1518 
1519 cleanup:
1520 	for (i = 0; i < ZEND_NUM_ARGS(); i++) {
1521 		zval_ptr_dtor(&args[i]);
1522 	}
1523 	efree(args);
1524 	smart_str_free(&str);
1525 	return;
1526 
1527 err:
1528 	for (i = 0; i < ZEND_NUM_ARGS(); i++) {
1529 		zval_ptr_dtor(&args[i]);
1530 	}
1531 	efree(args);
1532 	smart_str_free(&str);
1533 	RETURN_FALSE;
1534 }
1535 /* }}} */
1536 
1537 /* {{{ proto resource pg_connect(string connection_string[, int connect_type] | [string host, string port [, string options [, string tty,]]] string database)
1538    Open a PostgreSQL connection */
1539 PHP_FUNCTION(pg_connect)
1540 {
1541 	php_pgsql_do_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU,0);
1542 }
1543 /* }}} */
1544 
1545 /* {{{ proto resource pg_connect_poll(resource connection)
1546    Poll the status of an in-progress async PostgreSQL connection attempt*/
1547 PHP_FUNCTION(pg_connect_poll)
1548 {
1549 	zval *pgsql_link;
1550 	PGconn *pgsql;
1551 	int ret;
1552 
1553 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pgsql_link) == FAILURE) {
1554 		return;
1555 	}
1556 
1557 	if ((pgsql = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
1558 		RETURN_FALSE;
1559 	}
1560 
1561 	ret = PQconnectPoll(pgsql);
1562 
1563 	RETURN_LONG(ret);
1564 }
1565 /* }}} */
1566 
1567 /* {{{ proto resource pg_pconnect(string connection_string | [string host, string port [, string options [, string tty,]]] string database)
1568    Open a persistent PostgreSQL connection */
1569 PHP_FUNCTION(pg_pconnect)
1570 {
1571 	php_pgsql_do_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU,1);
1572 }
1573 /* }}} */
1574 
1575 /* {{{ proto bool pg_close([resource connection])
1576    Close a PostgreSQL connection */
1577 PHP_FUNCTION(pg_close)
1578 {
1579 	zval *pgsql_link = NULL;
1580 	zend_resource *link;
1581 
1582 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|r", &pgsql_link) == FAILURE) {
1583 		return;
1584 	}
1585 
1586 	if (!pgsql_link) {
1587 		link = PGG(default_link);
1588 		CHECK_DEFAULT_LINK(link);
1589 		zend_list_delete(link);
1590 		PGG(default_link) = NULL;
1591 		RETURN_TRUE;
1592 	}
1593 
1594 	link = Z_RES_P(pgsql_link);
1595 	if (zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink) == NULL) {
1596 		RETURN_FALSE;
1597 	}
1598 
1599 	if (link == PGG(default_link)) {
1600 		zend_list_delete(link);
1601 		PGG(default_link) = NULL;
1602 	}
1603 	zend_list_close(link);
1604 
1605 	RETURN_TRUE;
1606 }
1607 /* }}} */
1608 
1609 #define PHP_PG_DBNAME 1
1610 #define PHP_PG_ERROR_MESSAGE 2
1611 #define PHP_PG_OPTIONS 3
1612 #define PHP_PG_PORT 4
1613 #define PHP_PG_TTY 5
1614 #define PHP_PG_HOST 6
1615 #define PHP_PG_VERSION 7
1616 
1617 /* {{{ php_pgsql_get_link_info
1618  */
1619 static void php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
1620 {
1621 	zend_resource *link;
1622 	zval *pgsql_link = NULL;
1623 	int argc = ZEND_NUM_ARGS();
1624 	PGconn *pgsql;
1625 	char *msgbuf;
1626 	char *result;
1627 
1628 	if (zend_parse_parameters(argc, "|r", &pgsql_link) == FAILURE) {
1629 		return;
1630 	}
1631 
1632 	if (argc == 0) {
1633 		link = FETCH_DEFAULT_LINK();
1634 		CHECK_DEFAULT_LINK(link);
1635 	} else {
1636 		link = Z_RES_P(pgsql_link);
1637 	}
1638 
1639 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
1640 		RETURN_FALSE;
1641 	}
1642 
1643 	switch(entry_type) {
1644 		case PHP_PG_DBNAME:
1645 			result = PQdb(pgsql);
1646 			break;
1647 		case PHP_PG_ERROR_MESSAGE:
1648 			result = PQErrorMessageTrim(pgsql, &msgbuf);
1649 			RETVAL_STRING(result);
1650 			efree(result);
1651 			return;
1652 		case PHP_PG_OPTIONS:
1653 			result = PQoptions(pgsql);
1654 			break;
1655 		case PHP_PG_PORT:
1656 			result = PQport(pgsql);
1657 			break;
1658 		case PHP_PG_TTY:
1659 			result = PQtty(pgsql);
1660 			break;
1661 		case PHP_PG_HOST:
1662 			result = PQhost(pgsql);
1663 			break;
1664 		case PHP_PG_VERSION:
1665 			array_init(return_value);
1666 			add_assoc_string(return_value, "client", PG_VERSION);
1667 #if HAVE_PQPROTOCOLVERSION
1668 			add_assoc_long(return_value, "protocol", PQprotocolVersion(pgsql));
1669 #if HAVE_PQPARAMETERSTATUS
1670 			if (PQprotocolVersion(pgsql) >= 3) {
1671 				/* 8.0 or grater supports protorol version 3 */
1672 				char *tmp;
1673 				add_assoc_string(return_value, "server", (char*)PQparameterStatus(pgsql, "server_version"));
1674 
1675 #define PHP_PQ_COPY_PARAM(_x) tmp = (char*)PQparameterStatus(pgsql, _x); \
1676 	if(tmp) add_assoc_string(return_value, _x, tmp); \
1677 	else add_assoc_null(return_value, _x);
1678 
1679 				PHP_PQ_COPY_PARAM("server_encoding");
1680 				PHP_PQ_COPY_PARAM("client_encoding");
1681 				PHP_PQ_COPY_PARAM("is_superuser");
1682 				PHP_PQ_COPY_PARAM("session_authorization");
1683 				PHP_PQ_COPY_PARAM("DateStyle");
1684 				PHP_PQ_COPY_PARAM("IntervalStyle");
1685 				PHP_PQ_COPY_PARAM("TimeZone");
1686 				PHP_PQ_COPY_PARAM("integer_datetimes");
1687 				PHP_PQ_COPY_PARAM("standard_conforming_strings");
1688 				PHP_PQ_COPY_PARAM("application_name");
1689 			}
1690 #endif
1691 #endif
1692 			return;
1693 		default:
1694 			RETURN_FALSE;
1695 	}
1696 	if (result) {
1697 		RETURN_STRING(result);
1698 	} else {
1699 		RETURN_EMPTY_STRING();
1700 	}
1701 }
1702 /* }}} */
1703 
1704 /* {{{ proto string pg_dbname([resource connection])
1705    Get the database name */
1706 PHP_FUNCTION(pg_dbname)
1707 {
1708 	php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_DBNAME);
1709 }
1710 /* }}} */
1711 
1712 /* {{{ proto string pg_last_error([resource connection])
1713    Get the error message string */
1714 PHP_FUNCTION(pg_last_error)
1715 {
1716 	php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_ERROR_MESSAGE);
1717 }
1718 /* }}} */
1719 
1720 /* {{{ proto string pg_options([resource connection])
1721    Get the options associated with the connection */
1722 PHP_FUNCTION(pg_options)
1723 {
1724 	php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_OPTIONS);
1725 }
1726 /* }}} */
1727 
1728 /* {{{ proto int pg_port([resource connection])
1729    Return the port number associated with the connection */
1730 PHP_FUNCTION(pg_port)
1731 {
1732 	php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_PORT);
1733 }
1734 /* }}} */
1735 
1736 /* {{{ proto string pg_tty([resource connection])
1737    Return the tty name associated with the connection */
1738 PHP_FUNCTION(pg_tty)
1739 {
1740 	php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_TTY);
1741 }
1742 /* }}} */
1743 
1744 /* {{{ proto string pg_host([resource connection])
1745    Returns the host name associated with the connection */
1746 PHP_FUNCTION(pg_host)
1747 {
1748 	php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_HOST);
1749 }
1750 /* }}} */
1751 
1752 /* {{{ proto array pg_version([resource connection])
1753    Returns an array with client, protocol and server version (when available) */
1754 PHP_FUNCTION(pg_version)
1755 {
1756 	php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_VERSION);
1757 }
1758 /* }}} */
1759 
1760 #if HAVE_PQPARAMETERSTATUS
1761 /* {{{ proto string|false pg_parameter_status([resource connection,] string param_name)
1762    Returns the value of a server parameter */
1763 PHP_FUNCTION(pg_parameter_status)
1764 {
1765 	zval *pgsql_link = NULL;
1766 	zend_resource *link;
1767 	PGconn *pgsql;
1768 	char *param;
1769 	size_t len;
1770 
1771 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "rs", &pgsql_link, &param, &len) == FAILURE) {
1772 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &param, &len) == SUCCESS) {
1773 			link = FETCH_DEFAULT_LINK();
1774 			CHECK_DEFAULT_LINK(link);
1775 		} else {
1776 			RETURN_FALSE;
1777 		}
1778 	} else {
1779 		link = Z_RES_P(pgsql_link);
1780 	}
1781 
1782 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
1783 		RETURN_FALSE;
1784 	}
1785 
1786 	param = (char*)PQparameterStatus(pgsql, param);
1787 	if (param) {
1788 		RETURN_STRING(param);
1789 	} else {
1790 		RETURN_FALSE;
1791 	}
1792 }
1793 /* }}} */
1794 #endif
1795 
1796 /* {{{ proto bool pg_ping([resource connection])
1797    Ping database. If connection is bad, try to reconnect. */
1798 PHP_FUNCTION(pg_ping)
1799 {
1800 	zval *pgsql_link;
1801 	PGconn *pgsql;
1802 	PGresult *res;
1803 	zend_resource *link;
1804 
1805 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "r", &pgsql_link) == SUCCESS) {
1806 		link = Z_RES_P(pgsql_link);
1807 	} else {
1808 		link = FETCH_DEFAULT_LINK();
1809 		CHECK_DEFAULT_LINK(link);
1810 	}
1811 
1812 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
1813 		RETURN_FALSE;
1814 	}
1815 
1816 	/* ping connection */
1817 	res = PQexec(pgsql, "SELECT 1;");
1818 	PQclear(res);
1819 
1820 	/* check status. */
1821 	if (PQstatus(pgsql) == CONNECTION_OK)
1822 		RETURN_TRUE;
1823 
1824 	/* reset connection if it's broken */
1825 	PQreset(pgsql);
1826 	if (PQstatus(pgsql) == CONNECTION_OK) {
1827 		RETURN_TRUE;
1828 	}
1829 	RETURN_FALSE;
1830 }
1831 /* }}} */
1832 
1833 /* {{{ proto resource pg_query([resource connection,] string query)
1834    Execute a query */
1835 PHP_FUNCTION(pg_query)
1836 {
1837 	zval *pgsql_link = NULL;
1838 	char *query;
1839 	int  argc = ZEND_NUM_ARGS();
1840 	size_t query_len;
1841 	int leftover = 0;
1842 	zend_resource *link;
1843 	PGconn *pgsql;
1844 	PGresult *pgsql_result;
1845 	ExecStatusType status;
1846 
1847 	if (argc == 1) {
1848 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &query, &query_len) == FAILURE) {
1849 			return;
1850 		}
1851 		link = FETCH_DEFAULT_LINK();
1852 		CHECK_DEFAULT_LINK(link);
1853 	} else {
1854 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &pgsql_link, &query, &query_len) == FAILURE) {
1855 			return;
1856 		}
1857 		link = Z_RES_P(pgsql_link);
1858 	}
1859 
1860 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
1861 		RETURN_FALSE;
1862 	}
1863 
1864 	if (PQ_SETNONBLOCKING(pgsql, 0)) {
1865 		php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode");
1866 		RETURN_FALSE;
1867 	}
1868 	while ((pgsql_result = PQgetResult(pgsql))) {
1869 		PQclear(pgsql_result);
1870 		leftover = 1;
1871 	}
1872 	if (leftover) {
1873 		php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first");
1874 	}
1875 	pgsql_result = PQexec(pgsql, query);
1876 	if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
1877 		PQclear(pgsql_result);
1878 		PQreset(pgsql);
1879 		pgsql_result = PQexec(pgsql, query);
1880 	}
1881 
1882 	if (pgsql_result) {
1883 		status = PQresultStatus(pgsql_result);
1884 	} else {
1885 		status = (ExecStatusType) PQstatus(pgsql);
1886 	}
1887 
1888 	switch (status) {
1889 		case PGRES_EMPTY_QUERY:
1890 		case PGRES_BAD_RESPONSE:
1891 		case PGRES_NONFATAL_ERROR:
1892 		case PGRES_FATAL_ERROR:
1893 			PHP_PQ_ERROR("Query failed: %s", pgsql);
1894 			PQclear(pgsql_result);
1895 			RETURN_FALSE;
1896 			break;
1897 		case PGRES_COMMAND_OK: /* successful command that did not return rows */
1898 		default:
1899 			if (pgsql_result) {
1900 				pgsql_result_handle *pg_result = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
1901 				pg_result->conn = pgsql;
1902 				pg_result->result = pgsql_result;
1903 				pg_result->row = 0;
1904 				RETURN_RES(zend_register_resource(pg_result, le_result));
1905 			} else {
1906 				PQclear(pgsql_result);
1907 				RETURN_FALSE;
1908 			}
1909 			break;
1910 	}
1911 }
1912 /* }}} */
1913 
1914 #if HAVE_PQEXECPARAMS || HAVE_PQEXECPREPARED || HAVE_PQSENDQUERYPARAMS || HAVE_PQSENDQUERYPREPARED
1915 /* {{{ _php_pgsql_free_params */
1916 static void _php_pgsql_free_params(char **params, int num_params)
1917 {
1918 	if (num_params > 0) {
1919 		int i;
1920 		for (i = 0; i < num_params; i++) {
1921 			if (params[i]) {
1922 				efree(params[i]);
1923 			}
1924 		}
1925 		efree(params);
1926 	}
1927 }
1928 /* }}} */
1929 #endif
1930 
1931 #if HAVE_PQEXECPARAMS
1932 /* {{{ proto resource pg_query_params([resource connection,] string query, array params)
1933    Execute a query */
1934 PHP_FUNCTION(pg_query_params)
1935 {
1936 	zval *pgsql_link = NULL;
1937 	zval *pv_param_arr, *tmp;
1938 	char *query;
1939 	size_t query_len;
1940 	int argc = ZEND_NUM_ARGS();
1941 	int leftover = 0;
1942 	int num_params = 0;
1943 	char **params = NULL;
1944 	zend_resource *link;
1945 	PGconn *pgsql;
1946 	PGresult *pgsql_result;
1947 	ExecStatusType status;
1948 	pgsql_result_handle *pg_result;
1949 
1950 	if (argc == 2) {
1951 		if (zend_parse_parameters(argc, "sa", &query, &query_len, &pv_param_arr) == FAILURE) {
1952 			return;
1953 		}
1954 		link = FETCH_DEFAULT_LINK();
1955 		CHECK_DEFAULT_LINK(link);
1956 	} else {
1957 		if (zend_parse_parameters(argc, "rsa", &pgsql_link, &query, &query_len, &pv_param_arr) == FAILURE) {
1958 			return;
1959 		}
1960 		link = Z_RES_P(pgsql_link);
1961 	}
1962 
1963 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
1964 		RETURN_FALSE;
1965 	}
1966 
1967 	if (PQ_SETNONBLOCKING(pgsql, 0)) {
1968 		php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode");
1969 		RETURN_FALSE;
1970 	}
1971 	while ((pgsql_result = PQgetResult(pgsql))) {
1972 		PQclear(pgsql_result);
1973 		leftover = 1;
1974 	}
1975 	if (leftover) {
1976 		php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first");
1977 	}
1978 
1979 	num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
1980 	if (num_params > 0) {
1981 		int i = 0;
1982 		params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
1983 
1984 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pv_param_arr), tmp) {
1985 			ZVAL_DEREF(tmp);
1986 			if (Z_TYPE_P(tmp) == IS_NULL) {
1987 				params[i] = NULL;
1988 			} else {
1989 				zval tmp_val;
1990 
1991 				ZVAL_COPY(&tmp_val, tmp);
1992 				convert_to_cstring(&tmp_val);
1993 				if (Z_TYPE(tmp_val) != IS_STRING) {
1994 					php_error_docref(NULL, E_WARNING,"Error converting parameter");
1995 					zval_ptr_dtor(&tmp_val);
1996 					_php_pgsql_free_params(params, num_params);
1997 					RETURN_FALSE;
1998 				}
1999 				params[i] = estrndup(Z_STRVAL(tmp_val), Z_STRLEN(tmp_val));
2000 				zval_ptr_dtor(&tmp_val);
2001 			}
2002 			i++;
2003 		} ZEND_HASH_FOREACH_END();
2004 	}
2005 
2006 	pgsql_result = PQexecParams(pgsql, query, num_params,
2007 					NULL, (const char * const *)params, NULL, NULL, 0);
2008 	if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
2009 		PQclear(pgsql_result);
2010 		PQreset(pgsql);
2011 		pgsql_result = PQexecParams(pgsql, query, num_params,
2012 						NULL, (const char * const *)params, NULL, NULL, 0);
2013 	}
2014 
2015 	if (pgsql_result) {
2016 		status = PQresultStatus(pgsql_result);
2017 	} else {
2018 		status = (ExecStatusType) PQstatus(pgsql);
2019 	}
2020 
2021 	_php_pgsql_free_params(params, num_params);
2022 
2023 	switch (status) {
2024 		case PGRES_EMPTY_QUERY:
2025 		case PGRES_BAD_RESPONSE:
2026 		case PGRES_NONFATAL_ERROR:
2027 		case PGRES_FATAL_ERROR:
2028 			PHP_PQ_ERROR("Query failed: %s", pgsql);
2029 			PQclear(pgsql_result);
2030 			RETURN_FALSE;
2031 			break;
2032 		case PGRES_COMMAND_OK: /* successful command that did not return rows */
2033 		default:
2034 			if (pgsql_result) {
2035 				pg_result = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
2036 				pg_result->conn = pgsql;
2037 				pg_result->result = pgsql_result;
2038 				pg_result->row = 0;
2039 				RETURN_RES(zend_register_resource(pg_result, le_result));
2040 			} else {
2041 				PQclear(pgsql_result);
2042 				RETURN_FALSE;
2043 			}
2044 			break;
2045 	}
2046 }
2047 /* }}} */
2048 #endif
2049 
2050 #if HAVE_PQPREPARE
2051 /* {{{ proto resource pg_prepare([resource connection,] string stmtname, string query)
2052    Prepare a query for future execution */
2053 PHP_FUNCTION(pg_prepare)
2054 {
2055 	zval *pgsql_link = NULL;
2056 	char *query, *stmtname;
2057 	size_t query_len, stmtname_len;
2058 	int argc = ZEND_NUM_ARGS();
2059 	int leftover = 0;
2060 	PGconn *pgsql;
2061 	zend_resource *link;
2062 	PGresult *pgsql_result;
2063 	ExecStatusType status;
2064 	pgsql_result_handle *pg_result;
2065 
2066 	if (argc == 2) {
2067 		if (zend_parse_parameters(argc, "ss", &stmtname, &stmtname_len, &query, &query_len) == FAILURE) {
2068 			return;
2069 		}
2070 		link = FETCH_DEFAULT_LINK();
2071 		CHECK_DEFAULT_LINK(link);
2072 	} else {
2073 		if (zend_parse_parameters(argc, "rss", &pgsql_link, &stmtname, &stmtname_len, &query, &query_len) == FAILURE) {
2074 			return;
2075 		}
2076 		link = Z_RES_P(pgsql_link);
2077 	}
2078 
2079 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
2080 		RETURN_FALSE;
2081 	}
2082 
2083 	if (PQ_SETNONBLOCKING(pgsql, 0)) {
2084 		php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode");
2085 		RETURN_FALSE;
2086 	}
2087 	while ((pgsql_result = PQgetResult(pgsql))) {
2088 		PQclear(pgsql_result);
2089 		leftover = 1;
2090 	}
2091 	if (leftover) {
2092 		php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first");
2093 	}
2094 	pgsql_result = PQprepare(pgsql, stmtname, query, 0, NULL);
2095 	if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
2096 		PQclear(pgsql_result);
2097 		PQreset(pgsql);
2098 		pgsql_result = PQprepare(pgsql, stmtname, query, 0, NULL);
2099 	}
2100 
2101 	if (pgsql_result) {
2102 		status = PQresultStatus(pgsql_result);
2103 	} else {
2104 		status = (ExecStatusType) PQstatus(pgsql);
2105 	}
2106 
2107 	switch (status) {
2108 		case PGRES_EMPTY_QUERY:
2109 		case PGRES_BAD_RESPONSE:
2110 		case PGRES_NONFATAL_ERROR:
2111 		case PGRES_FATAL_ERROR:
2112 			PHP_PQ_ERROR("Query failed: %s", pgsql);
2113 			PQclear(pgsql_result);
2114 			RETURN_FALSE;
2115 			break;
2116 		case PGRES_COMMAND_OK: /* successful command that did not return rows */
2117 		default:
2118 			if (pgsql_result) {
2119 				pg_result = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
2120 				pg_result->conn = pgsql;
2121 				pg_result->result = pgsql_result;
2122 				pg_result->row = 0;
2123 				RETURN_RES(zend_register_resource(pg_result, le_result));
2124 			} else {
2125 				PQclear(pgsql_result);
2126 				RETURN_FALSE;
2127 			}
2128 			break;
2129 	}
2130 }
2131 /* }}} */
2132 #endif
2133 
2134 #if HAVE_PQEXECPREPARED
2135 /* {{{ proto resource pg_execute([resource connection,] string stmtname, array params)
2136    Execute a prepared query  */
2137 PHP_FUNCTION(pg_execute)
2138 {
2139 	zval *pgsql_link = NULL;
2140 	zval *pv_param_arr, *tmp;
2141 	char *stmtname;
2142 	size_t stmtname_len;
2143 	int argc = ZEND_NUM_ARGS();
2144 	int leftover = 0;
2145 	int num_params = 0;
2146 	char **params = NULL;
2147 	PGconn *pgsql;
2148 	zend_resource *link;
2149 	PGresult *pgsql_result;
2150 	ExecStatusType status;
2151 	pgsql_result_handle *pg_result;
2152 
2153 	if (argc == 2) {
2154 		if (zend_parse_parameters(argc, "sa", &stmtname, &stmtname_len, &pv_param_arr)==FAILURE) {
2155 			return;
2156 		}
2157 		link = FETCH_DEFAULT_LINK();
2158 		CHECK_DEFAULT_LINK(link);
2159 	} else {
2160 		if (zend_parse_parameters(argc, "rsa", &pgsql_link, &stmtname, &stmtname_len, &pv_param_arr) == FAILURE) {
2161 			return;
2162 		}
2163 		link = Z_RES_P(pgsql_link);
2164 	}
2165 
2166 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
2167 		RETURN_FALSE;
2168 	}
2169 
2170 	if (PQ_SETNONBLOCKING(pgsql, 0)) {
2171 		php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode");
2172 		RETURN_FALSE;
2173 	}
2174 	while ((pgsql_result = PQgetResult(pgsql))) {
2175 		PQclear(pgsql_result);
2176 		leftover = 1;
2177 	}
2178 	if (leftover) {
2179 		php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first");
2180 	}
2181 
2182 	num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
2183 	if (num_params > 0) {
2184 		int i = 0;
2185 		params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
2186 
2187 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pv_param_arr), tmp) {
2188 
2189 			if (Z_TYPE_P(tmp) == IS_NULL) {
2190 				params[i] = NULL;
2191 			} else {
2192 				zend_string *tmp_str;
2193 				zend_string *str = zval_get_tmp_string(tmp, &tmp_str);
2194 
2195 				params[i] = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
2196 				zend_tmp_string_release(tmp_str);
2197 			}
2198 
2199 			i++;
2200 		} ZEND_HASH_FOREACH_END();
2201 	}
2202 
2203 	pgsql_result = PQexecPrepared(pgsql, stmtname, num_params,
2204 					(const char * const *)params, NULL, NULL, 0);
2205 	if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
2206 		PQclear(pgsql_result);
2207 		PQreset(pgsql);
2208 		pgsql_result = PQexecPrepared(pgsql, stmtname, num_params,
2209 						(const char * const *)params, NULL, NULL, 0);
2210 	}
2211 
2212 	if (pgsql_result) {
2213 		status = PQresultStatus(pgsql_result);
2214 	} else {
2215 		status = (ExecStatusType) PQstatus(pgsql);
2216 	}
2217 
2218 	_php_pgsql_free_params(params, num_params);
2219 
2220 	switch (status) {
2221 		case PGRES_EMPTY_QUERY:
2222 		case PGRES_BAD_RESPONSE:
2223 		case PGRES_NONFATAL_ERROR:
2224 		case PGRES_FATAL_ERROR:
2225 			PHP_PQ_ERROR("Query failed: %s", pgsql);
2226 			PQclear(pgsql_result);
2227 			RETURN_FALSE;
2228 			break;
2229 		case PGRES_COMMAND_OK: /* successful command that did not return rows */
2230 		default:
2231 			if (pgsql_result) {
2232 				pg_result = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
2233 				pg_result->conn = pgsql;
2234 				pg_result->result = pgsql_result;
2235 				pg_result->row = 0;
2236 				RETURN_RES(zend_register_resource(pg_result, le_result));
2237 			} else {
2238 				PQclear(pgsql_result);
2239 				RETURN_FALSE;
2240 			}
2241 			break;
2242 	}
2243 }
2244 /* }}} */
2245 #endif
2246 
2247 #define PHP_PG_NUM_ROWS 1
2248 #define PHP_PG_NUM_FIELDS 2
2249 #define PHP_PG_CMD_TUPLES 3
2250 
2251 /* {{{ php_pgsql_get_result_info
2252  */
2253 static void php_pgsql_get_result_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
2254 {
2255 	zval *result;
2256 	PGresult *pgsql_result;
2257 	pgsql_result_handle *pg_result;
2258 
2259 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &result) == FAILURE) {
2260 		return;
2261 	}
2262 
2263 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2264 		RETURN_FALSE;
2265 	}
2266 
2267 	pgsql_result = pg_result->result;
2268 
2269 	switch (entry_type) {
2270 		case PHP_PG_NUM_ROWS:
2271 			RETVAL_LONG(PQntuples(pgsql_result));
2272 			break;
2273 		case PHP_PG_NUM_FIELDS:
2274 			RETVAL_LONG(PQnfields(pgsql_result));
2275 			break;
2276 		case PHP_PG_CMD_TUPLES:
2277 #if HAVE_PQCMDTUPLES
2278 			RETVAL_LONG(atoi(PQcmdTuples(pgsql_result)));
2279 #else
2280 			php_error_docref(NULL, E_WARNING, "Not supported under this build");
2281 			RETVAL_LONG(0);
2282 #endif
2283 			break;
2284 		default:
2285 			RETURN_FALSE;
2286 	}
2287 }
2288 /* }}} */
2289 
2290 /* {{{ proto int pg_num_rows(resource result)
2291    Return the number of rows in the result */
2292 PHP_FUNCTION(pg_num_rows)
2293 {
2294 	php_pgsql_get_result_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_NUM_ROWS);
2295 }
2296 /* }}} */
2297 
2298 /* {{{ proto int pg_num_fields(resource result)
2299    Return the number of fields in the result */
2300 PHP_FUNCTION(pg_num_fields)
2301 {
2302 	php_pgsql_get_result_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_NUM_FIELDS);
2303 }
2304 /* }}} */
2305 
2306 #if HAVE_PQCMDTUPLES
2307 /* {{{ proto int pg_affected_rows(resource result)
2308    Returns the number of affected tuples */
2309 PHP_FUNCTION(pg_affected_rows)
2310 {
2311 	php_pgsql_get_result_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_CMD_TUPLES);
2312 }
2313 /* }}} */
2314 #endif
2315 
2316 /* {{{ proto mixed pg_last_notice(resource connection [, int option])
2317    Returns the last notice set by the backend */
2318 PHP_FUNCTION(pg_last_notice)
2319 {
2320 	zval *pgsql_link = NULL;
2321 	zval *notice, *notices;
2322 	PGconn *pg_link;
2323 	zend_long option = PGSQL_NOTICE_LAST;
2324 
2325 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &pgsql_link, &option) == FAILURE) {
2326 		return;
2327 	}
2328 
2329 	/* Just to check if user passed valid resoruce */
2330 	if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
2331 		RETURN_FALSE;
2332 	}
2333 
2334 	notices = zend_hash_index_find(&PGG(notices), (zend_ulong)Z_RES_HANDLE_P(pgsql_link));
2335 	switch (option) {
2336 		case PGSQL_NOTICE_LAST:
2337 			if (notices) {
2338 				zend_hash_internal_pointer_end(Z_ARRVAL_P(notices));
2339 				if ((notice = zend_hash_get_current_data(Z_ARRVAL_P(notices))) == NULL) {
2340 					RETURN_EMPTY_STRING();
2341 				}
2342 				RETURN_ZVAL(notice, 1, 0);
2343 			} else {
2344 				RETURN_EMPTY_STRING();
2345 			}
2346 			break;
2347 		case PGSQL_NOTICE_ALL:
2348 			if (notices) {
2349 				RETURN_ZVAL(notices, 1, 0);
2350 			} else {
2351 				array_init(return_value);
2352 				return;
2353 			}
2354 			break;
2355 		case PGSQL_NOTICE_CLEAR:
2356 			if (notices) {
2357 				zend_hash_clean(&PGG(notices));
2358 			}
2359 			RETURN_TRUE;
2360 			break;
2361 		default:
2362 			php_error_docref(NULL, E_WARNING,
2363 				"Invalid option specified (" ZEND_LONG_FMT ")", option);
2364 	}
2365 	RETURN_FALSE;
2366 }
2367 /* }}} */
2368 
2369 /* {{{ get_field_name
2370  */
2371 static char *get_field_name(PGconn *pgsql, Oid oid, HashTable *list)
2372 {
2373 	smart_str str = {0};
2374 	zend_resource *field_type;
2375 	char *ret=NULL;
2376 
2377 	/* try to lookup the type in the resource list */
2378 	smart_str_appends(&str, "pgsql_oid_");
2379 	smart_str_append_unsigned(&str, oid);
2380 	smart_str_0(&str);
2381 
2382 	if ((field_type = zend_hash_find_ptr(list, str.s)) != NULL) {
2383 		ret = estrdup((char *)field_type->ptr);
2384 	} else { /* hash all oid's */
2385 		int i, num_rows;
2386 		int oid_offset,name_offset;
2387 		char *tmp_oid, *end_ptr, *tmp_name;
2388 		zend_resource new_oid_entry;
2389 		PGresult *result;
2390 
2391 		if ((result = PQexec(pgsql, "select oid,typname from pg_type")) == NULL || PQresultStatus(result) != PGRES_TUPLES_OK) {
2392 			if (result) {
2393 				PQclear(result);
2394 			}
2395 			smart_str_free(&str);
2396 			return estrndup("", sizeof("") - 1);
2397 		}
2398 		num_rows = PQntuples(result);
2399 		oid_offset = PQfnumber(result,"oid");
2400 		name_offset = PQfnumber(result,"typname");
2401 
2402 		for (i=0; i<num_rows; i++) {
2403 			if ((tmp_oid = PQgetvalue(result,i,oid_offset))==NULL) {
2404 				continue;
2405 			}
2406 
2407 			smart_str_free(&str);
2408 			smart_str_appends(&str, "pgsql_oid_");
2409 			smart_str_appends(&str, tmp_oid);
2410 			smart_str_0(&str);
2411 
2412 			if ((tmp_name = PQgetvalue(result,i,name_offset))==NULL) {
2413 				continue;
2414 			}
2415 			new_oid_entry.type = le_string;
2416 			new_oid_entry.ptr = estrdup(tmp_name);
2417 			zend_hash_update_mem(list, str.s, (void *) &new_oid_entry, sizeof(zend_resource));
2418 			if (!ret && strtoul(tmp_oid, &end_ptr, 10)==oid) {
2419 				ret = estrdup(tmp_name);
2420 			}
2421 		}
2422 		PQclear(result);
2423 	}
2424 
2425 	smart_str_free(&str);
2426 	return ret;
2427 }
2428 /* }}} */
2429 
2430 #ifdef HAVE_PQFTABLE
2431 /* {{{ proto mixed pg_field_table(resource result, int field_number[, bool oid_only])
2432    Returns the name of the table field belongs to, or table's oid if oid_only is true */
2433 PHP_FUNCTION(pg_field_table)
2434 {
2435 	zval *result;
2436 	pgsql_result_handle *pg_result;
2437 	zend_long fnum = -1;
2438 	zend_bool return_oid = 0;
2439 	Oid oid;
2440 	smart_str hash_key = {0};
2441 	char *table_name;
2442 	zend_resource *field_table;
2443 
2444 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl|b", &result, &fnum, &return_oid) == FAILURE) {
2445 		return;
2446 	}
2447 
2448 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2449 		RETURN_FALSE;
2450 	}
2451 
2452 	if (fnum < 0 || fnum >= PQnfields(pg_result->result)) {
2453 		php_error_docref(NULL, E_WARNING, "Bad field offset specified");
2454 		RETURN_FALSE;
2455 	}
2456 
2457 	oid = PQftable(pg_result->result, (int)fnum);
2458 
2459 	if (InvalidOid == oid) {
2460 		RETURN_FALSE;
2461 	}
2462 
2463 	if (return_oid) {
2464 #if UINT_MAX > ZEND_LONG_MAX /* Oid is unsigned int, we don't need this code, where LONG is wider */
2465 		if (oid > ZEND_LONG_MAX) {
2466 			smart_str oidstr = {0};
2467 			smart_str_append_unsigned(&oidstr, oid);
2468 			smart_str_0(&oidstr);
2469 			RETURN_NEW_STR(oidstr.s);
2470 		} else
2471 #endif
2472 			RETURN_LONG((zend_long)oid);
2473 	}
2474 
2475 	/* try to lookup the table name in the resource list */
2476 	smart_str_appends(&hash_key, "pgsql_table_oid_");
2477 	smart_str_append_unsigned(&hash_key, oid);
2478 	smart_str_0(&hash_key);
2479 
2480 	if ((field_table = zend_hash_find_ptr(&EG(regular_list), hash_key.s)) != NULL) {
2481 		smart_str_free(&hash_key);
2482 		RETURN_STRING((char *)field_table->ptr);
2483 	} else { /* Not found, lookup by querying PostgreSQL system tables */
2484 		PGresult *tmp_res;
2485 		smart_str querystr = {0};
2486 		zend_resource new_field_table;
2487 
2488 		smart_str_appends(&querystr, "select relname from pg_class where oid=");
2489 		smart_str_append_unsigned(&querystr, oid);
2490 		smart_str_0(&querystr);
2491 
2492 		if ((tmp_res = PQexec(pg_result->conn, ZSTR_VAL(querystr.s))) == NULL || PQresultStatus(tmp_res) != PGRES_TUPLES_OK) {
2493 			if (tmp_res) {
2494 				PQclear(tmp_res);
2495 			}
2496 			smart_str_free(&querystr);
2497 			smart_str_free(&hash_key);
2498 			RETURN_FALSE;
2499 		}
2500 
2501 		smart_str_free(&querystr);
2502 
2503 		if ((table_name = PQgetvalue(tmp_res, 0, 0)) == NULL) {
2504 			PQclear(tmp_res);
2505 			smart_str_free(&hash_key);
2506 			RETURN_FALSE;
2507 		}
2508 
2509 		new_field_table.type = le_string;
2510 		new_field_table.ptr = estrdup(table_name);
2511 		zend_hash_update_mem(&EG(regular_list), hash_key.s, (void *)&new_field_table, sizeof(zend_resource));
2512 
2513 		smart_str_free(&hash_key);
2514 		PQclear(tmp_res);
2515 		RETURN_STRING(table_name);
2516 	}
2517 
2518 }
2519 /* }}} */
2520 #endif
2521 
2522 #define PHP_PG_FIELD_NAME 1
2523 #define PHP_PG_FIELD_SIZE 2
2524 #define PHP_PG_FIELD_TYPE 3
2525 #define PHP_PG_FIELD_TYPE_OID 4
2526 
2527 /* {{{ php_pgsql_get_field_info
2528  */
2529 static void php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
2530 {
2531 	zval *result;
2532 	zend_long field;
2533 	PGresult *pgsql_result;
2534 	pgsql_result_handle *pg_result;
2535 	Oid oid;
2536 
2537 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &result, &field) == FAILURE) {
2538 		return;
2539 	}
2540 
2541 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2542 		RETURN_FALSE;
2543 	}
2544 
2545 
2546 	pgsql_result = pg_result->result;
2547 
2548 	if (field < 0 || field >= PQnfields(pgsql_result)) {
2549 		php_error_docref(NULL, E_WARNING, "Bad field offset specified");
2550 		RETURN_FALSE;
2551 	}
2552 
2553 	switch (entry_type) {
2554 		case PHP_PG_FIELD_NAME:
2555 			RETURN_STRING(PQfname(pgsql_result, (int)field));
2556 			break;
2557 		case PHP_PG_FIELD_SIZE:
2558 			RETURN_LONG(PQfsize(pgsql_result, (int)field));
2559 			break;
2560 		case PHP_PG_FIELD_TYPE: {
2561 				char *name = get_field_name(pg_result->conn, PQftype(pgsql_result, (int)field), &EG(regular_list));
2562 				RETVAL_STRING(name);
2563 				efree(name);
2564 			}
2565 			break;
2566 		case PHP_PG_FIELD_TYPE_OID:
2567 
2568 			oid = PQftype(pgsql_result, (int)field);
2569 #if UINT_MAX > ZEND_LONG_MAX
2570 			if (oid > ZEND_LONG_MAX) {
2571 				smart_str s = {0};
2572 				smart_str_append_unsigned(&s, oid);
2573 				smart_str_0(&s);
2574 				RETURN_NEW_STR(s.s);
2575 			} else
2576 #endif
2577 			{
2578 				RETURN_LONG((zend_long)oid);
2579 			}
2580 			break;
2581 		default:
2582 			RETURN_FALSE;
2583 	}
2584 }
2585 /* }}} */
2586 
2587 /* {{{ proto string pg_field_name(resource result, int field_number)
2588    Returns the name of the field */
2589 PHP_FUNCTION(pg_field_name)
2590 {
2591 	php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_NAME);
2592 }
2593 /* }}} */
2594 
2595 /* {{{ proto int pg_field_size(resource result, int field_number)
2596    Returns the internal size of the field */
2597 PHP_FUNCTION(pg_field_size)
2598 {
2599 	php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_SIZE);
2600 }
2601 /* }}} */
2602 
2603 /* {{{ proto string pg_field_type(resource result, int field_number)
2604    Returns the type name for the given field */
2605 PHP_FUNCTION(pg_field_type)
2606 {
2607 	php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_TYPE);
2608 }
2609 /* }}} */
2610 
2611 /* {{{ proto string pg_field_type_oid(resource result, int field_number)
2612    Returns the type oid for the given field */
2613 PHP_FUNCTION(pg_field_type_oid)
2614 {
2615 	php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_TYPE_OID);
2616 }
2617 /* }}} */
2618 
2619 /* {{{ proto int pg_field_num(resource result, string field_name)
2620    Returns the field number of the named field */
2621 PHP_FUNCTION(pg_field_num)
2622 {
2623 	zval *result;
2624 	char *field;
2625 	size_t field_len;
2626 	PGresult *pgsql_result;
2627 	pgsql_result_handle *pg_result;
2628 
2629 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &result, &field, &field_len) == FAILURE) {
2630 		return;
2631 	}
2632 
2633 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2634 		RETURN_FALSE;
2635 	}
2636 
2637 	pgsql_result = pg_result->result;
2638 
2639 	RETURN_LONG(PQfnumber(pgsql_result, field));
2640 }
2641 /* }}} */
2642 
2643 /* {{{ proto mixed pg_fetch_result(resource result, [int row_number,] mixed field_name)
2644    Returns values from a result identifier */
2645 PHP_FUNCTION(pg_fetch_result)
2646 {
2647 	zval *result, *field=NULL;
2648 	zend_long row;
2649 	PGresult *pgsql_result;
2650 	pgsql_result_handle *pg_result;
2651 	int field_offset, pgsql_row, argc = ZEND_NUM_ARGS();
2652 
2653 	if (argc == 2) {
2654 		if (zend_parse_parameters(argc, "rz", &result, &field) == FAILURE) {
2655 			return;
2656 		}
2657 	} else {
2658 		if (zend_parse_parameters(argc, "rlz", &result, &row, &field) == FAILURE) {
2659 			return;
2660 		}
2661 	}
2662 
2663 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2664 		RETURN_FALSE;
2665 	}
2666 
2667 	pgsql_result = pg_result->result;
2668 	if (argc == 2) {
2669 		if (pg_result->row < 0) {
2670 			pg_result->row = 0;
2671 		}
2672 		pgsql_row = pg_result->row;
2673 		if (pgsql_row >= PQntuples(pgsql_result)) {
2674 			RETURN_FALSE;
2675 		}
2676 		pg_result->row++;
2677 	} else {
2678 		if (row < 0 || row >= PQntuples(pgsql_result)) {
2679 			php_error_docref(NULL, E_WARNING, "Unable to jump to row " ZEND_LONG_FMT " on PostgreSQL result index " ZEND_LONG_FMT,
2680 							row, Z_LVAL_P(result));
2681 			RETURN_FALSE;
2682 		}
2683 		pgsql_row = (int)row;
2684 	}
2685 	switch (Z_TYPE_P(field)) {
2686 		case IS_STRING:
2687 			field_offset = PQfnumber(pgsql_result, Z_STRVAL_P(field));
2688 			if (field_offset < 0 || field_offset >= PQnfields(pgsql_result)) {
2689 				php_error_docref(NULL, E_WARNING, "Bad column offset specified");
2690 				RETURN_FALSE;
2691 			}
2692 			break;
2693 		default:
2694 			convert_to_long_ex(field);
2695 			if (Z_LVAL_P(field) < 0 || Z_LVAL_P(field) >= PQnfields(pgsql_result)) {
2696 				php_error_docref(NULL, E_WARNING, "Bad column offset specified");
2697 				RETURN_FALSE;
2698 			}
2699 			field_offset = (int)Z_LVAL_P(field);
2700 			break;
2701 	}
2702 
2703 	if (PQgetisnull(pgsql_result, pgsql_row, field_offset)) {
2704 		RETVAL_NULL();
2705 	} else {
2706 		RETVAL_STRINGL(PQgetvalue(pgsql_result, pgsql_row, field_offset),
2707 				PQgetlength(pgsql_result, pgsql_row, field_offset));
2708 	}
2709 }
2710 /* }}} */
2711 
2712 /* {{{ void php_pgsql_fetch_hash */
2713 static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_type, int into_object)
2714 {
2715 	zval                *result, *zrow = NULL;
2716 	PGresult            *pgsql_result;
2717 	pgsql_result_handle *pg_result;
2718 	int             i, num_fields, pgsql_row, use_row;
2719 	zend_long            row = -1;
2720 	char            *field_name;
2721 	zval            *ctor_params = NULL;
2722 	zend_class_entry *ce = NULL;
2723 
2724 	if (into_object) {
2725 		zend_string *class_name = NULL;
2726 
2727 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|z!Sz", &result, &zrow, &class_name, &ctor_params) == FAILURE) {
2728 			return;
2729 		}
2730 		if (!class_name) {
2731 			ce = zend_standard_class_def;
2732 		} else {
2733 			ce = zend_fetch_class(class_name, ZEND_FETCH_CLASS_AUTO);
2734 		}
2735 		if (!ce) {
2736 			php_error_docref(NULL, E_WARNING, "Could not find class '%s'", ZSTR_VAL(class_name));
2737 			return;
2738 		}
2739 		result_type = PGSQL_ASSOC;
2740 	} else {
2741 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|z!l", &result, &zrow, &result_type) == FAILURE) {
2742 			return;
2743 		}
2744 	}
2745 	if (zrow == NULL) {
2746 		row = -1;
2747 	} else {
2748 		convert_to_long(zrow);
2749 		row = Z_LVAL_P(zrow);
2750 		if (row < 0) {
2751 			php_error_docref(NULL, E_WARNING, "The row parameter must be greater or equal to zero");
2752 			RETURN_FALSE;
2753 		}
2754 	}
2755 	use_row = ZEND_NUM_ARGS() > 1 && row != -1;
2756 
2757 	if (!(result_type & PGSQL_BOTH)) {
2758 		php_error_docref(NULL, E_WARNING, "Invalid result type");
2759 		RETURN_FALSE;
2760 	}
2761 
2762 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2763 		RETURN_FALSE;
2764 	}
2765 
2766 	pgsql_result = pg_result->result;
2767 
2768 	if (use_row) {
2769 		if (row < 0 || row >= PQntuples(pgsql_result)) {
2770 			php_error_docref(NULL, E_WARNING, "Unable to jump to row " ZEND_LONG_FMT " on PostgreSQL result index " ZEND_LONG_FMT,
2771 							row, Z_LVAL_P(result));
2772 			RETURN_FALSE;
2773 		}
2774 		pgsql_row = (int)row;
2775 		pg_result->row = pgsql_row;
2776 	} else {
2777 		/* If 2nd param is NULL, use internal row counter to access next row */
2778 		pgsql_row = pg_result->row;
2779 		if (pgsql_row < 0 || pgsql_row >= PQntuples(pgsql_result)) {
2780 			RETURN_FALSE;
2781 		}
2782 		pg_result->row++;
2783 	}
2784 
2785 	array_init(return_value);
2786 	for (i = 0, num_fields = PQnfields(pgsql_result); i < num_fields; i++) {
2787 		if (PQgetisnull(pgsql_result, pgsql_row, i)) {
2788 			if (result_type & PGSQL_NUM) {
2789 				add_index_null(return_value, i);
2790 			}
2791 			if (result_type & PGSQL_ASSOC) {
2792 				field_name = PQfname(pgsql_result, i);
2793 				add_assoc_null(return_value, field_name);
2794 			}
2795 		} else {
2796 			char *element = PQgetvalue(pgsql_result, pgsql_row, i);
2797 			if (element) {
2798 				const size_t element_len = strlen(element);
2799 
2800 				if (result_type & PGSQL_NUM) {
2801 					add_index_stringl(return_value, i, element, element_len);
2802 				}
2803 
2804 				if (result_type & PGSQL_ASSOC) {
2805 					field_name = PQfname(pgsql_result, i);
2806 					add_assoc_stringl(return_value, field_name, element, element_len);
2807 				}
2808 			}
2809 		}
2810 	}
2811 
2812 	if (into_object) {
2813 		zval dataset;
2814 		zend_fcall_info fci;
2815 		zend_fcall_info_cache fcc;
2816 		zval retval;
2817 
2818 		ZVAL_COPY_VALUE(&dataset, return_value);
2819 		object_init_ex(return_value, ce);
2820 		if (!ce->default_properties_count && !ce->__set) {
2821 			Z_OBJ_P(return_value)->properties = Z_ARR(dataset);
2822 		} else {
2823 			zend_merge_properties(return_value, Z_ARRVAL(dataset));
2824 			zval_ptr_dtor(&dataset);
2825 		}
2826 
2827 		if (ce->constructor) {
2828 			fci.size = sizeof(fci);
2829 			ZVAL_UNDEF(&fci.function_name);
2830 			fci.object = Z_OBJ_P(return_value);
2831 			fci.retval = &retval;
2832 			fci.params = NULL;
2833 			fci.param_count = 0;
2834 			fci.no_separation = 1;
2835 
2836 			if (ctor_params && Z_TYPE_P(ctor_params) != IS_NULL) {
2837 				if (zend_fcall_info_args(&fci, ctor_params) == FAILURE) {
2838 					/* Two problems why we throw exceptions here: PHP is typeless
2839 					 * and hence passing one argument that's not an array could be
2840 					 * by mistake and the other way round is possible, too. The
2841 					 * single value is an array. Also we'd have to make that one
2842 					 * argument passed by reference.
2843 					 */
2844 					zend_throw_exception(zend_ce_exception, "Parameter ctor_params must be an array", 0);
2845 					return;
2846 				}
2847 			}
2848 
2849 			fcc.function_handler = ce->constructor;
2850 			fcc.called_scope = Z_OBJCE_P(return_value);
2851 			fcc.object = Z_OBJ_P(return_value);
2852 
2853 			if (zend_call_function(&fci, &fcc) == FAILURE) {
2854 				zend_throw_exception_ex(zend_ce_exception, 0, "Could not execute %s::%s()", ZSTR_VAL(ce->name), ZSTR_VAL(ce->constructor->common.function_name));
2855 			} else {
2856 				zval_ptr_dtor(&retval);
2857 			}
2858 			if (fci.params) {
2859 				efree(fci.params);
2860 			}
2861 		} else if (ctor_params) {
2862 			zend_throw_exception_ex(zend_ce_exception, 0, "Class %s does not have a constructor hence you cannot use ctor_params", ZSTR_VAL(ce->name));
2863 		}
2864 	}
2865 }
2866 /* }}} */
2867 
2868 /* {{{ proto array pg_fetch_row(resource result [, int row [, int result_type]])
2869    Get a row as an enumerated array */
2870 PHP_FUNCTION(pg_fetch_row)
2871 {
2872 	php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_NUM, 0);
2873 }
2874 /* }}} */
2875 
2876 /* {{{ proto array pg_fetch_assoc(resource result [, int row])
2877    Fetch a row as an assoc array */
2878 PHP_FUNCTION(pg_fetch_assoc)
2879 {
2880 	/* pg_fetch_assoc() is added from PHP 4.3.0. It should raise error, when
2881 	   there is 3rd parameter */
2882 	if (ZEND_NUM_ARGS() > 2)
2883 		WRONG_PARAM_COUNT;
2884 	php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_ASSOC, 0);
2885 }
2886 /* }}} */
2887 
2888 /* {{{ proto array pg_fetch_array(resource result [, int row [, int result_type]])
2889    Fetch a row as an array */
2890 PHP_FUNCTION(pg_fetch_array)
2891 {
2892 	php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_BOTH, 0);
2893 }
2894 /* }}} */
2895 
2896 /* {{{ proto object pg_fetch_object(resource result [, int row [, string class_name [, NULL|array ctor_params]]])
2897    Fetch a row as an object */
2898 PHP_FUNCTION(pg_fetch_object)
2899 {
2900 	/* pg_fetch_object() allowed result_type used to be. 3rd parameter
2901 	   must be allowed for compatibility */
2902 	php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_ASSOC, 1);
2903 }
2904 /* }}} */
2905 
2906 /* {{{ proto array pg_fetch_all(resource result [, int result_type])
2907    Fetch all rows into array */
2908 PHP_FUNCTION(pg_fetch_all)
2909 {
2910 	zval *result;
2911 	long result_type = PGSQL_ASSOC;
2912 	PGresult *pgsql_result;
2913 	pgsql_result_handle *pg_result;
2914 
2915 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &result, &result_type) == FAILURE) {
2916 		return;
2917 	}
2918 
2919 	if (!(result_type & PGSQL_BOTH)) {
2920 		php_error_docref(NULL, E_WARNING, "Invalid result type");
2921 		RETURN_FALSE;
2922 	}
2923 
2924 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2925 		RETURN_FALSE;
2926 	}
2927 
2928 	pgsql_result = pg_result->result;
2929 	array_init(return_value);
2930 	if (php_pgsql_result2array(pgsql_result, return_value, result_type) == FAILURE) {
2931 		zend_array_destroy(Z_ARR_P(return_value));
2932 		RETURN_FALSE;
2933 	}
2934 }
2935 /* }}} */
2936 
2937 /* {{{ proto array pg_fetch_all_columns(resource result [, int column_number])
2938    Fetch all rows into array */
2939 PHP_FUNCTION(pg_fetch_all_columns)
2940 {
2941 	zval *result;
2942 	PGresult *pgsql_result;
2943 	pgsql_result_handle *pg_result;
2944 	zend_long colno=0;
2945 	int pg_numrows, pg_row;
2946 	size_t num_fields;
2947 
2948 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &result, &colno) == FAILURE) {
2949 		RETURN_FALSE;
2950 	}
2951 
2952 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2953 		RETURN_FALSE;
2954 	}
2955 
2956 	pgsql_result = pg_result->result;
2957 
2958 	num_fields = PQnfields(pgsql_result);
2959 	if (colno >= (zend_long)num_fields || colno < 0) {
2960 		php_error_docref(NULL, E_WARNING, "Invalid column number '" ZEND_LONG_FMT "'", colno);
2961 		RETURN_FALSE;
2962 	}
2963 
2964 	array_init(return_value);
2965 
2966 	if ((pg_numrows = PQntuples(pgsql_result)) <= 0) {
2967 		return;
2968 	}
2969 
2970 	for (pg_row = 0; pg_row < pg_numrows; pg_row++) {
2971 		if (PQgetisnull(pgsql_result, pg_row, (int)colno)) {
2972 			add_next_index_null(return_value);
2973 		} else {
2974 			add_next_index_string(return_value, PQgetvalue(pgsql_result, pg_row, (int)colno));
2975 		}
2976 	}
2977 }
2978 /* }}} */
2979 
2980 /* {{{ proto bool pg_result_seek(resource result, int offset)
2981    Set internal row offset */
2982 PHP_FUNCTION(pg_result_seek)
2983 {
2984 	zval *result;
2985 	zend_long row;
2986 	pgsql_result_handle *pg_result;
2987 
2988 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &result, &row) == FAILURE) {
2989 		return;
2990 	}
2991 
2992 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
2993 		RETURN_FALSE;
2994 	}
2995 
2996 	if (row < 0 || row >= PQntuples(pg_result->result)) {
2997 		RETURN_FALSE;
2998 	}
2999 
3000 	/* seek to offset */
3001 	pg_result->row = (int)row;
3002 	RETURN_TRUE;
3003 }
3004 /* }}} */
3005 
3006 #define PHP_PG_DATA_LENGTH 1
3007 #define PHP_PG_DATA_ISNULL 2
3008 
3009 /* {{{ php_pgsql_data_info
3010  */
3011 static void php_pgsql_data_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
3012 {
3013 	zval *result, *field;
3014 	zend_long row;
3015 	PGresult *pgsql_result;
3016 	pgsql_result_handle *pg_result;
3017 	int field_offset, pgsql_row, argc = ZEND_NUM_ARGS();
3018 
3019 	if (argc == 2) {
3020 		if (zend_parse_parameters(argc, "rz", &result, &field) == FAILURE) {
3021 			return;
3022 		}
3023 	} else {
3024 		if (zend_parse_parameters(argc, "rlz", &result, &row, &field) == FAILURE) {
3025 			return;
3026 		}
3027 	}
3028 
3029 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
3030 		RETURN_FALSE;
3031 	}
3032 
3033 	pgsql_result = pg_result->result;
3034 	if (argc == 2) {
3035 		if (pg_result->row < 0) {
3036 			pg_result->row = 0;
3037 		}
3038 		pgsql_row = pg_result->row;
3039 		if (pgsql_row < 0 || pgsql_row >= PQntuples(pgsql_result)) {
3040 			RETURN_FALSE;
3041 		}
3042 	} else {
3043 		if (row < 0 || row >= PQntuples(pgsql_result)) {
3044 			php_error_docref(NULL, E_WARNING, "Unable to jump to row " ZEND_LONG_FMT " on PostgreSQL result index " ZEND_LONG_FMT,
3045 							row, Z_LVAL_P(result));
3046 			RETURN_FALSE;
3047 		}
3048 		pgsql_row = (int)row;
3049 	}
3050 
3051 	switch (Z_TYPE_P(field)) {
3052 		case IS_STRING:
3053 			field_offset = PQfnumber(pgsql_result, Z_STRVAL_P(field));
3054 			if (field_offset < 0 || field_offset >= PQnfields(pgsql_result)) {
3055 				php_error_docref(NULL, E_WARNING, "Bad column offset specified");
3056 				RETURN_FALSE;
3057 			}
3058 			break;
3059 		default:
3060 			convert_to_long_ex(field);
3061 			if (Z_LVAL_P(field) < 0 || Z_LVAL_P(field) >= PQnfields(pgsql_result)) {
3062 				php_error_docref(NULL, E_WARNING, "Bad column offset specified");
3063 				RETURN_FALSE;
3064 			}
3065 			field_offset = (int)Z_LVAL_P(field);
3066 			break;
3067 	}
3068 
3069 	switch (entry_type) {
3070 		case PHP_PG_DATA_LENGTH:
3071 			RETVAL_LONG(PQgetlength(pgsql_result, pgsql_row, field_offset));
3072 			break;
3073 		case PHP_PG_DATA_ISNULL:
3074 			RETVAL_LONG(PQgetisnull(pgsql_result, pgsql_row, field_offset));
3075 			break;
3076 	}
3077 }
3078 /* }}} */
3079 
3080 /* {{{ proto int pg_field_prtlen(resource result, [int row,] mixed field_name_or_number)
3081    Returns the printed length */
3082 PHP_FUNCTION(pg_field_prtlen)
3083 {
3084 	php_pgsql_data_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_DATA_LENGTH);
3085 }
3086 /* }}} */
3087 
3088 /* {{{ proto int pg_field_is_null(resource result, [int row,] mixed field_name_or_number)
3089    Test if a field is NULL */
3090 PHP_FUNCTION(pg_field_is_null)
3091 {
3092 	php_pgsql_data_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_DATA_ISNULL);
3093 }
3094 /* }}} */
3095 
3096 /* {{{ proto bool pg_free_result(resource result)
3097    Free result memory */
3098 PHP_FUNCTION(pg_free_result)
3099 {
3100 	zval *result;
3101 	pgsql_result_handle *pg_result;
3102 
3103 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &result) == FAILURE) {
3104 		return;
3105 	}
3106 
3107 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
3108 		RETURN_FALSE;
3109 	}
3110 
3111 	zend_list_close(Z_RES_P(result));
3112 	RETURN_TRUE;
3113 }
3114 /* }}} */
3115 
3116 /* {{{ proto string pg_last_oid(resource result)
3117    Returns the last object identifier */
3118 PHP_FUNCTION(pg_last_oid)
3119 {
3120 	zval *result;
3121 	PGresult *pgsql_result;
3122 	pgsql_result_handle *pg_result;
3123 #ifdef HAVE_PQOIDVALUE
3124 	Oid oid;
3125 #endif
3126 
3127 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &result) == FAILURE) {
3128 		return;
3129 	}
3130 
3131 	if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
3132 		RETURN_FALSE;
3133 	}
3134 
3135 	pgsql_result = pg_result->result;
3136 #ifdef HAVE_PQOIDVALUE
3137 	oid = PQoidValue(pgsql_result);
3138 	if (oid == InvalidOid) {
3139 		RETURN_FALSE;
3140 	}
3141 	PGSQL_RETURN_OID(oid);
3142 #else
3143 	Z_STRVAL_P(return_value) = (char *) PQoidStatus(pgsql_result);
3144 	if (Z_STRVAL_P(return_value)) {
3145 		RETURN_STRING(Z_STRVAL_P(return_value));
3146 	}
3147 	RETURN_EMPTY_STRING();
3148 #endif
3149 }
3150 /* }}} */
3151 
3152 /* {{{ proto bool pg_trace(string filename [, string mode [, resource connection]])
3153    Enable tracing a PostgreSQL connection */
3154 PHP_FUNCTION(pg_trace)
3155 {
3156 	char *z_filename, *mode = "w";
3157 	size_t z_filename_len, mode_len;
3158 	zval *pgsql_link = NULL;
3159 	int argc = ZEND_NUM_ARGS();
3160 	PGconn *pgsql;
3161 	FILE *fp = NULL;
3162 	php_stream *stream;
3163 	zend_resource *link;
3164 
3165 	if (zend_parse_parameters(argc, "p|sr", &z_filename, &z_filename_len, &mode, &mode_len, &pgsql_link) == FAILURE) {
3166 		return;
3167 	}
3168 
3169 	if (argc < 3) {
3170 		link = FETCH_DEFAULT_LINK();
3171 		CHECK_DEFAULT_LINK(link);
3172 	} else {
3173 		link = Z_RES_P(pgsql_link);
3174 	}
3175 
3176 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3177 		RETURN_FALSE;
3178 	}
3179 
3180 	stream = php_stream_open_wrapper(z_filename, mode, REPORT_ERRORS, NULL);
3181 
3182 	if (!stream) {
3183 		RETURN_FALSE;
3184 	}
3185 
3186 	if (FAILURE == php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void**)&fp, REPORT_ERRORS))	{
3187 		php_stream_close(stream);
3188 		RETURN_FALSE;
3189 	}
3190 	php_stream_auto_cleanup(stream);
3191 	PQtrace(pgsql, fp);
3192 	RETURN_TRUE;
3193 }
3194 /* }}} */
3195 
3196 /* {{{ proto bool pg_untrace([resource connection])
3197    Disable tracing of a PostgreSQL connection */
3198 PHP_FUNCTION(pg_untrace)
3199 {
3200 	zval *pgsql_link = NULL;
3201 	int argc = ZEND_NUM_ARGS();
3202 	PGconn *pgsql;
3203 	zend_resource *link;
3204 
3205 	if (zend_parse_parameters(argc, "|r", &pgsql_link) == FAILURE) {
3206 		return;
3207 	}
3208 
3209 	if (argc == 0) {
3210 		link = FETCH_DEFAULT_LINK();
3211 		CHECK_DEFAULT_LINK(link);
3212 	} else {
3213 		link = Z_RES_P(pgsql_link);
3214 	}
3215 
3216 	if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) {
3217 		RETURN_FALSE;
3218 	}
3219 
3220 	PQuntrace(pgsql);
3221 	RETURN_TRUE;
3222 }
3223 /* }}} */
3224 
3225 /* {{{ proto mixed pg_lo_create([resource connection],[mixed large_object_oid])
3226    Create a large object */
3227 PHP_FUNCTION(pg_lo_create)
3228 {
3229 	zval *pgsql_link = NULL, *oid = NULL;
3230 	PGconn *pgsql;
3231 	Oid pgsql_oid, wanted_oid = InvalidOid;
3232 	int argc = ZEND_NUM_ARGS();
3233 	zend_resource *link;
3234 
3235 	if (zend_parse_parameters(argc, "|zz", &pgsql_link, &oid) == FAILURE) {
3236 		return;
3237 	}
3238 
3239 	if ((argc == 1) && (Z_TYPE_P(pgsql_link) != IS_RESOURCE)) {
3240 		oid = pgsql_link;
3241 		pgsql_link = NULL;
3242 	}
3243 
3244 	if (pgsql_link == NULL) {
3245 		link = FETCH_DEFAULT_LINK();
3246 		CHECK_DEFAULT_LINK(link);
3247 	} else if ((Z_TYPE_P(pgsql_link) == IS_RESOURCE)) {
3248 		link = Z_RES_P(pgsql_link);
3249 	} else {
3250 		link = NULL;
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 */
3304 PHP_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;