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