xref: /PHP-5.6/ext/oci8/oci8.c (revision 78488a54)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2016 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Stig Sæther Bakken <ssb@php.net>                            |
16    |          Thies C. Arntzen <thies@thieso.net>                         |
17    |          Maxim Maletsky <maxim@maxim.cx>                             |
18    |                                                                      |
19    | Collection support by Andy Sautins <asautins@veripost.net>           |
20    | Temporary LOB support by David Benson <dbenson@mancala.com>          |
21    | ZTS per process OCIPLogon by Harald Radi <harald.radi@nme.at>        |
22    |                                                                      |
23    | Redesigned by: Antony Dovgal <antony@zend.com>                       |
24    |                Andi Gutmans <andi@zend.com>                          |
25    |                Wez Furlong <wez@omniti.com>                          |
26    +----------------------------------------------------------------------+
27 */
28 
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 
33 #include "php.h"
34 #include "ext/standard/info.h"
35 #include "php_ini.h"
36 #include "ext/standard/php_smart_str.h"
37 
38 #if HAVE_OCI8
39 
40 /* PHP 5.2 is the minimum supported version for OCI8 2.0 */
41 #if PHP_MAJOR_VERSION < 5 || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION <= 1)
42 #error Use PHP OCI8 1.4 for your version of PHP
43 #elif PHP_MAJOR_VERSION >= 7
44 #error Use PHP OCI8 2.1 for your version of PHP
45 #endif
46 
47 #include "php_oci8.h"
48 #include "php_oci8_int.h"
49 #include "zend_hash.h"
50 
51 #if defined(__PTRDIFF_TYPE__)
52 # define OCI8_INT_TO_PTR(I)  ((void*)(__PTRDIFF_TYPE__)(I))
53 # define OCI8_PTR_TO_INT(P)  ((int)(__PTRDIFF_TYPE__)(P))
54 #elif !defined(__GNUC__)
55 #define OCI8_INT_TO_PTR(I)  ((void*)&((char*)0)[I])
56 #define OCI8_PTR_TO_INT(P)  ((int)(((char*)P)-(char*)0))
57 #elif defined(HAVE_STDINT_H)
58 #define OCI8_INT_TO_PTR(I)  ((void*)(intptr_t)(I))
59 #define OCI8_PTR_TO_INT(P)  ((int)(intptr_t)(P))
60 #else
61 #define OCI8_INT_TO_PTR(I)  ((void*)(I))
62 #define OCI8_PTR_TO_INT(P)  ((int)(P))
63 #endif
64 
65 ZEND_DECLARE_MODULE_GLOBALS(oci)
66 static PHP_GINIT_FUNCTION(oci);
67 static PHP_GSHUTDOWN_FUNCTION(oci);
68 
69 /* Allow PHP 5.3 branch to be used in PECL for 5.x compatible builds */
70 #ifndef Z_ADDREF_P
71 #define Z_ADDREF_P(x) ZVAL_ADDREF(x)
72 #endif
73 
74 /* For a user friendly message about environment setup */
75 #if defined(PHP_WIN32)
76 #define PHP_OCI8_LIB_PATH_MSG "PATH"
77 #elif defined(__APPLE__)
78 #define PHP_OCI8_LIB_PATH_MSG "DYLD_LIBRARY_PATH"
79 #elif defined(_AIX)
80 #define PHP_OCI8_LIB_PATH_MSG "LIBPATH"
81 #elif defined(__hpux)
82 #define PHP_OCI8_LIB_PATH_MSG "SHLIB_PATH"
83 #else
84 #define PHP_OCI8_LIB_PATH_MSG "LD_LIBRARY_PATH"
85 #endif
86 
87 /* True globals, no need for thread safety */
88 int le_connection;
89 int le_pconnection;
90 int le_statement;
91 int le_descriptor;
92 int le_psessionpool;
93 int le_collection;
94 
95 zend_class_entry *oci_lob_class_entry_ptr;
96 zend_class_entry *oci_coll_class_entry_ptr;
97 
98 #ifndef SQLT_BFILEE
99 #define SQLT_BFILEE 114
100 #endif
101 #ifndef SQLT_CFILEE
102 #define SQLT_CFILEE 115
103 #endif
104 
105 #ifdef OCI_ERROR_MAXMSG_SIZE2
106 /* Bigger size is defined from 11.2.0.3 onwards */
107 #define PHP_OCI_ERRBUF_LEN OCI_ERROR_MAXMSG_SIZE2
108 #else
109 #define PHP_OCI_ERRBUF_LEN OCI_ERROR_MAXMSG_SIZE
110 #endif
111 
112 #if ZEND_MODULE_API_NO > 20020429
113 #define ONUPDATELONGFUNC OnUpdateLong
114 #else
115 #define ONUPDATELONGFUNC OnUpdateInt
116 #endif
117 
118 #ifdef ZTS
119 #define PHP_OCI_INIT_MODE (OCI_DEFAULT | OCI_OBJECT | OCI_THREADED | OCI_NO_MUTEX)
120 #else
121 #define PHP_OCI_INIT_MODE (OCI_DEFAULT | OCI_OBJECT)
122 #endif
123 
124 /* {{{ static protos */
125 static void php_oci_connection_list_dtor (zend_rsrc_list_entry * TSRMLS_DC);
126 static void php_oci_pconnection_list_dtor (zend_rsrc_list_entry * TSRMLS_DC);
127 static void php_oci_pconnection_list_np_dtor (zend_rsrc_list_entry * TSRMLS_DC);
128 static void php_oci_statement_list_dtor (zend_rsrc_list_entry * TSRMLS_DC);
129 static void php_oci_descriptor_list_dtor (zend_rsrc_list_entry * TSRMLS_DC);
130 static void php_oci_spool_list_dtor(zend_rsrc_list_entry *entry TSRMLS_DC);
131 static void php_oci_collection_list_dtor (zend_rsrc_list_entry * TSRMLS_DC);
132 
133 static int php_oci_persistent_helper(zend_rsrc_list_entry *le TSRMLS_DC);
134 static int php_oci_connection_ping(php_oci_connection * TSRMLS_DC);
135 static int php_oci_connection_status(php_oci_connection * TSRMLS_DC);
136 static int php_oci_connection_close(php_oci_connection * TSRMLS_DC);
137 static void php_oci_spool_close(php_oci_spool *session_pool TSRMLS_DC);
138 
139 static OCIEnv *php_oci_create_env(ub2 charsetid TSRMLS_DC);
140 static int php_oci_create_session(php_oci_connection *connection, php_oci_spool *session_pool, char *dbname, int dbname_len, char *username, int username_len, char *password, int password_len, char *new_password, int new_password_len, int session_mode TSRMLS_DC);
141 static int php_oci_old_create_session(php_oci_connection *connection, char *dbname, int dbname_len, char *username, int username_len, char *password, int password_len, char *new_password, int new_password_len, int session_mode TSRMLS_DC);
142 static php_oci_spool *php_oci_get_spool(char *username, int username_len, char *password, int password_len, char *dbname, int dbname_len, int charsetid TSRMLS_DC);
143 static php_oci_spool *php_oci_create_spool(char *username, int username_len, char *password, int password_len, char *dbname, int dbname_len, char *hash_key, int hash_key_len, int charsetid TSRMLS_DC);
144 static sword php_oci_ping_init(php_oci_connection *connection, OCIError *errh TSRMLS_DC);
145 /* }}} */
146 
147 /* {{{ dynamically loadable module stuff */
148 #if defined(COMPILE_DL_OCI8) || defined(COMPILE_DL_OCI8_11G) || defined(COMPILE_DL_OCI8_12C)
149 ZEND_GET_MODULE(oci8)
150 #endif /* COMPILE_DL */
151 /* }}} */
152 
153 #ifdef ZEND_ENGINE_2
154 
155 /* {{{ Function arginfo */
156 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_define_by_name, 0, 0, 3)
157 	ZEND_ARG_INFO(0, statement_resource)
158 	ZEND_ARG_INFO(0, column_name)
159 	ZEND_ARG_INFO(1, variable)
160 	ZEND_ARG_INFO(0, type)
161 ZEND_END_ARG_INFO()
162 
163 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_bind_by_name, 0, 0, 3)
164 	ZEND_ARG_INFO(0, statement_resource)
165 	ZEND_ARG_INFO(0, column_name)
166 	ZEND_ARG_INFO(1, variable)
167 	ZEND_ARG_INFO(0, maximum_length)
168 	ZEND_ARG_INFO(0, type)
169 ZEND_END_ARG_INFO()
170 
171 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_bind_array_by_name, 0, 0, 4)
172 	ZEND_ARG_INFO(0, statement_resource)
173 	ZEND_ARG_INFO(0, column_name)
174 	ZEND_ARG_INFO(1, variable)
175 	ZEND_ARG_INFO(0, maximum_array_length)
176 	ZEND_ARG_INFO(0, maximum_item_length)
177 	ZEND_ARG_INFO(0, type)
178 ZEND_END_ARG_INFO()
179 
180 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_free_descriptor, 0, 0, 1)
181 	ZEND_ARG_INFO(0, lob_descriptor)
182 ZEND_END_ARG_INFO()
183 
184 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_save, 0, 0, 2)
185 	ZEND_ARG_INFO(0, lob_descriptor)
186 	ZEND_ARG_INFO(0, data)
187 	ZEND_ARG_INFO(0, offset)
188 ZEND_END_ARG_INFO()
189 
190 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_import, 0, 0, 2)
191 	ZEND_ARG_INFO(0, lob_descriptor)
192 	ZEND_ARG_INFO(0, filename)
193 ZEND_END_ARG_INFO()
194 
195 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_load, 0, 0, 1)
196 	ZEND_ARG_INFO(0, lob_descriptor)
197 ZEND_END_ARG_INFO()
198 
199 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_read, 0, 0, 2)
200 	ZEND_ARG_INFO(0, lob_descriptor)
201 	ZEND_ARG_INFO(0, length)
202 ZEND_END_ARG_INFO()
203 
204 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_eof, 0, 0, 1)
205 	ZEND_ARG_INFO(0, lob_descriptor)
206 ZEND_END_ARG_INFO()
207 
208 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_tell, 0, 0, 1)
209 	ZEND_ARG_INFO(0, lob_descriptor)
210 ZEND_END_ARG_INFO()
211 
212 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_rewind, 0, 0, 1)
213 	ZEND_ARG_INFO(0, lob_descriptor)
214 ZEND_END_ARG_INFO()
215 
216 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_seek, 0, 0, 2)
217 	ZEND_ARG_INFO(0, lob_descriptor)
218 	ZEND_ARG_INFO(0, offset)
219 	ZEND_ARG_INFO(0, whence)
220 ZEND_END_ARG_INFO()
221 
222 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_size, 0, 0, 1)
223 	ZEND_ARG_INFO(0, lob_descriptor)
224 ZEND_END_ARG_INFO()
225 
226 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_write, 0, 0, 2)
227 	ZEND_ARG_INFO(0, lob_descriptor)
228 	ZEND_ARG_INFO(0, string)
229 	ZEND_ARG_INFO(0, length)
230 ZEND_END_ARG_INFO()
231 
232 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_append, 0, 0, 2)
233 	ZEND_ARG_INFO(0, lob_descriptor_to)
234 	ZEND_ARG_INFO(0, lob_descriptor_from)
235 ZEND_END_ARG_INFO()
236 
237 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_truncate, 0, 0, 1)
238 	ZEND_ARG_INFO(0, lob_descriptor)
239 	ZEND_ARG_INFO(0, length)
240 ZEND_END_ARG_INFO()
241 
242 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_erase, 0, 0, 1)
243 	ZEND_ARG_INFO(0, lob_descriptor)
244 	ZEND_ARG_INFO(0, offset)
245 	ZEND_ARG_INFO(0, length)
246 ZEND_END_ARG_INFO()
247 
248 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_flush, 0, 0, 1)
249 	ZEND_ARG_INFO(0, lob_descriptor)
250 	ZEND_ARG_INFO(0, flag)
251 ZEND_END_ARG_INFO()
252 
253 ZEND_BEGIN_ARG_INFO_EX(arginfo_ocisetbufferinglob, 0, 0, 2)
254 	ZEND_ARG_INFO(0, lob_descriptor)
255 	ZEND_ARG_INFO(0, mode)
256 ZEND_END_ARG_INFO()
257 
258 ZEND_BEGIN_ARG_INFO_EX(arginfo_ocigetbufferinglob, 0, 0, 1)
259 	ZEND_ARG_INFO(0, lob_descriptor)
260 ZEND_END_ARG_INFO()
261 
262 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_copy, 0, 0, 2)
263 	ZEND_ARG_INFO(0, lob_descriptor_to)
264 	ZEND_ARG_INFO(0, lob_descriptor_from)
265 	ZEND_ARG_INFO(0, length)
266 ZEND_END_ARG_INFO()
267 
268 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_is_equal, 0, 0, 2)
269 	ZEND_ARG_INFO(0, lob_descriptor)
270 	ZEND_ARG_INFO(0, lob_descriptor)
271 ZEND_END_ARG_INFO()
272 
273 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_export, 0, 0, 2)
274 	ZEND_ARG_INFO(0, lob_descriptor)
275 	ZEND_ARG_INFO(0, filename)
276 	ZEND_ARG_INFO(0, start)
277 	ZEND_ARG_INFO(0, length)
278 ZEND_END_ARG_INFO()
279 
280 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_new_descriptor, 0, 0, 1)
281 	ZEND_ARG_INFO(0, connection_resource)
282 	ZEND_ARG_INFO(0, type)
283 ZEND_END_ARG_INFO()
284 
285 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_rollback, 0, 0, 1)
286 	ZEND_ARG_INFO(0, connection_resource)
287 ZEND_END_ARG_INFO()
288 
289 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_commit, 0, 0, 1)
290 	ZEND_ARG_INFO(0, connection_resource)
291 ZEND_END_ARG_INFO()
292 
293 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_field_name, 0, 0, 2)
294 	ZEND_ARG_INFO(0, statement_resource)
295 	ZEND_ARG_INFO(0, column_number_or_name)
296 ZEND_END_ARG_INFO()
297 
298 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_field_size, 0, 0, 2)
299 	ZEND_ARG_INFO(0, statement_resource)
300 	ZEND_ARG_INFO(0, column_number_or_name)
301 ZEND_END_ARG_INFO()
302 
303 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_field_scale, 0, 0, 2)
304 	ZEND_ARG_INFO(0, statement_resource)
305 	ZEND_ARG_INFO(0, column_number_or_name)
306 ZEND_END_ARG_INFO()
307 
308 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_field_precision, 0, 0, 2)
309 	ZEND_ARG_INFO(0, statement_resource)
310 	ZEND_ARG_INFO(0, column_number_or_name)
311 ZEND_END_ARG_INFO()
312 
313 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_field_type, 0, 0, 2)
314 	ZEND_ARG_INFO(0, statement_resource)
315 	ZEND_ARG_INFO(0, column_number_or_name)
316 ZEND_END_ARG_INFO()
317 
318 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_field_type_raw, 0, 0, 2)
319 	ZEND_ARG_INFO(0, statement_resource)
320 	ZEND_ARG_INFO(0, column_number_or_name)
321 ZEND_END_ARG_INFO()
322 
323 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_field_is_null, 0, 0, 2)
324 	ZEND_ARG_INFO(0, statement_resource)
325 	ZEND_ARG_INFO(0, column_number_or_name)
326 ZEND_END_ARG_INFO()
327 
328 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_internal_debug, 0, 0, 1)
329 	ZEND_ARG_INFO(0, mode)
330 ZEND_END_ARG_INFO()
331 
332 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_execute, 0, 0, 1)
333 	ZEND_ARG_INFO(0, statement_resource)
334 	ZEND_ARG_INFO(0, mode)
335 ZEND_END_ARG_INFO()
336 
337 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_cancel, 0, 0, 1)
338 	ZEND_ARG_INFO(0, statement_resource)
339 ZEND_END_ARG_INFO()
340 
341 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_fetch, 0, 0, 1)
342 	ZEND_ARG_INFO(0, statement_resource)
343 ZEND_END_ARG_INFO()
344 
345 ZEND_BEGIN_ARG_INFO_EX(arginfo_ocifetchinto, 0, 0, 2)
346 	ZEND_ARG_INFO(0, statement_resource)
347 	ZEND_ARG_INFO(1, result)
348 	ZEND_ARG_INFO(0, mode)
349 ZEND_END_ARG_INFO()
350 
351 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_fetch_all, 0, 0, 2)
352 	ZEND_ARG_INFO(0, statement_resource)
353 	ZEND_ARG_INFO(1, output)
354 	ZEND_ARG_INFO(0, skip)
355 	ZEND_ARG_INFO(0, maximum_rows)
356 	ZEND_ARG_INFO(0, flags)
357 ZEND_END_ARG_INFO()
358 
359 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_fetch_object, 0, 0, 1)
360 	ZEND_ARG_INFO(0, statement_resource)
361 ZEND_END_ARG_INFO()
362 
363 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_fetch_row, 0, 0, 1)
364 	ZEND_ARG_INFO(0, statement_resource)
365 ZEND_END_ARG_INFO()
366 
367 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_fetch_assoc, 0, 0, 1)
368 	ZEND_ARG_INFO(0, statement_resource)
369 ZEND_END_ARG_INFO()
370 
371 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_fetch_array, 0, 0, 1)
372 	ZEND_ARG_INFO(0, statement_resource)
373 	ZEND_ARG_INFO(0, mode)
374 ZEND_END_ARG_INFO()
375 
376 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_free_statement, 0, 0, 1)
377 	ZEND_ARG_INFO(0, statement_resource)
378 ZEND_END_ARG_INFO()
379 
380 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_close, 0, 0, 1)
381 	ZEND_ARG_INFO(0, connection_resource)
382 ZEND_END_ARG_INFO()
383 
384 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_new_connect, 0, 0, 2)
385 	ZEND_ARG_INFO(0, username)
386 	ZEND_ARG_INFO(0, password)
387 	ZEND_ARG_INFO(0, connection_string)
388 	ZEND_ARG_INFO(0, character_set)
389 	ZEND_ARG_INFO(0, session_mode)
390 ZEND_END_ARG_INFO()
391 
392 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_connect, 0, 0, 2)
393 	ZEND_ARG_INFO(0, username)
394 	ZEND_ARG_INFO(0, password)
395 	ZEND_ARG_INFO(0, connection_string)
396 	ZEND_ARG_INFO(0, character_set)
397 	ZEND_ARG_INFO(0, session_mode)
398 ZEND_END_ARG_INFO()
399 
400 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_pconnect, 0, 0, 2)
401 	ZEND_ARG_INFO(0, username)
402 	ZEND_ARG_INFO(0, password)
403 	ZEND_ARG_INFO(0, connection_string)
404 	ZEND_ARG_INFO(0, character_set)
405 	ZEND_ARG_INFO(0, session_mode)
406 ZEND_END_ARG_INFO()
407 
408 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_error, 0, 0, 0)
409 	ZEND_ARG_INFO(0, connection_or_statement_resource)
410 ZEND_END_ARG_INFO()
411 
412 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_num_fields, 0, 0, 1)
413 	ZEND_ARG_INFO(0, statement_resource)
414 ZEND_END_ARG_INFO()
415 
416 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_parse, 0, 0, 2)
417 	ZEND_ARG_INFO(0, connection_resource)
418 	ZEND_ARG_INFO(0, sql_text)
419 ZEND_END_ARG_INFO()
420 
421 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_get_implicit_resultset, 0, 0, 1)
422 ZEND_ARG_INFO(0, statement_resource)
423 ZEND_END_ARG_INFO()
424 
425 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_set_prefetch, 0, 0, 2)
426 	ZEND_ARG_INFO(0, statement_resource)
427 	ZEND_ARG_INFO(0, number_of_rows)
428 ZEND_END_ARG_INFO()
429 
430 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_set_client_identifier, 0, 0, 2)
431 	ZEND_ARG_INFO(0, connection_resource)
432 	ZEND_ARG_INFO(0, client_identifier)
433 ZEND_END_ARG_INFO()
434 
435 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_set_edition, 0, 0, 1)
436 	ZEND_ARG_INFO(0, edition_name)
437 ZEND_END_ARG_INFO()
438 
439 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_set_module_name, 0, 0, 2)
440 	ZEND_ARG_INFO(0, connection_resource)
441 	ZEND_ARG_INFO(0, module_name)
442 ZEND_END_ARG_INFO()
443 
444 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_set_action, 0, 0, 2)
445 	ZEND_ARG_INFO(0, connection_resource)
446 	ZEND_ARG_INFO(0, action)
447 ZEND_END_ARG_INFO()
448 
449 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_set_client_info, 0, 0, 2)
450 	ZEND_ARG_INFO(0, connection_resource)
451 	ZEND_ARG_INFO(0, client_information)
452 ZEND_END_ARG_INFO()
453 
454 #ifdef WAITIING_ORACLE_BUG_16695981_FIX
455 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_set_db_operation, 0, 0, 2)
456 ZEND_ARG_INFO(0, connection_resource)
457 ZEND_ARG_INFO(0, action)
458 ZEND_END_ARG_INFO()
459 #endif
460 
461 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_password_change, 0, 0, 4)
462 	ZEND_ARG_INFO(0, connection_resource_or_connection_string)
463 	ZEND_ARG_INFO(0, username)
464 	ZEND_ARG_INFO(0, old_password)
465 	ZEND_ARG_INFO(0, new_password)
466 ZEND_END_ARG_INFO()
467 
468 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_new_cursor, 0, 0, 1)
469 	ZEND_ARG_INFO(0, connection_resource)
470 ZEND_END_ARG_INFO()
471 
472 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_result, 0, 0, 2)
473 	ZEND_ARG_INFO(0, statement_resource)
474 	ZEND_ARG_INFO(0, column_number_or_name)
475 ZEND_END_ARG_INFO()
476 
477 ZEND_BEGIN_ARG_INFO(arginfo_oci_client_version, 0)
478 ZEND_END_ARG_INFO()
479 
480 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_server_version, 0, 0, 1)
481 	ZEND_ARG_INFO(0, connection_resource)
482 ZEND_END_ARG_INFO()
483 
484 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_statement_type, 0, 0, 1)
485 	ZEND_ARG_INFO(0, statement_resource)
486 ZEND_END_ARG_INFO()
487 
488 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_num_rows, 0, 0, 1)
489 	ZEND_ARG_INFO(0, statement_resource)
490 ZEND_END_ARG_INFO()
491 
492 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_free_collection, 0, 0, 1)
493 	ZEND_ARG_INFO(0, collection)
494 ZEND_END_ARG_INFO()
495 
496 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_collection_append, 0, 0, 2)
497 	ZEND_ARG_INFO(0, collection)
498 	ZEND_ARG_INFO(0, value)
499 ZEND_END_ARG_INFO()
500 
501 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_collection_element_get, 0, 0, 2)
502 	ZEND_ARG_INFO(0, collection)
503 	ZEND_ARG_INFO(0, index)
504 ZEND_END_ARG_INFO()
505 
506 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_collection_assign, 0, 0, 2)
507 	ZEND_ARG_INFO(0, collection_to)
508 	ZEND_ARG_INFO(0, collection_from)
509 ZEND_END_ARG_INFO()
510 
511 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_collection_element_assign, 0, 0, 3)
512 	ZEND_ARG_INFO(0, collection)
513 	ZEND_ARG_INFO(0, index)
514 	ZEND_ARG_INFO(0, value)
515 ZEND_END_ARG_INFO()
516 
517 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_collection_size, 0, 0, 1)
518 	ZEND_ARG_INFO(0, collection)
519 ZEND_END_ARG_INFO()
520 
521 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_collection_max, 0, 0, 1)
522 	ZEND_ARG_INFO(0, collection)
523 ZEND_END_ARG_INFO()
524 
525 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_collection_trim, 0, 0, 2)
526 	ZEND_ARG_INFO(0, collection)
527 	ZEND_ARG_INFO(0, number)
528 ZEND_END_ARG_INFO()
529 
530 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_new_collection, 0, 0, 2)
531 	ZEND_ARG_INFO(0, connection_resource)
532 	ZEND_ARG_INFO(0, type_name)
533 	ZEND_ARG_INFO(0, schema_name)
534 ZEND_END_ARG_INFO()
535 /* }}} */
536 
537 /* {{{ LOB Method arginfo */
538 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_save_method, 0, 0, 1)
539 	ZEND_ARG_INFO(0, data)
540 	ZEND_ARG_INFO(0, offset)
541 ZEND_END_ARG_INFO()
542 
543 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_import_method, 0, 0, 1)
544 	ZEND_ARG_INFO(0, filename)
545 ZEND_END_ARG_INFO()
546 
547 ZEND_BEGIN_ARG_INFO(arginfo_oci_lob_load_method, 0)
548 ZEND_END_ARG_INFO()
549 
550 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_read_method, 0, 0, 1)
551 	ZEND_ARG_INFO(0, length)
552 ZEND_END_ARG_INFO()
553 
554 ZEND_BEGIN_ARG_INFO(arginfo_oci_lob_eof_method, 0)
555 ZEND_END_ARG_INFO()
556 
557 ZEND_BEGIN_ARG_INFO(arginfo_oci_lob_tell_method, 0)
558 ZEND_END_ARG_INFO()
559 
560 ZEND_BEGIN_ARG_INFO(arginfo_oci_lob_rewind_method, 0)
561 ZEND_END_ARG_INFO()
562 
563 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_seek_method, 0, 0, 1)
564 	ZEND_ARG_INFO(0, offset)
565 	ZEND_ARG_INFO(0, whence)
566 ZEND_END_ARG_INFO()
567 
568 ZEND_BEGIN_ARG_INFO(arginfo_oci_lob_size_method, 0)
569 ZEND_END_ARG_INFO()
570 
571 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_write_method, 0, 0, 1)
572 	ZEND_ARG_INFO(0, string)
573 	ZEND_ARG_INFO(0, length)
574 ZEND_END_ARG_INFO()
575 
576 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_append_method, 0, 0, 1)
577 	ZEND_ARG_INFO(0, lob_descriptor_from)
578 ZEND_END_ARG_INFO()
579 
580 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_truncate_method, 0, 0, 0)
581 	ZEND_ARG_INFO(0, length)
582 ZEND_END_ARG_INFO()
583 
584 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_erase_method, 0, 0, 0)
585 	ZEND_ARG_INFO(0, offset)
586 	ZEND_ARG_INFO(0, length)
587 ZEND_END_ARG_INFO()
588 
589 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_flush_method, 0, 0, 0)
590 	ZEND_ARG_INFO(0, flag)
591 ZEND_END_ARG_INFO()
592 
593 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_setbuffering_method, 0, 0, 1)
594 	ZEND_ARG_INFO(0, mode)
595 ZEND_END_ARG_INFO()
596 
597 ZEND_BEGIN_ARG_INFO(arginfo_oci_lob_getbuffering_method, 0)
598 ZEND_END_ARG_INFO()
599 
600 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_export_method, 0, 0, 1)
601 	ZEND_ARG_INFO(0, filename)
602 	ZEND_ARG_INFO(0, start)
603 	ZEND_ARG_INFO(0, length)
604 ZEND_END_ARG_INFO()
605 
606 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_lob_write_temporary_method, 0, 0, 1)
607 	ZEND_ARG_INFO(0, data)
608 	ZEND_ARG_INFO(0, type)
609 ZEND_END_ARG_INFO()
610 
611 ZEND_BEGIN_ARG_INFO(arginfo_oci_lob_close_method, 0)
612 ZEND_END_ARG_INFO()
613 
614 ZEND_BEGIN_ARG_INFO(arginfo_oci_free_descriptor_method, 0)
615 ZEND_END_ARG_INFO()
616 /* }}} */
617 
618 /* {{{ Collection Method arginfo */
619 ZEND_BEGIN_ARG_INFO(arginfo_oci_collection_free_method, 0)
620 ZEND_END_ARG_INFO()
621 
622 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_collection_append_method, 0, 0, 1)
623 	ZEND_ARG_INFO(0, value)
624 ZEND_END_ARG_INFO()
625 
626 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_collection_element_get_method, 0, 0, 1)
627 	ZEND_ARG_INFO(0, index)
628 ZEND_END_ARG_INFO()
629 
630 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_collection_assign_method, 0, 0, 1)
631 	ZEND_ARG_INFO(0, collection_from)
632 ZEND_END_ARG_INFO()
633 
634 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_collection_element_assign_method, 0, 0, 2)
635 	ZEND_ARG_INFO(0, index)
636 	ZEND_ARG_INFO(0, value)
637 ZEND_END_ARG_INFO()
638 
639 ZEND_BEGIN_ARG_INFO(arginfo_oci_collection_size_method, 0)
640 ZEND_END_ARG_INFO()
641 
642 ZEND_BEGIN_ARG_INFO(arginfo_oci_collection_max_method, 0)
643 ZEND_END_ARG_INFO()
644 
645 ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_collection_trim_method, 0, 0, 1)
646 	ZEND_ARG_INFO(0, number)
647 ZEND_END_ARG_INFO()
648 /* }}} */
649 
650 #else /* ZEND_ENGINE_2 */
651 /* {{{ Keep the old arginfo behavior when building with PHP 4 */
652 
653 static unsigned char arginfo_ocifetchinto[]  = { 2, BYREF_NONE, BYREF_FORCE };
654 static unsigned char arginfo_oci_fetch_all[] = { 2, BYREF_NONE, BYREF_FORCE };
655 static unsigned char arginfo_oci_define_by_name[] = { 3, BYREF_NONE, BYREF_NONE, BYREF_FORCE };
656 static unsigned char arginfo_oci_bind_by_name[] = { 3, BYREF_NONE, BYREF_NONE, BYREF_FORCE };
657 static unsigned char arginfo_oci_bind_array_by_name[] = { 3, BYREF_NONE, BYREF_NONE, BYREF_FORCE };
658 
659 #define arginfo_oci_free_descriptor						NULL
660 #define arginfo_oci_lob_save							NULL
661 #define arginfo_oci_lob_import							NULL
662 #define arginfo_oci_lob_load							NULL
663 #define arginfo_oci_lob_read							NULL
664 #define arginfo_oci_lob_eof								NULL
665 #define arginfo_oci_lob_tell							NULL
666 #define arginfo_oci_lob_rewind							NULL
667 #define arginfo_oci_lob_seek							NULL
668 #define arginfo_oci_lob_size							NULL
669 #define arginfo_oci_lob_write							NULL
670 #define arginfo_oci_lob_append							NULL
671 #define arginfo_oci_lob_truncate						NULL
672 #define arginfo_oci_lob_erase							NULL
673 #define arginfo_oci_lob_flush							NULL
674 #define arginfo_ocisetbufferinglob						NULL
675 #define arginfo_ocigetbufferinglob						NULL
676 #define arginfo_oci_lob_copy							NULL
677 #define arginfo_oci_lob_is_equal						NULL
678 #define arginfo_oci_lob_export							NULL
679 #define arginfo_oci_new_descriptor						NULL
680 #define arginfo_oci_rollback							NULL
681 #define arginfo_oci_commit								NULL
682 #define arginfo_oci_field_name							NULL
683 #define arginfo_oci_field_size							NULL
684 #define arginfo_oci_field_scale							NULL
685 #define arginfo_oci_field_precision						NULL
686 #define arginfo_oci_field_type							NULL
687 #define arginfo_oci_field_type_raw						NULL
688 #define arginfo_oci_field_is_null						NULL
689 #define arginfo_oci_internal_debug						NULL
690 #define arginfo_oci_execute								NULL
691 #define arginfo_oci_cancel								NULL
692 #define arginfo_oci_fetch								NULL
693 #define arginfo_oci_fetch_object						NULL
694 #define arginfo_oci_fetch_row							NULL
695 #define arginfo_oci_fetch_assoc							NULL
696 #define arginfo_oci_fetch_array							NULL
697 #define arginfo_oci_free_statement						NULL
698 #define arginfo_oci_close								NULL
699 #define arginfo_oci_new_connect							NULL
700 #define arginfo_oci_connect								NULL
701 #define arginfo_oci_pconnect							NULL
702 #define arginfo_oci_error								NULL
703 #define arginfo_oci_num_fields							NULL
704 #define arginfo_oci_parse								NULL
705 #define arginfo_oci_get_implicit_resultset				NULL
706 #define arginfo_oci_set_prefetch						NULL
707 #define arginfo_oci_set_client_identifier				NULL
708 #define arginfo_oci_set_edition							NULL
709 #define arginfo_oci_set_module_name						NULL
710 #define arginfo_oci_set_action							NULL
711 #define arginfo_oci_set_client_info						NULL
712 #ifdef WAITIING_ORACLE_BUG_16695981_FIX
713 #define arginfo_oci_set_db_operation					NULL
714 #endif
715 #define arginfo_oci_password_change						NULL
716 #define arginfo_oci_new_cursor							NULL
717 #define arginfo_oci_result								NULL
718 #define arginfo_oci_client_version						NULL
719 #define arginfo_oci_server_version						NULL
720 #define arginfo_oci_statement_type						NULL
721 #define arginfo_oci_num_rows							NULL
722 #define arginfo_oci_free_collection						NULL
723 #define arginfo_oci_collection_append					NULL
724 #define arginfo_oci_collection_element_get				NULL
725 #define arginfo_oci_collection_assign					NULL
726 #define arginfo_oci_collection_element_assign			NULL
727 #define arginfo_oci_collection_size						NULL
728 #define arginfo_oci_collection_max						NULL
729 #define arginfo_oci_collection_trim						NULL
730 #define arginfo_oci_new_collection						NULL
731 #define arginfo_oci_lob_size_method						NULL
732 #define arginfo_oci_lob_getbuffering_method				NULL
733 #define arginfo_oci_lob_close_method					NULL
734 #define arginfo_oci_lob_save_method						NULL
735 #define arginfo_oci_lob_import_method					NULL
736 #define arginfo_oci_lob_read_method						NULL
737 #define arginfo_oci_lob_seek_method						NULL
738 #define arginfo_oci_lob_write_method					NULL
739 #define arginfo_oci_lob_append_method					NULL
740 #define arginfo_oci_lob_truncate_method					NULL
741 #define arginfo_oci_lob_erase_method					NULL
742 #define arginfo_oci_lob_flush_method					NULL
743 #define arginfo_oci_lob_setbuffering_method				NULL
744 #define arginfo_oci_lob_export_method					NULL
745 #define arginfo_oci_lob_write_temporary_method			NULL
746 #define arginfo_oci_lob_load_method						NULL
747 #define arginfo_oci_lob_tell_method						NULL
748 #define arginfo_oci_lob_rewind_method					NULL
749 #define arginfo_oci_lob_eof_method						NULL
750 #define arginfo_oci_free_descriptor_method				NULL
751 #define arginfo_oci_collection_append_method			NULL
752 #define arginfo_oci_collection_element_get_method		NULL
753 #define arginfo_oci_collection_assign_method			NULL
754 #define arginfo_oci_collection_size_method				NULL
755 #define arginfo_oci_collection_element_assign_method	NULL
756 #define arginfo_oci_collection_max_method				NULL
757 #define arginfo_oci_collection_trim_method				NULL
758 #define arginfo_oci_collection_free_method				NULL
759 /* }}} */
760 #endif /* ZEND_ENGINE_2 */
761 
762 /* {{{ extension function prototypes
763 */
764 PHP_FUNCTION(oci_bind_by_name);
765 PHP_FUNCTION(oci_bind_array_by_name);
766 PHP_FUNCTION(oci_define_by_name);
767 PHP_FUNCTION(oci_field_is_null);
768 PHP_FUNCTION(oci_field_name);
769 PHP_FUNCTION(oci_field_size);
770 PHP_FUNCTION(oci_field_scale);
771 PHP_FUNCTION(oci_field_precision);
772 PHP_FUNCTION(oci_field_type);
773 PHP_FUNCTION(oci_field_type_raw);
774 PHP_FUNCTION(oci_execute);
775 PHP_FUNCTION(oci_fetch);
776 PHP_FUNCTION(oci_cancel);
777 PHP_FUNCTION(ocifetchinto);
778 PHP_FUNCTION(oci_fetch_object);
779 PHP_FUNCTION(oci_fetch_row);
780 PHP_FUNCTION(oci_fetch_assoc);
781 PHP_FUNCTION(oci_fetch_array);
782 PHP_FUNCTION(ocifetchstatement);
783 PHP_FUNCTION(oci_fetch_all);
784 PHP_FUNCTION(oci_free_statement);
785 PHP_FUNCTION(oci_internal_debug);
786 PHP_FUNCTION(oci_close);
787 PHP_FUNCTION(oci_connect);
788 PHP_FUNCTION(oci_new_connect);
789 PHP_FUNCTION(oci_pconnect);
790 PHP_FUNCTION(oci_error);
791 PHP_FUNCTION(oci_free_descriptor);
792 PHP_FUNCTION(oci_commit);
793 PHP_FUNCTION(oci_rollback);
794 PHP_FUNCTION(oci_new_descriptor);
795 PHP_FUNCTION(oci_num_fields);
796 PHP_FUNCTION(oci_parse);
797 PHP_FUNCTION(oci_get_implicit_resultset);
798 PHP_FUNCTION(oci_new_cursor);
799 PHP_FUNCTION(oci_result);
800 PHP_FUNCTION(oci_client_version);
801 PHP_FUNCTION(oci_server_version);
802 PHP_FUNCTION(oci_statement_type);
803 PHP_FUNCTION(oci_num_rows);
804 PHP_FUNCTION(oci_set_prefetch);
805 PHP_FUNCTION(oci_set_client_identifier);
806 #ifdef WAITIING_ORACLE_BUG_16695981_FIX
807 PHP_FUNCTION(oci_set_db_operation);
808 #endif
809 PHP_FUNCTION(oci_set_edition);
810 PHP_FUNCTION(oci_set_module_name);
811 PHP_FUNCTION(oci_set_action);
812 PHP_FUNCTION(oci_set_client_info);
813 PHP_FUNCTION(oci_password_change);
814 PHP_FUNCTION(oci_lob_save);
815 PHP_FUNCTION(oci_lob_import);
816 PHP_FUNCTION(oci_lob_export);
817 PHP_FUNCTION(oci_lob_load);
818 PHP_FUNCTION(oci_lob_tell);
819 PHP_FUNCTION(oci_lob_write);
820 PHP_FUNCTION(oci_lob_append);
821 PHP_FUNCTION(oci_lob_copy);
822 PHP_FUNCTION(oci_lob_truncate);
823 PHP_FUNCTION(oci_lob_erase);
824 PHP_FUNCTION(oci_lob_flush);
825 PHP_FUNCTION(ocisetbufferinglob);
826 PHP_FUNCTION(ocigetbufferinglob);
827 PHP_FUNCTION(oci_lob_is_equal);
828 PHP_FUNCTION(oci_lob_rewind);
829 PHP_FUNCTION(oci_lob_read);
830 PHP_FUNCTION(oci_lob_eof);
831 PHP_FUNCTION(oci_lob_seek);
832 PHP_FUNCTION(oci_lob_size);
833 PHP_FUNCTION(oci_lob_write_temporary);
834 PHP_FUNCTION(oci_lob_close);
835 PHP_FUNCTION(oci_new_collection);
836 PHP_FUNCTION(oci_free_collection);
837 PHP_FUNCTION(oci_collection_append);
838 PHP_FUNCTION(oci_collection_element_get);
839 PHP_FUNCTION(oci_collection_element_assign);
840 PHP_FUNCTION(oci_collection_assign);
841 PHP_FUNCTION(oci_collection_size);
842 PHP_FUNCTION(oci_collection_max);
843 PHP_FUNCTION(oci_collection_trim);
844 /* }}} */
845 
846 /* {{{ extension definition structures
847 */
848 static
849 #if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 2) || (PHP_MAJOR_VERSION > 5)
850 /* This "if" allows PECL builds from this file to be portable to older PHP releases */
851 const
852 #endif
853 zend_function_entry php_oci_functions[] = {
854 	PHP_FE(oci_define_by_name,			arginfo_oci_define_by_name)
855 	PHP_FE(oci_bind_by_name,			arginfo_oci_bind_by_name)
856 	PHP_FE(oci_bind_array_by_name,		arginfo_oci_bind_array_by_name)
857 	PHP_FE(oci_field_is_null,			arginfo_oci_field_is_null)
858 	PHP_FE(oci_field_name,				arginfo_oci_field_name)
859 	PHP_FE(oci_field_size,				arginfo_oci_field_size)
860 	PHP_FE(oci_field_scale,				arginfo_oci_field_scale)
861 	PHP_FE(oci_field_precision,			arginfo_oci_field_precision)
862 	PHP_FE(oci_field_type,				arginfo_oci_field_type)
863 	PHP_FE(oci_field_type_raw,			arginfo_oci_field_type_raw)
864 	PHP_FE(oci_execute,					arginfo_oci_execute)
865 	PHP_FE(oci_cancel,					arginfo_oci_cancel)
866 	PHP_FE(oci_fetch,					arginfo_oci_fetch)
867 	PHP_FE(oci_fetch_object,			arginfo_oci_fetch_object)
868 	PHP_FE(oci_fetch_row,				arginfo_oci_fetch_row)
869 	PHP_FE(oci_fetch_assoc,				arginfo_oci_fetch_assoc)
870 	PHP_FE(oci_fetch_array,				arginfo_oci_fetch_array)
871 	PHP_FE(ocifetchinto,				arginfo_ocifetchinto)
872 	PHP_FE(oci_fetch_all,				arginfo_oci_fetch_all)
873 	PHP_FE(oci_free_statement,			arginfo_oci_free_statement)
874 	PHP_FE(oci_internal_debug,			arginfo_oci_internal_debug)
875 	PHP_FE(oci_num_fields,				arginfo_oci_num_fields)
876 	PHP_FE(oci_parse,					arginfo_oci_parse)
877 	PHP_FE(oci_get_implicit_resultset,	arginfo_oci_get_implicit_resultset)
878 	PHP_FE(oci_new_cursor,				arginfo_oci_new_cursor)
879 	PHP_FE(oci_result,					arginfo_oci_result)
880 	PHP_FE(oci_client_version,			arginfo_oci_client_version)
881 	PHP_FE(oci_server_version,			arginfo_oci_server_version)
882 	PHP_FE(oci_statement_type,			arginfo_oci_statement_type)
883 	PHP_FE(oci_num_rows,				arginfo_oci_num_rows)
884 	PHP_FE(oci_close,					arginfo_oci_close)
885 	PHP_FE(oci_connect,					arginfo_oci_connect)
886 	PHP_FE(oci_new_connect,				arginfo_oci_new_connect)
887 	PHP_FE(oci_pconnect,				arginfo_oci_pconnect)
888 	PHP_FE(oci_error,					arginfo_oci_error)
889 	PHP_FE(oci_free_descriptor,			arginfo_oci_free_descriptor)
890 	PHP_FE(oci_lob_save,				arginfo_oci_lob_save)
891 	PHP_FE(oci_lob_import,				arginfo_oci_lob_import)
892 	PHP_FE(oci_lob_size,				arginfo_oci_lob_size)
893 	PHP_FE(oci_lob_load,				arginfo_oci_lob_load)
894 	PHP_FE(oci_lob_read,				arginfo_oci_lob_read)
895 	PHP_FE(oci_lob_eof,					arginfo_oci_lob_eof)
896 	PHP_FE(oci_lob_tell,				arginfo_oci_lob_tell)
897 	PHP_FE(oci_lob_truncate,			arginfo_oci_lob_truncate)
898 	PHP_FE(oci_lob_erase,				arginfo_oci_lob_erase)
899 	PHP_FE(oci_lob_flush,				arginfo_oci_lob_flush)
900 	PHP_FE(ocisetbufferinglob,			arginfo_ocisetbufferinglob)
901 	PHP_FE(ocigetbufferinglob,			arginfo_ocigetbufferinglob)
902 	PHP_FE(oci_lob_is_equal,			arginfo_oci_lob_is_equal)
903 	PHP_FE(oci_lob_rewind,				arginfo_oci_lob_rewind)
904 	PHP_FE(oci_lob_write,				arginfo_oci_lob_write)
905 	PHP_FE(oci_lob_append,				arginfo_oci_lob_append)
906 	PHP_FE(oci_lob_copy,				arginfo_oci_lob_copy)
907 	PHP_FE(oci_lob_export,				arginfo_oci_lob_export)
908 	PHP_FE(oci_lob_seek,				arginfo_oci_lob_seek)
909 	PHP_FE(oci_commit,					arginfo_oci_commit)
910 	PHP_FE(oci_rollback,				arginfo_oci_rollback)
911 	PHP_FE(oci_new_descriptor,			arginfo_oci_new_descriptor)
912 	PHP_FE(oci_set_prefetch,			arginfo_oci_set_prefetch)
913 	PHP_FE(oci_set_client_identifier,	arginfo_oci_set_client_identifier)
914 #ifdef WAITIING_ORACLE_BUG_16695981_FIX
915 	PHP_FE(oci_set_db_operation,		arginfo_oci_set_db_operation)
916 #endif
917 	PHP_FE(oci_set_edition,				arginfo_oci_set_edition)
918 	PHP_FE(oci_set_module_name,			arginfo_oci_set_module_name)
919 	PHP_FE(oci_set_action,				arginfo_oci_set_action)
920 	PHP_FE(oci_set_client_info,			arginfo_oci_set_client_info)
921 	PHP_FE(oci_password_change,			arginfo_oci_password_change)
922 	PHP_FE(oci_free_collection,			arginfo_oci_free_collection)
923 	PHP_FE(oci_collection_append,		arginfo_oci_collection_append)
924 	PHP_FE(oci_collection_element_get,	arginfo_oci_collection_element_get)
925 	PHP_FE(oci_collection_element_assign,	arginfo_oci_collection_element_assign)
926 	PHP_FE(oci_collection_assign,		arginfo_oci_collection_assign)
927 	PHP_FE(oci_collection_size,			arginfo_oci_collection_size)
928 	PHP_FE(oci_collection_max,			arginfo_oci_collection_max)
929 	PHP_FE(oci_collection_trim,			arginfo_oci_collection_trim)
930 	PHP_FE(oci_new_collection,			arginfo_oci_new_collection)
931 
932 	PHP_FALIAS(oci_free_cursor,		oci_free_statement,		arginfo_oci_free_statement)
933 	PHP_FALIAS(ocifreecursor,		oci_free_statement,		arginfo_oci_free_statement)
934 	PHP_FALIAS(ocibindbyname,		oci_bind_by_name,		arginfo_oci_bind_by_name)
935 	PHP_FALIAS(ocidefinebyname,		oci_define_by_name,		arginfo_oci_define_by_name)
936 	PHP_FALIAS(ocicolumnisnull,		oci_field_is_null,		arginfo_oci_field_is_null)
937 	PHP_FALIAS(ocicolumnname,		oci_field_name,			arginfo_oci_field_name)
938 	PHP_FALIAS(ocicolumnsize,		oci_field_size,			arginfo_oci_field_size)
939 	PHP_FALIAS(ocicolumnscale,		oci_field_scale,		arginfo_oci_field_scale)
940 	PHP_FALIAS(ocicolumnprecision,	oci_field_precision,	arginfo_oci_field_precision)
941 	PHP_FALIAS(ocicolumntype,		oci_field_type,			arginfo_oci_field_type)
942 	PHP_FALIAS(ocicolumntyperaw,	oci_field_type_raw,		arginfo_oci_field_type_raw)
943 	PHP_FALIAS(ociexecute,			oci_execute,			arginfo_oci_execute)
944 	PHP_FALIAS(ocicancel,			oci_cancel,				arginfo_oci_cancel)
945 	PHP_FALIAS(ocifetch,			oci_fetch,				arginfo_oci_fetch)
946 	PHP_FALIAS(ocifetchstatement,	oci_fetch_all,			arginfo_oci_fetch_all)
947 	PHP_FALIAS(ocifreestatement,	oci_free_statement,		arginfo_oci_free_statement)
948 	PHP_FALIAS(ociinternaldebug,	oci_internal_debug,		arginfo_oci_internal_debug)
949 	PHP_FALIAS(ocinumcols,			oci_num_fields,			arginfo_oci_num_fields)
950 	PHP_FALIAS(ociparse,			oci_parse,				arginfo_oci_parse)
951 	PHP_FALIAS(ocinewcursor,		oci_new_cursor,			arginfo_oci_new_cursor)
952 	PHP_FALIAS(ociresult,			oci_result,				arginfo_oci_result)
953 	PHP_FALIAS(ociserverversion,	oci_server_version,		arginfo_oci_server_version)
954 	PHP_FALIAS(ocistatementtype,	oci_statement_type,		arginfo_oci_statement_type)
955 	PHP_FALIAS(ocirowcount,			oci_num_rows,			arginfo_oci_num_rows)
956 	PHP_FALIAS(ocilogoff,			oci_close,				arginfo_oci_close)
957 	PHP_FALIAS(ocilogon,			oci_connect,			arginfo_oci_connect)
958 	PHP_FALIAS(ocinlogon,			oci_new_connect,		arginfo_oci_new_connect)
959 	PHP_FALIAS(ociplogon,			oci_pconnect,			arginfo_oci_pconnect)
960 	PHP_FALIAS(ocierror,			oci_error,				arginfo_oci_error)
961 	PHP_FALIAS(ocifreedesc,			oci_free_descriptor,	arginfo_oci_free_descriptor)
962 	PHP_FALIAS(ocisavelob,			oci_lob_save,			arginfo_oci_lob_save)
963 	PHP_FALIAS(ocisavelobfile,		oci_lob_import,			arginfo_oci_lob_import)
964 	PHP_FALIAS(ociwritelobtofile,	oci_lob_export,			arginfo_oci_lob_export)
965 	PHP_FALIAS(ociloadlob,			oci_lob_load,			arginfo_oci_lob_load)
966 	PHP_FALIAS(ocicommit,			oci_commit,				arginfo_oci_commit)
967 	PHP_FALIAS(ocirollback,			oci_rollback,			arginfo_oci_rollback)
968 	PHP_FALIAS(ocinewdescriptor,	oci_new_descriptor,		arginfo_oci_new_descriptor)
969 	PHP_FALIAS(ocisetprefetch,		oci_set_prefetch,		arginfo_oci_set_prefetch)
970 	PHP_FALIAS(ocipasswordchange,	oci_password_change,	arginfo_oci_password_change)
971 	PHP_FALIAS(ocifreecollection,	oci_free_collection,	arginfo_oci_free_collection)
972 	PHP_FALIAS(ocinewcollection,	oci_new_collection,		arginfo_oci_new_collection)
973 	PHP_FALIAS(ocicollappend,		oci_collection_append,	arginfo_oci_collection_append)
974 	PHP_FALIAS(ocicollgetelem,		oci_collection_element_get,		arginfo_oci_collection_element_get)
975 	PHP_FALIAS(ocicollassignelem,	oci_collection_element_assign,	arginfo_oci_collection_element_assign)
976 	PHP_FALIAS(ocicollsize,			oci_collection_size,	arginfo_oci_collection_size)
977 	PHP_FALIAS(ocicollmax,			oci_collection_max,		arginfo_oci_collection_max)
978 	PHP_FALIAS(ocicolltrim,			oci_collection_trim,	arginfo_oci_collection_trim)
979 #if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 3 && PHP_RELEASE_VERSION >= 7) || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
980 	PHP_FE_END
981 #else
982 	{NULL,NULL,NULL}
983 #endif
984 };
985 
986 static
987 #if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 2) || (PHP_MAJOR_VERSION > 5)
988 /* This "if" allows PECL builds from this file to be portable to older PHP releases */
989 const
990 #endif
991 zend_function_entry php_oci_lob_class_functions[] = {
992 	PHP_FALIAS(load,		oci_lob_load,			arginfo_oci_lob_load_method)
993 	PHP_FALIAS(tell,		oci_lob_tell,			arginfo_oci_lob_tell_method)
994 	PHP_FALIAS(truncate,	oci_lob_truncate,		arginfo_oci_lob_truncate_method)
995 	PHP_FALIAS(erase,		oci_lob_erase,			arginfo_oci_lob_erase_method)
996 	PHP_FALIAS(flush,		oci_lob_flush,			arginfo_oci_lob_flush_method)
997 	PHP_FALIAS(setbuffering,ocisetbufferinglob,		arginfo_oci_lob_setbuffering_method)
998 	PHP_FALIAS(getbuffering,ocigetbufferinglob,		arginfo_oci_lob_getbuffering_method)
999 	PHP_FALIAS(rewind,		oci_lob_rewind,			arginfo_oci_lob_rewind_method)
1000 	PHP_FALIAS(read,		oci_lob_read,			arginfo_oci_lob_read_method)
1001 	PHP_FALIAS(eof,			oci_lob_eof,			arginfo_oci_lob_eof_method)
1002 	PHP_FALIAS(seek,		oci_lob_seek,			arginfo_oci_lob_seek_method)
1003 	PHP_FALIAS(write,		oci_lob_write,			arginfo_oci_lob_write_method)
1004 	PHP_FALIAS(append,		oci_lob_append,			arginfo_oci_lob_append_method)
1005 	PHP_FALIAS(size,		oci_lob_size,			arginfo_oci_lob_size_method)
1006 	PHP_FALIAS(writetofile, oci_lob_export,			arginfo_oci_lob_export_method)
1007 	PHP_FALIAS(export,		oci_lob_export,			arginfo_oci_lob_export_method)
1008 	PHP_FALIAS(import,		oci_lob_import,			arginfo_oci_lob_import_method)
1009 	PHP_FALIAS(writetemporary,	oci_lob_write_temporary,	arginfo_oci_lob_write_temporary_method)
1010 	PHP_FALIAS(close,			oci_lob_close,				arginfo_oci_lob_close_method)
1011 	PHP_FALIAS(save,		oci_lob_save,			arginfo_oci_lob_save_method)
1012 	PHP_FALIAS(savefile,	oci_lob_import,			arginfo_oci_lob_import_method)
1013 	PHP_FALIAS(free,		oci_free_descriptor,	arginfo_oci_free_descriptor_method)
1014 #if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 3 && PHP_RELEASE_VERSION >= 7) || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
1015 	PHP_FE_END
1016 #else
1017 	{NULL,NULL,NULL}
1018 #endif
1019 };
1020 
1021 static
1022 #if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 2) || (PHP_MAJOR_VERSION > 5)
1023 /* This "if" allows PECL builds from this file to be portable to older PHP releases */
1024 const
1025 #endif
1026 zend_function_entry php_oci_coll_class_functions[] = {
1027 	PHP_FALIAS(append,		  oci_collection_append,			arginfo_oci_collection_append_method)
1028 	PHP_FALIAS(getelem,		  oci_collection_element_get,		arginfo_oci_collection_element_get_method)
1029 	PHP_FALIAS(assignelem,	  oci_collection_element_assign,	arginfo_oci_collection_element_assign_method)
1030 	PHP_FALIAS(assign,		  oci_collection_assign,			arginfo_oci_collection_assign_method)
1031 	PHP_FALIAS(size,		  oci_collection_size,				arginfo_oci_collection_size_method)
1032 	PHP_FALIAS(max,			  oci_collection_max,				arginfo_oci_collection_max_method)
1033 	PHP_FALIAS(trim,		  oci_collection_trim,				arginfo_oci_collection_trim_method)
1034 	PHP_FALIAS(free,		  oci_free_collection,				arginfo_oci_collection_free_method)
1035 #if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 3 && PHP_RELEASE_VERSION >= 7) || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
1036 	PHP_FE_END
1037 #else
1038 	{NULL,NULL,NULL}
1039 #endif
1040 };
1041 
1042 zend_module_entry oci8_module_entry = {
1043 	STANDARD_MODULE_HEADER,
1044 	"oci8",				  /* extension name */
1045 	php_oci_functions,	  /* extension function list */
1046 	PHP_MINIT(oci),		  /* extension-wide startup function */
1047 	PHP_MSHUTDOWN(oci),	  /* extension-wide shutdown function */
1048 	PHP_RINIT(oci),		  /* per-request startup function */
1049 	PHP_RSHUTDOWN(oci),	  /* per-request shutdown function */
1050 	PHP_MINFO(oci),		  /* information function */
1051 	PHP_OCI8_VERSION,
1052 	PHP_MODULE_GLOBALS(oci),  /* globals descriptor */
1053 	PHP_GINIT(oci),			  /* globals ctor */
1054 	PHP_GSHUTDOWN(oci),		  /* globals dtor */
1055 	NULL,					  /* post deactivate */
1056 	STANDARD_MODULE_PROPERTIES_EX
1057 };
1058 /* }}} */
1059 
1060 /* {{{ PHP_INI */
1061 PHP_INI_BEGIN()
1062 	STD_PHP_INI_ENTRY(	"oci8.max_persistent",			"-1",	PHP_INI_SYSTEM,	ONUPDATELONGFUNC,	max_persistent,			zend_oci_globals,	oci_globals)
1063 	STD_PHP_INI_ENTRY(	"oci8.persistent_timeout",		"-1",	PHP_INI_SYSTEM,	ONUPDATELONGFUNC,	persistent_timeout,		zend_oci_globals,	oci_globals)
1064 	STD_PHP_INI_ENTRY(	"oci8.ping_interval",			"60",	PHP_INI_SYSTEM,	ONUPDATELONGFUNC,	ping_interval,			zend_oci_globals,	oci_globals)
1065 	STD_PHP_INI_BOOLEAN("oci8.privileged_connect",		"0",	PHP_INI_SYSTEM,	OnUpdateBool,		privileged_connect,		zend_oci_globals,	oci_globals)
1066 	STD_PHP_INI_ENTRY(	"oci8.statement_cache_size",	"20",	PHP_INI_SYSTEM,	ONUPDATELONGFUNC,	statement_cache_size,	zend_oci_globals,	oci_globals)
1067 	STD_PHP_INI_ENTRY(	"oci8.default_prefetch",		"100",	PHP_INI_SYSTEM,	ONUPDATELONGFUNC,	default_prefetch,		zend_oci_globals,	oci_globals)
1068 	STD_PHP_INI_BOOLEAN("oci8.old_oci_close_semantics",	"0",	PHP_INI_SYSTEM,	OnUpdateBool,		old_oci_close_semantics,zend_oci_globals,	oci_globals)
1069 #if (OCI_MAJOR_VERSION >= 11)
1070 	STD_PHP_INI_ENTRY(	"oci8.connection_class",		"",		PHP_INI_ALL,	OnUpdateString,		connection_class,		zend_oci_globals,	oci_globals)
1071 #endif
1072 #if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2)))
1073 	STD_PHP_INI_BOOLEAN("oci8.events",					"0",	PHP_INI_SYSTEM,	OnUpdateBool,		events,					zend_oci_globals,	oci_globals)
1074 #endif
PHP_INI_END()1075 PHP_INI_END()
1076 /* }}} */
1077 
1078 /* {{{ startup, shutdown and info functions
1079 */
1080 
1081 /* {{{	php_oci_init_global_handles()
1082  *
1083  * Initialize global handles only when they are needed
1084  */
1085 static void php_oci_init_global_handles(TSRMLS_D)
1086 {
1087 	sword errstatus;
1088 	sb4   ora_error_code = 0;
1089 	text  tmp_buf[OCI_ERROR_MAXMSG_SIZE];  /* Use traditional smaller size: non-PL/SQL errors should fit and it keeps the stack smaller */
1090 
1091 	errstatus = OCIEnvNlsCreate(&OCI_G(env), PHP_OCI_INIT_MODE, 0, NULL, NULL, NULL, 0, NULL, 0, 0);
1092 
1093 	if (errstatus == OCI_ERROR) {
1094 #ifdef HAVE_OCI_INSTANT_CLIENT
1095 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "OCIEnvNlsCreate() failed. There is something wrong with your system - please check that " PHP_OCI8_LIB_PATH_MSG " includes the directory with Oracle Instant Client libraries");
1096 #else
1097 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "OCIEnvNlsCreate() failed. There is something wrong with your system - please check that ORACLE_HOME and " PHP_OCI8_LIB_PATH_MSG " are set and point to the right directories");
1098 #endif
1099 		if (OCI_G(env)
1100 			&& OCIErrorGet(OCI_G(env), (ub4)1, NULL, &ora_error_code, tmp_buf, (ub4)OCI_ERROR_MAXMSG_SIZE, (ub4)OCI_HTYPE_ENV) == OCI_SUCCESS
1101 			&& *tmp_buf) {
1102 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", tmp_buf);
1103 		}
1104 
1105 		OCI_G(env) = NULL;
1106 		OCI_G(err) = NULL;
1107 		return;
1108 	}
1109 
1110 	errstatus = OCIHandleAlloc (OCI_G(env), (dvoid **)&OCI_G(err), OCI_HTYPE_ERROR, 0, NULL);
1111 
1112 	if (errstatus == OCI_SUCCESS) {
1113 #if !defined(OCI_MAJOR_VERSION) || (OCI_MAJOR_VERSION < 11)
1114 		/* This fixes PECL bug 15988 (sqlnet.ora not being read).  The
1115 		 * root cause was fixed in Oracle 10.2.0.4 but there is no
1116 		 * compile time method to check for that precise patch level,
1117 		 * nor can it be guaranteed that runtime will use the same
1118 		 * patch level the code was compiled with.  So, we do this
1119 		 * code for all non 11g versions.
1120 		 */
1121 		OCICPool *cpoolh;
1122 		ub4 cpoolmode = 0x80000000;	/* Pass invalid mode to OCIConnectionPoolCreate */
1123 		PHP_OCI_CALL(OCIHandleAlloc, (OCI_G(env), (dvoid **) &cpoolh, OCI_HTYPE_CPOOL, (size_t) 0, (dvoid **) 0));
1124 		PHP_OCI_CALL(OCIConnectionPoolCreate, (OCI_G(env), OCI_G(err), cpoolh, NULL, 0, NULL, 0, 0, 0, 0, NULL, 0, NULL, 0, cpoolmode));
1125 		PHP_OCI_CALL(OCIConnectionPoolDestroy, (cpoolh, OCI_G(err), OCI_DEFAULT));
1126 		PHP_OCI_CALL(OCIHandleFree, (cpoolh, OCI_HTYPE_CPOOL));
1127 #endif
1128 	} else {
1129 		OCIErrorGet(OCI_G(env), (ub4)1, NULL, &ora_error_code, tmp_buf, (ub4)OCI_ERROR_MAXMSG_SIZE, (ub4)OCI_HTYPE_ERROR);
1130 
1131 		if (ora_error_code) {
1132 			int tmp_buf_len = strlen((char *)tmp_buf);
1133 
1134 			if (tmp_buf_len > 0 && tmp_buf[tmp_buf_len - 1] == '\n') {
1135 				tmp_buf[tmp_buf_len - 1] = '\0';
1136 			}
1137 
1138 			if (errstatus == OCI_SUCCESS_WITH_INFO) {
1139 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Initialization error: OCI_SUCCESS_WITH_INFO: %s", tmp_buf);
1140 			} else {
1141 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Initialization error: OCI_ERROR: %s", tmp_buf);
1142 
1143 				OCIHandleFree((dvoid *) OCI_G(env), OCI_HTYPE_ENV);
1144 
1145 				OCI_G(env) = NULL;
1146 				OCI_G(err) = NULL;
1147 			}
1148 		}
1149 	}
1150 }
1151 /* }}} */
1152 
1153 /* {{{ php_oci_cleanup_global_handles()
1154  *
1155  * Free global handles (if they were initialized before)
1156  */
php_oci_cleanup_global_handles(TSRMLS_D)1157 static void php_oci_cleanup_global_handles(TSRMLS_D)
1158 {
1159 	if (OCI_G(err)) {
1160 		PHP_OCI_CALL(OCIHandleFree, ((dvoid *) OCI_G(err), OCI_HTYPE_ERROR));
1161 		OCI_G(err) = NULL;
1162 	}
1163 
1164 	if (OCI_G(env)) {
1165 		PHP_OCI_CALL(OCIHandleFree, ((dvoid *) OCI_G(env), OCI_HTYPE_ENV));
1166 		OCI_G(env) = NULL;
1167 	}
1168 }
1169 /* }}} */
1170 
1171 /* {{{ PHP_GINIT_FUNCTION
1172  *
1173  * Zerofill globals during module init
1174  */
PHP_GINIT_FUNCTION(oci)1175 static PHP_GINIT_FUNCTION(oci)
1176 {
1177 	memset(oci_globals, 0, sizeof(zend_oci_globals));
1178 }
1179 /* }}} */
1180 
1181 /* {{{ PHP_GSHUTDOWN_FUNCTION
1182  *
1183  * Called for thread shutdown in ZTS, after module shutdown for non-ZTS
1184  */
PHP_GSHUTDOWN_FUNCTION(oci)1185 static PHP_GSHUTDOWN_FUNCTION(oci)
1186 {
1187 	php_oci_cleanup_global_handles(TSRMLS_C);
1188 }
1189 /* }}} */
1190 
PHP_MINIT_FUNCTION(oci)1191 PHP_MINIT_FUNCTION(oci)
1192 {
1193 	zend_class_entry oci_lob_class_entry;
1194 	zend_class_entry oci_coll_class_entry;
1195 
1196 	REGISTER_INI_ENTRIES();
1197 
1198 	le_statement = zend_register_list_destructors_ex(php_oci_statement_list_dtor, NULL, "oci8 statement", module_number);
1199 	le_connection = zend_register_list_destructors_ex(php_oci_connection_list_dtor, NULL, "oci8 connection", module_number);
1200 	le_pconnection = zend_register_list_destructors_ex(php_oci_pconnection_list_np_dtor, php_oci_pconnection_list_dtor, "oci8 persistent connection", module_number);
1201 	le_psessionpool = zend_register_list_destructors_ex(NULL, php_oci_spool_list_dtor, "oci8 persistent session pool", module_number);
1202 	le_descriptor = zend_register_list_destructors_ex(php_oci_descriptor_list_dtor, NULL, "oci8 descriptor", module_number);
1203 	le_collection = zend_register_list_destructors_ex(php_oci_collection_list_dtor, NULL, "oci8 collection", module_number);
1204 
1205 	INIT_CLASS_ENTRY(oci_lob_class_entry, "OCI-Lob", php_oci_lob_class_functions);
1206 	INIT_CLASS_ENTRY(oci_coll_class_entry, "OCI-Collection", php_oci_coll_class_functions);
1207 
1208 	oci_lob_class_entry_ptr = zend_register_internal_class(&oci_lob_class_entry TSRMLS_CC);
1209 	oci_coll_class_entry_ptr = zend_register_internal_class(&oci_coll_class_entry TSRMLS_CC);
1210 
1211 /* thies@thieso.net 990203 i do not think that we will need all of them - just in here for completeness for now! */
1212 	REGISTER_LONG_CONSTANT("OCI_DEFAULT",OCI_DEFAULT, CONST_CS | CONST_PERSISTENT);
1213 	REGISTER_LONG_CONSTANT("OCI_SYSOPER",OCI_SYSOPER, CONST_CS | CONST_PERSISTENT);
1214 	REGISTER_LONG_CONSTANT("OCI_SYSDBA",OCI_SYSDBA, CONST_CS | CONST_PERSISTENT);
1215 	REGISTER_LONG_CONSTANT("OCI_CRED_EXT",PHP_OCI_CRED_EXT, CONST_CS | CONST_PERSISTENT);
1216 	REGISTER_LONG_CONSTANT("OCI_DESCRIBE_ONLY",OCI_DESCRIBE_ONLY, CONST_CS | CONST_PERSISTENT);
1217 	REGISTER_LONG_CONSTANT("OCI_COMMIT_ON_SUCCESS",OCI_COMMIT_ON_SUCCESS, CONST_CS | CONST_PERSISTENT);
1218 	REGISTER_LONG_CONSTANT("OCI_NO_AUTO_COMMIT",OCI_DEFAULT, CONST_CS | CONST_PERSISTENT);
1219 	REGISTER_LONG_CONSTANT("OCI_EXACT_FETCH",OCI_EXACT_FETCH, CONST_CS | CONST_PERSISTENT);
1220 
1221 /* for $LOB->seek() */
1222 	REGISTER_LONG_CONSTANT("OCI_SEEK_SET",PHP_OCI_SEEK_SET, CONST_CS | CONST_PERSISTENT);
1223 	REGISTER_LONG_CONSTANT("OCI_SEEK_CUR",PHP_OCI_SEEK_CUR, CONST_CS | CONST_PERSISTENT);
1224 	REGISTER_LONG_CONSTANT("OCI_SEEK_END",PHP_OCI_SEEK_END, CONST_CS | CONST_PERSISTENT);
1225 
1226 /*	for $LOB->flush() */
1227 	REGISTER_LONG_CONSTANT("OCI_LOB_BUFFER_FREE",OCI_LOB_BUFFER_FREE, CONST_CS | CONST_PERSISTENT);
1228 
1229 /* for OCIBindByName (real "oci" names + short "php" names */
1230 	REGISTER_LONG_CONSTANT("SQLT_BFILEE",SQLT_BFILEE, CONST_CS | CONST_PERSISTENT);
1231 	REGISTER_LONG_CONSTANT("SQLT_CFILEE",SQLT_CFILEE, CONST_CS | CONST_PERSISTENT);
1232 	REGISTER_LONG_CONSTANT("SQLT_CLOB",SQLT_CLOB, CONST_CS | CONST_PERSISTENT);
1233 	REGISTER_LONG_CONSTANT("SQLT_BLOB",SQLT_BLOB, CONST_CS | CONST_PERSISTENT);
1234 	REGISTER_LONG_CONSTANT("SQLT_RDD",SQLT_RDD, CONST_CS | CONST_PERSISTENT);
1235 	REGISTER_LONG_CONSTANT("SQLT_INT",SQLT_INT, CONST_CS | CONST_PERSISTENT);
1236 	REGISTER_LONG_CONSTANT("SQLT_NUM",SQLT_NUM, CONST_CS | CONST_PERSISTENT);
1237 	REGISTER_LONG_CONSTANT("SQLT_RSET",SQLT_RSET, CONST_CS | CONST_PERSISTENT);
1238 	REGISTER_LONG_CONSTANT("SQLT_AFC",SQLT_AFC, CONST_CS | CONST_PERSISTENT);
1239 	REGISTER_LONG_CONSTANT("SQLT_CHR",SQLT_CHR, CONST_CS | CONST_PERSISTENT);
1240 	REGISTER_LONG_CONSTANT("SQLT_VCS",SQLT_VCS, CONST_CS | CONST_PERSISTENT);
1241 	REGISTER_LONG_CONSTANT("SQLT_AVC",SQLT_AVC, CONST_CS | CONST_PERSISTENT);
1242 	REGISTER_LONG_CONSTANT("SQLT_STR",SQLT_STR, CONST_CS | CONST_PERSISTENT);
1243 	REGISTER_LONG_CONSTANT("SQLT_LVC",SQLT_LVC, CONST_CS | CONST_PERSISTENT);
1244 	REGISTER_LONG_CONSTANT("SQLT_FLT",SQLT_FLT, CONST_CS | CONST_PERSISTENT);
1245 	REGISTER_LONG_CONSTANT("SQLT_UIN",SQLT_UIN, CONST_CS | CONST_PERSISTENT);
1246 	REGISTER_LONG_CONSTANT("SQLT_LNG",SQLT_LNG, CONST_CS | CONST_PERSISTENT);
1247 	REGISTER_LONG_CONSTANT("SQLT_LBI",SQLT_LBI, CONST_CS | CONST_PERSISTENT);
1248 	REGISTER_LONG_CONSTANT("SQLT_BIN",SQLT_BIN, CONST_CS | CONST_PERSISTENT);
1249 	REGISTER_LONG_CONSTANT("SQLT_ODT",SQLT_ODT, CONST_CS | CONST_PERSISTENT);
1250 #if defined(HAVE_OCI_INSTANT_CLIENT) || (defined(OCI_MAJOR_VERSION) && OCI_MAJOR_VERSION >= 10)
1251 	REGISTER_LONG_CONSTANT("SQLT_BDOUBLE",SQLT_BDOUBLE, CONST_CS | CONST_PERSISTENT);
1252 	REGISTER_LONG_CONSTANT("SQLT_BFLOAT",SQLT_BFLOAT, CONST_CS | CONST_PERSISTENT);
1253 #endif
1254 #if defined(OCI_MAJOR_VERSION) && OCI_MAJOR_VERSION >= 12
1255 	REGISTER_LONG_CONSTANT("SQLT_BOL",SQLT_BOL, CONST_CS | CONST_PERSISTENT);
1256 #endif
1257 
1258 	REGISTER_LONG_CONSTANT("OCI_B_NTY",SQLT_NTY, CONST_CS | CONST_PERSISTENT);
1259 	REGISTER_LONG_CONSTANT("SQLT_NTY",SQLT_NTY, CONST_CS | CONST_PERSISTENT);
1260 	REGISTER_STRING_CONSTANT("OCI_SYSDATE","SYSDATE", CONST_CS | CONST_PERSISTENT);
1261 
1262 	REGISTER_LONG_CONSTANT("OCI_B_BFILE",SQLT_BFILEE, CONST_CS | CONST_PERSISTENT);
1263 	REGISTER_LONG_CONSTANT("OCI_B_CFILEE",SQLT_CFILEE, CONST_CS | CONST_PERSISTENT);
1264 	REGISTER_LONG_CONSTANT("OCI_B_CLOB",SQLT_CLOB, CONST_CS | CONST_PERSISTENT);
1265 	REGISTER_LONG_CONSTANT("OCI_B_BLOB",SQLT_BLOB, CONST_CS | CONST_PERSISTENT);
1266 	REGISTER_LONG_CONSTANT("OCI_B_ROWID",SQLT_RDD, CONST_CS | CONST_PERSISTENT);
1267 	REGISTER_LONG_CONSTANT("OCI_B_CURSOR",SQLT_RSET, CONST_CS | CONST_PERSISTENT);
1268 	REGISTER_LONG_CONSTANT("OCI_B_BIN",SQLT_BIN, CONST_CS | CONST_PERSISTENT);
1269 	REGISTER_LONG_CONSTANT("OCI_B_INT",SQLT_INT, CONST_CS | CONST_PERSISTENT);
1270 	REGISTER_LONG_CONSTANT("OCI_B_NUM",SQLT_NUM, CONST_CS | CONST_PERSISTENT);
1271 #if defined(OCI_MAJOR_VERSION) && OCI_MAJOR_VERSION >= 12
1272 	REGISTER_LONG_CONSTANT("OCI_B_BOL",SQLT_BOL, CONST_CS | CONST_PERSISTENT);
1273 #endif
1274 
1275 /* for OCIFetchStatement */
1276 	REGISTER_LONG_CONSTANT("OCI_FETCHSTATEMENT_BY_COLUMN", PHP_OCI_FETCHSTATEMENT_BY_COLUMN, CONST_CS | CONST_PERSISTENT);
1277 	REGISTER_LONG_CONSTANT("OCI_FETCHSTATEMENT_BY_ROW", PHP_OCI_FETCHSTATEMENT_BY_ROW, CONST_CS | CONST_PERSISTENT);
1278 
1279 /* for OCIFetchInto & OCIResult */
1280 	REGISTER_LONG_CONSTANT("OCI_ASSOC",PHP_OCI_ASSOC, CONST_CS | CONST_PERSISTENT);
1281 	REGISTER_LONG_CONSTANT("OCI_NUM",PHP_OCI_NUM, CONST_CS | CONST_PERSISTENT);
1282 	REGISTER_LONG_CONSTANT("OCI_BOTH",PHP_OCI_BOTH, CONST_CS | CONST_PERSISTENT);
1283 	REGISTER_LONG_CONSTANT("OCI_RETURN_NULLS",PHP_OCI_RETURN_NULLS, CONST_CS | CONST_PERSISTENT);
1284 	REGISTER_LONG_CONSTANT("OCI_RETURN_LOBS",PHP_OCI_RETURN_LOBS, CONST_CS | CONST_PERSISTENT);
1285 
1286 /* for OCINewDescriptor (real "oci" names + short "php" names */
1287 	REGISTER_LONG_CONSTANT("OCI_DTYPE_FILE",OCI_DTYPE_FILE, CONST_CS | CONST_PERSISTENT);
1288 	REGISTER_LONG_CONSTANT("OCI_DTYPE_LOB",OCI_DTYPE_LOB, CONST_CS | CONST_PERSISTENT);
1289 	REGISTER_LONG_CONSTANT("OCI_DTYPE_ROWID",OCI_DTYPE_ROWID, CONST_CS | CONST_PERSISTENT);
1290 
1291 	REGISTER_LONG_CONSTANT("OCI_D_FILE",OCI_DTYPE_FILE, CONST_CS | CONST_PERSISTENT);
1292 	REGISTER_LONG_CONSTANT("OCI_D_LOB",OCI_DTYPE_LOB, CONST_CS | CONST_PERSISTENT);
1293 	REGISTER_LONG_CONSTANT("OCI_D_ROWID",OCI_DTYPE_ROWID, CONST_CS | CONST_PERSISTENT);
1294 
1295 /* for OCIWriteTemporaryLob */
1296 	REGISTER_LONG_CONSTANT("OCI_TEMP_CLOB",OCI_TEMP_CLOB, CONST_CS | CONST_PERSISTENT);
1297 	REGISTER_LONG_CONSTANT("OCI_TEMP_BLOB",OCI_TEMP_BLOB, CONST_CS | CONST_PERSISTENT);
1298 
1299 	return SUCCESS;
1300 }
1301 
PHP_RINIT_FUNCTION(oci)1302 PHP_RINIT_FUNCTION(oci)
1303 {
1304 	OCI_G(num_links) = OCI_G(num_persistent);
1305 	OCI_G(errcode) = 0;
1306 	OCI_G(edition) = NULL;
1307 
1308 	return SUCCESS;
1309 }
1310 
PHP_MSHUTDOWN_FUNCTION(oci)1311 PHP_MSHUTDOWN_FUNCTION(oci)
1312 {
1313 	OCI_G(shutdown) = 1;
1314 
1315 	UNREGISTER_INI_ENTRIES();
1316 
1317 	return SUCCESS;
1318 }
1319 
PHP_RSHUTDOWN_FUNCTION(oci)1320 PHP_RSHUTDOWN_FUNCTION(oci)
1321 {
1322 	/* Check persistent connections and do the necessary actions if needed. If persistent_helper is
1323 	 * unable to process a pconnection because of a refcount, the processing would happen from
1324 	 * np-destructor which is called when refcount goes to zero - php_oci_pconnection_list_np_dtor
1325 	 */
1326 	zend_hash_apply(&EG(persistent_list), (apply_func_t) php_oci_persistent_helper TSRMLS_CC);
1327 
1328 	if (OCI_G(edition)) {
1329 		efree(OCI_G(edition));
1330 	}
1331 
1332 	return SUCCESS;
1333 }
1334 
PHP_MINFO_FUNCTION(oci)1335 PHP_MINFO_FUNCTION(oci)
1336 {
1337 	char buf[32];
1338 #if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2)))
1339 	char *ver;
1340 #endif
1341 
1342 	php_info_print_table_start();
1343 	php_info_print_table_row(2, "OCI8 Support", "enabled");
1344 #if defined(HAVE_OCI8_DTRACE)
1345 	php_info_print_table_row(2, "OCI8 DTrace Support", "enabled");
1346 #else
1347 	php_info_print_table_row(2, "OCI8 DTrace Support", "disabled");
1348 #endif
1349 	php_info_print_table_row(2, "OCI8 Version", PHP_OCI8_VERSION);
1350 	php_info_print_table_row(2, "Revision", "$Id: 020312b6429ebb9d6272ac9bc28f6dce529434b6 $");
1351 
1352 #if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2)))
1353 	php_oci_client_get_version(&ver TSRMLS_CC);
1354 	php_info_print_table_row(2, "Oracle Run-time Client Library Version", ver);
1355 	efree(ver);
1356 #else
1357 	php_info_print_table_row(2, "Oracle Run-time Client Library Version", "Unknown");
1358 #endif
1359 #if	defined(OCI_MAJOR_VERSION) && defined(OCI_MINOR_VERSION)
1360 	snprintf(buf, sizeof(buf), "%d.%d", OCI_MAJOR_VERSION, OCI_MINOR_VERSION);
1361 #elif defined(PHP_OCI8_ORACLE_VERSION)
1362 	snprintf(buf, sizeof(buf), "%s", PHP_OCI8_ORACLE_VERSION);
1363 #else
1364 	snprintf(buf, sizeof(buf), "Unknown");
1365 #endif
1366 #if defined(HAVE_OCI_INSTANT_CLIENT)
1367 	php_info_print_table_row(2, "Oracle Compile-time Instant Client Version", buf);
1368 #else
1369 	php_info_print_table_row(2, "Oracle Compile-time Version", buf);
1370 #endif
1371 
1372 #if !defined(PHP_WIN32) && !defined(HAVE_OCI_INSTANT_CLIENT)
1373 #if defined(PHP_OCI8_DEF_DIR)
1374 	php_info_print_table_row(2, "Compile-time ORACLE_HOME", PHP_OCI8_DEF_DIR);
1375 #endif
1376 #if defined(PHP_OCI8_DEF_SHARED_LIBADD)
1377 	php_info_print_table_row(2, "Libraries Used", PHP_OCI8_DEF_SHARED_LIBADD);
1378 #endif
1379 #endif
1380 
1381 
1382 	php_info_print_table_end();
1383 
1384 	DISPLAY_INI_ENTRIES();
1385 
1386 	php_info_print_table_start();
1387 	php_info_print_table_header(2, "Statistics", "");
1388 	snprintf(buf, sizeof(buf), "%ld", OCI_G(num_persistent));
1389 	php_info_print_table_row(2, "Active Persistent Connections", buf);
1390 	snprintf(buf, sizeof(buf), "%ld", OCI_G(num_links));
1391 	php_info_print_table_row(2, "Active Connections", buf);
1392 	php_info_print_table_end();
1393 }
1394 /* }}} */
1395 
1396 /* {{{ list destructors */
1397 
1398 /* {{{ php_oci_connection_list_dtor()
1399  *
1400  * Non-persistent connection destructor
1401  */
php_oci_connection_list_dtor(zend_rsrc_list_entry * entry TSRMLS_DC)1402 static void php_oci_connection_list_dtor(zend_rsrc_list_entry *entry TSRMLS_DC)
1403 {
1404 	php_oci_connection *connection = (php_oci_connection *)entry->ptr;
1405 
1406 	if (connection) {
1407 		php_oci_connection_close(connection TSRMLS_CC);
1408 		OCI_G(num_links)--;
1409 	}
1410 }
1411 /* }}} */
1412 
1413 /* {{{ php_oci_pconnection_list_dtor()
1414  *
1415  * Persistent connection destructor
1416  */
php_oci_pconnection_list_dtor(zend_rsrc_list_entry * entry TSRMLS_DC)1417 static void php_oci_pconnection_list_dtor(zend_rsrc_list_entry *entry TSRMLS_DC)
1418 {
1419 	php_oci_connection *connection = (php_oci_connection *)entry->ptr;
1420 
1421 	if (connection) {
1422 		php_oci_connection_close(connection TSRMLS_CC);
1423 		OCI_G(num_persistent)--;
1424 		OCI_G(num_links)--;
1425 	}
1426 }
1427 /* }}} */
1428 
1429 /* {{{ php_oci_pconnection_list_np_dtor()
1430  *
1431  * Non-Persistent destructor for persistent connection - This gets invoked when
1432  * the refcount of this goes to zero in the regular list
1433  */
php_oci_pconnection_list_np_dtor(zend_rsrc_list_entry * entry TSRMLS_DC)1434 static void php_oci_pconnection_list_np_dtor(zend_rsrc_list_entry *entry TSRMLS_DC)
1435 {
1436 	php_oci_connection *connection = (php_oci_connection *)entry->ptr;
1437 	zend_rsrc_list_entry *le;
1438 
1439 	/*
1440 	 * We cannot get connection as NULL or as a stub in this function. This is the function that
1441 	 * turns a pconnection to a stub
1442 	 *
1443 	 * If oci_password_change() changed the password of a persistent connection, close the
1444 	 * connection and remove it from the persistent connection cache.  This means subsequent scripts
1445 	 * will be prevented from being able to present the old (now invalid) password to a usable
1446 	 * connection to the database; they must use the new password.
1447 	 *
1448 	 * Check for conditions that warrant removal of the hash entry
1449 	 */
1450 
1451 	if (!connection->is_open ||
1452 		connection->passwd_changed ||
1453 		(PG(connection_status) & PHP_CONNECTION_TIMEOUT) ||
1454 		OCI_G(in_call)) {
1455 
1456 		/* Remove the hash entry if present */
1457 		if ((zend_hash_find(&EG(persistent_list), connection->hash_key, strlen(connection->hash_key)+1, (void **) &le)== SUCCESS) && (le->type == le_pconnection) && (le->ptr == connection)) {
1458 			zend_hash_del(&EG(persistent_list), connection->hash_key, strlen(connection->hash_key)+1);
1459 		}
1460 		else {
1461 			php_oci_connection_close(connection TSRMLS_CC);
1462 			OCI_G(num_persistent)--;
1463 		}
1464 
1465 #ifdef HAVE_OCI8_DTRACE
1466 		if (DTRACE_OCI8_CONNECT_P_DTOR_CLOSE_ENABLED()) {
1467 			DTRACE_OCI8_CONNECT_P_DTOR_CLOSE(connection);
1468 		}
1469 #endif /* HAVE_OCI8_DTRACE */
1470 	} else {
1471 		/*
1472 		 * Release the connection to underlying pool.  We do this unconditionally so that
1473 		 * out-of-scope pconnects are now consistent with oci_close and out-of-scope new connect
1474 		 * semantics. With the PECL OCI 1.3.x extensions, we release pconnections when oci_close
1475 		 * takes the refcount to zero.
1476 		 *
1477 		 * If oci_old_close_semantics is set, we artifically bump up the refcount and decremented
1478 		 * only at request shutdown.
1479 		 */
1480 		php_oci_connection_release(connection TSRMLS_CC);
1481 
1482 #ifdef HAVE_OCI8_DTRACE
1483 		if (DTRACE_OCI8_CONNECT_P_DTOR_RELEASE_ENABLED()) {
1484 			DTRACE_OCI8_CONNECT_P_DTOR_RELEASE(connection);
1485 		}
1486 #endif /* HAVE_OCI8_DTRACE */
1487 	}
1488 }
1489 /* }}} */
1490 
1491 /* {{{ php_oci_statement_list_dtor()
1492  *
1493  * Statement destructor
1494  */
php_oci_statement_list_dtor(zend_rsrc_list_entry * entry TSRMLS_DC)1495 static void php_oci_statement_list_dtor(zend_rsrc_list_entry *entry TSRMLS_DC)
1496 {
1497 	php_oci_statement *statement = (php_oci_statement *)entry->ptr;
1498 	php_oci_statement_free(statement TSRMLS_CC);
1499 }
1500 /* }}} */
1501 
1502 /* {{{ php_oci_descriptor_list_dtor()
1503  *
1504  *	Descriptor destructor
1505  */
php_oci_descriptor_list_dtor(zend_rsrc_list_entry * entry TSRMLS_DC)1506 static void php_oci_descriptor_list_dtor(zend_rsrc_list_entry *entry TSRMLS_DC)
1507 {
1508 	php_oci_descriptor *descriptor = (php_oci_descriptor *)entry->ptr;
1509 	php_oci_lob_free(descriptor TSRMLS_CC);
1510 }
1511 /* }}} */
1512 
1513 /* {{{ php_oci_collection_list_dtor()
1514  *
1515  * Collection destructor
1516  */
php_oci_collection_list_dtor(zend_rsrc_list_entry * entry TSRMLS_DC)1517 static void php_oci_collection_list_dtor(zend_rsrc_list_entry *entry TSRMLS_DC)
1518 {
1519 	php_oci_collection *collection = (php_oci_collection *)entry->ptr;
1520 	php_oci_collection_close(collection TSRMLS_CC);
1521 }
1522 /* }}} */
1523 
1524 /* }}} */
1525 
1526 /* {{{ Hash Destructors */
1527 
1528 /* {{{ php_oci_define_hash_dtor()
1529  *
1530  * Define hash destructor
1531  */
php_oci_define_hash_dtor(void * data)1532 void php_oci_define_hash_dtor(void *data)
1533 {
1534 	php_oci_define *define = (php_oci_define *) data;
1535 
1536 	zval_ptr_dtor(&define->zval);
1537 
1538 	if (define->name) {
1539 		efree(define->name);
1540 		define->name = NULL;
1541 	}
1542 }
1543 /* }}} */
1544 
1545 /* {{{ php_oci_bind_hash_dtor()
1546  *
1547  * Bind hash destructor
1548  */
php_oci_bind_hash_dtor(void * data)1549 void php_oci_bind_hash_dtor(void *data)
1550 {
1551 	php_oci_bind *bind = (php_oci_bind *) data;
1552 
1553 	if (bind->array.elements) {
1554 		efree(bind->array.elements);
1555 	}
1556 
1557 	if (bind->array.element_lengths) {
1558 		efree(bind->array.element_lengths);
1559 	}
1560 
1561 	if (bind->array.indicators) {
1562 		efree(bind->array.indicators);
1563 	}
1564 
1565 	zval_ptr_dtor(&bind->zval);
1566 }
1567 /* }}} */
1568 
1569 /* {{{ php_oci_column_hash_dtor()
1570  *
1571  * Column hash destructor
1572  */
php_oci_column_hash_dtor(void * data)1573 void php_oci_column_hash_dtor(void *data)
1574 {
1575 	php_oci_out_column *column = (php_oci_out_column *) data;
1576 	TSRMLS_FETCH();
1577 
1578 	if (column->stmtid) {
1579 		zend_list_delete(column->stmtid);
1580 	}
1581 
1582 	if (column->is_descr) {
1583 		zend_list_delete(column->descid);
1584 	}
1585 
1586 	if (column->data) {
1587 		efree(column->data);
1588 	}
1589 
1590 	if (column->name) {
1591 		efree(column->name);
1592 	}
1593 }
1594 /* }}} */
1595 
1596 /* {{{ php_oci_descriptor_flush_hash_dtor()
1597  *
1598  * Flush descriptors on commit
1599  */
php_oci_descriptor_flush_hash_dtor(void * data)1600 void php_oci_descriptor_flush_hash_dtor(void *data)
1601 {
1602 	php_oci_descriptor *descriptor = *(php_oci_descriptor **)data;
1603 	TSRMLS_FETCH();
1604 
1605 	if (descriptor && descriptor->buffering == PHP_OCI_LOB_BUFFER_USED && (descriptor->type == OCI_DTYPE_LOB || descriptor->type == OCI_DTYPE_FILE)) {
1606 		php_oci_lob_flush(descriptor, OCI_LOB_BUFFER_FREE TSRMLS_CC);
1607 		descriptor->buffering = PHP_OCI_LOB_BUFFER_ENABLED;
1608 	}
1609 	data = NULL;
1610 }
1611 /* }}} */
1612 
1613 /* }}} */
1614 
1615 /* {{{ php_oci_connection_descriptors_free()
1616  *
1617  * Free descriptors for a connection
1618  */
php_oci_connection_descriptors_free(php_oci_connection * connection TSRMLS_DC)1619 void php_oci_connection_descriptors_free(php_oci_connection *connection TSRMLS_DC)
1620 {
1621 	zend_hash_destroy(connection->descriptors);
1622 	efree(connection->descriptors);
1623 	connection->descriptors = NULL;
1624 	connection->descriptor_count = 0;
1625 }
1626 /* }}} */
1627 
1628 /* {{{ php_oci_error()
1629  *
1630  * Fetch & print out error message if we get an error
1631  * Returns an Oracle error number
1632  */
php_oci_error(OCIError * err_p,sword errstatus TSRMLS_DC)1633 sb4 php_oci_error(OCIError *err_p, sword errstatus TSRMLS_DC)
1634 {
1635 	text *errbuf = (text *)NULL;
1636 	sb4 errcode = 0; /* Oracle error number */
1637 
1638 	switch (errstatus) {
1639 		case OCI_SUCCESS:
1640 			break;
1641 		case OCI_SUCCESS_WITH_INFO:
1642 			errcode = php_oci_fetch_errmsg(err_p, &errbuf TSRMLS_CC);
1643 			if (errbuf) {
1644 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "OCI_SUCCESS_WITH_INFO: %s", errbuf);
1645 				efree(errbuf);
1646 			} else {
1647 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "OCI_SUCCESS_WITH_INFO: failed to fetch error message");
1648 			}
1649 			break;
1650 		case OCI_NEED_DATA:
1651 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "OCI_NEED_DATA");
1652 			break;
1653 		case OCI_NO_DATA:
1654 			errcode = php_oci_fetch_errmsg(err_p, &errbuf TSRMLS_CC);
1655 			if (errbuf) {
1656 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", errbuf);
1657 				efree(errbuf);
1658 			} else {
1659 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "OCI_NO_DATA: failed to fetch error message");
1660 			}
1661 			break;
1662 		case OCI_ERROR:
1663 			errcode = php_oci_fetch_errmsg(err_p, &errbuf TSRMLS_CC);
1664 			if (errbuf) {
1665 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", errbuf);
1666 				efree(errbuf);
1667 			} else {
1668 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to fetch error message");
1669 			}
1670 			break;
1671 		case OCI_INVALID_HANDLE:
1672 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "OCI_INVALID_HANDLE");
1673 			break;
1674 		case OCI_STILL_EXECUTING:
1675 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "OCI_STILL_EXECUTING");
1676 			break;
1677 		case OCI_CONTINUE:
1678 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "OCI_CONTINUE");
1679 			break;
1680 		default:
1681 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown OCI error code: %d", errstatus);
1682 			break;
1683 	}
1684 
1685 #ifdef HAVE_OCI8_DTRACE
1686 	if (DTRACE_OCI8_ERROR_ENABLED()) {
1687 		DTRACE_OCI8_ERROR((int)errstatus, (long)errcode);
1688 	}
1689 #endif /* HAVE_OCI8_DTRACE */
1690 
1691 	return errcode;
1692 }
1693 /* }}} */
1694 
1695 /* {{{ php_oci_fetch_errmsg()
1696  *
1697  * Fetch error message into the buffer from the error handle provided
1698  */
php_oci_fetch_errmsg(OCIError * error_handle,text ** error_buf TSRMLS_DC)1699 sb4 php_oci_fetch_errmsg(OCIError *error_handle, text **error_buf TSRMLS_DC)
1700 {
1701 	sb4 error_code = 0;
1702 	text err_buf[PHP_OCI_ERRBUF_LEN];
1703 
1704 	memset(err_buf, 0, sizeof(err_buf));
1705 	PHP_OCI_CALL(OCIErrorGet, (error_handle, (ub4)1, NULL, &error_code, err_buf, (ub4)PHP_OCI_ERRBUF_LEN, (ub4)OCI_HTYPE_ERROR));
1706 
1707 	if (error_code) {
1708 		int err_buf_len = strlen((char *)err_buf);
1709 
1710 		if (err_buf_len && err_buf[err_buf_len - 1] == '\n') {
1711 			err_buf[err_buf_len - 1] = '\0';
1712 		}
1713 		if (err_buf_len && error_buf) {
1714 			*error_buf = NULL;
1715 			*error_buf = (text *)estrndup((char *)err_buf, err_buf_len);
1716 		}
1717 	}
1718 	return error_code;
1719 }
1720 /* }}} */
1721 
1722 /* {{{ php_oci_fetch_sqltext_offset()
1723  *
1724  * Compute offset in the SQL statement
1725  */
php_oci_fetch_sqltext_offset(php_oci_statement * statement,text ** sqltext,ub2 * error_offset TSRMLS_DC)1726 int php_oci_fetch_sqltext_offset(php_oci_statement *statement, text **sqltext, ub2 *error_offset TSRMLS_DC)
1727 {
1728 	sword errstatus;
1729 
1730 	*sqltext = NULL;
1731 	*error_offset = 0;
1732 	PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)statement->stmt, OCI_HTYPE_STMT, (dvoid *) sqltext, (ub4 *)0, OCI_ATTR_STATEMENT, statement->err));
1733 
1734 	if (errstatus != OCI_SUCCESS) {
1735 		statement->errcode = php_oci_error(statement->err, errstatus TSRMLS_CC);
1736 		PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
1737 		return 1;
1738 	}
1739 
1740 	PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)statement->stmt, OCI_HTYPE_STMT, (ub2 *)error_offset, (ub4 *)0, OCI_ATTR_PARSE_ERROR_OFFSET, statement->err));
1741 
1742 	if (errstatus != OCI_SUCCESS) {
1743 		statement->errcode = php_oci_error(statement->err, errstatus TSRMLS_CC);
1744 		PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
1745 		return 1;
1746 	}
1747 	return 0;
1748 }
1749 /* }}} */
1750 
1751 /* {{{ php_oci_do_connect()
1752  *
1753  * Connect wrapper
1754  */
php_oci_do_connect(INTERNAL_FUNCTION_PARAMETERS,int persistent,int exclusive)1755 void php_oci_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent, int exclusive)
1756 {
1757 	php_oci_connection *connection;
1758 	char *username, *password;
1759 	char *dbname = NULL, *charset = NULL;
1760 	int username_len = 0, password_len = 0;
1761 	int dbname_len = 0, charset_len = 0;
1762 	long session_mode = OCI_DEFAULT;
1763 
1764 	/* if a fourth parameter is handed over, it is the charset identifier (but is only used in Oracle 9i+) */
1765 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|ssl", &username, &username_len, &password, &password_len, &dbname, &dbname_len, &charset, &charset_len, &session_mode) == FAILURE) {
1766 		return;
1767 	}
1768 
1769 #ifdef HAVE_OCI8_DTRACE
1770 	if (DTRACE_OCI8_CONNECT_ENTRY_ENABLED()) {
1771 		DTRACE_OCI8_CONNECT_ENTRY(username, dbname, charset, session_mode, persistent, exclusive);
1772 	}
1773 #endif /* HAVE_OCI8_DTRACE */
1774 
1775 	if (!charset_len) {
1776 		charset = NULL;
1777 	}
1778 
1779 	connection = php_oci_do_connect_ex(username, username_len, password, password_len, NULL, 0, dbname, dbname_len, charset, session_mode, persistent, exclusive TSRMLS_CC);
1780 
1781 #ifdef HAVE_OCI8_DTRACE
1782 	if (DTRACE_OCI8_CONNECT_RETURN_ENABLED()) {
1783 		DTRACE_OCI8_CONNECT_RETURN(connection);
1784 	}
1785 #endif /* HAVE_OCI8_DTRACE */
1786 
1787 
1788 	if (!connection) {
1789 		RETURN_FALSE;
1790 	}
1791 	RETURN_RESOURCE(connection->id);
1792 
1793 }
1794 /* }}} */
1795 
1796 /* {{{ php_oci_do_connect_ex()
1797  *
1798  * The real connect function. Allocates all the resources needed, establishes the connection and
1799  * returns the result handle (or NULL)
1800  */
php_oci_do_connect_ex(char * username,int username_len,char * password,int password_len,char * new_password,int new_password_len,char * dbname,int dbname_len,char * charset,long session_mode,int persistent,int exclusive TSRMLS_DC)1801 php_oci_connection *php_oci_do_connect_ex(char *username, int username_len, char *password, int password_len, char *new_password, int new_password_len, char *dbname, int dbname_len, char *charset, long session_mode, int persistent, int exclusive TSRMLS_DC)
1802 {
1803 	zend_rsrc_list_entry *le;
1804 	zend_rsrc_list_entry new_le;
1805 	php_oci_connection *connection = NULL;
1806 	smart_str hashed_details = {0};
1807 	time_t timestamp;
1808 	php_oci_spool *session_pool = NULL;
1809 	zend_bool use_spool = 1;	   /* Default is to use client-side session pool */
1810 	zend_bool ping_done = 0;
1811 
1812 	ub2 charsetid = 0;
1813 	ub2 charsetid_nls_lang = 0;
1814 
1815 	if (session_mode & ~(OCI_SYSOPER | OCI_SYSDBA | PHP_OCI_CRED_EXT)) {
1816 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid session mode specified (%ld)", session_mode);
1817 		return NULL;
1818 	}
1819 	if (session_mode & (OCI_SYSOPER | OCI_SYSDBA | PHP_OCI_CRED_EXT)) {
1820 		if ((session_mode & OCI_SYSOPER) && (session_mode & OCI_SYSDBA)) {
1821 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "OCI_SYSDBA and OCI_SYSOPER cannot be used together");
1822 			return NULL;
1823 		}
1824 		if (session_mode & PHP_OCI_CRED_EXT) {
1825 #ifdef PHP_WIN32
1826 			/* Disable external authentication on Windows as Impersonation is not yet handled.
1827 			 * TODO: Re-enable this once OCI provides capability.
1828 			 */
1829 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "External Authentication is not supported on Windows");
1830 			return NULL;
1831 #endif
1832 			if (username_len != 1 || username[0] != '/' || password_len != 0) {
1833 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "OCI_CRED_EXT can only be used with a username of \"/\" and a NULL password");
1834 				return NULL;
1835 			}
1836 		}
1837 		if (session_mode & (OCI_SYSOPER | OCI_SYSDBA)) {
1838 			/* Increase security by not caching privileged oci_pconnect() connections. The
1839 			 * connection becomes equivalent to oci_connect() or oci_new_connect().
1840 			 */
1841 			persistent = 0;
1842 			if (!OCI_G(privileged_connect)) {
1843 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Privileged connect is disabled. Enable oci8.privileged_connect to be able to connect as SYSOPER or SYSDBA");
1844 				return NULL;
1845 			}
1846 #if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 4) || (PHP_MAJOR_VERSION < 5)
1847 			/* Safe mode has been removed in PHP 5.4 */
1848 			if (PG(safe_mode)) {
1849 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Privileged connect is disabled in Safe Mode");
1850 				return NULL;
1851 			}
1852 #endif
1853 		}
1854 	}
1855 
1856 	/* Initialize global handles if they weren't initialized before */
1857 	if (OCI_G(env) == NULL) {
1858 		php_oci_init_global_handles(TSRMLS_C);
1859 		if (OCI_G(env) == NULL) {
1860 			return NULL;
1861 		}
1862 	}
1863 
1864 	/* We cannot use the new session create logic (OCISessionGet from
1865 	 * client-side session pool) when privileged connect or password
1866 	 * change is attempted or OCI_CRED_EXT mode is specified.
1867 	 * TODO: Re-enable this when OCI provides support.
1868 	 */
1869 	if ((session_mode & (OCI_SYSOPER | OCI_SYSDBA | PHP_OCI_CRED_EXT)) || (new_password_len)) {
1870 		use_spool = 0;
1871 	}
1872 
1873 	smart_str_appendl_ex(&hashed_details, "oci8***", sizeof("oci8***") - 1, 0);
1874 	smart_str_appendl_ex(&hashed_details, username, username_len, 0);
1875 	smart_str_appendl_ex(&hashed_details, "**", sizeof("**") - 1, 0);
1876 
1877 	/* DRCP: connection_class is an attribute of a connection */
1878 	if (OCI_G(connection_class)){
1879 		smart_str_appendl_ex(&hashed_details, OCI_G(connection_class), strlen(OCI_G(connection_class)), 0);
1880 	}
1881 	smart_str_appendl_ex(&hashed_details, "**", sizeof("**") - 1, 0);
1882 
1883 	/* Add edition attribute to the hash */
1884 	if (OCI_G(edition)){
1885 		smart_str_appendl_ex(&hashed_details, OCI_G(edition), strlen(OCI_G(edition)), 0);
1886 	}
1887 	smart_str_appendl_ex(&hashed_details, "**", sizeof("**") - 1, 0);
1888 
1889 	if (password_len) {
1890 		ulong password_hash;
1891 		password_hash = zend_inline_hash_func(password, password_len);
1892 		smart_str_append_unsigned_ex(&hashed_details, password_hash, 0);
1893 	}
1894 	smart_str_appendl_ex(&hashed_details, "**", sizeof("**") - 1, 0);
1895 
1896 	if (dbname) {
1897 		smart_str_appendl_ex(&hashed_details, dbname, dbname_len, 0);
1898 	}
1899 	smart_str_appendl_ex(&hashed_details, "**", sizeof("**") - 1, 0);
1900 
1901 	if (charset && *charset) {
1902 		PHP_OCI_CALL_RETURN(charsetid, OCINlsCharSetNameToId, (OCI_G(env), (CONST oratext *)charset));
1903 		if (!charsetid) {
1904 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid character set name: %s", charset);
1905 		} else {
1906 			smart_str_append_unsigned_ex(&hashed_details, charsetid, 0);
1907 		}
1908 	}
1909 
1910 	/* use NLS_LANG if no or invalid charset specified */
1911 	if (!charsetid) {
1912 		size_t rsize = 0;
1913 		sword result;
1914 
1915 		PHP_OCI_CALL_RETURN(result, OCINlsEnvironmentVariableGet, (&charsetid_nls_lang, 0, OCI_NLS_CHARSET_ID, 0, &rsize));
1916 		if (result != OCI_SUCCESS) {
1917 			charsetid_nls_lang = 0;
1918 		}
1919 		smart_str_append_unsigned_ex(&hashed_details, charsetid_nls_lang, 0);
1920 	}
1921 
1922 	timestamp = time(NULL);
1923 
1924 	smart_str_append_unsigned_ex(&hashed_details, session_mode, 0);
1925 	smart_str_0(&hashed_details);
1926 
1927 	/* make it lowercase */
1928 	php_strtolower(hashed_details.c, hashed_details.len);
1929 
1930 	if (!exclusive && !new_password) {
1931 		zend_bool found = 0;
1932 
1933 		if (persistent && zend_hash_find(&EG(persistent_list), hashed_details.c, hashed_details.len+1, (void **) &le) == SUCCESS) {
1934 			found = 1;
1935 			/* found */
1936 			if (le->type == le_pconnection) {
1937 				connection = (php_oci_connection *)le->ptr;
1938 			}
1939 		} else if (!persistent && zend_hash_find(&EG(regular_list), hashed_details.c, hashed_details.len+1, (void **) &le) == SUCCESS) {
1940 			found = 1;
1941 			if (le->type == le_index_ptr) {
1942 				int type, link;
1943 				void *ptr;
1944 
1945 				link = OCI8_PTR_TO_INT(le->ptr);
1946 				ptr = zend_list_find(link,&type);
1947 				if (ptr && (type == le_connection)) {
1948 					connection = (php_oci_connection *)ptr;
1949 				}
1950 			}
1951 		}
1952 
1953 #ifdef HAVE_OCI8_DTRACE
1954 		if (DTRACE_OCI8_CONNECT_LOOKUP_ENABLED()) {
1955 			DTRACE_OCI8_CONNECT_LOOKUP(connection, connection && connection->is_stub ? 1 : 0);
1956 		}
1957 #endif /* HAVE_OCI8_DTRACE */
1958 
1959 		/* If we got a pconnection stub, then 'load'(OCISessionGet) the real connection from its
1960 		 * private spool A connection is a stub if it is only a cached structure and the real
1961 		 * connection is released to its underlying private session pool.  We currently do not have
1962 		 * stub support for non-persistent conns.
1963 		 *
1964 		 * TODO: put in negative code for non-persistent stubs
1965 		 */
1966 		if (connection && connection->is_persistent && connection->is_stub) {
1967 			if (php_oci_create_session(connection, NULL, dbname, dbname_len, username, username_len, password, password_len, new_password, new_password_len, session_mode TSRMLS_CC)) {
1968 				smart_str_free_ex(&hashed_details, 0);
1969 				zend_hash_del(&EG(persistent_list), connection->hash_key, strlen(connection->hash_key)+1);
1970 
1971 				return NULL;
1972 			}
1973 			/* We do the ping in php_oci_create_session, no need to ping again below */
1974 			ping_done = 1;
1975 		}
1976 
1977 		if (connection) {
1978 			if (connection->is_open) {
1979 				/* found an open connection. now ping it */
1980 				if (connection->is_persistent) {
1981 					int rsrc_type;
1982 
1983 					/* Check connection liveness in the following order:
1984 					 * 1) always check OCI_ATTR_SERVER_STATUS
1985 					 * 2) see if it's time to ping it
1986 					 * 3) ping it if needed
1987 					 */
1988 					if (php_oci_connection_status(connection TSRMLS_CC)) {
1989 						/* Only ping if:
1990 						 *
1991 						 * 1) next_ping > 0, which means that ping_interval is not -1 (aka "Off")
1992 						 *
1993 						 * 2) current_timestamp > next_ping, which means "it's time to check if it's
1994 						 * still alive"
1995 						 */
1996 						if (!ping_done && (*(connection->next_pingp) > 0) && (timestamp >= *(connection->next_pingp)) && !php_oci_connection_ping(connection TSRMLS_CC)) {
1997 							/* server died */
1998 						} else {
1999 							php_oci_connection *tmp;
2000 
2001 							/* okay, the connection is open and the server is still alive */
2002 							connection->used_this_request = 1;
2003 							tmp = (php_oci_connection *)zend_list_find(connection->id, &rsrc_type);
2004 
2005 							if (tmp != NULL && rsrc_type == le_pconnection && strlen(tmp->hash_key) == hashed_details.len &&
2006 								memcmp(tmp->hash_key, hashed_details.c, hashed_details.len) == 0 && zend_list_addref(connection->id) == SUCCESS) {
2007 								/* do nothing */
2008 							} else {
2009 								PHP_OCI_REGISTER_RESOURCE(connection, le_pconnection);
2010 								/* Persistent connections: For old close semantics we artificially
2011 								 * bump up the refcount to prevent the non-persistent destructor
2012 								 * from getting called until request shutdown. The refcount is
2013 								 * decremented in the persistent helper
2014 								 */
2015 								if (OCI_G(old_oci_close_semantics)) {
2016 									zend_list_addref(connection->id);
2017 								}
2018 							}
2019 							smart_str_free_ex(&hashed_details, 0);
2020 							return connection;
2021 						}
2022 					}
2023 					/* server died */
2024 				} else {
2025 					/* we do not ping non-persistent connections */
2026 					smart_str_free_ex(&hashed_details, 0);
2027 					zend_list_addref(connection->id);
2028 					return connection;
2029 				}
2030 			} /* is_open is true? */
2031 
2032 			/* Server died - connection not usable. The is_open=true can also fall through to here,
2033 			 * if ping fails
2034 			 */
2035 			if (persistent){
2036 				int rsrc_type;
2037 
2038 				connection->is_open = 0;
2039 				connection->used_this_request = 1;
2040 
2041 				/* We have to do a hash_del but need to preserve the resource if there is a positive
2042 				 * refcount. Set the data pointer in the list entry to NULL
2043 				 */
2044 				if (connection == zend_list_find(connection->id, &rsrc_type) && rsrc_type == le_pconnection) {
2045 					le->ptr = NULL;
2046 				}
2047 
2048 				zend_hash_del(&EG(persistent_list), hashed_details.c, hashed_details.len+1);
2049 			} else {
2050 				/* We only remove the hash entry. The resource and the list entry with its pointer
2051 				 * to the resource are still intact
2052 				 */
2053 				zend_hash_del(&EG(regular_list), hashed_details.c, hashed_details.len+1);
2054 			}
2055 
2056 			connection = NULL;
2057 		} else if (found) {
2058 			/* found something, but it's not a connection, delete it */
2059 			if (persistent) {
2060 				zend_hash_del(&EG(persistent_list), hashed_details.c, hashed_details.len+1);
2061 			} else {
2062 				zend_hash_del(&EG(regular_list), hashed_details.c, hashed_details.len+1);
2063 			}
2064 		}
2065 	}
2066 
2067 	/* Check if we have reached max_persistent. If so, try to remove a few timed-out connections. As
2068 	 * a last resort, return a non-persistent connection.
2069 	 */
2070 	if (persistent) {
2071 		zend_bool alloc_non_persistent = 0;
2072 
2073 		if (OCI_G(max_persistent) != -1 && OCI_G(num_persistent) >= OCI_G(max_persistent)) {
2074 			/* try to find an idle connection and kill it */
2075 			zend_hash_apply(&EG(persistent_list), (apply_func_t) php_oci_persistent_helper TSRMLS_CC);
2076 
2077 			if (OCI_G(max_persistent) != -1 && OCI_G(num_persistent) >= OCI_G(max_persistent)) {
2078 				/* all persistent connactions are in use, fallback to non-persistent connection creation */
2079 				php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Too many open persistent connections (%ld)", OCI_G(num_persistent));
2080 				alloc_non_persistent = 1;
2081 			}
2082 		}
2083 
2084 		if (alloc_non_persistent) {
2085 			connection = (php_oci_connection *) ecalloc(1, sizeof(php_oci_connection));
2086 			connection->hash_key = estrndup(hashed_details.c, hashed_details.len);
2087 			connection->is_persistent = 0;
2088 #ifdef HAVE_OCI8_DTRACE
2089 			connection->client_id = NULL;
2090 #endif
2091 		} else {
2092 			connection = (php_oci_connection *) calloc(1, sizeof(php_oci_connection));
2093 			if (connection == NULL) {
2094 				return NULL;
2095 			}
2096 			connection->hash_key = zend_strndup(hashed_details.c, hashed_details.len);
2097 			if (connection->hash_key == NULL) {
2098 				free(connection);
2099 				return NULL;
2100 			}
2101 			connection->is_persistent = 1;
2102 #ifdef HAVE_OCI8_DTRACE
2103 			connection->client_id = NULL;
2104 #endif
2105 		}
2106 	} else {
2107 		connection = (php_oci_connection *) ecalloc(1, sizeof(php_oci_connection));
2108 		connection->hash_key = estrndup(hashed_details.c, hashed_details.len);
2109 		connection->is_persistent = 0;
2110 #ifdef HAVE_OCI8_DTRACE
2111 		connection->client_id = NULL;
2112 #endif
2113 	}
2114 
2115 	/* {{{ Get the session pool that suits this connection request from the persistent list. This
2116 	 * step is only for non-persistent connections as persistent connections have private session
2117 	 * pools. Non-persistent conns use shared session pool to allow for optimizations such as
2118 	 * caching the physical connection (for DRCP) even when the non-persistent php connection is
2119 	 * destroyed.
2120 	 *
2121 	 * TODO: Unconditionally do this once OCI provides extended OCISessionGet capability
2122 	 */
2123 	if (use_spool && !connection->is_persistent) {
2124 		if ((session_pool = php_oci_get_spool(username, username_len, password, password_len, dbname, dbname_len, charsetid ? charsetid:charsetid_nls_lang TSRMLS_CC))==NULL)
2125 		{
2126 			php_oci_connection_close(connection TSRMLS_CC);
2127 			smart_str_free_ex(&hashed_details, 0);
2128 			return NULL;
2129 		}
2130 	}
2131 	/* }}} */
2132 
2133 	connection->idle_expiry = (OCI_G(persistent_timeout) > 0) ? (timestamp + OCI_G(persistent_timeout)) : 0;
2134 
2135 	/* Mark password as unchanged by PHP during the duration of the database session */
2136 	connection->passwd_changed = 0;
2137 
2138 	smart_str_free_ex(&hashed_details, 0);
2139 
2140 	if (charsetid) {
2141 		connection->charset = charsetid;
2142 	} else {
2143 		connection->charset = charsetid_nls_lang;
2144 	}
2145 
2146 	/* Old session creation semantics when session pool cannot be used Eg: privileged
2147 	 * connect/password change
2148 	 */
2149 	if (!use_spool) {
2150 		if (php_oci_old_create_session(connection, dbname, dbname_len, username, username_len, password, password_len, new_password, new_password_len, session_mode TSRMLS_CC)) {
2151 			php_oci_connection_close(connection TSRMLS_CC);
2152 			return NULL;
2153 		}
2154 	} else {
2155 		/* create using the client-side session pool */
2156 		if (php_oci_create_session(connection, session_pool, dbname, dbname_len, username, username_len, password, password_len, new_password, new_password_len, session_mode TSRMLS_CC)) {
2157 			php_oci_connection_close(connection TSRMLS_CC);
2158 			return NULL;
2159 		}
2160 	}
2161 
2162 	/* Mark it as open */
2163 	connection->is_open = 1;
2164 
2165 	/* add to the appropriate hash */
2166 	if (connection->is_persistent) {
2167 		new_le.ptr = connection;
2168 		new_le.type = le_pconnection;
2169 		connection->used_this_request = 1;
2170 		PHP_OCI_REGISTER_RESOURCE(connection, le_pconnection);
2171 
2172 		/* Persistent connections: For old close semantics we artificially bump up the refcount to
2173 		 * prevent the non-persistent destructor from getting called until request shutdown. The
2174 		 * refcount is decremented in the persistent helper
2175 		 */
2176 		if (OCI_G(old_oci_close_semantics)) {
2177 			zend_list_addref(connection->id);
2178 		}
2179 		zend_hash_update(&EG(persistent_list), connection->hash_key, strlen(connection->hash_key)+1, (void *)&new_le, sizeof(zend_rsrc_list_entry), NULL);
2180 		OCI_G(num_persistent)++;
2181 		OCI_G(num_links)++;
2182 	} else if (!exclusive) {
2183 		PHP_OCI_REGISTER_RESOURCE(connection, le_connection);
2184 		new_le.ptr = OCI8_INT_TO_PTR(connection->id);
2185 		new_le.type = le_index_ptr;
2186 		zend_hash_update(&EG(regular_list), connection->hash_key, strlen(connection->hash_key)+1, (void *)&new_le, sizeof(zend_rsrc_list_entry), NULL);
2187 		OCI_G(num_links)++;
2188 	} else {
2189 		PHP_OCI_REGISTER_RESOURCE(connection, le_connection);
2190 		OCI_G(num_links)++;
2191 	}
2192 
2193 #ifdef HAVE_OCI8_DTRACE
2194 	if (DTRACE_OCI8_CONNECT_TYPE_ENABLED()) {
2195 		DTRACE_OCI8_CONNECT_TYPE(connection->is_persistent ? 1 : 0, exclusive ? 1 : 0, connection, OCI_G(num_persistent), OCI_G(num_links));
2196 	}
2197 #endif /* HAVE_OCI8_DTRACE */
2198 
2199 	return connection;
2200 }
2201 /* }}} */
2202 
2203 /* {{{ php_oci_connection_ping()
2204  *
2205  * Ping connection. Uses OCIPing() or OCIServerVersion() depending on the Oracle Client version
2206  */
php_oci_connection_ping(php_oci_connection * connection TSRMLS_DC)2207 static int php_oci_connection_ping(php_oci_connection *connection TSRMLS_DC)
2208 {
2209 	sword errstatus;
2210 #if (!((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))))
2211 	char version[256];
2212 #endif
2213 
2214 	OCI_G(errcode) = 0;  		/* assume ping is successful */
2215 
2216 	/* Use OCIPing instead of OCIServerVersion. If OCIPing returns ORA-1010 (invalid OCI operation)
2217 	 * such as from Pre-10.1 servers, the error is still from the server and we would have
2218 	 * successfully performed a roundtrip and validated the connection. Use OCIServerVersion for
2219 	 * Pre-10.2 clients
2220 	 */
2221 #if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2)))	/* OCIPing available 10.2 onwards */
2222 	PHP_OCI_CALL_RETURN(errstatus, OCIPing, (connection->svc, OCI_G(err), OCI_DEFAULT));
2223 #else
2224 	/* use good old OCIServerVersion() */
2225 	PHP_OCI_CALL_RETURN(errstatus, OCIServerVersion, (connection->svc, OCI_G(err), (text *)version, sizeof(version), OCI_HTYPE_SVCCTX));
2226 #endif
2227 
2228 	if (errstatus == OCI_SUCCESS) {
2229 		return 1;
2230 	} else {
2231 		sb4 error_code = 0;
2232 		text tmp_buf[OCI_ERROR_MAXMSG_SIZE];
2233 
2234 		/* Treat ORA-1010 as a successful Ping */
2235 		OCIErrorGet(OCI_G(err), (ub4)1, NULL, &error_code, tmp_buf, (ub4)OCI_ERROR_MAXMSG_SIZE, (ub4)OCI_HTYPE_ERROR);
2236 		if (error_code == 1010) {
2237 			return 1;
2238 		}
2239 		OCI_G(errcode) = error_code;
2240 	}
2241 
2242 	return 0;
2243 }
2244 /* }}} */
2245 
2246 /* {{{ php_oci_connection_status()
2247  *
2248  * Check connection status (pre-ping check)
2249  */
php_oci_connection_status(php_oci_connection * connection TSRMLS_DC)2250 static int php_oci_connection_status(php_oci_connection *connection TSRMLS_DC)
2251 {
2252 	ub4 ss = OCI_SERVER_NOT_CONNECTED;
2253 	sword errstatus;
2254 
2255 	/* get OCI_ATTR_SERVER_STATUS */
2256 	PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)connection->server, OCI_HTYPE_SERVER, (dvoid *)&ss, (ub4 *)0, OCI_ATTR_SERVER_STATUS, OCI_G(err)));
2257 
2258 	if (errstatus == OCI_SUCCESS && ss == OCI_SERVER_NORMAL) {
2259 		return 1;
2260 	}
2261 
2262 	/* ignore errors here, just return failure */
2263 	return 0;
2264 }
2265 /* }}} */
2266 
2267 /* {{{ php_oci_connection_rollback()
2268  *
2269  * Rollback connection
2270  */
php_oci_connection_rollback(php_oci_connection * connection TSRMLS_DC)2271 int php_oci_connection_rollback(php_oci_connection *connection TSRMLS_DC)
2272 {
2273 	sword errstatus;
2274 
2275 	PHP_OCI_CALL_RETURN(errstatus, OCITransRollback, (connection->svc, connection->err, (ub4) 0));
2276 	connection->rb_on_disconnect = 0;
2277 
2278 	if (errstatus != OCI_SUCCESS) {
2279 		connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
2280 		PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
2281 		return 1;
2282 	}
2283 	connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
2284 	return 0;
2285 }
2286 /* }}} */
2287 
2288 /* {{{ php_oci_connection_commit()
2289  *
2290  * Commit connection
2291  */
php_oci_connection_commit(php_oci_connection * connection TSRMLS_DC)2292 int php_oci_connection_commit(php_oci_connection *connection TSRMLS_DC)
2293 {
2294 	sword errstatus;
2295 
2296 	PHP_OCI_CALL_RETURN(errstatus, OCITransCommit, (connection->svc, connection->err, (ub4) 0));
2297 	connection->rb_on_disconnect = 0;
2298 
2299 	if (errstatus != OCI_SUCCESS) {
2300 		connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
2301 		PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
2302 		return 1;
2303 	}
2304 	connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
2305 	return 0;
2306 }
2307 /* }}} */
2308 
2309 /* {{{ php_oci_connection_close()
2310  *
2311  * Close the connection and free all its resources
2312  */
php_oci_connection_close(php_oci_connection * connection TSRMLS_DC)2313 static int php_oci_connection_close(php_oci_connection *connection TSRMLS_DC)
2314 {
2315 	int result = 0;
2316 	zend_bool in_call_save = OCI_G(in_call);
2317 
2318 #ifdef HAVE_OCI8_DTRACE
2319 	if (DTRACE_OCI8_CONNECTION_CLOSE_ENABLED()) {
2320 		DTRACE_OCI8_CONNECTION_CLOSE(connection);
2321 	}
2322 #endif /* HAVE_OCI8_DTRACE */
2323 
2324 	if (!connection->is_stub) {
2325 		/* Release resources associated with connection */
2326 		php_oci_connection_release(connection TSRMLS_CC);
2327 	}
2328 
2329 	if (!connection->using_spool && connection->svc) {
2330 		PHP_OCI_CALL(OCISessionEnd, (connection->svc, connection->err, connection->session, (ub4) 0));
2331 	}
2332 
2333 	if (connection->err) {
2334 		PHP_OCI_CALL(OCIHandleFree, ((dvoid *) connection->err, (ub4) OCI_HTYPE_ERROR));
2335 	}
2336 	if (connection->authinfo) {
2337 		PHP_OCI_CALL(OCIHandleFree, ((dvoid *) connection->authinfo, (ub4) OCI_HTYPE_AUTHINFO));
2338 	}
2339 
2340 	/* No Handlefrees for session pool connections */
2341 	if (!connection->using_spool) {
2342 		if (connection->session) {
2343 			PHP_OCI_CALL(OCIHandleFree, ((dvoid *) connection->session, OCI_HTYPE_SESSION));
2344 		}
2345 
2346 		if (connection->is_attached) {
2347 			PHP_OCI_CALL(OCIServerDetach, (connection->server, OCI_G(err), OCI_DEFAULT));
2348 		}
2349 
2350 		if (connection->svc) {
2351 			PHP_OCI_CALL(OCIHandleFree, ((dvoid *) connection->svc, (ub4) OCI_HTYPE_SVCCTX));
2352 		}
2353 
2354 		if (connection->server) {
2355 			PHP_OCI_CALL(OCIHandleFree, ((dvoid *) connection->server, (ub4) OCI_HTYPE_SERVER));
2356 		}
2357 
2358 		if (connection->env) {
2359 			PHP_OCI_CALL(OCIHandleFree, ((dvoid *) connection->env, OCI_HTYPE_ENV));
2360 		}
2361 	} else if (connection->private_spool) {
2362 	/* Keep this as the last member to be freed, as there are dependencies
2363 	 * (like env) on the session pool
2364 	 */
2365 		php_oci_spool_close(connection->private_spool TSRMLS_CC);
2366 	}
2367 
2368 	if (connection->hash_key) {
2369 		pefree(connection->hash_key, connection->is_persistent);
2370 	}
2371 #ifdef HAVE_OCI8_DTRACE
2372 	if (connection->client_id) {
2373 		pefree(connection->client_id, connection->is_persistent);
2374 	}
2375 #endif /* HAVE_OCI8_DTRACE */
2376 	pefree(connection, connection->is_persistent);
2377 	connection = NULL;
2378 	OCI_G(in_call) = in_call_save;
2379 	return result;
2380 }
2381 /* }}} */
2382 
2383 /* {{{ php_oci_connection_release()
2384  *
2385  * Release the connection's resources. This involves freeing descriptors and rolling back
2386  * transactions, setting timeout-related parameters etc. For session-pool using connections, the
2387  * underlying connection is released to its session pool.
2388  */
php_oci_connection_release(php_oci_connection * connection TSRMLS_DC)2389 int php_oci_connection_release(php_oci_connection *connection TSRMLS_DC)
2390 {
2391 	int result = 0;
2392 	zend_bool in_call_save = OCI_G(in_call);
2393 	time_t timestamp = time(NULL);
2394 
2395 	if (connection->is_stub) {
2396 		return 0;
2397 	}
2398 
2399 	if (connection->descriptors) {
2400 		php_oci_connection_descriptors_free(connection TSRMLS_CC);
2401 	}
2402 
2403 	if (connection->svc) {
2404 		/* rollback outstanding transactions */
2405 		if (connection->rb_on_disconnect) {
2406 			if (php_oci_connection_rollback(connection TSRMLS_CC)) {
2407 				/* rollback failed */
2408 				result = 1;
2409 			}
2410 		}
2411 	}
2412 
2413 	if (OCI_G(persistent_timeout) > 0) {
2414 		connection->idle_expiry = timestamp + OCI_G(persistent_timeout);
2415 	}
2416 
2417 	/* We may have half-cooked connections to clean up */
2418 	if (connection->next_pingp) {
2419 		if (OCI_G(ping_interval) >= 0) {
2420 			*(connection->next_pingp) = timestamp + OCI_G(ping_interval);
2421 		} else {
2422 			/* ping_interval is -1 */
2423 			*(connection->next_pingp) = 0;
2424 		}
2425 	}
2426 
2427 	/* Release the session (stubs are filtered out at the beginning)*/
2428 	if (connection->using_spool) {
2429 		ub4 rlsMode = OCI_DEFAULT;
2430 
2431 		if (result) {
2432 			rlsMode |= OCI_SESSRLS_DROPSESS;
2433 		}
2434 
2435 		/* Sessions for non-persistent connections should be dropped.  For 11 and above, the session
2436 		 * pool has its own mechanism for doing so for purity NEW connections. We need to do so
2437 		 * explicitly for 10.2 and earlier.
2438 		 */
2439 #if (!(OCI_MAJOR_VERSION >= 11))
2440 		if (!connection->is_persistent) {
2441 			rlsMode |= OCI_SESSRLS_DROPSESS;
2442 		}
2443 #endif
2444 
2445 		if (connection->svc) {
2446 			PHP_OCI_CALL(OCISessionRelease, (connection->svc, connection->err, NULL,
2447 										 0, rlsMode));
2448 		}
2449 		/* It no longer has relation with the database session. However authinfo and env are
2450 		 * cached
2451 		 */
2452 		connection->svc = NULL;
2453 		connection->server = NULL;
2454 		connection->session = NULL;
2455 
2456 		connection->is_attached = connection->is_open = connection->rb_on_disconnect = connection->used_this_request = 0;
2457 		connection->is_stub = 1;
2458 
2459 		/* Cut the link between the connection structure and the time_t structure allocated within
2460 		 * the OCI session
2461 		 */
2462 		connection->next_pingp = NULL;
2463 #ifdef HAVE_OCI8_DTRACE
2464 		if (connection->client_id) {
2465 			pefree(connection->client_id, connection->is_persistent);
2466 			connection->client_id = NULL;
2467 		}
2468 #endif /* HAVE_OCI8_DTRACE */
2469 	}
2470 
2471 	OCI_G(in_call) = in_call_save;
2472 	return result;
2473 }
2474 /* }}} */
2475 
2476 /* {{{ php_oci_password_change()
2477  *
2478  * Change password for the user with the username given
2479  */
php_oci_password_change(php_oci_connection * connection,char * user,int user_len,char * pass_old,int pass_old_len,char * pass_new,int pass_new_len TSRMLS_DC)2480 int php_oci_password_change(php_oci_connection *connection, char *user, int user_len, char *pass_old, int pass_old_len, char *pass_new, int pass_new_len TSRMLS_DC)
2481 {
2482 	sword errstatus;
2483 
2484 	PHP_OCI_CALL_RETURN(errstatus, OCIPasswordChange, (connection->svc, connection->err, (text *)user, user_len, (text *)pass_old, pass_old_len, (text *)pass_new, pass_new_len, OCI_DEFAULT));
2485 
2486 	if (errstatus != OCI_SUCCESS) {
2487 		connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
2488 		PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
2489 		return 1;
2490 	}
2491 	connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
2492 	connection->passwd_changed = 1;
2493 	return 0;
2494 }
2495 /* }}} */
2496 
2497 /* {{{ php_oci_client_get_version()
2498  *
2499  * Get Oracle client library version
2500  */
php_oci_client_get_version(char ** version TSRMLS_DC)2501 void php_oci_client_get_version(char **version TSRMLS_DC)
2502 {
2503 	char  version_buff[256];
2504 #if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2)))	/* OCIClientVersion only available 10.2 onwards */
2505 	sword major_version = 0;
2506 	sword minor_version = 0;
2507 	sword update_num = 0;
2508 	sword patch_num = 0;
2509 	sword port_update_num = 0;
2510 
2511 	PHP_OCI_CALL(OCIClientVersion, (&major_version, &minor_version, &update_num, &patch_num, &port_update_num));
2512 	snprintf(version_buff, sizeof(version_buff), "%d.%d.%d.%d.%d", major_version, minor_version, update_num, patch_num, port_update_num);
2513 #else
2514 	memcpy(version_buff, "Unknown", sizeof("Unknown"));
2515 #endif
2516 	*version = estrdup(version_buff);
2517 }
2518 /* }}} */
2519 
2520 /* {{{ php_oci_server_get_version()
2521  *
2522  * Get Oracle server version
2523  */
php_oci_server_get_version(php_oci_connection * connection,char ** version TSRMLS_DC)2524 int php_oci_server_get_version(php_oci_connection *connection, char **version TSRMLS_DC)
2525 {
2526 	sword errstatus;
2527 	char version_buff[256];
2528 
2529 	PHP_OCI_CALL_RETURN(errstatus, OCIServerVersion, (connection->svc, connection->err, (text *)version_buff, sizeof(version_buff), OCI_HTYPE_SVCCTX));
2530 
2531 	if (errstatus != OCI_SUCCESS) {
2532 		connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
2533 		PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
2534 		return 1;
2535 	}
2536 
2537 	*version = estrdup(version_buff);
2538 	return 0;
2539 }
2540 /* }}} */
2541 
2542 /* {{{ php_oci_column_to_zval()
2543  *
2544  * Convert php_oci_out_column struct into zval
2545  */
php_oci_column_to_zval(php_oci_out_column * column,zval * value,int mode TSRMLS_DC)2546 int php_oci_column_to_zval(php_oci_out_column *column, zval *value, int mode TSRMLS_DC)
2547 {
2548 	php_oci_descriptor *descriptor;
2549 	ub4 lob_length;
2550 	int column_size;
2551 	char *lob_buffer;
2552 	int lob_fetch_status;
2553 
2554 	if (column->indicator == -1) { /* column is NULL */
2555 		ZVAL_NULL(value);
2556 		return 0;
2557 	}
2558 
2559 	if (column->is_cursor) { /* REFCURSOR -> simply return the statement id */
2560 		ZVAL_RESOURCE(value, column->stmtid);
2561 		zend_list_addref(column->stmtid);
2562 	} else if (column->is_descr) {
2563 
2564 		if (column->data_type != SQLT_RDD) {
2565 			int rsrc_type;
2566 
2567 			/* reset descriptor's length */
2568 			descriptor = (php_oci_descriptor *) zend_list_find(column->descid, &rsrc_type);
2569 
2570 			if (!descriptor || rsrc_type != le_descriptor) {
2571 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to find LOB descriptor #%d", column->descid);
2572 				return 1;
2573 			}
2574 
2575 			descriptor->lob_size = -1;
2576 			descriptor->lob_current_position = 0;
2577 			descriptor->buffering = 0;
2578 		}
2579 
2580 		if (column->data_type != SQLT_RDD && (mode & PHP_OCI_RETURN_LOBS)) {
2581 			/* PHP_OCI_RETURN_LOBS means that we want the content of the LOB back instead of the locator */
2582 
2583 			if (column->chunk_size)
2584 				descriptor->chunk_size = column->chunk_size;
2585 			lob_fetch_status = php_oci_lob_read(descriptor, -1, 0, &lob_buffer, &lob_length TSRMLS_CC);
2586 			if (descriptor->chunk_size)  /* Cache the chunk_size to avoid recalling OCILobGetChunkSize */
2587 				column->chunk_size = descriptor->chunk_size;
2588 			php_oci_temp_lob_close(descriptor TSRMLS_CC);
2589 			if (lob_fetch_status) {
2590 				ZVAL_FALSE(value);
2591 				return 1;
2592 			} else {
2593 				if (lob_length > 0) {
2594 					ZVAL_STRINGL(value, lob_buffer, lob_length, 0);
2595 				} else {
2596 					ZVAL_EMPTY_STRING(value);
2597 				}
2598 				return 0;
2599 			}
2600 		} else {
2601 			/* return the locator */
2602 			object_init_ex(value, oci_lob_class_entry_ptr);
2603 			add_property_resource(value, "descriptor", column->descid);
2604 			zend_list_addref(column->descid);
2605 		}
2606 	} else {
2607 		switch (column->retcode) {
2608 			case 0:
2609 				/* intact value */
2610 				if (column->piecewise) {
2611 					column_size = column->retlen4;
2612 				} else {
2613 					column_size = column->retlen;
2614 				}
2615 				break;
2616 
2617 			default:
2618 				ZVAL_FALSE(value);
2619 				return 0;
2620 		}
2621 
2622 		ZVAL_STRINGL(value, column->data, column_size, 1);
2623 	}
2624 	return 0;
2625 }
2626 /* }}} */
2627 
2628 
2629 /* {{{ php_oci_fetch_row()
2630  *
2631  * Fetch the next row from the given statement
2632  * Has logic for Oracle 12c Implicit Result Sets
2633  */
php_oci_fetch_row(INTERNAL_FUNCTION_PARAMETERS,int mode,int expected_args)2634 void php_oci_fetch_row (INTERNAL_FUNCTION_PARAMETERS, int mode, int expected_args)
2635 {
2636 	zval *z_statement, *array;
2637 	php_oci_statement *statement;		  /* statement that will be fetched from */
2638 #if (OCI_MAJOR_VERSION >= 12)
2639 	php_oci_statement *invokedstatement;  /* statement this function was invoked with */
2640 #endif /* OCI_MAJOR_VERSION */
2641 	php_oci_out_column *column;
2642 	ub4 nrows = 1;
2643 	int i;
2644 	long fetch_mode = 0;
2645 
2646 	if (expected_args > 2) {
2647 		/* only for ocifetchinto BC */
2648 
2649 		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rz|l", &z_statement, &array, &fetch_mode) == FAILURE) {
2650 			return;
2651 		}
2652 
2653 		if (ZEND_NUM_ARGS() == 2) {
2654 			fetch_mode = mode;
2655 		}
2656 	} else if (expected_args == 2) {
2657 		/* only for oci_fetch_array() */
2658 
2659 		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &z_statement, &fetch_mode) == FAILURE) {
2660 			return;
2661 		}
2662 
2663 		if (ZEND_NUM_ARGS() == 1) {
2664 			fetch_mode = mode;
2665 		}
2666 	} else {
2667 		/* for all oci_fetch_*() */
2668 
2669 		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &z_statement) == FAILURE) {
2670 			return;
2671 		}
2672 
2673 		fetch_mode = mode;
2674 	}
2675 
2676 	if (!(fetch_mode & PHP_OCI_NUM) && !(fetch_mode & PHP_OCI_ASSOC)) {
2677 		/* none of the modes present, use the default one */
2678 		if (mode & PHP_OCI_ASSOC) {
2679 			fetch_mode |= PHP_OCI_ASSOC;
2680 		}
2681 		if (mode & PHP_OCI_NUM) {
2682 			fetch_mode |= PHP_OCI_NUM;
2683 		}
2684 	}
2685 
2686 #if (OCI_MAJOR_VERSION < 12)
2687 	PHP_OCI_ZVAL_TO_STATEMENT(z_statement, statement);
2688 
2689 	if (php_oci_statement_fetch(statement, nrows TSRMLS_CC)) {
2690 		RETURN_FALSE;			/* end of fetch */
2691 	}
2692 #else /* OCI_MAJOR_VERSION */
2693 	PHP_OCI_ZVAL_TO_STATEMENT(z_statement, invokedstatement);
2694 
2695 	if (invokedstatement->impres_flag == PHP_OCI_IMPRES_NO_CHILDREN ||
2696         invokedstatement->impres_flag == PHP_OCI_IMPRES_IS_CHILD) {
2697 		/* Already know there are no Implicit Result Sets */
2698 	    statement = invokedstatement;
2699 	} else if (invokedstatement->impres_flag == PHP_OCI_IMPRES_HAS_CHILDREN) {
2700 		/* Previously saw an Implicit Result Set in an earlier invocation of php_oci_fetch_row */
2701 		statement = (php_oci_statement *)invokedstatement->impres_child_stmt;
2702 	} else {
2703 		sword errstatus;
2704 
2705 		/* Check for an Implicit Result Set on this statement handle */
2706 		PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)invokedstatement->stmt, OCI_HTYPE_STMT,
2707 						    (dvoid *) &invokedstatement->impres_count,
2708 						    (ub4 *)NULL, OCI_ATTR_IMPLICIT_RESULT_COUNT, invokedstatement->err));
2709 		if (errstatus) {
2710 			RETURN_FALSE;
2711 		}
2712 		if (invokedstatement->impres_count > 0) {
2713 			/* Make it so the fetch occurs on the first Implicit Result Set */
2714 			statement = php_oci_get_implicit_resultset(invokedstatement TSRMLS_CC);
2715 			if (!statement || php_oci_statement_execute(statement, (ub4)OCI_DEFAULT TSRMLS_CC))
2716 				RETURN_FALSE;
2717 			invokedstatement->impres_count--;
2718 			invokedstatement->impres_child_stmt = (struct php_oci_statement *)statement;
2719 			invokedstatement->impres_flag = PHP_OCI_IMPRES_HAS_CHILDREN;
2720 		} else {
2721 			statement = invokedstatement; /* didn't find Implicit Result Sets */
2722 			invokedstatement->impres_flag = PHP_OCI_IMPRES_NO_CHILDREN;  /* Don't bother checking again */
2723 		}
2724 	}
2725 
2726 	if (php_oci_statement_fetch(statement, nrows TSRMLS_CC)) {
2727 		/* End of fetch */
2728 		if (invokedstatement->impres_count > 0) {
2729 			/* Check next Implicit Result Set */
2730 	        statement = php_oci_get_implicit_resultset(invokedstatement TSRMLS_CC);
2731 			if (!statement || php_oci_statement_execute(statement, (ub4)OCI_DEFAULT TSRMLS_CC))
2732 				RETURN_FALSE;
2733 			invokedstatement->impres_count--;
2734 			invokedstatement->impres_child_stmt = (struct php_oci_statement *)statement;
2735 			if (php_oci_statement_fetch(statement, nrows TSRMLS_CC)) {
2736 				/* End of all fetches */
2737 	            RETURN_FALSE;
2738 			}
2739 		} else {
2740 			RETURN_FALSE;
2741 		}
2742     }
2743 #endif /* OCI_MAJOR_VERSION */
2744 
2745 	array_init(return_value);
2746 
2747 	for (i = 0; i < statement->ncolumns; i++) {
2748 
2749 		column = php_oci_statement_get_column(statement, i + 1, NULL, 0 TSRMLS_CC);
2750 
2751 		if (column == NULL) {
2752 			continue;
2753 		}
2754 		if ((column->indicator == -1) && ((fetch_mode & PHP_OCI_RETURN_NULLS) == 0)) {
2755 			continue;
2756 		}
2757 
2758 		if (!(column->indicator == -1)) {
2759 			zval *element;
2760 
2761 			MAKE_STD_ZVAL(element);
2762 			php_oci_column_to_zval(column, element, fetch_mode TSRMLS_CC);
2763 
2764 			if (fetch_mode & PHP_OCI_NUM || !(fetch_mode & PHP_OCI_ASSOC)) {
2765 				add_index_zval(return_value, i, element);
2766 			}
2767 			if (fetch_mode & PHP_OCI_ASSOC) {
2768 				if (fetch_mode & PHP_OCI_NUM) {
2769 					Z_ADDREF_P(element);
2770 				}
2771 				add_assoc_zval(return_value, column->name, element);
2772 			}
2773 
2774 		} else {
2775 			if (fetch_mode & PHP_OCI_NUM || !(fetch_mode & PHP_OCI_ASSOC)) {
2776 				add_index_null(return_value, i);
2777 			}
2778 			if (fetch_mode & PHP_OCI_ASSOC) {
2779 				add_assoc_null(return_value, column->name);
2780 			}
2781 		}
2782 	}
2783 
2784 	if (expected_args > 2) {
2785 		/* Only for ocifetchinto BC.  In all other cases we return array, not long */
2786 		REPLACE_ZVAL_VALUE(&array, return_value, 1); /* copy return_value to given reference */
2787 		zval_dtor(return_value);
2788 		RETURN_LONG(statement->ncolumns);
2789 	}
2790 }
2791 /* }}} */
2792 
2793 /* {{{ php_oci_persistent_helper()
2794  *
2795  * Helper function to close/rollback persistent connections at the end of request. A return value of
2796  * 1 indicates that the connection is to be destroyed
2797  */
php_oci_persistent_helper(zend_rsrc_list_entry * le TSRMLS_DC)2798 static int php_oci_persistent_helper(zend_rsrc_list_entry *le TSRMLS_DC)
2799 {
2800 	time_t timestamp;
2801 	php_oci_connection *connection;
2802 
2803 	timestamp = time(NULL);
2804 
2805 	/* Persistent connection stubs are also counted as they have private session pools */
2806 	if (le->type == le_pconnection) {
2807 		connection = (php_oci_connection *)le->ptr;
2808 
2809 		if (!connection->used_this_request && OCI_G(persistent_timeout) != -1) {
2810 #ifdef HAVE_OCI8_DTRACE
2811 			if (DTRACE_OCI8_CONNECT_EXPIRY_ENABLED()) {
2812 				DTRACE_OCI8_CONNECT_EXPIRY(connection, connection->is_stub ? 1 : 0, (long)connection->idle_expiry, (long)timestamp);
2813 			}
2814 #endif /* HAVE_OCI8_DTRACE */
2815 			if (connection->idle_expiry < timestamp) {
2816 				/* connection has timed out */
2817 				return ZEND_HASH_APPLY_REMOVE;
2818 			}
2819 		}
2820 	}
2821 	return ZEND_HASH_APPLY_KEEP;
2822 }
2823 /* }}} */
2824 
2825 /* {{{ php_oci_create_spool()
2826  *
2827  *	 Create(alloc + Init) Session pool for the given dbname and charsetid
2828  */
php_oci_create_spool(char * username,int username_len,char * password,int password_len,char * dbname,int dbname_len,char * hash_key,int hash_key_len,int charsetid TSRMLS_DC)2829 static php_oci_spool *php_oci_create_spool(char *username, int username_len, char *password, int password_len, char *dbname, int dbname_len, char *hash_key, int hash_key_len, int charsetid TSRMLS_DC)
2830 {
2831 	php_oci_spool *session_pool = NULL;
2832 	zend_bool iserror = 0;
2833 	ub4 poolmode = OCI_DEFAULT;	/* Mode to be passed to OCISessionPoolCreate */
2834 	OCIAuthInfo *spoolAuth = NULL;
2835 	sword errstatus;
2836 
2837 	/* Allocate sessionpool out of persistent memory */
2838 	session_pool = (php_oci_spool *) calloc(1, sizeof(php_oci_spool));
2839 	if (session_pool == NULL) {
2840 		iserror = 1;
2841 		goto exit_create_spool;
2842 	}
2843 
2844 	/* Populate key if passed */
2845 	if (hash_key_len) {
2846 		session_pool->spool_hash_key = zend_strndup(hash_key, hash_key_len);
2847 		if (session_pool->spool_hash_key == NULL) {
2848 			iserror = 1;
2849 			goto exit_create_spool;
2850 		}
2851 	}
2852 
2853 	/* Create the session pool's env */
2854 	if (!(session_pool->env = php_oci_create_env(charsetid TSRMLS_CC))) {
2855 		iserror = 1;
2856 		goto exit_create_spool;
2857 	}
2858 
2859 	/* Allocate the pool handle */
2860 	PHP_OCI_CALL_RETURN(errstatus, OCIHandleAlloc, (session_pool->env, (dvoid **) &session_pool->poolh, OCI_HTYPE_SPOOL, (size_t) 0, (dvoid **) 0));
2861 
2862 	if (errstatus != OCI_SUCCESS) {
2863 		OCI_G(errcode) = php_oci_error(OCI_G(err), errstatus TSRMLS_CC);
2864 		iserror = 1;
2865 		goto exit_create_spool;
2866 	}
2867 
2868 	/* Allocate the session pool error handle - This only for use in the destructor, as there is a
2869 	 * generic bug which can free up the OCI_G(err) variable before destroying connections. We
2870 	 * cannot use this for other roundtrip calls as there is no way the user can access this error
2871 	 */
2872 	PHP_OCI_CALL_RETURN(errstatus, OCIHandleAlloc, ((dvoid *) session_pool->env, (dvoid **)&(session_pool->err), (ub4) OCI_HTYPE_ERROR,(size_t) 0, (dvoid **) 0));
2873 
2874 	if (errstatus != OCI_SUCCESS) {
2875 		OCI_G(errcode) = php_oci_error(OCI_G(err), errstatus TSRMLS_CC);
2876 		iserror = 1;
2877 		goto exit_create_spool;
2878 	}
2879 
2880 /* Disable RLB as we mostly have single-connection pools */
2881 #if (OCI_MAJOR_VERSION > 10)
2882 	poolmode = OCI_SPC_NO_RLB | OCI_SPC_HOMOGENEOUS;
2883 #else
2884 	poolmode = OCI_SPC_HOMOGENEOUS;
2885 #endif
2886 
2887 #if ((OCI_MAJOR_VERSION > 11) || ((OCI_MAJOR_VERSION == 11) && (OCI_MINOR_VERSION >= 2)))
2888 	/* {{{ Allocate auth handle for session pool */
2889 	PHP_OCI_CALL_RETURN(errstatus, OCIHandleAlloc, (session_pool->env, (dvoid **)&(spoolAuth), OCI_HTYPE_AUTHINFO, 0, NULL));
2890 
2891 	if (errstatus != OCI_SUCCESS) {
2892 		OCI_G(errcode) = php_oci_error(OCI_G(err), errstatus TSRMLS_CC);
2893 		iserror = 1;
2894 		goto exit_create_spool;
2895  	}
2896 	/* }}} */
2897 
2898 	/* {{{ Set the edition attribute on the auth handle */
2899 	if (OCI_G(edition)) {
2900 		PHP_OCI_CALL_RETURN(errstatus, OCIAttrSet, ((dvoid *) spoolAuth, (ub4) OCI_HTYPE_AUTHINFO, (dvoid *) OCI_G(edition), (ub4)(strlen(OCI_G(edition))), (ub4)OCI_ATTR_EDITION, OCI_G(err)));
2901 
2902 		if (errstatus != OCI_SUCCESS) {
2903 			OCI_G(errcode) = php_oci_error(OCI_G(err), errstatus TSRMLS_CC);
2904 			iserror = 1;
2905 			goto exit_create_spool;
2906 		}
2907 	}
2908 	/* }}} */
2909 
2910 	/* {{{ Set the driver name attribute on the auth handle */
2911 	PHP_OCI_CALL_RETURN(errstatus, OCIAttrSet, ((dvoid *) spoolAuth, (ub4) OCI_HTYPE_AUTHINFO, (dvoid *) PHP_OCI8_DRIVER_NAME, (ub4) sizeof(PHP_OCI8_DRIVER_NAME)-1, (ub4) OCI_ATTR_DRIVER_NAME, OCI_G(err)));
2912 
2913 	if (errstatus != OCI_SUCCESS) {
2914 		OCI_G(errcode) = php_oci_error(OCI_G(err), errstatus TSRMLS_CC);
2915 		iserror = 1;
2916 		goto exit_create_spool;
2917 	}
2918 	/* }}} */
2919 
2920 	/* {{{ Set the auth handle on the session pool */
2921 	PHP_OCI_CALL_RETURN(errstatus, OCIAttrSet, ((dvoid *) (session_pool->poolh),(ub4) OCI_HTYPE_SPOOL, (dvoid *) spoolAuth, (ub4)0, (ub4)OCI_ATTR_SPOOL_AUTH, OCI_G(err)));
2922 
2923 	if (errstatus != OCI_SUCCESS) {
2924 		OCI_G(errcode) = php_oci_error(OCI_G(err), errstatus TSRMLS_CC);
2925 		iserror = 1;
2926 		goto exit_create_spool;
2927 	}
2928 	/* }}} */
2929 #endif
2930 
2931 	/* Create the homogeneous session pool - We have different session pools for every different
2932 	 * username, password, charset and dbname.
2933 	 */
2934 	PHP_OCI_CALL_RETURN(errstatus, OCISessionPoolCreate,(session_pool->env, OCI_G(err), session_pool->poolh, (OraText **)&session_pool->poolname, &session_pool->poolname_len, (OraText *)dbname, (ub4)dbname_len, 0, UB4MAXVAL, 1,(OraText *)username, (ub4)username_len, (OraText *)password,(ub4)password_len, poolmode));
2935 
2936 	if (errstatus != OCI_SUCCESS) {
2937 		OCI_G(errcode) = php_oci_error(OCI_G(err), errstatus TSRMLS_CC);
2938 		iserror = 1;
2939 	}
2940 
2941 exit_create_spool:
2942 	if (iserror && session_pool) {
2943 		php_oci_spool_close(session_pool TSRMLS_CC);
2944 		session_pool = NULL;
2945 	}
2946 
2947 	if (spoolAuth) {
2948 		PHP_OCI_CALL(OCIHandleFree, ((dvoid *) spoolAuth, (ub4) OCI_HTYPE_AUTHINFO));
2949 	}
2950 
2951 #ifdef HAVE_OCI8_DTRACE
2952 	if (DTRACE_OCI8_SESSPOOL_CREATE_ENABLED()) {
2953 		DTRACE_OCI8_SESSPOOL_CREATE(session_pool);
2954 	}
2955 #endif /* HAVE_OCI8_DTRACE */
2956 
2957 	return session_pool;
2958 }
2959 /* }}} */
2960 
2961 /* {{{ php_oci_get_spool()
2962  *
2963  * Get Session pool for the given dbname and charsetid from the persistent list. Function called for
2964  * non-persistent connections.
2965  */
php_oci_get_spool(char * username,int username_len,char * password,int password_len,char * dbname,int dbname_len,int charsetid TSRMLS_DC)2966 static php_oci_spool *php_oci_get_spool(char *username, int username_len, char *password, int password_len, char *dbname, int dbname_len, int charsetid TSRMLS_DC)
2967 {
2968 	smart_str spool_hashed_details = {0};
2969 	php_oci_spool *session_pool = NULL;
2970 	zend_rsrc_list_entry spool_le = {0};
2971 	zend_rsrc_list_entry *spool_out_le = NULL;
2972 	zend_bool iserror = 0;
2973 
2974 	/* {{{ Create the spool hash key */
2975 	smart_str_appendl_ex(&spool_hashed_details, "oci8spool***", sizeof("oci8spool***") - 1, 0);
2976 	smart_str_appendl_ex(&spool_hashed_details, username, username_len, 0);
2977 	smart_str_appendl_ex(&spool_hashed_details, "**", sizeof("**") - 1, 0);
2978 	/* Add edition attribute to the hash */
2979 	if (OCI_G(edition)){
2980 		smart_str_appendl_ex(&spool_hashed_details, OCI_G(edition), strlen(OCI_G(edition)), 0);
2981 	}
2982 	smart_str_appendl_ex(&spool_hashed_details, "**", sizeof("**") - 1, 0);
2983 	if (password_len) {
2984 		ulong password_hash;
2985 		password_hash = zend_inline_hash_func(password, password_len);
2986 		smart_str_append_unsigned_ex(&spool_hashed_details, password_hash, 0);
2987 	}
2988 	smart_str_appendl_ex(&spool_hashed_details, "**", sizeof("**") - 1, 0);
2989 
2990 	if (dbname_len) {
2991 		smart_str_appendl_ex(&spool_hashed_details, dbname, dbname_len, 0);
2992 	}
2993 	smart_str_appendl_ex(&spool_hashed_details, "**", sizeof("**") - 1, 0);
2994 
2995 	smart_str_append_unsigned_ex(&spool_hashed_details, charsetid, 0);
2996 
2997 	/* Session Pool Hash Key : oci8spool***username**edition**hashedpassword**dbname**charset */
2998 
2999 	smart_str_0(&spool_hashed_details);
3000 	php_strtolower(spool_hashed_details.c, spool_hashed_details.len);
3001 	/* }}} */
3002 
3003 	if (zend_hash_find(&EG(persistent_list),spool_hashed_details.c, spool_hashed_details.len+1, (void **)&spool_out_le) == FAILURE) {
3004 
3005 		session_pool = php_oci_create_spool(username, username_len, password, password_len, dbname, dbname_len, spool_hashed_details.c, spool_hashed_details.len, charsetid TSRMLS_CC);
3006 
3007 		if (session_pool == NULL) {
3008 			iserror = 1;
3009 			goto exit_get_spool;
3010 		}
3011 		spool_le.ptr  = session_pool;
3012 		spool_le.type = le_psessionpool;
3013 		PHP_OCI_REGISTER_RESOURCE(session_pool, le_psessionpool);
3014 		zend_hash_update(&EG(persistent_list), session_pool->spool_hash_key, strlen(session_pool->spool_hash_key)+1,(void *)&spool_le, sizeof(zend_rsrc_list_entry),NULL);
3015 	} else if (spool_out_le->type == le_psessionpool &&
3016 		strlen(((php_oci_spool *)(spool_out_le->ptr))->spool_hash_key) == spool_hashed_details.len &&
3017 		memcmp(((php_oci_spool *)(spool_out_le->ptr))->spool_hash_key, spool_hashed_details.c, spool_hashed_details.len) == 0) {
3018 		/* retrieve the cached session pool */
3019 		session_pool = (php_oci_spool *)(spool_out_le->ptr);
3020 	}
3021 
3022 exit_get_spool:
3023 	smart_str_free_ex(&spool_hashed_details, 0);
3024 	if (iserror && session_pool) {
3025 		php_oci_spool_close(session_pool TSRMLS_CC);
3026 		session_pool = NULL;
3027 	}
3028 
3029 	return session_pool;
3030 
3031 }
3032 /* }}} */
3033 
3034 /* {{{ php_oci_create_env()
3035  *
3036  * Create the OCI environment choosing the correct function for the OCI version
3037  */
php_oci_create_env(ub2 charsetid TSRMLS_DC)3038 static OCIEnv *php_oci_create_env(ub2 charsetid TSRMLS_DC)
3039 {
3040 	OCIEnv *retenv = NULL;
3041 
3042 	/* create an environment using the character set id */
3043 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIEnvNlsCreate, (&retenv, OCI_G(events) ? PHP_OCI_INIT_MODE | OCI_EVENTS : PHP_OCI_INIT_MODE, 0, NULL, NULL, NULL, 0, NULL, charsetid, charsetid));
3044 
3045 	if (OCI_G(errcode) != OCI_SUCCESS) {
3046 		sb4   ora_error_code = 0;
3047 		text  ora_msg_buf[OCI_ERROR_MAXMSG_SIZE];  /* Use traditional smaller size: non-PL/SQL errors should fit and it keeps the stack smaller */
3048 
3049 #ifdef HAVE_OCI_INSTANT_CLIENT
3050 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "OCIEnvNlsCreate() failed. There is something wrong with your system - please check that " PHP_OCI8_LIB_PATH_MSG " includes the directory with Oracle Instant Client libraries");
3051 #else
3052 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "OCIEnvNlsCreate() failed. There is something wrong with your system - please check that ORACLE_HOME and " PHP_OCI8_LIB_PATH_MSG " are set and point to the right directories");
3053 #endif
3054 		if (retenv
3055 			&& OCIErrorGet(retenv, (ub4)1, NULL, &ora_error_code, ora_msg_buf, (ub4)OCI_ERROR_MAXMSG_SIZE, (ub4)OCI_HTYPE_ENV) == OCI_SUCCESS
3056 			&& *ora_msg_buf) {
3057 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ora_msg_buf);
3058 		}
3059 
3060 		return NULL;
3061 	}
3062 	return retenv;
3063 }
3064 /* }}} */
3065 
3066 /* {{{ php_oci_old_create_session()
3067  *
3068  * This function is to be deprecated in future in favour of OCISessionGet which is used in
3069  * php_oci_do_connect_ex
3070  */
php_oci_old_create_session(php_oci_connection * connection,char * dbname,int dbname_len,char * username,int username_len,char * password,int password_len,char * new_password,int new_password_len,int session_mode TSRMLS_DC)3071 static int php_oci_old_create_session(php_oci_connection *connection, char *dbname, int dbname_len, char *username, int username_len, char *password, int password_len, char *new_password, int new_password_len, int session_mode TSRMLS_DC)
3072 {
3073 	ub4 statement_cache_size = (OCI_G(statement_cache_size) > 0) ? OCI_G(statement_cache_size) : 0;
3074 
3075 	/* Create the OCI environment separate for each connection */
3076 	if (!(connection->env = php_oci_create_env(connection->charset TSRMLS_CC))) {
3077 		return 1;
3078 	}
3079 
3080 	/* {{{ Allocate our server handle */
3081 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIHandleAlloc, (connection->env, (dvoid **)&(connection->server), OCI_HTYPE_SERVER, 0, NULL));
3082 
3083 	if (OCI_G(errcode) != OCI_SUCCESS) {
3084 		php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3085 		return 1;
3086 	}
3087 	/* }}} */
3088 
3089 	/* {{{ Attach to the server */
3090 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIServerAttach, (connection->server, OCI_G(err), (text *)dbname, dbname_len, (ub4) OCI_DEFAULT));
3091 
3092 	if (OCI_G(errcode) != OCI_SUCCESS) {
3093 		php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3094 		return 1;
3095 	}
3096 	/* }}} */
3097 	connection->is_attached = 1;
3098 
3099 	/* {{{ Allocate our session handle */
3100 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIHandleAlloc, (connection->env, (dvoid **)&(connection->session), OCI_HTYPE_SESSION, 0, NULL));
3101 
3102 	if (OCI_G(errcode) != OCI_SUCCESS) {
3103 		php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3104 		return 1;
3105 	}
3106 	/* }}} */
3107 
3108 	/* {{{ Allocate our private error-handle */
3109 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIHandleAlloc, (connection->env, (dvoid **)&(connection->err), OCI_HTYPE_ERROR, 0, NULL));
3110 
3111 	if (OCI_G(errcode) != OCI_SUCCESS) {
3112 		php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3113 		return 1;
3114 	}
3115 	/* }}} */
3116 
3117 	/* {{{ Allocate our service-context */
3118 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIHandleAlloc, (connection->env, (dvoid **)&(connection->svc), OCI_HTYPE_SVCCTX, 0, NULL));
3119 
3120 	if (OCI_G(errcode) != OCI_SUCCESS) {
3121 		php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3122 		return 1;
3123 	}
3124 	/* }}} */
3125 
3126 	/* {{{ Set the username */
3127 	if (username) {
3128 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->session, (ub4) OCI_HTYPE_SESSION, (dvoid *) username, (ub4) username_len, (ub4) OCI_ATTR_USERNAME, OCI_G(err)));
3129 
3130 		if (OCI_G(errcode) != OCI_SUCCESS) {
3131 			php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3132 			return 1;
3133 		}
3134 	}
3135 	/* }}} */
3136 
3137 	/* {{{ Set the password */
3138 	if (password) {
3139 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->session, (ub4) OCI_HTYPE_SESSION, (dvoid *) password, (ub4) password_len, (ub4) OCI_ATTR_PASSWORD, OCI_G(err)));
3140 
3141 		if (OCI_G(errcode) != OCI_SUCCESS) {
3142 			php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3143 			return 1;
3144 		}
3145 	}
3146 	/* }}} */
3147 
3148 	/* {{{ Set the edition attribute on the session handle */
3149 #if ((OCI_MAJOR_VERSION > 11) || ((OCI_MAJOR_VERSION == 11) && (OCI_MINOR_VERSION >= 2)))
3150 	if (OCI_G(edition)) {
3151 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->session, (ub4) OCI_HTYPE_SESSION, (dvoid *) OCI_G(edition), (ub4) (strlen(OCI_G(edition))), (ub4) OCI_ATTR_EDITION, OCI_G(err)));
3152 
3153 		if (OCI_G(errcode) != OCI_SUCCESS) {
3154 			php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3155 			return 1;
3156 		}
3157 	}
3158 #endif
3159 /* }}} */
3160 
3161 	/* {{{ Set the driver name attribute on the session handle */
3162 #if (OCI_MAJOR_VERSION >= 11)
3163 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->session, (ub4) OCI_HTYPE_SESSION, (dvoid *) PHP_OCI8_DRIVER_NAME, (ub4) sizeof(PHP_OCI8_DRIVER_NAME)-1, (ub4) OCI_ATTR_DRIVER_NAME, OCI_G(err)));
3164 
3165 	if (OCI_G(errcode) != OCI_SUCCESS) {
3166 		php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3167 		return 1;
3168 	}
3169 #endif
3170 /* }}} */
3171 
3172 	/* {{{ Set the server handle in the service handle */
3173 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, (connection->svc, OCI_HTYPE_SVCCTX, connection->server, 0, OCI_ATTR_SERVER, OCI_G(err)));
3174 
3175 	if (OCI_G(errcode) != OCI_SUCCESS) {
3176 		php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3177 		return 1;
3178 	}
3179 	/* }}} */
3180 
3181 	/* {{{ Set the authentication handle in the service handle */
3182 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, (connection->svc, OCI_HTYPE_SVCCTX, connection->session, 0, OCI_ATTR_SESSION, OCI_G(err)));
3183 
3184 	if (OCI_G(errcode) != OCI_SUCCESS) {
3185 		php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3186 		return 1;
3187 	}
3188 	/* }}} */
3189 
3190 	if (new_password) {
3191 		/* {{{ Try to change password if new one was provided */
3192 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIPasswordChange, (connection->svc, OCI_G(err), (text *)username, username_len, (text *)password, password_len, (text *)new_password, new_password_len, OCI_AUTH));
3193 
3194 		if (OCI_G(errcode) != OCI_SUCCESS) {
3195 			php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3196 			return 1;
3197 		}
3198 
3199 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrGet, ((dvoid *)connection->svc, OCI_HTYPE_SVCCTX, (dvoid *)&(connection->session), (ub4 *)0, OCI_ATTR_SESSION, OCI_G(err)));
3200 
3201 		if (OCI_G(errcode) != OCI_SUCCESS) {
3202 			php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3203 			return 1;
3204 		}
3205 		/* }}} */
3206 	} else {
3207 		/* {{{ start the session */
3208 		ub4 cred_type = OCI_CRED_RDBMS;
3209 
3210 		/* Extract the overloaded session_mode parameter into valid Oracle credential and session mode values */
3211 		if (session_mode & PHP_OCI_CRED_EXT) {
3212 			cred_type = OCI_CRED_EXT;
3213 			session_mode ^= PHP_OCI_CRED_EXT;
3214 		}
3215 
3216 		session_mode |= OCI_STMT_CACHE;
3217 
3218 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCISessionBegin, (connection->svc, OCI_G(err), connection->session, (ub4) cred_type, (ub4) session_mode));
3219 
3220 		if (OCI_G(errcode) != OCI_SUCCESS) {
3221 			php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3222 			/* OCISessionBegin returns OCI_SUCCESS_WITH_INFO when
3223 			 * user's password has expired, but is still usable.
3224 			 */
3225 			if (OCI_G(errcode) != OCI_SUCCESS_WITH_INFO) {
3226 				return 1;
3227 			}
3228 		}
3229 		/* }}} */
3230 	}
3231 
3232 	/* Brand new connection: Init and update the next_ping in the connection */
3233 	if (php_oci_ping_init(connection, OCI_G(err) TSRMLS_CC) != OCI_SUCCESS) {
3234 		php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3235 		return 1;
3236 	}
3237 
3238 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->svc, (ub4) OCI_HTYPE_SVCCTX, (ub4 *) &statement_cache_size, 0, (ub4) OCI_ATTR_STMTCACHESIZE, OCI_G(err)));
3239 
3240 	if (OCI_G(errcode) != OCI_SUCCESS) {
3241 		php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3242 		return 1;
3243 	}
3244 
3245 	/* Successfully created session */
3246 	return 0;
3247 }
3248 /* }}} */
3249 
3250 /* {{{ php_oci_create_session()
3251  *
3252  * Create session using client-side session pool - new norm
3253  */
php_oci_create_session(php_oci_connection * connection,php_oci_spool * session_pool,char * dbname,int dbname_len,char * username,int username_len,char * password,int password_len,char * new_password,int new_password_len,int session_mode TSRMLS_DC)3254 static int php_oci_create_session(php_oci_connection *connection, php_oci_spool *session_pool, char *dbname, int dbname_len, char *username, int username_len, char *password, int password_len, char *new_password, int new_password_len, int session_mode TSRMLS_DC)
3255 {
3256 	php_oci_spool *actual_spool = NULL;
3257 #if (OCI_MAJOR_VERSION > 10)
3258 	ub4 purity = -2;				/* Illegal value to initialize */
3259 #endif
3260 	time_t timestamp = time(NULL);
3261 	ub4 statement_cache_size = (OCI_G(statement_cache_size) > 0) ? OCI_G(statement_cache_size) : 0;
3262 
3263 	/* Persistent connections have private session pools */
3264 	if (connection->is_persistent && !connection->private_spool &&
3265 		!(connection->private_spool = php_oci_create_spool(username, username_len, password, password_len, dbname, dbname_len, NULL, 0, connection->charset TSRMLS_CC))) {
3266 			return 1;
3267 	}
3268 	actual_spool = (connection->is_persistent) ? (connection->private_spool) : (session_pool);
3269 
3270 	connection->env = actual_spool->env;
3271 
3272 	/* Do this upfront so that connection close on an error would know that this is a session pool
3273 	 * connection. Failure to do this would result in crashes in error scenarios
3274 	 */
3275 	if (!connection->using_spool) {
3276 		connection->using_spool = 1;
3277 	}
3278 
3279 #ifdef HAVE_OCI8_DTRACE
3280 	if (DTRACE_OCI8_SESSPOOL_TYPE_ENABLED()) {
3281 		DTRACE_OCI8_SESSPOOL_TYPE(session_pool ? 1 : 0, session_pool ? session_pool : connection->private_spool);
3282 	}
3283 #endif /* HAVE_OCI8_DTRACE */
3284 
3285 	/* The passed in "connection" can be a cached stub from plist or freshly created. In the former
3286 	 * case, we do not have to allocate any handles
3287 	 */
3288 
3289 	if (!connection->err) {
3290 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIHandleAlloc, (connection->env, (dvoid **)&(connection->err), OCI_HTYPE_ERROR, 0, NULL));
3291 
3292 		if (OCI_G(errcode) != OCI_SUCCESS) {
3293 			php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3294 			return 1;
3295 		}
3296 	}
3297 
3298 	/* {{{ Allocate and initialize the connection-private authinfo handle if not allocated yet */
3299 	if (!connection->authinfo) {
3300 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIHandleAlloc, (connection->env, (dvoid **)&(connection->authinfo), OCI_HTYPE_AUTHINFO, 0, NULL));
3301 
3302 		if (OCI_G(errcode) != OCI_SUCCESS) {
3303 			php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3304 			return 1;
3305 		}
3306 
3307 		/* Set the Connection class and purity if OCI client version >= 11g */
3308 #if (OCI_MAJOR_VERSION > 10)
3309 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->authinfo,(ub4) OCI_HTYPE_SESSION, (dvoid *) OCI_G(connection_class), (ub4)(strlen(OCI_G(connection_class))), (ub4)OCI_ATTR_CONNECTION_CLASS, OCI_G(err)));
3310 
3311 		if (OCI_G(errcode) != OCI_SUCCESS) {
3312 			php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3313 			return 1;
3314 		}
3315 
3316 		if (connection->is_persistent)
3317 			purity = OCI_ATTR_PURITY_SELF;
3318 		else
3319 			purity = OCI_ATTR_PURITY_NEW;
3320 
3321 		PHP_OCI_CALL_RETURN(OCI_G(errcode),OCIAttrSet, ((dvoid *) connection->authinfo,(ub4) OCI_HTYPE_AUTHINFO, (dvoid *) &purity, (ub4)0, (ub4)OCI_ATTR_PURITY, OCI_G(err)));
3322 
3323 		if (OCI_G(errcode) != OCI_SUCCESS) {
3324 			php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3325 			return 1;
3326 		}
3327 #endif
3328 	}
3329 	/* }}} */
3330 
3331 	/* {{{ Debug statements */
3332 #ifdef HAVE_OCI8_DTRACE
3333 	if (DTRACE_OCI8_SESSPOOL_STATS_ENABLED()) {
3334 		ub4 numfree = 0, numbusy = 0, numopen = 0;
3335 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrGet, ((dvoid *)actual_spool->poolh, OCI_HTYPE_SPOOL, (dvoid *)&numopen, (ub4 *)0, OCI_ATTR_SPOOL_OPEN_COUNT, OCI_G(err)));
3336 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrGet, ((dvoid *)actual_spool->poolh, OCI_HTYPE_SPOOL, (dvoid *)&numbusy, (ub4 *)0, OCI_ATTR_SPOOL_BUSY_COUNT, OCI_G(err)));
3337 		numfree = numopen - numbusy;	/* number of free connections in the pool */
3338 		DTRACE_OCI8_SESSPOOL_STATS(numfree, numbusy, numopen);
3339 	}
3340 #endif /* HAVE_OCI8_DTRACE */
3341 	/* }}} */
3342 
3343 		/* Ping loop: Ping and loop till we get a good connection. When a database instance goes
3344 		 * down, it can leave several bad connections that need to be flushed out before getting a
3345 		 * good one. In non-RAC, we always get a brand new connection at the end of the loop and in
3346 		 * RAC, we can get a good connection from a different instance before flushing out all bad
3347 		 * ones. We do not need to ping brand new connections.
3348 		 */
3349 	do {
3350 		/* Continue to use the global error handle as the connection is closed when an error occurs */
3351 		PHP_OCI_CALL_RETURN(OCI_G(errcode),OCISessionGet, (connection->env, OCI_G(err), &(connection->svc), (OCIAuthInfo *)connection->authinfo, (OraText *)actual_spool->poolname, (ub4)actual_spool->poolname_len, NULL, 0, NULL, NULL, NULL, OCI_SESSGET_SPOOL));
3352 
3353 		if (OCI_G(errcode) != OCI_SUCCESS) {
3354 			php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3355 
3356 			/* Session creation returns OCI_SUCCESS_WITH_INFO when user's password has expired, but
3357 			 * is still usable.
3358 			 */
3359 
3360 			if (OCI_G(errcode) != OCI_SUCCESS_WITH_INFO) {
3361 				return 1;
3362 			}
3363 		}
3364 
3365 		/* {{{ Populate the session and server fields of the connection */
3366 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrGet, ((dvoid *)connection->svc, OCI_HTYPE_SVCCTX, (dvoid *)&(connection->server), (ub4 *)0, OCI_ATTR_SERVER, OCI_G(err)));
3367 
3368 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrGet, ((dvoid *)connection->svc, OCI_HTYPE_SVCCTX, (dvoid *)&(connection->session), (ub4 *)0, OCI_ATTR_SESSION, OCI_G(err)));
3369 		/* }}} */
3370 
3371 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIContextGetValue, (connection->session, OCI_G(err), (ub1 *)"NEXT_PING", (ub1)sizeof("NEXT_PING"), (void **)&(connection->next_pingp)));
3372 		if (OCI_G(errcode) != OCI_SUCCESS) {
3373 			php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3374 			return 1;
3375 		}
3376 
3377 		if (!(connection->next_pingp)){
3378 			/* This is a brand new connection, we need not ping, but have to initialize ping */
3379 			if (php_oci_ping_init(connection, OCI_G(err) TSRMLS_CC) != OCI_SUCCESS) {
3380 				php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3381 				return 1;
3382 			}
3383 		} else if ((*(connection->next_pingp) > 0) && (timestamp >= *(connection->next_pingp))) {
3384 			if (php_oci_connection_ping(connection TSRMLS_CC)) {
3385 				/* Got a good connection - update next_ping and get out of ping loop */
3386 				*(connection->next_pingp) = timestamp + OCI_G(ping_interval);
3387 			} else {
3388 				/* Bad connection - remove from pool */
3389 				PHP_OCI_CALL(OCISessionRelease, (connection->svc, connection->err, NULL,0, (ub4) OCI_SESSRLS_DROPSESS));
3390 				connection->svc = NULL;
3391 				connection->server = NULL;
3392 				connection->session = NULL;
3393 			}
3394 		}	/* If ping applicable */
3395 	} while (!(connection->svc));
3396 
3397 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->svc, (ub4) OCI_HTYPE_SVCCTX, (ub4 *) &statement_cache_size, 0, (ub4) OCI_ATTR_STMTCACHESIZE, OCI_G(err)));
3398 
3399 	if (OCI_G(errcode) != OCI_SUCCESS) {
3400 		php_oci_error(OCI_G(err), OCI_G(errcode) TSRMLS_CC);
3401 		return 1;
3402 	}
3403 
3404 	/* Session is now taken from the session pool and attached and open */
3405 	connection->is_stub = 0;
3406 	connection->is_attached = connection->is_open = 1;
3407 
3408 	return 0;
3409 }
3410 /* }}} */
3411 
3412 /* {{{ php_oci_spool_list_dtor()
3413  *
3414  * Session pool destructor function
3415  */
php_oci_spool_list_dtor(zend_rsrc_list_entry * entry TSRMLS_DC)3416 static void php_oci_spool_list_dtor(zend_rsrc_list_entry *entry TSRMLS_DC)
3417 {
3418 	php_oci_spool *session_pool = (php_oci_spool *)entry->ptr;
3419 
3420 	if (session_pool) {
3421 		php_oci_spool_close(session_pool TSRMLS_CC);
3422 	}
3423 
3424 	return;
3425 }
3426 /* }}} */
3427 
3428 /* {{{	php_oci_spool_close()
3429  *
3430  * Destroys the OCI Session Pool
3431  */
php_oci_spool_close(php_oci_spool * session_pool TSRMLS_DC)3432 static void php_oci_spool_close(php_oci_spool *session_pool TSRMLS_DC)
3433 {
3434 	if (session_pool->poolname_len) {
3435 		PHP_OCI_CALL(OCISessionPoolDestroy, ((dvoid *) session_pool->poolh,
3436 			(dvoid *) session_pool->err, OCI_SPD_FORCE));
3437 	}
3438 
3439 	if (session_pool->poolh) {
3440 		PHP_OCI_CALL(OCIHandleFree, ((dvoid *) session_pool->poolh, OCI_HTYPE_SPOOL));
3441 	}
3442 
3443 	if (session_pool->err) {
3444 		PHP_OCI_CALL(OCIHandleFree, ((dvoid *) session_pool->err, OCI_HTYPE_ERROR));
3445 	}
3446 
3447 	if (session_pool->env) {
3448 		PHP_OCI_CALL(OCIHandleFree, ((dvoid *) session_pool->env, OCI_HTYPE_ENV));
3449 	}
3450 
3451 	if (session_pool->spool_hash_key) {
3452 		free(session_pool->spool_hash_key);
3453 	}
3454 
3455 	free(session_pool);
3456 }
3457 /* }}} */
3458 
3459 /* {{{ php_oci_ping_init()
3460  *
3461  * Initializes the next_ping time as a context value in the connection.	 We now use
3462  * OCIContext{Get,Set}Value to store the next_ping because we need to support ping for
3463  * non-persistent DRCP connections
3464  */
php_oci_ping_init(php_oci_connection * connection,OCIError * errh TSRMLS_DC)3465 static sword php_oci_ping_init(php_oci_connection *connection, OCIError *errh TSRMLS_DC)
3466 {
3467 	time_t *next_pingp = NULL;
3468 
3469 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIContextGetValue, (connection->session, errh, (ub1 *)"NEXT_PING", (ub1)sizeof("NEXT_PING"), (void **)&next_pingp));
3470 	if (OCI_G(errcode) != OCI_SUCCESS) {
3471 		return OCI_G(errcode);
3472 	}
3473 
3474 	/* This must be a brand-new connection. Allocate memory for the ping */
3475 	if (!next_pingp) {
3476 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIMemoryAlloc, (connection->session, errh, (void **)&next_pingp, OCI_DURATION_SESSION, sizeof(time_t), OCI_MEMORY_CLEARED));
3477 		if (OCI_G(errcode) != OCI_SUCCESS) {
3478 			return OCI_G(errcode);
3479 		}
3480 	}
3481 
3482 	if (OCI_G(ping_interval) >= 0) {
3483 		time_t timestamp = time(NULL);
3484 		*next_pingp = timestamp + OCI_G(ping_interval);
3485 	} else {
3486 		*next_pingp = 0;
3487 	}
3488 
3489 	/* Set the new ping value into the connection */
3490 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIContextSetValue, (connection->session, errh, OCI_DURATION_SESSION, (ub1 *)"NEXT_PING", (ub1)sizeof("NEXT_PING"), next_pingp));
3491 	if (OCI_G(errcode) != OCI_SUCCESS) {
3492 		OCIMemoryFree(connection->session, errh, next_pingp);
3493 		return OCI_G(errcode);
3494 	}
3495 
3496 	/* Cache the pointer so we do not have to do OCIContextGetValue repeatedly */
3497 	connection->next_pingp = next_pingp;
3498 
3499 	return OCI_SUCCESS;
3500 }
3501 /* }}} */
3502 
3503 /* {{{ php_oci_dtrace_check_connection()
3504  *
3505  * DTrace output for connections that may have become invalid and marked for reopening
3506  */
php_oci_dtrace_check_connection(php_oci_connection * connection,sb4 errcode,ub4 serverStatus)3507 void php_oci_dtrace_check_connection(php_oci_connection *connection, sb4 errcode, ub4 serverStatus)
3508 {
3509 #ifdef HAVE_OCI8_DTRACE
3510 	if (DTRACE_OCI8_CHECK_CONNECTION_ENABLED()) {
3511 		DTRACE_OCI8_CHECK_CONNECTION(connection, connection->client_id, connection->is_open ? 1 : 0, (long)errcode, (unsigned long)serverStatus);
3512 	}
3513 #endif /* HAVE_OCI8_DTRACE */
3514 }
3515 /* }}} */
3516 
3517 #endif /* HAVE_OCI8 */
3518 
3519 /*
3520  * Local variables:
3521  * tab-width: 4
3522  * c-basic-offset: 4
3523  * End:
3524  * vim600: noet sw=4 ts=4 fdm=marker
3525  * vim<600: noet sw=4 ts=4
3526  */
3527