/[cvs]/nfo/php/libs/net.php.pear/DB/oci8.php
ViewVC logotype

Contents of /nfo/php/libs/net.php.pear/DB/oci8.php

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.1 - (show annotations)
Tue Oct 29 19:11:41 2002 UTC (21 years, 8 months ago) by cvsjoko
Branch: MAIN
CVS Tags: HEAD
+ new pear-libraries

1 <?php
2 //
3 // +----------------------------------------------------------------------+
4 // | PHP Version 4 |
5 // +----------------------------------------------------------------------+
6 // | Copyright (c) 1997-2002 The PHP Group |
7 // +----------------------------------------------------------------------+
8 // | This source file is subject to version 2.02 of the PHP license, |
9 // | that is bundled with this package in the file LICENSE, and is |
10 // | available at through the world-wide-web at |
11 // | http://www.php.net/license/2_02.txt. |
12 // | If you did not receive a copy of the PHP license and are unable to |
13 // | obtain it through the world-wide-web, please send a note to |
14 // | license@php.net so we can mail you a copy immediately. |
15 // +----------------------------------------------------------------------+
16 // | Author: James L. Pine <jlp@valinux.com> |
17 // +----------------------------------------------------------------------+
18 //
19 // $Id: oci8.php,v 1.47.2.1 2002/04/09 19:04:15 ssb Exp $
20 //
21 // Database independent query interface definition for PHP's Oracle 8
22 // call-interface extension.
23 //
24
25 //
26 // be aware... OCIError() only appears to return anything when given a
27 // statement, so functions return the generic DB_ERROR instead of more
28 // useful errors that have to do with feedback from the database.
29 //
30
31
32 require_once 'DB/common.php';
33
34 class DB_oci8 extends DB_common
35 {
36 // {{{ properties
37
38 var $connection;
39 var $phptype, $dbsyntax;
40 var $manip_query = array();
41 var $prepare_types = array();
42 var $autoCommit = 1;
43 var $last_stmt = false;
44
45 // }}}
46 // {{{ constructor
47
48 function DB_oci8()
49 {
50 $this->DB_common();
51 $this->phptype = 'oci8';
52 $this->dbsyntax = 'oci8';
53 $this->features = array(
54 'prepare' => false,
55 'pconnect' => true,
56 'transactions' => true,
57 'limit' => 'alter'
58 );
59 $this->errorcode_map = array(
60 900 => DB_ERROR_SYNTAX,
61 904 => DB_ERROR_NOSUCHFIELD,
62 923 => DB_ERROR_SYNTAX,
63 942 => DB_ERROR_NOSUCHTABLE,
64 955 => DB_ERROR_ALREADY_EXISTS,
65 1476 => DB_ERROR_DIVZERO,
66 1722 => DB_ERROR_INVALID_NUMBER,
67 2289 => DB_ERROR_NOSUCHTABLE,
68 2291 => DB_ERROR_CONSTRAINT,
69 2449 => DB_ERROR_CONSTRAINT,
70 );
71 }
72
73 // }}}
74 // {{{ connect()
75
76 /**
77 * Connect to a database and log in as the specified user.
78 *
79 * @param $dsn the data source name (see DB::parseDSN for syntax)
80 * @param $persistent (optional) whether the connection should
81 * be persistent
82 *
83 * @return int DB_OK on success, a DB error code on failure
84 */
85 function connect($dsninfo, $persistent = false)
86 {
87 if (!DB::assertExtension('oci8')) {
88 return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
89 }
90 $this->dsn = $dsninfo;
91 $user = $dsninfo['username'];
92 $pw = $dsninfo['password'];
93 $hostspec = $dsninfo['hostspec'];
94
95 $connect_function = $persistent ? 'OCIPLogon' : 'OCILogon';
96
97 if ($hostspec) {
98 $conn = @$connect_function($user,$pw,$hostspec);
99 } elseif ($user || $pw) {
100 $conn = @$connect_function($user,$pw);
101 } else {
102 $conn = false;
103 }
104 if ($conn == false) {
105 $error = OCIError();
106 $error = (is_array($error)) ? $error['message'] : null;
107 return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null,
108 null, $error);
109 }
110 $this->connection = $conn;
111 return DB_OK;
112 }
113
114 // }}}
115 // {{{ disconnect()
116
117 /**
118 * Log out and disconnect from the database.
119 *
120 * @return bool TRUE on success, FALSE if not connected.
121 */
122 function disconnect()
123 {
124 $ret = @OCILogOff($this->connection);
125 $this->connection = null;
126 return $ret;
127 }
128
129 // }}}
130 // {{{ simpleQuery()
131
132 /**
133 * Send a query to oracle and return the results as an oci8 resource
134 * identifier.
135 *
136 * @param $query the SQL query
137 *
138 * @return int returns a valid oci8 result for successful SELECT
139 * queries, DB_OK for other successful queries. A DB error code
140 * is returned on failure.
141 */
142 function simpleQuery($query)
143 {
144 $this->last_query = $query;
145 $query = $this->modifyQuery($query);
146 $result = @OCIParse($this->connection, $query);
147 if (!$result) {
148 return $this->oci8RaiseError();
149 }
150 if ($this->autoCommit) {
151 $success = @OCIExecute($result,OCI_COMMIT_ON_SUCCESS);
152 } else {
153 $success = @OCIExecute($result,OCI_DEFAULT);
154 }
155 if (!$success) {
156 return $this->oci8RaiseError($result);
157 }
158 $this->last_stmt=$result;
159 // Determine which queries that should return data, and which
160 // should return an error code only.
161 return DB::isManip($query) ? DB_OK : $result;
162 }
163
164 // }}}
165 // {{{ nextResult()
166
167 /**
168 * Move the internal oracle result pointer to the next available result
169 *
170 * @param a valid oci8 result resource
171 *
172 * @access public
173 *
174 * @return true if a result is available otherwise return false
175 */
176 function nextResult($result)
177 {
178 return false;
179 }
180
181 // }}}
182 // {{{ fetchRow()
183
184 /**
185 * Fetch a row and return as array.
186 *
187 * @param $result oci8 result identifier
188 * @param $fetchmode how the resulting array should be indexed
189 *
190 * @return int an array on success, a DB error code on failure, NULL
191 * if there is no more data
192 */
193 function &fetchRow($result, $fetchmode = DB_FETCHMODE_DEFAULT)
194 {
195 if ($fetchmode == DB_FETCHMODE_DEFAULT) {
196 $fetchmode = $this->fetchmode;
197 }
198 if ($fetchmode & DB_FETCHMODE_ASSOC) {
199 $moredata = @OCIFetchInto($result, $row, OCI_ASSOC + OCI_RETURN_NULLS + OCI_RETURN_LOBS);
200 } else {
201 $moredata = @OCIFetchInto($result, $row, OCI_RETURN_NULLS + OCI_RETURN_LOBS);
202 }
203 if (!$moredata) {
204 return NULL;
205 }
206 return $row;
207 }
208
209 // }}}
210 // {{{ fetchInto()
211
212 /**
213 * Fetch a row and insert the data into an existing array.
214 *
215 * @param $result oci8 result identifier
216 * @param $arr (reference) array where data from the row is stored
217 * @param $fetchmode how the array data should be indexed
218 * @param $rownum the row number to fetch (not yet supported)
219 *
220 * @return int DB_OK on success, a DB error code on failure
221 */
222 function fetchInto($result, &$arr, $fetchmode = DB_FETCHMODE_DEFAULT, $rownum=NULL)
223 {
224 if ($rownum !== NULL) {
225 return $this->raiseError(DB_ERROR_NOT_CAPABLE);
226 }
227 if ($fetchmode & DB_FETCHMODE_ASSOC) {
228 $moredata = @OCIFetchInto($result,$arr,OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS);
229 if ($moredata && $this->options['optimize'] == 'portability') {
230 $arr = array_change_key_case($arr, CASE_LOWER);
231 }
232 } else {
233 $moredata = @OCIFetchInto($result,$arr,OCI_RETURN_NULLS+OCI_RETURN_LOBS);
234 }
235 if (!$moredata) {
236 return NULL;
237 }
238 return DB_OK;
239 }
240
241 // }}}
242 // {{{ freeResult()
243
244 /**
245 * Free the internal resources associated with $result.
246 *
247 * @param $result oci8 result identifier or DB statement identifier
248 *
249 * @return bool TRUE on success, FALSE if $result is invalid
250 */
251 function freeResult($result)
252 {
253 if (is_resource($result)) {
254 return @OCIFreeStatement($result);
255 }
256 if (!isset($this->prepare_tokens[(int)$result])) {
257 return false;
258 }
259 unset($this->prepare_tokens[(int)$result]);
260 unset($this->prepare_types[(int)$result]);
261 unset($this->manip_query[(int)$result]);
262 return true;
263 }
264
265 // }}}
266 // {{{ numRows()
267
268 function numRows($result)
269 {
270 // emulate numRows for Oracle. yuck.
271 if ($this->options['optimize'] == 'portability' &&
272 $result === $this->last_stmt) {
273 $countquery = preg_replace('/^\s*SELECT\s+(.*?)\s+FROM\s+/is',
274 'SELECT COUNT(*) FROM ',
275 $this->last_query);
276 $save_query = $this->last_query;
277 $save_stmt = $this->last_stmt;
278 $count = $this->query($countquery);
279 if (DB::isError($count) ||
280 DB::isError($row = $count->fetchRow(DB_FETCHMODE_ORDERED)))
281 {
282 $this->last_query = $save_query;
283 $this->last_stmt = $save_stmt;
284 return $this->raiseError(DB_ERROR_NOT_CAPABLE);
285 }
286 return $row[0];
287 }
288 return $this->raiseError(DB_ERROR_NOT_CAPABLE);
289 }
290
291 // }}}
292 // {{{ numCols()
293
294 /**
295 * Get the number of columns in a result set.
296 *
297 * @param $result oci8 result identifier
298 *
299 * @return int the number of columns per row in $result
300 */
301 function numCols($result)
302 {
303 $cols = @OCINumCols($result);
304 if (!$cols) {
305 return $this->oci8RaiseError($result);
306 }
307 return $cols;
308 }
309
310 // }}}
311 // {{{ errorNative()
312
313 /**
314 * Get the native error code of the last error (if any) that occured
315 * on the current connection. This does not work, as OCIError does
316 * not work unless given a statement. If OCIError does return
317 * something, so will this.
318 *
319 * @return int native oci8 error code
320 */
321 function errorNative()
322 {
323 if (is_resource($this->last_stmt)) {
324 $error = @OCIError($this->last_stmt);
325 } else {
326 $error = @OCIError($this->connection);
327 }
328 if (is_array($error)) {
329 return $error['code'];
330 }
331 return false;
332 }
333
334 // }}}
335 // {{{ prepare()
336
337 /**
338 * Prepares a query for multiple execution with execute(). With
339 * oci8, this is emulated.
340 * @param $query query to be prepared
341 *
342 * @return DB statement resource
343 */
344 function prepare($query)
345 {
346 $tokens = split('[\&\?]', $query);
347 $token = 0;
348 $types = array();
349 for ($i = 0; $i < strlen($query); $i++) {
350 switch ($query[$i]) {
351 case '?':
352 $types[$token++] = DB_PARAM_SCALAR;
353 break;
354 case '&':
355 $types[$token++] = DB_PARAM_OPAQUE;
356 break;
357 }
358 }
359 $binds = sizeof($tokens) - 1;
360 $newquery = '';
361 for ($i = 0; $i < $binds; $i++) {
362 $newquery .= $tokens[$i] . ":bind" . $i;
363 }
364 $newquery .= $tokens[$i];
365 $this->last_query = $query;
366 $newquery = $this->modifyQuery($newquery);
367 $stmt = @OCIParse($this->connection, $newquery);
368 $this->prepare_types[$stmt] = $types;
369 $this->manip_query[(int)$stmt] = DB::isManip($query);
370 return $stmt;
371 }
372
373 // }}}
374 // {{{ execute()
375
376 /**
377 * Executes a DB statement prepared with prepare().
378 *
379 * @param $stmt a DB statement resource (returned from prepare())
380 * @param $data data to be used in execution of the statement
381 *
382 * @return int returns an oci8 result resource for successful
383 * SELECT queries, DB_OK for other successful queries. A DB error
384 * code is returned on failure.
385 */
386 function execute($stmt, $data = false)
387 {
388 $types=&$this->prepare_types[$stmt];
389 if (($size = sizeof($types)) != sizeof($data)) {
390 return $this->raiseError(DB_ERROR_MISMATCH);
391 }
392 for ($i = 0; $i < $size; $i++) {
393 if (is_array($data)) {
394 $pdata[$i] = &$data[$i];
395 }
396 else {
397 $pdata[$i] = &$data;
398 }
399 if ($types[$i] == DB_PARAM_OPAQUE) {
400 $fp = fopen($pdata[$i], "r");
401 $pdata[$i] = '';
402 if ($fp) {
403 while (($buf = fread($fp, 4096)) != false) {
404 $pdata[$i] .= $buf;
405 }
406 }
407 }
408 if (!@OCIBindByName($stmt, ":bind" . $i, $pdata[$i], -1)) {
409 return $this->oci8RaiseError($stmt);
410 }
411 }
412 if ($this->autoCommit) {
413 $success = @OCIExecute($stmt, OCI_COMMIT_ON_SUCCESS);
414 }
415 else {
416 $success = @OCIExecute($stmt, OCI_DEFAULT);
417 }
418 if (!$success) {
419 return $this->oci8RaiseError($stmt);
420 }
421 $this->last_stmt = $stmt;
422 if ($this->manip_query[(int)$stmt]) {
423 return DB_OK;
424 } else {
425 return new DB_result($this, $stmt);
426 }
427 }
428
429 // }}}
430 // {{{ autoCommit()
431
432 /**
433 * Enable/disable automatic commits
434 *
435 * @param $onoff true/false whether to autocommit
436 */
437 function autoCommit($onoff = false)
438 {
439 $this->autoCommit = (bool)$onoff;;
440 return DB_OK;
441 }
442
443 // }}}
444 // {{{ commit()
445
446 /**
447 * Commit transactions on the current connection
448 *
449 * @return DB_ERROR or DB_OK
450 */
451 function commit()
452 {
453 $result = @OCICommit($this->connection);
454 if (!$result) {
455 return $this->oci8RaiseError();
456 }
457 return DB_OK;
458 }
459
460 // }}}
461 // {{{ rollback()
462
463 /**
464 * Roll back all uncommitted transactions on the current connection.
465 *
466 * @return DB_ERROR or DB_OK
467 */
468 function rollback()
469 {
470 $result = @OCIRollback($this->connection);
471 if (!$result) {
472 return $this->oci8RaiseError();
473 }
474 return DB_OK;
475 }
476
477 // }}}
478 // {{{ affectedRows()
479
480 /**
481 * Gets the number of rows affected by the last query.
482 * if the last query was a select, returns 0.
483 *
484 * @return number of rows affected by the last query or DB_ERROR
485 */
486 function affectedRows()
487 {
488 if ($this->last_stmt === false) {
489 return $this->oci8RaiseError();
490 }
491 $result = @OCIRowCount($this->last_stmt);
492 if ($result === false) {
493 return $this->oci8RaiseError($this->last_stmt);
494 }
495 return $result;
496 }
497
498 // }}}
499 // {{{ modifyQuery()
500
501 function modifyQuery($query)
502 {
503 // "SELECT 2+2" must be "SELECT 2+2 FROM dual" in Oracle
504 if (preg_match('/^\s*SELECT/i', $query) &&
505 !preg_match('/\sFROM\s/i', $query)) {
506 $query .= " FROM dual";
507 }
508 return $query;
509 }
510
511 // }}}
512 // {{{ modifyLimitQuery()
513
514 /**
515 * Emulate the row limit support altering the query
516 *
517 * @param string $query The query to treat
518 * @param int $from The row to start to fetch from
519 * @param int $count The offset
520 * @return string The modified query
521 *
522 * @author Tomas V.V.Cox <cox@idecnet.com>
523 */
524 function modifyLimitQuery($query, $from, $count)
525 {
526 // Let Oracle return the name of the columns instead of
527 // coding a "home" SQL parser
528 $q_fields = "SELECT * FROM ($query) WHERE NULL = NULL";
529 if (!$result = OCIParse($this->connection, $q_fields)) {
530 return $this->oci8RaiseError();
531 }
532 if (!OCIExecute($result, OCI_DEFAULT)) {
533 return $this->oci8RaiseError($result);
534 }
535 $ncols = OCINumCols($result);
536 $cols = array();
537 for ( $i = 1; $i <= $ncols; $i++ ) {
538 $cols[] = OCIColumnName($result, $i);
539 }
540 $fields = implode(', ', $cols);
541 // XXX Test that (tip by John Lim)
542 //if(preg_match('/^\s*SELECT\s+/is', $query, $match)) {
543 // // Introduce the FIRST_ROWS Oracle query optimizer
544 // $query = substr($query, strlen($match[0]), strlen($query));
545 // $query = "SELECT /* +FIRST_ROWS */ " . $query;
546 //}
547
548 // Construct the query
549 // more at: http://marc.theaimsgroup.com/?l=php-db&m=99831958101212&w=2
550 // Perhaps this could be optimized with the use of Unions
551 $from += 1; // in Oracle rownum starts at 1
552 $query = "SELECT $fields FROM".
553 " (SELECT rownum as linenum, $fields FROM".
554 " ($query)".
555 " WHERE rownum <= ". ($from + $count) .
556 ") WHERE linenum >= $from";
557 return $query;
558 }
559
560 // }}}
561 // {{{ nextId()
562
563 /**
564 * Get the next value in a sequence. We emulate sequences
565 * for MySQL. Will create the sequence if it does not exist.
566 *
567 * @access public
568 *
569 * @param $seq_name the name of the sequence
570 *
571 * @param $ondemand whether to create the sequence table on demand
572 * (default is true)
573 *
574 * @return a sequence integer, or a DB error
575 */
576 function nextId($seq_name, $ondemand = true)
577 {
578 $seqname = $this->getSequenceName($seq_name);
579 $repeat = 0;
580 do {
581 $this->expectError(DB_ERROR_NOSUCHTABLE);
582 $result = $this->query("SELECT ${seqname}.nextval FROM dual");
583 $this->popExpect();
584 if ($ondemand && DB::isError($result) &&
585 $result->getCode() == DB_ERROR_NOSUCHTABLE) {
586 $repeat = 1;
587 $result = $this->createSequence($seq_name);
588 if (DB::isError($result)) {
589 return $this->raiseError($result);
590 }
591 } else {
592 $repeat = 0;
593 }
594 } while ($repeat);
595 if (DB::isError($result)) {
596 return $this->raiseError($result);
597 }
598 $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
599 return $arr[0];
600 }
601
602 // }}}
603 // {{{ createSequence()
604
605 function createSequence($seq_name)
606 {
607 $seqname = $this->getSequenceName($seq_name);
608 return $this->query("CREATE SEQUENCE ${seqname}");
609 }
610
611 // }}}
612 // {{{ dropSequence()
613
614 function dropSequence($seq_name)
615 {
616 $seqname = $this->getSequenceName($seq_name);
617 return $this->query("DROP SEQUENCE ${seqname}");
618 }
619
620 // }}}
621 // {{{ oci8RaiseError()
622
623 function oci8RaiseError($errno = null)
624 {
625 if ($errno === null) {
626 $error = @OCIError($this->connection);
627 return $this->raiseError($this->errorCode($error['code']),
628 null, null, null, $error['message']);
629 } elseif (is_resource($errno)) {
630 $error = @OCIError($errno);
631 return $this->raiseError($this->errorCode($error['code']),
632 null, null, null, $error['message']);
633 }
634 return $this->raiseError($this->errorCode($errno));
635 }
636
637 // }}}
638 // {{{ getSpecialQuery()
639
640 /**
641 * Returns the query needed to get some backend info
642 * @param string $type What kind of info you want to retrieve
643 * @return string The SQL query string
644 */
645 function getSpecialQuery($type)
646 {
647 switch ($type) {
648 case 'tables':
649 $sql = "SELECT table_name FROM user_tables";
650 break;
651 default:
652 return null;
653 }
654 return $sql;
655 }
656
657 // }}}
658
659 }
660 // Local variables:
661 // tab-width: 4
662 // c-basic-offset: 4
663 // End:
664 ?>

MailToCvsAdmin">MailToCvsAdmin
ViewVC Help
Powered by ViewVC 1.1.26 RSS 2.0 feed