Редакция 2 | Содержимое файла | Сравнить с предыдущей | Последнее изменение | Открыть журнал | RSS
Редакция | Автор | № строки | Строка |
---|---|---|---|
2 | alex-w | 1 | <?php |
2 | |||
3 | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
||
4 | |||
5 | /** |
||
6 | * The PEAR DB driver for PHP's oci8 extension |
||
7 | * for interacting with Oracle databases |
||
8 | * |
||
9 | * PHP versions 4 and 5 |
||
10 | * |
||
11 | * LICENSE: This source file is subject to version 3.0 of the PHP license |
||
12 | * that is available through the world-wide-web at the following URI: |
||
13 | * http://www.php.net/license/3_0.txt. If you did not receive a copy of |
||
14 | * the PHP License and are unable to obtain it through the web, please |
||
15 | * send a note to license@php.net so we can mail you a copy immediately. |
||
16 | * |
||
17 | * @category Database |
||
18 | * @package DB |
||
19 | * @author James L. Pine <jlp@valinux.com> |
||
20 | * @author Daniel Convissor <danielc@php.net> |
||
21 | * @copyright 1997-2007 The PHP Group |
||
22 | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
||
23 | * @version CVS: $Id: oci8.php,v 1.115 2007/09/21 13:40:41 aharvey Exp $ |
||
24 | * @link http://pear.php.net/package/DB |
||
25 | */ |
||
26 | |||
27 | /** |
||
28 | * Obtain the DB_common class so it can be extended from |
||
29 | */ |
||
30 | require_once 'DB/common.php'; |
||
31 | |||
32 | /** |
||
33 | * The methods PEAR DB uses to interact with PHP's oci8 extension |
||
34 | * for interacting with Oracle databases |
||
35 | * |
||
36 | * Definitely works with versions 8 and 9 of Oracle. |
||
37 | * |
||
38 | * These methods overload the ones declared in DB_common. |
||
39 | * |
||
40 | * Be aware... OCIError() only appears to return anything when given a |
||
41 | * statement, so functions return the generic DB_ERROR instead of more |
||
42 | * useful errors that have to do with feedback from the database. |
||
43 | * |
||
44 | * @category Database |
||
45 | * @package DB |
||
46 | * @author James L. Pine <jlp@valinux.com> |
||
47 | * @author Daniel Convissor <danielc@php.net> |
||
48 | * @copyright 1997-2007 The PHP Group |
||
49 | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
||
50 | * @version Release: 1.7.13 |
||
51 | * @link http://pear.php.net/package/DB |
||
52 | */ |
||
53 | class DB_oci8 extends DB_common |
||
54 | { |
||
55 | // {{{ properties |
||
56 | |||
57 | /** |
||
58 | * The DB driver type (mysql, oci8, odbc, etc.) |
||
59 | * @var string |
||
60 | */ |
||
61 | var $phptype = 'oci8'; |
||
62 | |||
63 | /** |
||
64 | * The database syntax variant to be used (db2, access, etc.), if any |
||
65 | * @var string |
||
66 | */ |
||
67 | var $dbsyntax = 'oci8'; |
||
68 | |||
69 | /** |
||
70 | * The capabilities of this DB implementation |
||
71 | * |
||
72 | * The 'new_link' element contains the PHP version that first provided |
||
73 | * new_link support for this DBMS. Contains false if it's unsupported. |
||
74 | * |
||
75 | * Meaning of the 'limit' element: |
||
76 | * + 'emulate' = emulate with fetch row by number |
||
77 | * + 'alter' = alter the query |
||
78 | * + false = skip rows |
||
79 | * |
||
80 | * @var array |
||
81 | */ |
||
82 | var $features = array( |
||
83 | 'limit' => 'alter', |
||
84 | 'new_link' => '5.0.0', |
||
85 | 'numrows' => 'subquery', |
||
86 | 'pconnect' => true, |
||
87 | 'prepare' => true, |
||
88 | 'ssl' => false, |
||
89 | 'transactions' => true, |
||
90 | ); |
||
91 | |||
92 | /** |
||
93 | * A mapping of native error codes to DB error codes |
||
94 | * @var array |
||
95 | */ |
||
96 | var $errorcode_map = array( |
||
97 | 1 => DB_ERROR_CONSTRAINT, |
||
98 | 900 => DB_ERROR_SYNTAX, |
||
99 | 904 => DB_ERROR_NOSUCHFIELD, |
||
100 | 913 => DB_ERROR_VALUE_COUNT_ON_ROW, |
||
101 | 921 => DB_ERROR_SYNTAX, |
||
102 | 923 => DB_ERROR_SYNTAX, |
||
103 | 942 => DB_ERROR_NOSUCHTABLE, |
||
104 | 955 => DB_ERROR_ALREADY_EXISTS, |
||
105 | 1400 => DB_ERROR_CONSTRAINT_NOT_NULL, |
||
106 | 1401 => DB_ERROR_INVALID, |
||
107 | 1407 => DB_ERROR_CONSTRAINT_NOT_NULL, |
||
108 | 1418 => DB_ERROR_NOT_FOUND, |
||
109 | 1476 => DB_ERROR_DIVZERO, |
||
110 | 1722 => DB_ERROR_INVALID_NUMBER, |
||
111 | 2289 => DB_ERROR_NOSUCHTABLE, |
||
112 | 2291 => DB_ERROR_CONSTRAINT, |
||
113 | 2292 => DB_ERROR_CONSTRAINT, |
||
114 | 2449 => DB_ERROR_CONSTRAINT, |
||
115 | 12899 => DB_ERROR_INVALID, |
||
116 | ); |
||
117 | |||
118 | /** |
||
119 | * The raw database connection created by PHP |
||
120 | * @var resource |
||
121 | */ |
||
122 | var $connection; |
||
123 | |||
124 | /** |
||
125 | * The DSN information for connecting to a database |
||
126 | * @var array |
||
127 | */ |
||
128 | var $dsn = array(); |
||
129 | |||
130 | |||
131 | /** |
||
132 | * Should data manipulation queries be committed automatically? |
||
133 | * @var bool |
||
134 | * @access private |
||
135 | */ |
||
136 | var $autocommit = true; |
||
137 | |||
138 | /** |
||
139 | * Stores the $data passed to execute() in the oci8 driver |
||
140 | * |
||
141 | * Gets reset to array() when simpleQuery() is run. |
||
142 | * |
||
143 | * Needed in case user wants to call numRows() after prepare/execute |
||
144 | * was used. |
||
145 | * |
||
146 | * @var array |
||
147 | * @access private |
||
148 | */ |
||
149 | var $_data = array(); |
||
150 | |||
151 | /** |
||
152 | * The result or statement handle from the most recently executed query |
||
153 | * @var resource |
||
154 | */ |
||
155 | var $last_stmt; |
||
156 | |||
157 | /** |
||
158 | * Is the given prepared statement a data manipulation query? |
||
159 | * @var array |
||
160 | * @access private |
||
161 | */ |
||
162 | var $manip_query = array(); |
||
163 | |||
164 | /** |
||
165 | * Store of prepared SQL queries. |
||
166 | * @var array |
||
167 | * @access private |
||
168 | */ |
||
169 | var $_prepared_queries = array(); |
||
170 | |||
171 | |||
172 | // }}} |
||
173 | // {{{ constructor |
||
174 | |||
175 | /** |
||
176 | * This constructor calls <kbd>$this->DB_common()</kbd> |
||
177 | * |
||
178 | * @return void |
||
179 | */ |
||
180 | function DB_oci8() |
||
181 | { |
||
182 | $this->DB_common(); |
||
183 | } |
||
184 | |||
185 | // }}} |
||
186 | // {{{ connect() |
||
187 | |||
188 | /** |
||
189 | * Connect to the database server, log in and open the database |
||
190 | * |
||
191 | * Don't call this method directly. Use DB::connect() instead. |
||
192 | * |
||
193 | * If PHP is at version 5.0.0 or greater: |
||
194 | * + Generally, oci_connect() or oci_pconnect() are used. |
||
195 | * + But if the new_link DSN option is set to true, oci_new_connect() |
||
196 | * is used. |
||
197 | * |
||
198 | * When using PHP version 4.x, OCILogon() or OCIPLogon() are used. |
||
199 | * |
||
200 | * PEAR DB's oci8 driver supports the following extra DSN options: |
||
201 | * + charset The character set to be used on the connection. |
||
202 | * Only used if PHP is at version 5.0.0 or greater |
||
203 | * and the Oracle server is at 9.2 or greater. |
||
204 | * Available since PEAR DB 1.7.0. |
||
205 | * + new_link If set to true, causes subsequent calls to |
||
206 | * connect() to return a new connection link |
||
207 | * instead of the existing one. WARNING: this is |
||
208 | * not portable to other DBMS's. |
||
209 | * Available since PEAR DB 1.7.0. |
||
210 | * |
||
211 | * @param array $dsn the data source name |
||
212 | * @param bool $persistent should the connection be persistent? |
||
213 | * |
||
214 | * @return int DB_OK on success. A DB_Error object on failure. |
||
215 | */ |
||
216 | function connect($dsn, $persistent = false) |
||
217 | { |
||
218 | if (!PEAR::loadExtension('oci8')) { |
||
219 | return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
||
220 | } |
||
221 | |||
222 | $this->dsn = $dsn; |
||
223 | if ($dsn['dbsyntax']) { |
||
224 | $this->dbsyntax = $dsn['dbsyntax']; |
||
225 | } |
||
226 | |||
227 | // Backwards compatibility with DB < 1.7.0 |
||
228 | if (empty($dsn['database']) && !empty($dsn['hostspec'])) { |
||
229 | $db = $dsn['hostspec']; |
||
230 | } else { |
||
231 | $db = $dsn['database']; |
||
232 | } |
||
233 | |||
234 | if (function_exists('oci_connect')) { |
||
235 | if (isset($dsn['new_link']) |
||
236 | && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true)) |
||
237 | { |
||
238 | $connect_function = 'oci_new_connect'; |
||
239 | } else { |
||
240 | $connect_function = $persistent ? 'oci_pconnect' |
||
241 | : 'oci_connect'; |
||
242 | } |
||
243 | if (isset($this->dsn['port']) && $this->dsn['port']) { |
||
244 | $db = '//'.$db.':'.$this->dsn['port']; |
||
245 | } |
||
246 | |||
247 | $char = empty($dsn['charset']) ? null : $dsn['charset']; |
||
248 | $this->connection = @$connect_function($dsn['username'], |
||
249 | $dsn['password'], |
||
250 | $db, |
||
251 | $char); |
||
252 | $error = OCIError(); |
||
253 | if (!empty($error) && $error['code'] == 12541) { |
||
254 | // Couldn't find TNS listener. Try direct connection. |
||
255 | $this->connection = @$connect_function($dsn['username'], |
||
256 | $dsn['password'], |
||
257 | null, |
||
258 | $char); |
||
259 | } |
||
260 | } else { |
||
261 | $connect_function = $persistent ? 'OCIPLogon' : 'OCILogon'; |
||
262 | if ($db) { |
||
263 | $this->connection = @$connect_function($dsn['username'], |
||
264 | $dsn['password'], |
||
265 | $db); |
||
266 | } elseif ($dsn['username'] || $dsn['password']) { |
||
267 | $this->connection = @$connect_function($dsn['username'], |
||
268 | $dsn['password']); |
||
269 | } |
||
270 | } |
||
271 | |||
272 | if (!$this->connection) { |
||
273 | $error = OCIError(); |
||
274 | $error = (is_array($error)) ? $error['message'] : null; |
||
275 | return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
||
276 | null, null, null, |
||
277 | $error); |
||
278 | } |
||
279 | return DB_OK; |
||
280 | } |
||
281 | |||
282 | // }}} |
||
283 | // {{{ disconnect() |
||
284 | |||
285 | /** |
||
286 | * Disconnects from the database server |
||
287 | * |
||
288 | * @return bool TRUE on success, FALSE on failure |
||
289 | */ |
||
290 | function disconnect() |
||
291 | { |
||
292 | if (function_exists('oci_close')) { |
||
293 | $ret = @oci_close($this->connection); |
||
294 | } else { |
||
295 | $ret = @OCILogOff($this->connection); |
||
296 | } |
||
297 | $this->connection = null; |
||
298 | return $ret; |
||
299 | } |
||
300 | |||
301 | // }}} |
||
302 | // {{{ simpleQuery() |
||
303 | |||
304 | /** |
||
305 | * Sends a query to the database server |
||
306 | * |
||
307 | * To determine how many rows of a result set get buffered using |
||
308 | * ocisetprefetch(), see the "result_buffering" option in setOptions(). |
||
309 | * This option was added in Release 1.7.0. |
||
310 | * |
||
311 | * @param string the SQL query string |
||
312 | * |
||
313 | * @return mixed + a PHP result resrouce for successful SELECT queries |
||
314 | * + the DB_OK constant for other successful queries |
||
315 | * + a DB_Error object on failure |
||
316 | */ |
||
317 | function simpleQuery($query) |
||
318 | { |
||
319 | $this->_data = array(); |
||
320 | $this->last_parameters = array(); |
||
321 | $this->last_query = $query; |
||
322 | $query = $this->modifyQuery($query); |
||
323 | $result = @OCIParse($this->connection, $query); |
||
324 | if (!$result) { |
||
325 | return $this->oci8RaiseError(); |
||
326 | } |
||
327 | if ($this->autocommit) { |
||
328 | $success = @OCIExecute($result,OCI_COMMIT_ON_SUCCESS); |
||
329 | } else { |
||
330 | $success = @OCIExecute($result,OCI_DEFAULT); |
||
331 | } |
||
332 | if (!$success) { |
||
333 | return $this->oci8RaiseError($result); |
||
334 | } |
||
335 | $this->last_stmt = $result; |
||
336 | if ($this->_checkManip($query)) { |
||
337 | return DB_OK; |
||
338 | } else { |
||
339 | @ocisetprefetch($result, $this->options['result_buffering']); |
||
340 | return $result; |
||
341 | } |
||
342 | } |
||
343 | |||
344 | // }}} |
||
345 | // {{{ nextResult() |
||
346 | |||
347 | /** |
||
348 | * Move the internal oracle result pointer to the next available result |
||
349 | * |
||
350 | * @param a valid oci8 result resource |
||
351 | * |
||
352 | * @access public |
||
353 | * |
||
354 | * @return true if a result is available otherwise return false |
||
355 | */ |
||
356 | function nextResult($result) |
||
357 | { |
||
358 | return false; |
||
359 | } |
||
360 | |||
361 | // }}} |
||
362 | // {{{ fetchInto() |
||
363 | |||
364 | /** |
||
365 | * Places a row from the result set into the given array |
||
366 | * |
||
367 | * Formating of the array and the data therein are configurable. |
||
368 | * See DB_result::fetchInto() for more information. |
||
369 | * |
||
370 | * This method is not meant to be called directly. Use |
||
371 | * DB_result::fetchInto() instead. It can't be declared "protected" |
||
372 | * because DB_result is a separate object. |
||
373 | * |
||
374 | * @param resource $result the query result resource |
||
375 | * @param array $arr the referenced array to put the data in |
||
376 | * @param int $fetchmode how the resulting array should be indexed |
||
377 | * @param int $rownum the row number to fetch (0 = first row) |
||
378 | * |
||
379 | * @return mixed DB_OK on success, NULL when the end of a result set is |
||
380 | * reached or on failure |
||
381 | * |
||
382 | * @see DB_result::fetchInto() |
||
383 | */ |
||
384 | function fetchInto($result, &$arr, $fetchmode, $rownum = null) |
||
385 | { |
||
386 | if ($rownum !== null) { |
||
387 | return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
||
388 | } |
||
389 | if ($fetchmode & DB_FETCHMODE_ASSOC) { |
||
390 | $moredata = @OCIFetchInto($result,$arr,OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS); |
||
391 | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && |
||
392 | $moredata) |
||
393 | { |
||
394 | $arr = array_change_key_case($arr, CASE_LOWER); |
||
395 | } |
||
396 | } else { |
||
397 | $moredata = OCIFetchInto($result,$arr,OCI_RETURN_NULLS+OCI_RETURN_LOBS); |
||
398 | } |
||
399 | if (!$moredata) { |
||
400 | return null; |
||
401 | } |
||
402 | if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { |
||
403 | $this->_rtrimArrayValues($arr); |
||
404 | } |
||
405 | if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { |
||
406 | $this->_convertNullArrayValuesToEmpty($arr); |
||
407 | } |
||
408 | return DB_OK; |
||
409 | } |
||
410 | |||
411 | // }}} |
||
412 | // {{{ freeResult() |
||
413 | |||
414 | /** |
||
415 | * Deletes the result set and frees the memory occupied by the result set |
||
416 | * |
||
417 | * This method is not meant to be called directly. Use |
||
418 | * DB_result::free() instead. It can't be declared "protected" |
||
419 | * because DB_result is a separate object. |
||
420 | * |
||
421 | * @param resource $result PHP's query result resource |
||
422 | * |
||
423 | * @return bool TRUE on success, FALSE if $result is invalid |
||
424 | * |
||
425 | * @see DB_result::free() |
||
426 | */ |
||
427 | function freeResult($result) |
||
428 | { |
||
429 | return is_resource($result) ? OCIFreeStatement($result) : false; |
||
430 | } |
||
431 | |||
432 | /** |
||
433 | * Frees the internal resources associated with a prepared query |
||
434 | * |
||
435 | * @param resource $stmt the prepared statement's resource |
||
436 | * @param bool $free_resource should the PHP resource be freed too? |
||
437 | * Use false if you need to get data |
||
438 | * from the result set later. |
||
439 | * |
||
440 | * @return bool TRUE on success, FALSE if $result is invalid |
||
441 | * |
||
442 | * @see DB_oci8::prepare() |
||
443 | */ |
||
444 | function freePrepared($stmt, $free_resource = true) |
||
445 | { |
||
446 | if (!is_resource($stmt)) { |
||
447 | return false; |
||
448 | } |
||
449 | if ($free_resource) { |
||
450 | @ocifreestatement($stmt); |
||
451 | } |
||
452 | if (isset($this->prepare_types[(int)$stmt])) { |
||
453 | unset($this->prepare_types[(int)$stmt]); |
||
454 | unset($this->manip_query[(int)$stmt]); |
||
455 | } else { |
||
456 | return false; |
||
457 | } |
||
458 | return true; |
||
459 | } |
||
460 | |||
461 | // }}} |
||
462 | // {{{ numRows() |
||
463 | |||
464 | /** |
||
465 | * Gets the number of rows in a result set |
||
466 | * |
||
467 | * Only works if the DB_PORTABILITY_NUMROWS portability option |
||
468 | * is turned on. |
||
469 | * |
||
470 | * This method is not meant to be called directly. Use |
||
471 | * DB_result::numRows() instead. It can't be declared "protected" |
||
472 | * because DB_result is a separate object. |
||
473 | * |
||
474 | * @param resource $result PHP's query result resource |
||
475 | * |
||
476 | * @return int the number of rows. A DB_Error object on failure. |
||
477 | * |
||
478 | * @see DB_result::numRows(), DB_common::setOption() |
||
479 | */ |
||
480 | function numRows($result) |
||
481 | { |
||
482 | // emulate numRows for Oracle. yuck. |
||
483 | if ($this->options['portability'] & DB_PORTABILITY_NUMROWS && |
||
484 | $result === $this->last_stmt) |
||
485 | { |
||
486 | $countquery = 'SELECT COUNT(*) FROM ('.$this->last_query.')'; |
||
487 | $save_query = $this->last_query; |
||
488 | $save_stmt = $this->last_stmt; |
||
489 | |||
490 | $count = $this->query($countquery); |
||
491 | |||
492 | // Restore the last query and statement. |
||
493 | $this->last_query = $save_query; |
||
494 | $this->last_stmt = $save_stmt; |
||
495 | |||
496 | if (DB::isError($count) || |
||
497 | DB::isError($row = $count->fetchRow(DB_FETCHMODE_ORDERED))) |
||
498 | { |
||
499 | return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
||
500 | } |
||
501 | |||
502 | return $row[0]; |
||
503 | } |
||
504 | return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
||
505 | } |
||
506 | |||
507 | // }}} |
||
508 | // {{{ numCols() |
||
509 | |||
510 | /** |
||
511 | * Gets the number of columns in a result set |
||
512 | * |
||
513 | * This method is not meant to be called directly. Use |
||
514 | * DB_result::numCols() instead. It can't be declared "protected" |
||
515 | * because DB_result is a separate object. |
||
516 | * |
||
517 | * @param resource $result PHP's query result resource |
||
518 | * |
||
519 | * @return int the number of columns. A DB_Error object on failure. |
||
520 | * |
||
521 | * @see DB_result::numCols() |
||
522 | */ |
||
523 | function numCols($result) |
||
524 | { |
||
525 | $cols = @OCINumCols($result); |
||
526 | if (!$cols) { |
||
527 | return $this->oci8RaiseError($result); |
||
528 | } |
||
529 | return $cols; |
||
530 | } |
||
531 | |||
532 | // }}} |
||
533 | // {{{ prepare() |
||
534 | |||
535 | /** |
||
536 | * Prepares a query for multiple execution with execute(). |
||
537 | * |
||
538 | * With oci8, this is emulated. |
||
539 | * |
||
540 | * prepare() requires a generic query as string like <code> |
||
541 | * INSERT INTO numbers VALUES (?, ?, ?) |
||
542 | * </code>. The <kbd>?</kbd> characters are placeholders. |
||
543 | * |
||
544 | * Three types of placeholders can be used: |
||
545 | * + <kbd>?</kbd> a quoted scalar value, i.e. strings, integers |
||
546 | * + <kbd>!</kbd> value is inserted 'as is' |
||
547 | * + <kbd>&</kbd> requires a file name. The file's contents get |
||
548 | * inserted into the query (i.e. saving binary |
||
549 | * data in a db) |
||
550 | * |
||
551 | * Use backslashes to escape placeholder characters if you don't want |
||
552 | * them to be interpreted as placeholders. Example: <code> |
||
553 | * "UPDATE foo SET col=? WHERE col='over \& under'" |
||
554 | * </code> |
||
555 | * |
||
556 | * @param string $query the query to be prepared |
||
557 | * |
||
558 | * @return mixed DB statement resource on success. DB_Error on failure. |
||
559 | * |
||
560 | * @see DB_oci8::execute() |
||
561 | */ |
||
562 | function prepare($query) |
||
563 | { |
||
564 | $tokens = preg_split('/((?<!\\\)[&?!])/', $query, -1, |
||
565 | PREG_SPLIT_DELIM_CAPTURE); |
||
566 | $binds = count($tokens) - 1; |
||
567 | $token = 0; |
||
568 | $types = array(); |
||
569 | $newquery = ''; |
||
570 | |||
571 | foreach ($tokens as $key => $val) { |
||
572 | switch ($val) { |
||
573 | case '?': |
||
574 | $types[$token++] = DB_PARAM_SCALAR; |
||
575 | unset($tokens[$key]); |
||
576 | break; |
||
577 | case '&': |
||
578 | $types[$token++] = DB_PARAM_OPAQUE; |
||
579 | unset($tokens[$key]); |
||
580 | break; |
||
581 | case '!': |
||
582 | $types[$token++] = DB_PARAM_MISC; |
||
583 | unset($tokens[$key]); |
||
584 | break; |
||
585 | default: |
||
586 | $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val); |
||
587 | if ($key != $binds) { |
||
588 | $newquery .= $tokens[$key] . ':bind' . $token; |
||
589 | } else { |
||
590 | $newquery .= $tokens[$key]; |
||
591 | } |
||
592 | } |
||
593 | } |
||
594 | |||
595 | $this->last_query = $query; |
||
596 | $newquery = $this->modifyQuery($newquery); |
||
597 | if (!$stmt = @OCIParse($this->connection, $newquery)) { |
||
598 | return $this->oci8RaiseError(); |
||
599 | } |
||
600 | $this->prepare_types[(int)$stmt] = $types; |
||
601 | $this->manip_query[(int)$stmt] = DB::isManip($query); |
||
602 | $this->_prepared_queries[(int)$stmt] = $newquery; |
||
603 | return $stmt; |
||
604 | } |
||
605 | |||
606 | // }}} |
||
607 | // {{{ execute() |
||
608 | |||
609 | /** |
||
610 | * Executes a DB statement prepared with prepare(). |
||
611 | * |
||
612 | * To determine how many rows of a result set get buffered using |
||
613 | * ocisetprefetch(), see the "result_buffering" option in setOptions(). |
||
614 | * This option was added in Release 1.7.0. |
||
615 | * |
||
616 | * @param resource $stmt a DB statement resource returned from prepare() |
||
617 | * @param mixed $data array, string or numeric data to be used in |
||
618 | * execution of the statement. Quantity of items |
||
619 | * passed must match quantity of placeholders in |
||
620 | * query: meaning 1 for non-array items or the |
||
621 | * quantity of elements in the array. |
||
622 | * |
||
623 | * @return mixed returns an oic8 result resource for successful SELECT |
||
624 | * queries, DB_OK for other successful queries. |
||
625 | * A DB error object is returned on failure. |
||
626 | * |
||
627 | * @see DB_oci8::prepare() |
||
628 | */ |
||
629 | function &execute($stmt, $data = array()) |
||
630 | { |
||
631 | $data = (array)$data; |
||
632 | $this->last_parameters = $data; |
||
633 | $this->last_query = $this->_prepared_queries[(int)$stmt]; |
||
634 | $this->_data = $data; |
||
635 | |||
636 | $types = $this->prepare_types[(int)$stmt]; |
||
637 | if (count($types) != count($data)) { |
||
638 | $tmp = $this->raiseError(DB_ERROR_MISMATCH); |
||
639 | return $tmp; |
||
640 | } |
||
641 | |||
642 | $i = 0; |
||
643 | foreach ($data as $key => $value) { |
||
644 | if ($types[$i] == DB_PARAM_MISC) { |
||
645 | /* |
||
646 | * Oracle doesn't seem to have the ability to pass a |
||
647 | * parameter along unchanged, so strip off quotes from start |
||
648 | * and end, plus turn two single quotes to one single quote, |
||
649 | * in order to avoid the quotes getting escaped by |
||
650 | * Oracle and ending up in the database. |
||
651 | */ |
||
652 | $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]); |
||
653 | $data[$key] = str_replace("''", "'", $data[$key]); |
||
654 | } elseif ($types[$i] == DB_PARAM_OPAQUE) { |
||
655 | $fp = @fopen($data[$key], 'rb'); |
||
656 | if (!$fp) { |
||
657 | $tmp = $this->raiseError(DB_ERROR_ACCESS_VIOLATION); |
||
658 | return $tmp; |
||
659 | } |
||
660 | $data[$key] = fread($fp, filesize($data[$key])); |
||
661 | fclose($fp); |
||
662 | } elseif ($types[$i] == DB_PARAM_SCALAR) { |
||
663 | // Floats have to be converted to a locale-neutral |
||
664 | // representation. |
||
665 | if (is_float($data[$key])) { |
||
666 | $data[$key] = $this->quoteFloat($data[$key]); |
||
667 | } |
||
668 | } |
||
669 | if (!@OCIBindByName($stmt, ':bind' . $i, $data[$key], -1)) { |
||
670 | $tmp = $this->oci8RaiseError($stmt); |
||
671 | return $tmp; |
||
672 | } |
||
673 | $this->last_query = str_replace(':bind'.$i, $this->quoteSmart($data[$key]), $this->last_query); |
||
674 | $i++; |
||
675 | } |
||
676 | if ($this->autocommit) { |
||
677 | $success = @OCIExecute($stmt, OCI_COMMIT_ON_SUCCESS); |
||
678 | } else { |
||
679 | $success = @OCIExecute($stmt, OCI_DEFAULT); |
||
680 | } |
||
681 | if (!$success) { |
||
682 | $tmp = $this->oci8RaiseError($stmt); |
||
683 | return $tmp; |
||
684 | } |
||
685 | $this->last_stmt = $stmt; |
||
686 | if ($this->manip_query[(int)$stmt] || $this->_next_query_manip) { |
||
687 | $this->_last_query_manip = true; |
||
688 | $this->_next_query_manip = false; |
||
689 | $tmp = DB_OK; |
||
690 | } else { |
||
691 | $this->_last_query_manip = false; |
||
692 | @ocisetprefetch($stmt, $this->options['result_buffering']); |
||
693 | $tmp = new DB_result($this, $stmt); |
||
694 | } |
||
695 | return $tmp; |
||
696 | } |
||
697 | |||
698 | // }}} |
||
699 | // {{{ autoCommit() |
||
700 | |||
701 | /** |
||
702 | * Enables or disables automatic commits |
||
703 | * |
||
704 | * @param bool $onoff true turns it on, false turns it off |
||
705 | * |
||
706 | * @return int DB_OK on success. A DB_Error object if the driver |
||
707 | * doesn't support auto-committing transactions. |
||
708 | */ |
||
709 | function autoCommit($onoff = false) |
||
710 | { |
||
711 | $this->autocommit = (bool)$onoff;; |
||
712 | return DB_OK; |
||
713 | } |
||
714 | |||
715 | // }}} |
||
716 | // {{{ commit() |
||
717 | |||
718 | /** |
||
719 | * Commits the current transaction |
||
720 | * |
||
721 | * @return int DB_OK on success. A DB_Error object on failure. |
||
722 | */ |
||
723 | function commit() |
||
724 | { |
||
725 | $result = @OCICommit($this->connection); |
||
726 | if (!$result) { |
||
727 | return $this->oci8RaiseError(); |
||
728 | } |
||
729 | return DB_OK; |
||
730 | } |
||
731 | |||
732 | // }}} |
||
733 | // {{{ rollback() |
||
734 | |||
735 | /** |
||
736 | * Reverts the current transaction |
||
737 | * |
||
738 | * @return int DB_OK on success. A DB_Error object on failure. |
||
739 | */ |
||
740 | function rollback() |
||
741 | { |
||
742 | $result = @OCIRollback($this->connection); |
||
743 | if (!$result) { |
||
744 | return $this->oci8RaiseError(); |
||
745 | } |
||
746 | return DB_OK; |
||
747 | } |
||
748 | |||
749 | // }}} |
||
750 | // {{{ affectedRows() |
||
751 | |||
752 | /** |
||
753 | * Determines the number of rows affected by a data maniuplation query |
||
754 | * |
||
755 | * 0 is returned for queries that don't manipulate data. |
||
756 | * |
||
757 | * @return int the number of rows. A DB_Error object on failure. |
||
758 | */ |
||
759 | function affectedRows() |
||
760 | { |
||
761 | if ($this->last_stmt === false) { |
||
762 | return $this->oci8RaiseError(); |
||
763 | } |
||
764 | $result = @OCIRowCount($this->last_stmt); |
||
765 | if ($result === false) { |
||
766 | return $this->oci8RaiseError($this->last_stmt); |
||
767 | } |
||
768 | return $result; |
||
769 | } |
||
770 | |||
771 | // }}} |
||
772 | // {{{ modifyQuery() |
||
773 | |||
774 | /** |
||
775 | * Changes a query string for various DBMS specific reasons |
||
776 | * |
||
777 | * "SELECT 2+2" must be "SELECT 2+2 FROM dual" in Oracle. |
||
778 | * |
||
779 | * @param string $query the query string to modify |
||
780 | * |
||
781 | * @return string the modified query string |
||
782 | * |
||
783 | * @access protected |
||
784 | */ |
||
785 | function modifyQuery($query) |
||
786 | { |
||
787 | if (preg_match('/^\s*SELECT/i', $query) && |
||
788 | !preg_match('/\sFROM\s/i', $query)) { |
||
789 | $query .= ' FROM dual'; |
||
790 | } |
||
791 | return $query; |
||
792 | } |
||
793 | |||
794 | // }}} |
||
795 | // {{{ modifyLimitQuery() |
||
796 | |||
797 | /** |
||
798 | * Adds LIMIT clauses to a query string according to current DBMS standards |
||
799 | * |
||
800 | * @param string $query the query to modify |
||
801 | * @param int $from the row to start to fetching (0 = the first row) |
||
802 | * @param int $count the numbers of rows to fetch |
||
803 | * @param mixed $params array, string or numeric data to be used in |
||
804 | * execution of the statement. Quantity of items |
||
805 | * passed must match quantity of placeholders in |
||
806 | * query: meaning 1 placeholder for non-array |
||
807 | * parameters or 1 placeholder per array element. |
||
808 | * |
||
809 | * @return string the query string with LIMIT clauses added |
||
810 | * |
||
811 | * @access protected |
||
812 | */ |
||
813 | function modifyLimitQuery($query, $from, $count, $params = array()) |
||
814 | { |
||
815 | // Let Oracle return the name of the columns instead of |
||
816 | // coding a "home" SQL parser |
||
817 | |||
818 | if (count($params)) { |
||
819 | $result = $this->prepare("SELECT * FROM ($query) " |
||
820 | . 'WHERE NULL = NULL'); |
||
821 | $tmp = $this->execute($result, $params); |
||
822 | } else { |
||
823 | $q_fields = "SELECT * FROM ($query) WHERE NULL = NULL"; |
||
824 | |||
825 | if (!$result = @OCIParse($this->connection, $q_fields)) { |
||
826 | $this->last_query = $q_fields; |
||
827 | return $this->oci8RaiseError(); |
||
828 | } |
||
829 | if (!@OCIExecute($result, OCI_DEFAULT)) { |
||
830 | $this->last_query = $q_fields; |
||
831 | return $this->oci8RaiseError($result); |
||
832 | } |
||
833 | } |
||
834 | |||
835 | $ncols = OCINumCols($result); |
||
836 | $cols = array(); |
||
837 | for ( $i = 1; $i <= $ncols; $i++ ) { |
||
838 | $cols[] = '"' . OCIColumnName($result, $i) . '"'; |
||
839 | } |
||
840 | $fields = implode(', ', $cols); |
||
841 | // XXX Test that (tip by John Lim) |
||
842 | //if (preg_match('/^\s*SELECT\s+/is', $query, $match)) { |
||
843 | // // Introduce the FIRST_ROWS Oracle query optimizer |
||
844 | // $query = substr($query, strlen($match[0]), strlen($query)); |
||
845 | // $query = "SELECT /* +FIRST_ROWS */ " . $query; |
||
846 | //} |
||
847 | |||
848 | // Construct the query |
||
849 | // more at: http://marc.theaimsgroup.com/?l=php-db&m=99831958101212&w=2 |
||
850 | // Perhaps this could be optimized with the use of Unions |
||
851 | $query = "SELECT $fields FROM". |
||
852 | " (SELECT rownum as linenum, $fields FROM". |
||
853 | " ($query)". |
||
854 | ' WHERE rownum <= '. ($from + $count) . |
||
855 | ') WHERE linenum >= ' . ++$from; |
||
856 | return $query; |
||
857 | } |
||
858 | |||
859 | // }}} |
||
860 | // {{{ nextId() |
||
861 | |||
862 | /** |
||
863 | * Returns the next free id in a sequence |
||
864 | * |
||
865 | * @param string $seq_name name of the sequence |
||
866 | * @param boolean $ondemand when true, the seqence is automatically |
||
867 | * created if it does not exist |
||
868 | * |
||
869 | * @return int the next id number in the sequence. |
||
870 | * A DB_Error object on failure. |
||
871 | * |
||
872 | * @see DB_common::nextID(), DB_common::getSequenceName(), |
||
873 | * DB_oci8::createSequence(), DB_oci8::dropSequence() |
||
874 | */ |
||
875 | function nextId($seq_name, $ondemand = true) |
||
876 | { |
||
877 | $seqname = $this->getSequenceName($seq_name); |
||
878 | $repeat = 0; |
||
879 | do { |
||
880 | $this->expectError(DB_ERROR_NOSUCHTABLE); |
||
881 | $result = $this->query("SELECT ${seqname}.nextval FROM dual"); |
||
882 | $this->popExpect(); |
||
883 | if ($ondemand && DB::isError($result) && |
||
884 | $result->getCode() == DB_ERROR_NOSUCHTABLE) { |
||
885 | $repeat = 1; |
||
886 | $result = $this->createSequence($seq_name); |
||
887 | if (DB::isError($result)) { |
||
888 | return $this->raiseError($result); |
||
889 | } |
||
890 | } else { |
||
891 | $repeat = 0; |
||
892 | } |
||
893 | } while ($repeat); |
||
894 | if (DB::isError($result)) { |
||
895 | return $this->raiseError($result); |
||
896 | } |
||
897 | $arr = $result->fetchRow(DB_FETCHMODE_ORDERED); |
||
898 | return $arr[0]; |
||
899 | } |
||
900 | |||
901 | /** |
||
902 | * Creates a new sequence |
||
903 | * |
||
904 | * @param string $seq_name name of the new sequence |
||
905 | * |
||
906 | * @return int DB_OK on success. A DB_Error object on failure. |
||
907 | * |
||
908 | * @see DB_common::createSequence(), DB_common::getSequenceName(), |
||
909 | * DB_oci8::nextID(), DB_oci8::dropSequence() |
||
910 | */ |
||
911 | function createSequence($seq_name) |
||
912 | { |
||
913 | return $this->query('CREATE SEQUENCE ' |
||
914 | . $this->getSequenceName($seq_name)); |
||
915 | } |
||
916 | |||
917 | // }}} |
||
918 | // {{{ dropSequence() |
||
919 | |||
920 | /** |
||
921 | * Deletes a sequence |
||
922 | * |
||
923 | * @param string $seq_name name of the sequence to be deleted |
||
924 | * |
||
925 | * @return int DB_OK on success. A DB_Error object on failure. |
||
926 | * |
||
927 | * @see DB_common::dropSequence(), DB_common::getSequenceName(), |
||
928 | * DB_oci8::nextID(), DB_oci8::createSequence() |
||
929 | */ |
||
930 | function dropSequence($seq_name) |
||
931 | { |
||
932 | return $this->query('DROP SEQUENCE ' |
||
933 | . $this->getSequenceName($seq_name)); |
||
934 | } |
||
935 | |||
936 | // }}} |
||
937 | // {{{ oci8RaiseError() |
||
938 | |||
939 | /** |
||
940 | * Produces a DB_Error object regarding the current problem |
||
941 | * |
||
942 | * @param int $errno if the error is being manually raised pass a |
||
943 | * DB_ERROR* constant here. If this isn't passed |
||
944 | * the error information gathered from the DBMS. |
||
945 | * |
||
946 | * @return object the DB_Error object |
||
947 | * |
||
948 | * @see DB_common::raiseError(), |
||
949 | * DB_oci8::errorNative(), DB_oci8::errorCode() |
||
950 | */ |
||
951 | function oci8RaiseError($errno = null) |
||
952 | { |
||
953 | if ($errno === null) { |
||
954 | $error = @OCIError($this->connection); |
||
955 | return $this->raiseError($this->errorCode($error['code']), |
||
956 | null, null, null, $error['message']); |
||
957 | } elseif (is_resource($errno)) { |
||
958 | $error = @OCIError($errno); |
||
959 | return $this->raiseError($this->errorCode($error['code']), |
||
960 | null, null, null, $error['message']); |
||
961 | } |
||
962 | return $this->raiseError($this->errorCode($errno)); |
||
963 | } |
||
964 | |||
965 | // }}} |
||
966 | // {{{ errorNative() |
||
967 | |||
968 | /** |
||
969 | * Gets the DBMS' native error code produced by the last query |
||
970 | * |
||
971 | * @return int the DBMS' error code. FALSE if the code could not be |
||
972 | * determined |
||
973 | */ |
||
974 | function errorNative() |
||
975 | { |
||
976 | if (is_resource($this->last_stmt)) { |
||
977 | $error = @OCIError($this->last_stmt); |
||
978 | } else { |
||
979 | $error = @OCIError($this->connection); |
||
980 | } |
||
981 | if (is_array($error)) { |
||
982 | return $error['code']; |
||
983 | } |
||
984 | return false; |
||
985 | } |
||
986 | |||
987 | // }}} |
||
988 | // {{{ tableInfo() |
||
989 | |||
990 | /** |
||
991 | * Returns information about a table or a result set |
||
992 | * |
||
993 | * NOTE: only supports 'table' and 'flags' if <var>$result</var> |
||
994 | * is a table name. |
||
995 | * |
||
996 | * NOTE: flags won't contain index information. |
||
997 | * |
||
998 | * @param object|string $result DB_result object from a query or a |
||
999 | * string containing the name of a table. |
||
1000 | * While this also accepts a query result |
||
1001 | * resource identifier, this behavior is |
||
1002 | * deprecated. |
||
1003 | * @param int $mode a valid tableInfo mode |
||
1004 | * |
||
1005 | * @return array an associative array with the information requested. |
||
1006 | * A DB_Error object on failure. |
||
1007 | * |
||
1008 | * @see DB_common::tableInfo() |
||
1009 | */ |
||
1010 | function tableInfo($result, $mode = null) |
||
1011 | { |
||
1012 | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
||
1013 | $case_func = 'strtolower'; |
||
1014 | } else { |
||
1015 | $case_func = 'strval'; |
||
1016 | } |
||
1017 | |||
1018 | $res = array(); |
||
1019 | |||
1020 | if (is_string($result)) { |
||
1021 | /* |
||
1022 | * Probably received a table name. |
||
1023 | * Create a result resource identifier. |
||
1024 | */ |
||
1025 | $result = strtoupper($result); |
||
1026 | $q_fields = 'SELECT column_name, data_type, data_length, ' |
||
1027 | . 'nullable ' |
||
1028 | . 'FROM user_tab_columns ' |
||
1029 | . "WHERE table_name='$result' ORDER BY column_id"; |
||
1030 | |||
1031 | $this->last_query = $q_fields; |
||
1032 | |||
1033 | if (!$stmt = @OCIParse($this->connection, $q_fields)) { |
||
1034 | return $this->oci8RaiseError(DB_ERROR_NEED_MORE_DATA); |
||
1035 | } |
||
1036 | if (!@OCIExecute($stmt, OCI_DEFAULT)) { |
||
1037 | return $this->oci8RaiseError($stmt); |
||
1038 | } |
||
1039 | |||
1040 | $i = 0; |
||
1041 | while (@OCIFetch($stmt)) { |
||
1042 | $res[$i] = array( |
||
1043 | 'table' => $case_func($result), |
||
1044 | 'name' => $case_func(@OCIResult($stmt, 1)), |
||
1045 | 'type' => @OCIResult($stmt, 2), |
||
1046 | 'len' => @OCIResult($stmt, 3), |
||
1047 | 'flags' => (@OCIResult($stmt, 4) == 'N') ? 'not_null' : '', |
||
1048 | ); |
||
1049 | if ($mode & DB_TABLEINFO_ORDER) { |
||
1050 | $res['order'][$res[$i]['name']] = $i; |
||
1051 | } |
||
1052 | if ($mode & DB_TABLEINFO_ORDERTABLE) { |
||
1053 | $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; |
||
1054 | } |
||
1055 | $i++; |
||
1056 | } |
||
1057 | |||
1058 | if ($mode) { |
||
1059 | $res['num_fields'] = $i; |
||
1060 | } |
||
1061 | @OCIFreeStatement($stmt); |
||
1062 | |||
1063 | } else { |
||
1064 | if (isset($result->result)) { |
||
1065 | /* |
||
1066 | * Probably received a result object. |
||
1067 | * Extract the result resource identifier. |
||
1068 | */ |
||
1069 | $result = $result->result; |
||
1070 | } |
||
1071 | |||
1072 | $res = array(); |
||
1073 | |||
1074 | if ($result === $this->last_stmt) { |
||
1075 | $count = @OCINumCols($result); |
||
1076 | if ($mode) { |
||
1077 | $res['num_fields'] = $count; |
||
1078 | } |
||
1079 | for ($i = 0; $i < $count; $i++) { |
||
1080 | $res[$i] = array( |
||
1081 | 'table' => '', |
||
1082 | 'name' => $case_func(@OCIColumnName($result, $i+1)), |
||
1083 | 'type' => @OCIColumnType($result, $i+1), |
||
1084 | 'len' => @OCIColumnSize($result, $i+1), |
||
1085 | 'flags' => '', |
||
1086 | ); |
||
1087 | if ($mode & DB_TABLEINFO_ORDER) { |
||
1088 | $res['order'][$res[$i]['name']] = $i; |
||
1089 | } |
||
1090 | if ($mode & DB_TABLEINFO_ORDERTABLE) { |
||
1091 | $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; |
||
1092 | } |
||
1093 | } |
||
1094 | } else { |
||
1095 | return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
||
1096 | } |
||
1097 | } |
||
1098 | return $res; |
||
1099 | } |
||
1100 | |||
1101 | // }}} |
||
1102 | // {{{ getSpecialQuery() |
||
1103 | |||
1104 | /** |
||
1105 | * Obtains the query string needed for listing a given type of objects |
||
1106 | * |
||
1107 | * @param string $type the kind of objects you want to retrieve |
||
1108 | * |
||
1109 | * @return string the SQL query string or null if the driver doesn't |
||
1110 | * support the object type requested |
||
1111 | * |
||
1112 | * @access protected |
||
1113 | * @see DB_common::getListOf() |
||
1114 | */ |
||
1115 | function getSpecialQuery($type) |
||
1116 | { |
||
1117 | switch ($type) { |
||
1118 | case 'tables': |
||
1119 | return 'SELECT table_name FROM user_tables'; |
||
1120 | case 'synonyms': |
||
1121 | return 'SELECT synonym_name FROM user_synonyms'; |
||
1122 | case 'views': |
||
1123 | return 'SELECT view_name FROM user_views'; |
||
1124 | default: |
||
1125 | return null; |
||
1126 | } |
||
1127 | } |
||
1128 | |||
1129 | // }}} |
||
1130 | // {{{ quoteFloat() |
||
1131 | |||
1132 | /** |
||
1133 | * Formats a float value for use within a query in a locale-independent |
||
1134 | * manner. |
||
1135 | * |
||
1136 | * @param float the float value to be quoted. |
||
1137 | * @return string the quoted string. |
||
1138 | * @see DB_common::quoteSmart() |
||
1139 | * @since Method available since release 1.7.8. |
||
1140 | */ |
||
1141 | function quoteFloat($float) { |
||
1142 | return $this->escapeSimple(str_replace(',', '.', strval(floatval($float)))); |
||
1143 | } |
||
1144 | |||
1145 | // }}} |
||
1146 | |||
1147 | } |
||
1148 | |||
1149 | /* |
||
1150 | * Local variables: |
||
1151 | * tab-width: 4 |
||
1152 | * c-basic-offset: 4 |
||
1153 | * End: |
||
1154 | */ |
||
1155 | |||
1156 | ?> |