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