1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | http://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Wez Furlong <wez@php.net> |
14 +----------------------------------------------------------------------+
15 */
16
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include "php.h"
22 #include "php_ini.h"
23 #include "ext/standard/info.h"
24 #include "pdo/php_pdo.h"
25 #include "pdo/php_pdo_driver.h"
26 #include "php_pdo_oci.h"
27 #include "php_pdo_oci_int.h"
28 #include "Zend/zend_exceptions.h"
29
30 static inline ub4 pdo_oci_sanitize_prefetch(long prefetch);
31
pdo_oci_fetch_error_func(pdo_dbh_t * dbh,pdo_stmt_t * stmt,zval * info)32 static int pdo_oci_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */
33 {
34 pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data;
35 pdo_oci_error_info *einfo;
36
37 einfo = &H->einfo;
38
39 if (stmt) {
40 pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
41
42 if (S->einfo.errmsg) {
43 einfo = &S->einfo;
44 }
45 }
46
47 if (einfo->errcode) {
48 add_next_index_long(info, einfo->errcode);
49 add_next_index_string(info, einfo->errmsg);
50 }
51
52 return 1;
53 }
54 /* }}} */
55
_oci_error(OCIError * err,pdo_dbh_t * dbh,pdo_stmt_t * stmt,char * what,sword status,int isinit,const char * file,int line)56 ub4 _oci_error(OCIError *err, pdo_dbh_t *dbh, pdo_stmt_t *stmt, char *what, sword status, int isinit, const char *file, int line) /* {{{ */
57 {
58 text errbuf[1024] = "<<Unknown>>";
59 char tmp_buf[2048];
60 pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data;
61 pdo_oci_error_info *einfo;
62 pdo_oci_stmt *S = NULL;
63 pdo_error_type *pdo_err = &dbh->error_code;
64
65 if (stmt) {
66 S = (pdo_oci_stmt*)stmt->driver_data;
67 einfo = &S->einfo;
68 pdo_err = &stmt->error_code;
69 }
70 else {
71 einfo = &H->einfo;
72 }
73
74 if (einfo->errmsg) {
75 pefree(einfo->errmsg, dbh->is_persistent);
76 }
77
78 einfo->errmsg = NULL;
79 einfo->errcode = 0;
80 einfo->file = file;
81 einfo->line = line;
82
83 if (isinit) { /* Initialization error */
84 strcpy(*pdo_err, "HY000");
85 slprintf(tmp_buf, sizeof(tmp_buf), "%s (%s:%d)", what, file, line);
86 einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);
87 }
88 else {
89 switch (status) {
90 case OCI_SUCCESS:
91 strcpy(*pdo_err, "00000");
92 break;
93 case OCI_ERROR:
94 OCIErrorGet(err, (ub4)1, NULL, &einfo->errcode, errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR);
95 slprintf(tmp_buf, sizeof(tmp_buf), "%s: %s (%s:%d)", what, errbuf, file, line);
96 einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);
97 break;
98 case OCI_SUCCESS_WITH_INFO:
99 OCIErrorGet(err, (ub4)1, NULL, &einfo->errcode, errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR);
100 slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_SUCCESS_WITH_INFO: %s (%s:%d)", what, errbuf, file, line);
101 einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);
102 break;
103 case OCI_NEED_DATA:
104 slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_NEED_DATA (%s:%d)", what, file, line);
105 einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);
106 break;
107 case OCI_NO_DATA:
108 slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_NO_DATA (%s:%d)", what, file, line);
109 einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);
110 break;
111 case OCI_INVALID_HANDLE:
112 slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_INVALID_HANDLE (%s:%d)", what, file, line);
113 einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);
114 break;
115 case OCI_STILL_EXECUTING:
116 slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_STILL_EXECUTING (%s:%d)", what, file, line);
117 einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);
118 break;
119 case OCI_CONTINUE:
120 slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_CONTINUE (%s:%d)", what, file, line);
121 einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);
122 break;
123 }
124
125 if (einfo->errcode) {
126 switch (einfo->errcode) {
127 case 1013: /* user requested cancel of current operation */
128 zend_bailout();
129 break;
130
131 case 12154: /* ORA-12154: TNS:could not resolve service name */
132 strcpy(*pdo_err, "42S02");
133 break;
134
135 case 22: /* ORA-00022: invalid session id */
136 case 378:
137 case 602:
138 case 603:
139 case 604:
140 case 609:
141 case 1012: /* ORA-01012: */
142 case 1033:
143 case 1041:
144 case 1043:
145 case 1089:
146 case 1090:
147 case 1092:
148 case 3113: /* ORA-03133: end of file on communication channel */
149 case 3114:
150 case 3122:
151 case 3135:
152 case 12153:
153 case 27146:
154 case 28511:
155 /* consider the connection closed */
156 dbh->is_closed = 1;
157 H->attached = 0;
158 strcpy(*pdo_err, "01002"); /* FIXME */
159 break;
160
161 default:
162 strcpy(*pdo_err, "HY000");
163 }
164 }
165
166 if (stmt) {
167 /* always propagate the error code back up to the dbh,
168 * so that we can catch the error information when execute
169 * is called via query. See Bug #33707 */
170 if (H->einfo.errmsg) {
171 pefree(H->einfo.errmsg, dbh->is_persistent);
172 }
173 H->einfo = *einfo;
174 H->einfo.errmsg = einfo->errmsg ? pestrdup(einfo->errmsg, dbh->is_persistent) : NULL;
175 strcpy(dbh->error_code, stmt->error_code);
176 }
177 }
178
179 /* little mini hack so that we can use this code from the dbh ctor */
180 if (!dbh->methods) {
181 zend_throw_exception_ex(php_pdo_get_exception(), einfo->errcode, "SQLSTATE[%s]: %s", *pdo_err, einfo->errmsg);
182 }
183
184 return einfo->errcode;
185 }
186 /* }}} */
187
oci_handle_closer(pdo_dbh_t * dbh)188 static int oci_handle_closer(pdo_dbh_t *dbh) /* {{{ */
189 {
190 pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data;
191
192 if (H->svc) {
193 /* rollback any outstanding work */
194 OCITransRollback(H->svc, H->err, 0);
195 }
196
197 if (H->session) {
198 OCIHandleFree(H->session, OCI_HTYPE_SESSION);
199 H->session = NULL;
200 }
201
202 if (H->svc) {
203 OCIHandleFree(H->svc, OCI_HTYPE_SVCCTX);
204 H->svc = NULL;
205 }
206
207 if (H->server && H->attached) {
208 H->last_err = OCIServerDetach(H->server, H->err, OCI_DEFAULT);
209 if (H->last_err) {
210 oci_drv_error("OCIServerDetach");
211 }
212 H->attached = 0;
213 }
214
215 if (H->server) {
216 OCIHandleFree(H->server, OCI_HTYPE_SERVER);
217 H->server = NULL;
218 }
219
220 if (H->err) {
221 OCIHandleFree(H->err, OCI_HTYPE_ERROR);
222 H->err = NULL;
223 }
224
225 if (H->charset && H->env) {
226 OCIHandleFree(H->env, OCI_HTYPE_ENV);
227 H->env = NULL;
228 }
229
230 if (H->einfo.errmsg) {
231 pefree(H->einfo.errmsg, dbh->is_persistent);
232 H->einfo.errmsg = NULL;
233 }
234
235 pefree(H, dbh->is_persistent);
236
237 return 0;
238 }
239 /* }}} */
240
oci_handle_preparer(pdo_dbh_t * dbh,const char * sql,size_t sql_len,pdo_stmt_t * stmt,zval * driver_options)241 static int oci_handle_preparer(pdo_dbh_t *dbh, const char *sql, size_t sql_len, pdo_stmt_t *stmt, zval *driver_options) /* {{{ */
242 {
243 pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data;
244 pdo_oci_stmt *S = ecalloc(1, sizeof(*S));
245 ub4 prefetch;
246 char *nsql = NULL;
247 size_t nsql_len = 0;
248 int ret;
249
250 #ifdef HAVE_OCISTMTFETCH2
251 S->exec_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR,
252 PDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL ?
253 OCI_STMT_SCROLLABLE_READONLY : OCI_DEFAULT;
254 #else
255 S->exec_type = OCI_DEFAULT;
256 #endif
257
258 S->H = H;
259 stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED;
260 ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len);
261
262 if (ret == 1) {
263 /* query was re-written */
264 sql = nsql;
265 sql_len = nsql_len;
266 } else if (ret == -1) {
267 /* couldn't grok it */
268 strcpy(dbh->error_code, stmt->error_code);
269 efree(S);
270 return 0;
271 }
272
273 /* create an OCI statement handle */
274 OCIHandleAlloc(H->env, (dvoid*)&S->stmt, OCI_HTYPE_STMT, 0, NULL);
275
276 /* and our own private error handle */
277 OCIHandleAlloc(H->env, (dvoid*)&S->err, OCI_HTYPE_ERROR, 0, NULL);
278
279 if (sql_len) {
280 H->last_err = OCIStmtPrepare(S->stmt, H->err, (text*)sql, (ub4) sql_len, OCI_NTV_SYNTAX, OCI_DEFAULT);
281 if (nsql) {
282 efree(nsql);
283 nsql = NULL;
284 }
285 if (H->last_err) {
286 H->last_err = oci_drv_error("OCIStmtPrepare");
287 OCIHandleFree(S->stmt, OCI_HTYPE_STMT);
288 OCIHandleFree(S->err, OCI_HTYPE_ERROR);
289 efree(S);
290 return 0;
291 }
292
293 }
294
295 prefetch = H->prefetch; /* Note 0 is allowed so in future REF CURSORs can be used & then passed with no row loss*/
296 H->last_err = OCIAttrSet(S->stmt, OCI_HTYPE_STMT, &prefetch, 0,
297 OCI_ATTR_PREFETCH_ROWS, H->err);
298 if (!H->last_err) {
299 prefetch *= PDO_OCI_PREFETCH_ROWSIZE;
300 H->last_err = OCIAttrSet(S->stmt, OCI_HTYPE_STMT, &prefetch, 0,
301 OCI_ATTR_PREFETCH_MEMORY, H->err);
302 }
303
304 stmt->driver_data = S;
305 stmt->methods = &oci_stmt_methods;
306 if (nsql) {
307 efree(nsql);
308 nsql = NULL;
309 }
310
311 return 1;
312 }
313 /* }}} */
314
oci_handle_doer(pdo_dbh_t * dbh,const char * sql,size_t sql_len)315 static zend_long oci_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_len) /* {{{ */
316 {
317 pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data;
318 OCIStmt *stmt;
319 ub2 stmt_type;
320 ub4 rowcount;
321 int ret = -1;
322
323 OCIHandleAlloc(H->env, (dvoid*)&stmt, OCI_HTYPE_STMT, 0, NULL);
324
325 H->last_err = OCIStmtPrepare(stmt, H->err, (text*)sql, (ub4) sql_len, OCI_NTV_SYNTAX, OCI_DEFAULT);
326 if (H->last_err) {
327 H->last_err = oci_drv_error("OCIStmtPrepare");
328 OCIHandleFree(stmt, OCI_HTYPE_STMT);
329 return -1;
330 }
331
332 H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &stmt_type, 0, OCI_ATTR_STMT_TYPE, H->err);
333
334 if (stmt_type == OCI_STMT_SELECT) {
335 /* invalid usage; cancel it */
336 OCIHandleFree(stmt, OCI_HTYPE_STMT);
337 php_error_docref(NULL, E_WARNING, "issuing a SELECT query here is invalid");
338 return -1;
339 }
340
341 /* now we are good to go */
342 H->last_err = OCIStmtExecute(H->svc, stmt, H->err, 1, 0, NULL, NULL,
343 (dbh->auto_commit && !dbh->in_txn) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT);
344
345 if (H->last_err) {
346 H->last_err = oci_drv_error("OCIStmtExecute");
347 } else {
348 /* return the number of affected rows */
349 H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, H->err);
350 ret = rowcount;
351 }
352
353 OCIHandleFree(stmt, OCI_HTYPE_STMT);
354
355 return ret;
356 }
357 /* }}} */
358
oci_handle_quoter(pdo_dbh_t * dbh,const char * unquoted,size_t unquotedlen,char ** quoted,size_t * quotedlen,enum pdo_param_type paramtype)359 static int oci_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type paramtype ) /* {{{ */
360 {
361 int qcount = 0;
362 char const *cu, *l, *r;
363 char *c;
364
365 if (!unquotedlen) {
366 *quotedlen = 2;
367 *quoted = emalloc(*quotedlen+1);
368 strcpy(*quoted, "''");
369 return 1;
370 }
371
372 /* count single quotes */
373 for (cu = unquoted; (cu = strchr(cu,'\'')); qcount++, cu++)
374 ; /* empty loop */
375
376 *quotedlen = unquotedlen + qcount + 2;
377 *quoted = c = emalloc(*quotedlen+1);
378 *c++ = '\'';
379
380 /* foreach (chunk that ends in a quote) */
381 for (l = unquoted; (r = strchr(l,'\'')); l = r+1) {
382 strncpy(c, l, r-l+1);
383 c += (r-l+1);
384 *c++ = '\''; /* add second quote */
385 }
386
387 /* Copy remainder and add enclosing quote */
388 strncpy(c, l, *quotedlen-(c-*quoted)-1);
389 (*quoted)[*quotedlen-1] = '\'';
390 (*quoted)[*quotedlen] = '\0';
391
392 return 1;
393 }
394 /* }}} */
395
oci_handle_begin(pdo_dbh_t * dbh)396 static int oci_handle_begin(pdo_dbh_t *dbh) /* {{{ */
397 {
398 /* with Oracle, there is nothing special to be done */
399 return 1;
400 }
401 /* }}} */
402
oci_handle_commit(pdo_dbh_t * dbh)403 static int oci_handle_commit(pdo_dbh_t *dbh) /* {{{ */
404 {
405 pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data;
406
407 H->last_err = OCITransCommit(H->svc, H->err, 0);
408
409 if (H->last_err) {
410 H->last_err = oci_drv_error("OCITransCommit");
411 return 0;
412 }
413 return 1;
414 }
415 /* }}} */
416
oci_handle_rollback(pdo_dbh_t * dbh)417 static int oci_handle_rollback(pdo_dbh_t *dbh) /* {{{ */
418 {
419 pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data;
420
421 H->last_err = OCITransRollback(H->svc, H->err, 0);
422
423 if (H->last_err) {
424 H->last_err = oci_drv_error("OCITransRollback");
425 return 0;
426 }
427 return 1;
428 }
429 /* }}} */
430
oci_handle_set_attribute(pdo_dbh_t * dbh,zend_long attr,zval * val)431 static int oci_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val) /* {{{ */
432 {
433 zend_long lval = zval_get_long(val);
434 pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data;
435
436 switch (attr) {
437 case PDO_ATTR_AUTOCOMMIT:
438 {
439 if (dbh->in_txn) {
440 /* Assume they want to commit whatever is outstanding */
441 H->last_err = OCITransCommit(H->svc, H->err, 0);
442
443 if (H->last_err) {
444 H->last_err = oci_drv_error("OCITransCommit");
445 return 0;
446 }
447 dbh->in_txn = 0;
448 }
449
450 dbh->auto_commit = (unsigned int)lval? 1 : 0;
451 return 1;
452 }
453 case PDO_ATTR_PREFETCH:
454 {
455 H->prefetch = pdo_oci_sanitize_prefetch(lval);
456 return 1;
457 }
458 case PDO_OCI_ATTR_ACTION:
459 {
460 #if (OCI_MAJOR_VERSION >= 10)
461 zend_string *action = zval_try_get_string(val);
462 if (UNEXPECTED(!action)) {
463 return 0;
464 }
465
466 H->last_err = OCIAttrSet(H->session, OCI_HTYPE_SESSION,
467 (dvoid *) ZSTR_VAL(action), (ub4) ZSTR_LEN(action),
468 OCI_ATTR_ACTION, H->err);
469 if (H->last_err) {
470 oci_drv_error("OCIAttrSet: OCI_ATTR_ACTION");
471 return 0;
472 }
473 return 1;
474 #else
475 oci_drv_error("Unsupported attribute type");
476 return 0;
477 #endif
478 }
479 case PDO_OCI_ATTR_CLIENT_INFO:
480 {
481 #if (OCI_MAJOR_VERSION >= 10)
482 zend_string *client_info = zval_try_get_string(val);
483 if (UNEXPECTED(!client_info)) {
484 return 0;
485 }
486
487 H->last_err = OCIAttrSet(H->session, OCI_HTYPE_SESSION,
488 (dvoid *) ZSTR_VAL(client_info), (ub4) ZSTR_LEN(client_info),
489 OCI_ATTR_CLIENT_INFO, H->err);
490 if (H->last_err) {
491 oci_drv_error("OCIAttrSet: OCI_ATTR_CLIENT_INFO");
492 return 0;
493 }
494 return 1;
495 #else
496 oci_drv_error("Unsupported attribute type");
497 return 0;
498 #endif
499 }
500 case PDO_OCI_ATTR_CLIENT_IDENTIFIER:
501 {
502 #if (OCI_MAJOR_VERSION >= 10)
503 zend_string *identifier = zval_try_get_string(val);
504 if (UNEXPECTED(!identifier)) {
505 return 0;
506 }
507
508 H->last_err = OCIAttrSet(H->session, OCI_HTYPE_SESSION,
509 (dvoid *) ZSTR_VAL(identifier), (ub4) ZSTR_LEN(identifier),
510 OCI_ATTR_CLIENT_IDENTIFIER, H->err);
511 if (H->last_err) {
512 oci_drv_error("OCIAttrSet: OCI_ATTR_CLIENT_IDENTIFIER");
513 return 0;
514 }
515 return 1;
516 #else
517 oci_drv_error("Unsupported attribute type");
518 return 0;
519 #endif
520 }
521 case PDO_OCI_ATTR_MODULE:
522 {
523 #if (OCI_MAJOR_VERSION >= 10)
524 zend_string *module = zval_try_get_string(val);
525 if (UNEXPECTED(!module)) {
526 return 0;
527 }
528
529 H->last_err = OCIAttrSet(H->session, OCI_HTYPE_SESSION,
530 (dvoid *) ZSTR_VAL(module), (ub4) ZSTR_LEN(module),
531 OCI_ATTR_MODULE, H->err);
532 if (H->last_err) {
533 oci_drv_error("OCIAttrSet: OCI_ATTR_MODULE");
534 return 0;
535 }
536 return 1;
537 #else
538 oci_drv_error("Unsupported attribute type");
539 return 0;
540 #endif
541 }
542 case PDO_OCI_ATTR_CALL_TIMEOUT:
543 {
544 #if (OCI_MAJOR_VERSION >= 18)
545 ub4 timeout = (ub4) lval;
546
547 H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX,
548 (dvoid *) &timeout, (ub4) 0,
549 OCI_ATTR_CALL_TIMEOUT, H->err);
550 if (H->last_err) {
551 oci_drv_error("OCIAttrSet: OCI_ATTR_CALL_TIMEOUT");
552 return 0;
553 }
554 return 1;
555 #else
556 oci_drv_error("Unsupported attribute type");
557 return 0;
558 #endif
559 }
560 default:
561 return 0;
562 }
563
564 }
565 /* }}} */
566
oci_handle_get_attribute(pdo_dbh_t * dbh,zend_long attr,zval * return_value)567 static int oci_handle_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) /* {{{ */
568 {
569 pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data;
570
571 switch (attr) {
572 case PDO_ATTR_SERVER_VERSION:
573 case PDO_ATTR_SERVER_INFO:
574 {
575 text infostr[512];
576 char verstr[15];
577 ub4 vernum;
578
579 if (OCIServerRelease(H->svc, H->err, infostr, (ub4)sizeof(infostr), (ub1)OCI_HTYPE_SVCCTX, &vernum))
580 {
581 ZVAL_STRING(return_value, "<<Unknown>>");
582 } else {
583 if (attr == PDO_ATTR_SERVER_INFO) {
584 ZVAL_STRING(return_value, (char *)infostr);
585 } else {
586 slprintf(verstr, sizeof(verstr), "%d.%d.%d.%d.%d",
587 (int)((vernum>>24) & 0xFF), /* version number */
588 (int)((vernum>>20) & 0x0F), /* release number*/
589 (int)((vernum>>12) & 0xFF), /* update number */
590 (int)((vernum>>8) & 0x0F), /* port release number */
591 (int)((vernum>>0) & 0xFF)); /* port update number */
592
593 ZVAL_STRING(return_value, verstr);
594 }
595 }
596 return TRUE;
597 }
598
599 case PDO_ATTR_CLIENT_VERSION:
600 {
601 #if OCI_MAJOR_VERSION > 10 || (OCI_MAJOR_VERSION == 10 && OCI_MINOR_VERSION >= 2)
602 /* Run time client version */
603 sword major, minor, update, patch, port_update;
604 char verstr[15];
605
606 OCIClientVersion(&major, &minor, &update, &patch, &port_update);
607 slprintf(verstr, sizeof(verstr), "%d.%d.%d.%d.%d", major, minor, update, patch, port_update);
608 ZVAL_STRING(return_value, verstr);
609 #elif defined(PHP_PDO_OCI_CLIENT_VERSION)
610 /* Compile time client version */
611 ZVAL_STRING(return_value, PHP_PDO_OCI_CLIENT_VERSION);
612 #else
613 return FALSE;
614
615 #endif /* Check for OCIClientVersion() support */
616
617 return TRUE;
618 }
619
620 case PDO_ATTR_AUTOCOMMIT:
621 ZVAL_BOOL(return_value, dbh->auto_commit);
622 return TRUE;
623
624 case PDO_ATTR_PREFETCH:
625 ZVAL_LONG(return_value, H->prefetch);
626 return TRUE;
627 case PDO_OCI_ATTR_CALL_TIMEOUT:
628 {
629 #if (OCI_MAJOR_VERSION >= 18)
630 ub4 timeout;
631
632 H->last_err = OCIAttrGet(H->svc, OCI_HTYPE_SVCCTX,
633 (dvoid *) &timeout, NULL,
634 OCI_ATTR_CALL_TIMEOUT, H->err);
635 if (H->last_err) {
636 oci_drv_error("OCIAttrGet: OCI_ATTR_CALL_TIMEOUT");
637 return FALSE;
638 }
639
640 ZVAL_LONG(return_value, (zend_long) timeout);
641 return TRUE;
642 #else
643 oci_drv_error("Unsupported attribute type");
644 return FALSE;
645 #endif
646 }
647 default:
648 return FALSE;
649
650 }
651 return FALSE;
652
653 }
654 /* }}} */
655
pdo_oci_check_liveness(pdo_dbh_t * dbh)656 static int pdo_oci_check_liveness(pdo_dbh_t *dbh) /* {{{ */
657 {
658 pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data;
659 sb4 error_code = 0;
660 #if (!((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))))
661 char version[256];
662 #endif
663
664 /* TODO move attached check to PDO level */
665 if (H->attached == 0) {
666 return FAILURE;
667 }
668 /* TODO add persistent_timeout check at PDO level */
669
670
671 /* Use OCIPing instead of OCIServerVersion. If OCIPing returns ORA-1010 (invalid OCI operation)
672 * such as from Pre-10.1 servers, the error is still from the server and we would have
673 * successfully performed a roundtrip and validated the connection. Use OCIServerVersion for
674 * Pre-10.2 clients
675 */
676 #if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))) /* OCIPing available 10.2 onwards */
677 H->last_err = OCIPing (H->svc, H->err, OCI_DEFAULT);
678 #else
679 /* use good old OCIServerVersion() */
680 H->last_err = OCIServerVersion (H->svc, H->err, (text *)version, sizeof(version), OCI_HTYPE_SVCCTX);
681 #endif
682 if (H->last_err == OCI_SUCCESS) {
683 return SUCCESS;
684 }
685
686 OCIErrorGet (H->err, (ub4)1, NULL, &error_code, NULL, 0, OCI_HTYPE_ERROR);
687
688 if (error_code == 1010) {
689 return SUCCESS;
690 }
691 return FAILURE;
692 }
693 /* }}} */
694
695 static const struct pdo_dbh_methods oci_methods = {
696 oci_handle_closer,
697 oci_handle_preparer,
698 oci_handle_doer,
699 oci_handle_quoter,
700 oci_handle_begin,
701 oci_handle_commit,
702 oci_handle_rollback,
703 oci_handle_set_attribute,
704 NULL,
705 pdo_oci_fetch_error_func,
706 oci_handle_get_attribute,
707 pdo_oci_check_liveness, /* check_liveness */
708 NULL, /* get_driver_methods */
709 NULL,
710 NULL
711 };
712
pdo_oci_handle_factory(pdo_dbh_t * dbh,zval * driver_options)713 static int pdo_oci_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */
714 {
715 pdo_oci_db_handle *H;
716 int i, ret = 0;
717 struct pdo_data_src_parser vars[] = {
718 { "charset", NULL, 0 },
719 { "dbname", "", 0 },
720 { "user", NULL, 0 },
721 { "password", NULL, 0 }
722 };
723
724 php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, 4);
725
726 H = pecalloc(1, sizeof(*H), dbh->is_persistent);
727 dbh->driver_data = H;
728
729 dbh->skip_param_evt =
730 1 << PDO_PARAM_EVT_FETCH_PRE |
731 1 << PDO_PARAM_EVT_FETCH_POST |
732 1 << PDO_PARAM_EVT_NORMALIZE;
733
734 H->prefetch = PDO_OCI_PREFETCH_DEFAULT;
735
736 /* allocate an environment */
737 #ifdef HAVE_OCIENVNLSCREATE
738 if (vars[0].optval) {
739 H->charset = OCINlsCharSetNameToId(pdo_oci_Env, (const oratext *)vars[0].optval);
740 if (!H->charset) {
741 oci_init_error("OCINlsCharSetNameToId: unknown character set name");
742 goto cleanup;
743 } else {
744 if (OCIEnvNlsCreate(&H->env, PDO_OCI_INIT_MODE, 0, NULL, NULL, NULL, 0, NULL, H->charset, H->charset) != OCI_SUCCESS) {
745 oci_init_error("OCIEnvNlsCreate: Check the character set is valid and that PHP has access to Oracle libraries and NLS data");
746 goto cleanup;
747 }
748 }
749 }
750 #endif
751 if (H->env == NULL) {
752 /* use the global environment */
753 H->env = pdo_oci_Env;
754 }
755
756 /* something to hold errors */
757 OCIHandleAlloc(H->env, (dvoid **)&H->err, OCI_HTYPE_ERROR, 0, NULL);
758
759 /* handle for the server */
760 OCIHandleAlloc(H->env, (dvoid **)&H->server, OCI_HTYPE_SERVER, 0, NULL);
761
762 H->last_err = OCIServerAttach(H->server, H->err, (text*)vars[1].optval,
763 (sb4) strlen(vars[1].optval), OCI_DEFAULT);
764
765 if (H->last_err) {
766 oci_drv_error("pdo_oci_handle_factory");
767 goto cleanup;
768 }
769
770 H->attached = 1;
771
772 /* create a service context */
773 H->last_err = OCIHandleAlloc(H->env, (dvoid**)&H->svc, OCI_HTYPE_SVCCTX, 0, NULL);
774 if (H->last_err) {
775 oci_drv_error("OCIHandleAlloc: OCI_HTYPE_SVCCTX");
776 goto cleanup;
777 }
778
779 H->last_err = OCIHandleAlloc(H->env, (dvoid**)&H->session, OCI_HTYPE_SESSION, 0, NULL);
780 if (H->last_err) {
781 oci_drv_error("OCIHandleAlloc: OCI_HTYPE_SESSION");
782 goto cleanup;
783 }
784
785 /* set server handle into service handle */
786 H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->server, 0, OCI_ATTR_SERVER, H->err);
787 if (H->last_err) {
788 oci_drv_error("OCIAttrSet: OCI_ATTR_SERVER");
789 goto cleanup;
790 }
791
792 /* username */
793 if (!dbh->username && vars[2].optval) {
794 dbh->username = pestrdup(vars[2].optval, dbh->is_persistent);
795 }
796
797 if (dbh->username) {
798 H->last_err = OCIAttrSet(H->session, OCI_HTYPE_SESSION,
799 dbh->username, (ub4) strlen(dbh->username),
800 OCI_ATTR_USERNAME, H->err);
801 if (H->last_err) {
802 oci_drv_error("OCIAttrSet: OCI_ATTR_USERNAME");
803 goto cleanup;
804 }
805 }
806
807 /* password */
808 if (!dbh->password && vars[3].optval) {
809 dbh->password = pestrdup(vars[3].optval, dbh->is_persistent);
810 }
811
812 if (dbh->password) {
813 H->last_err = OCIAttrSet(H->session, OCI_HTYPE_SESSION,
814 dbh->password, (ub4) strlen(dbh->password),
815 OCI_ATTR_PASSWORD, H->err);
816 if (H->last_err) {
817 oci_drv_error("OCIAttrSet: OCI_ATTR_PASSWORD");
818 goto cleanup;
819 }
820 }
821
822 /* Now fire up the session */
823 H->last_err = OCISessionBegin(H->svc, H->err, H->session, OCI_CRED_RDBMS, OCI_DEFAULT);
824 if (H->last_err) {
825 oci_drv_error("OCISessionBegin");
826 goto cleanup;
827 }
828
829 /* set the server handle into service handle */
830 H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->session, 0, OCI_ATTR_SESSION, H->err);
831 if (H->last_err) {
832 oci_drv_error("OCIAttrSet: OCI_ATTR_SESSION");
833 goto cleanup;
834 }
835
836 /* Get max character width */
837 H->last_err = OCINlsNumericInfoGet(H->env, H->err, &H->max_char_width, OCI_NLS_CHARSET_MAXBYTESZ);
838 if (H->last_err) {
839 oci_drv_error("OCINlsNumericInfoGet: OCI_NLS_CHARSET_MAXBYTESZ");
840 goto cleanup;
841 }
842
843 dbh->methods = &oci_methods;
844 dbh->alloc_own_columns = 1;
845 dbh->native_case = PDO_CASE_UPPER;
846
847 ret = 1;
848
849 cleanup:
850 for (i = 0; i < sizeof(vars)/sizeof(vars[0]); i++) {
851 if (vars[i].freeme) {
852 efree(vars[i].optval);
853 }
854 }
855
856 if (!ret) {
857 oci_handle_closer(dbh);
858 }
859
860 return ret;
861 }
862 /* }}} */
863
864 const pdo_driver_t pdo_oci_driver = {
865 PDO_DRIVER_HEADER(oci),
866 pdo_oci_handle_factory
867 };
868
pdo_oci_sanitize_prefetch(long prefetch)869 static inline ub4 pdo_oci_sanitize_prefetch(long prefetch) /* {{{ */
870 {
871 if (prefetch < 0) {
872 prefetch = 0;
873 } else if (prefetch > UB4MAXVAL / PDO_OCI_PREFETCH_ROWSIZE) {
874 prefetch = PDO_OCI_PREFETCH_DEFAULT;
875 }
876 return ((ub4)prefetch);
877 }
878 /* }}} */
879