blob: a88b474bd1c437daf42c6913a9b80073842a3236 [file] [log] [blame]
admin0fce0f22006-09-20 21:13:41 +00001<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
2/**
3 * Code Igniter
4 *
5 * An open source application development framework for PHP 4.3.2 or newer
6 *
7 * @package CodeIgniter
8 * @author Rick Ellis
9 * @copyright Copyright (c) 2006, pMachine, Inc.
10 * @license http://www.codeignitor.com/user_guide/license.html
11 * @link http://www.codeigniter.com
12 * @since Version 1.0
13 * @filesource
14 */
15
16// ------------------------------------------------------------------------
17
18/**
19 * oci8 Database Adapter Class
20 *
21 * Note: _DB is an extender class that the app controller
22 * creates dynamically based on whether the active record
23 * class is being used or not.
24 *
25 * @package CodeIgniter
26 * @subpackage Drivers
27 * @category Database
28 * @author Rick Ellis
29 * @link http://www.codeigniter.com/user_guide/libraries/database/
30 */
31
32/**
33 * oci8 Database Adapter Class
34 *
35 * This is a modification of the DB_driver class to
36 * permit access to oracle databases
37 *
38 * NOTE: this uses the PHP 4 oci methods
39 *
40 * @author Kelly McArdle
41 *
42 */
43
44class CI_DB_oci8 extends CI_DB {
45
admine885d782006-09-23 20:25:05 +000046 // Set "auto commit" by default
47 var $_commit = OCI_COMMIT_ON_SUCCESS;
48
admin0fce0f22006-09-20 21:13:41 +000049 // need to track statement id and cursor id
50 var $stmt_id;
51 var $curs_id;
52
53 // if we use a limit, we will add a field that will
54 // throw off num_fields later
55 var $limit_used;
admine885d782006-09-23 20:25:05 +000056
admin0fce0f22006-09-20 21:13:41 +000057 /**
58 * Non-persistent database connection
59 *
60 * @access private called by the base class
61 * @return resource
62 */
63 function db_connect()
64 {
65 return ocilogon($this->username, $this->password, $this->hostname);
66 }
67
68 // --------------------------------------------------------------------
69
70 /**
71 * Persistent database connection
72 *
73 * @access private called by the base class
74 * @return resource
75 */
76 function db_pconnect()
77 {
78 return ociplogon($this->username, $this->password, $this->hostname);
79 }
80
81 // --------------------------------------------------------------------
82
83 /**
84 * Select the database
85 *
86 * @access private called by the base class
87 * @return resource
88 */
89 function db_select()
90 {
91 return TRUE;
92 }
93
94 // --------------------------------------------------------------------
95
96 /**
97 * Execute the query
98 *
99 * @access private called by the base class
100 * @param string an SQL query
101 * @return resource
102 */
admine885d782006-09-23 20:25:05 +0000103 function _execute($sql)
admin0fce0f22006-09-20 21:13:41 +0000104 {
105 // oracle must parse the query before it
106 // is run, all of the actions with
107 // the query are based off the statement id
108 // returned by ociparse
109 $this->_set_stmt_id($sql);
110 ocisetprefetch($this->stmt_id, 1000);
admine885d782006-09-23 20:25:05 +0000111 return @ociexecute($this->stmt_id, $this->_commit);
admin0fce0f22006-09-20 21:13:41 +0000112 }
113
114 /**
115 * Generate a statement ID
116 *
117 * @access private
118 * @param string an SQL query
119 * @return none
120 */
121 function _set_stmt_id($sql)
122 {
123 if ( ! is_resource($this->stmt_id))
124 {
125 $this->stmt_id = ociparse($this->conn_id, $this->_prep_query($sql));
126 }
127 }
128
129 // --------------------------------------------------------------------
130
131 /**
132 * Prep the query
133 *
134 * If needed, each database adapter can prep the query string
135 *
136 * @access private called by execute()
137 * @param string an SQL query
138 * @return string
139 */
140 function _prep_query($sql)
141 {
142 return $sql;
143 }
144
145 // --------------------------------------------------------------------
146
147 /**
148 * getCursor. Returns a cursor from the datbase
149 *
150 * @access public
151 * @return cursor id
152 */
153 function get_cursor()
154 {
155 return $this->curs_id = ocinewcursor($this->conn_id);
156 }
157
158 // --------------------------------------------------------------------
159
160 /**
161 * Stored Procedure. Executes a stored procedure
162 *
163 * @access public
164 * @param package package stored procedure is in
165 * @param procedure stored procedure to execute
166 * @param params array of parameters
167 * @return array
168 *
169 * params array keys
170 *
171 * KEY OPTIONAL NOTES
172 * name no the name of the parameter should be in :<param_name> format
173 * value no the value of the parameter. If this is an OUT or IN OUT parameter,
174 * this should be a reference to a variable
175 * type yes the type of the parameter
176 * length yes the max size of the parameter
177 */
178 function stored_procedure($package, $procedure, $params)
179 {
180 if ($package == '' OR $procedure == '' OR ! is_array($params))
181 {
182 if ($this->db_debug)
183 {
184 log_message('error', 'Invalid query: '.$package.'.'.$procedure);
185 return $this->display_error('db_invalid_query');
186 }
187 return FALSE;
188 }
189
190 // build the query string
191 $sql = "begin $package.$procedure(";
192
193 $have_cursor = FALSE;
194 foreach($params as $param)
195 {
196 $sql .= $param['name'] . ",";
197
198 if (array_key_exists('type', $param) && ($param['type'] == OCI_B_CURSOR))
199 {
200 $have_cursor = TRUE;
201 }
202 }
203 $sql = trim($sql, ",") . "); end;";
204
205 $this->stmt_id = FALSE;
206 $this->_set_stmt_id($sql);
207 $this->_bind_params($params);
208 $this->query($sql, FALSE, $have_cursor);
209 }
210
211 // --------------------------------------------------------------------
212
213 /**
214 * Bind parameters
215 *
216 * @access private
217 * @return none
218 */
219 function _bind_params($params)
220 {
221 if ( ! is_array($params) OR ! is_resource($this->stmt_id))
222 {
223 return;
224 }
225
226 foreach ($params as $param)
227 {
228 foreach (array('name', 'value', 'type', 'length') as $val)
229 {
230 if ( ! isset($param[$val]))
231 {
232 $param[$val] = '';
233 }
234 }
235
236 ocibindbyname($this->stmt_id, $param['name'], $param['value'], $param['length'], $param['type']);
237 }
238 }
239
admine885d782006-09-23 20:25:05 +0000240 // --------------------------------------------------------------------
241
242 /**
243 * Begin Transaction
244 *
245 * @access public
246 * @return bool
247 */
admin8b180be2006-09-24 01:12:22 +0000248 function trans_begin($test_mode = FALSE)
admine885d782006-09-23 20:25:05 +0000249 {
250 if ( ! $this->trans_enabled)
251 {
252 return TRUE;
253 }
254
255 // When transactions are nested we only begin/commit/rollback the outermost ones
256 if ($this->_trans_depth > 0)
257 {
258 return TRUE;
259 }
260
admin8b180be2006-09-24 01:12:22 +0000261 // Reset the transaction failure flag.
262 // If the $test_mode flag is set to TRUE transactions will be rolled back
263 // even if the queries produce a successful result.
264 $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
265
admine885d782006-09-23 20:25:05 +0000266 $this->_commit = OCI_DEFAULT;
267 return TRUE;
268 }
269
270 // --------------------------------------------------------------------
271
272 /**
273 * Commit Transaction
274 *
275 * @access public
276 * @return bool
277 */
278 function trans_commit()
279 {
280 if ( ! $this->trans_enabled)
281 {
282 return TRUE;
283 }
284
285 // When transactions are nested we only begin/commit/rollback the outermost ones
286 if ($this->_trans_depth > 0)
287 {
288 return TRUE;
289 }
290
291 $ret = OCIcommit($this->conn_id);
292 $this->_commit = OCI_COMMIT_ON_SUCCESS;
293 return $ret;
294 }
295
296 // --------------------------------------------------------------------
297
298 /**
299 * Rollback Transaction
300 *
301 * @access public
302 * @return bool
303 */
304 function trans_rollback()
305 {
306 if ( ! $this->trans_enabled)
307 {
308 return TRUE;
309 }
310
311 // When transactions are nested we only begin/commit/rollback the outermost ones
312 if ($this->_trans_depth > 0)
313 {
314 return TRUE;
315 }
316
317 $ret = OCIrollback($this->conn_id);
318 $this->_commit = OCI_COMMIT_ON_SUCCESS;
319 return $ret;
320 }
321
admin0fce0f22006-09-20 21:13:41 +0000322 // --------------------------------------------------------------------
323
324 /**
325 * Escape String
326 *
327 * @access public
328 * @param string
329 * @return string
330 */
331 function escape_str($str)
332 {
333 return $str;
334 }
335
336 // --------------------------------------------------------------------
337
338 /**
339 * Close DB Connection
340 *
341 * @access public
342 * @param resource
343 * @return void
344 */
345 function destroy($conn_id)
346 {
347 ocilogoff($conn_id);
348 }
349
350 // --------------------------------------------------------------------
351
352 /**
353 * Affected Rows
354 *
355 * @access public
356 * @return integer
357 */
358 function affected_rows()
359 {
360 return @ocirowcount($this->stmt_id);
361 }
362
363 // --------------------------------------------------------------------
364
365 /**
366 * Insert ID
367 *
368 * @access public
369 * @return integer
370 */
371 function insert_id()
372 {
373 // not supported in oracle
374 return 0;
375 }
376
377 // --------------------------------------------------------------------
378
379 /**
380 * "Count All" query
381 *
382 * Generates a platform-specific query string that counts all records in
383 * the specified database
384 *
385 * @access public
386 * @param string
387 * @return string
388 */
389 function count_all($table = '')
390 {
391 if ($table == '')
392 return '0';
393
394 $query = $this->query("SELECT COUNT(1) AS numrows FROM ".$table);
395
396 if ($query == FALSE)
397 {
398 return 0;
399 }
400
401 $row = $query->row();
402 return $row->NUMROWS;
403 }
404
405 // --------------------------------------------------------------------
406
407 /**
408 * The error message string
409 *
410 * @access public
411 * @return string
412 */
413 function error_message()
414 {
415 $error = ocierror($this->conn_id);
416 return $error['message'];
417 }
418
419 // --------------------------------------------------------------------
420
421 /**
422 * The error message number
423 *
424 * @access public
425 * @return integer
426 */
427 function error_number()
428 {
429 $error = ocierror($this->conn_id);
430 return $error['code'];
431 }
432
433 // --------------------------------------------------------------------
434
435 /**
436 * Escape Table Name
437 *
438 * This function adds backticks if the table name has a period
439 * in it. Some DBs will get cranky unless periods are escaped
440 *
441 * @access public
442 * @param string the table name
443 * @return string
444 */
445 function escape_table($table)
446 {
447 if (stristr($table, '.'))
448 {
449 $table = preg_replace("/\./", "`.`", $table);
450 }
451
452 return $table;
453 }
454
455 // --------------------------------------------------------------------
456
457 /**
458 * Field data query
459 *
460 * Generates a platform-specific query so that the column data can be retrieved
461 *
462 * @access public
463 * @param string the table name
464 * @return object
465 */
466 function _field_data($table)
467 {
468 $sql = "SELECT * FROM ".$this->escape_table($table)." where rownum = 1";
469 $query = $this->query($sql);
470 return $query->field_data();
471 }
472
473 // --------------------------------------------------------------------
474
475 /**
476 * Insert statement
477 *
478 * Generates a platform-specific insert string from the supplied data
479 *
480 * @access public
481 * @param string the table name
482 * @param array the insert keys
483 * @param array the insert values
484 * @return string
485 */
486 function _insert($table, $keys, $values)
487 {
488 return "INSERT INTO ".$this->escape_table($table)." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
489 }
490
491 // --------------------------------------------------------------------
492
493 /**
494 * Update statement
495 *
496 * Generates a platform-specific update string from the supplied data
497 *
498 * @access public
499 * @param string the table name
500 * @param array the update data
501 * @param array the where clause
502 * @return string
503 */
504 function _update($table, $values, $where)
505 {
506 foreach($values as $key => $val)
507 {
508 $valstr[] = $key." = ".$val;
509 }
510
511 return "UPDATE ".$this->escape_table($table)." SET ".implode(', ', $valstr)." WHERE ".implode(" ", $where);
512 }
513
514 // --------------------------------------------------------------------
515
516 /**
517 * Delete statement
518 *
519 * Generates a platform-specific delete string from the supplied data
520 *
521 * @access public
522 * @param string the table name
523 * @param array the where clause
524 * @return string
525 */
526 function _delete($table, $where)
527 {
528 return "DELETE FROM ".$this->escape_table($table)." WHERE ".implode(" ", $where);
529 }
530
531 // --------------------------------------------------------------------
532
533 /**
534 * Version number query string
535 *
536 * @access public
537 * @return string
538 */
539 function _version()
540 {
541 $ver = ociserverversion($this->conn_id);
542 return $ver;
543 }
544
545 // --------------------------------------------------------------------
546
547 /**
548 * Show table query
549 *
550 * Generates a platform-specific query string so that the table names can be fetched
551 *
552 * @access public
553 * @return string
554 */
555 function _show_tables()
556 {
557 return "select TABLE_NAME FROM ALL_TABLES";
558 }
559
560 // --------------------------------------------------------------------
561
562 /**
563 * Show columnn query
564 *
565 * Generates a platform-specific query string so that the column names can be fetched
566 *
567 * @access public
568 * @param string the table name
569 * @return string
570 */
571 function _show_columns($table = '')
572 {
573 return "SELECT COLUMN_NAME FROM all_tab_columns WHERE table_name = '$table'";
574 }
575
576 // --------------------------------------------------------------------
577
578 /**
579 * Limit string
580 *
581 * Generates a platform-specific LIMIT clause
582 *
583 * @access public
584 * @param string the sql query string
585 * @param integer the number of rows to limit the query to
586 * @param integer the offset value
587 * @return string
588 */
589 function _limit($sql, $limit, $offset)
590 {
591 $limit = $offset + $limit;
592 $newsql = "SELECT * FROM (select inner_query.*, rownum rnum FROM ($sql) inner_query WHERE rownum < $limit)";
593
594 if ($offset != 0)
595 {
596 $newsql .= " WHERE rnum >= $offset";
597 }
598
599 // remember that we used limits
600 $this->limit_used = TRUE;
601
602 return $newsql;
603 }
604
605}
606
607
608/**
609 * oci8 Result Class
610 *
611 * This class extends the parent result class: CI_DB_result
612 *
613 * @category Database
614 * @author Rick Ellis
615 * @link http://www.codeigniter.com/user_guide/libraries/database/
616 */
617class CI_DB_oci8_result extends CI_DB_result {
618
619 var $stmt_id;
620 var $curs_id;
621 var $limit_used;
622
623 /**
624 * Number of rows in the result set
625 *
626 * @access public
627 * @return integer
628 */
629 function num_rows()
630 {
631 // get the results, count them,
632 // rerun query - otherwise we
633 // won't have data after calling
634 // num_rows()
635 $this->result_array();
636 $rowcount = count($this->result_array);
637 @ociexecute($this->stmt_id);
638 if ($this->curs_id)
639 {
640 @ociexecute($this->curs_id);
641 }
642 return $rowcount;
643 }
644
645 // --------------------------------------------------------------------
646
647 /**
648 * Number of fields in the result set
649 *
650 * @access public
651 * @return integer
652 */
653 function num_fields()
654 {
655 $count = @ocinumcols($this->stmt_id);
656
657 // if we used a limit, we added a field,
658 // subtract it out
659 if ($this->limit_used)
660 {
661 $count = $count - 1;
662 }
663
664 return $count;
665 }
666
667 // --------------------------------------------------------------------
668
669 /**
670 * Field data
671 *
672 * Generates an array of objects containing field meta-data
673 *
674 * @access public
675 * @return array
676 */
677 function field_data()
678 {
679 $retval = array();
680 $fieldCount = $this->num_fields();
681 for ($c = 1; $c <= $fieldCount; $c++)
682 {
683 $F = new stdClass();
684 $F->name = ocicolumnname($this->stmt_id, $c);
685 $F->type = ocicolumntype($this->stmt_id, $c);
686 $F->max_length = ocicolumnsize($this->stmt_id, $c);
687
688 $retval[] = $F;
689 }
690
691 return $retval;
692 }
693
694 // --------------------------------------------------------------------
695
696 /**
697 * Result - associative array
698 *
699 * Returns the result set as an array
700 *
701 * @access private
702 * @return array
703 */
704 function _fetch_assoc(&$row)
705 {
706 // if pulling from a cursor, use curs_id
707 if ($this->curs_id)
708 {
709 return ocifetchinto($this->curs_id, $row, OCI_ASSOC + OCI_RETURN_NULLS);
710 }
711 else
712 {
713 return ocifetchinto($this->stmt_id, $row, OCI_ASSOC + OCI_RETURN_NULLS);
714 }
715 }
716
717 // --------------------------------------------------------------------
718
719 /**
720 * Result - object
721 *
722 * Returns the result set as an object
723 *
724 * @access private
725 * @return object
726 */
727 function _fetch_object()
728 {
729 // the PHP 4 version of the oracle functions do not
730 // have a fetch method so we call the array version
731 // and build an object from that
732
733 $row = array();
734 $res = $this->_fetch_assoc($row);
735 if ($res != FALSE)
736 {
737 $obj = new stdClass();
738 foreach ($row as $key => $value)
739 {
740 $obj->{$key} = $value;
741 }
742
743 $res = $obj;
744 }
745 return $res;
746 }
747
748 /**
749 * Query result. "array" version.
750 *
751 * @access public
752 * @return array
753 */
754 function result_array()
755 {
756 if (count($this->result_array) > 0)
757 {
758 return $this->result_array;
759 }
760
761 // oracle's fetch functions do not
762 // return arrays, the information
763 // is returned in reference parameters
764 //
765 $row = NULL;
766 while ($this->_fetch_assoc($row))
767 {
768 $this->result_array[] = $row;
769 }
770
771 if (count($this->result_array) == 0)
772 {
773 return FALSE;
774 }
775
776 return $this->result_array;
777 }
778
779}
780
781?>