blob: 1fdb1bc454475c798b5f6a7b559b81fed0abd87e [file] [log] [blame]
Derek Allard2067d1a2008-11-13 22:59:24 +00001<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
2/**
3 * CodeIgniter
4 *
5 * An open source application development framework for PHP 4.3.2 or newer
6 *
7 * @package CodeIgniter
8 * @author ExpressionEngine Dev Team
9 * @copyright Copyright (c) 2008, EllisLab, Inc.
10 * @license http://codeigniter.com/user_guide/license.html
11 * @link http://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 ExpressionEngine Dev Team
29 * @link http://codeigniter.com/user_guide/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_driver extends CI_DB {
45
46 var $dbdriver = 'oci8';
47
48 // The character used for excaping
49 var $_escape_char = '"';
Derek Jonese4ed5832009-02-20 21:44:59 +000050
51 // clause and character used for LIKE escape sequences
52 var $_like_escape_str = " escape '%s' ";
53 var $_like_escape_chr = '!';
54
Derek Allard2067d1a2008-11-13 22:59:24 +000055 /**
56 * The syntax to count rows is slightly different across different
57 * database engines, so this string appears in each driver and is
58 * used for the count_all() and count_all_results() functions.
59 */
60 var $_count_string = "SELECT COUNT(1) AS ";
61 var $_random_keyword = ' ASC'; // not currently supported
62
63 // Set "auto commit" by default
64 var $_commit = OCI_COMMIT_ON_SUCCESS;
65
66 // need to track statement id and cursor id
67 var $stmt_id;
68 var $curs_id;
69
70 // if we use a limit, we will add a field that will
71 // throw off num_fields later
72 var $limit_used;
73
74 /**
75 * Non-persistent database connection
76 *
77 * @access private called by the base class
78 * @return resource
79 */
80 function db_connect()
81 {
82 return @ocilogon($this->username, $this->password, $this->hostname);
83 }
84
85 // --------------------------------------------------------------------
86
87 /**
88 * Persistent database connection
89 *
90 * @access private called by the base class
91 * @return resource
92 */
93 function db_pconnect()
94 {
95 return @ociplogon($this->username, $this->password, $this->hostname);
96 }
97
98 // --------------------------------------------------------------------
99
100 /**
101 * Select the database
102 *
103 * @access private called by the base class
104 * @return resource
105 */
106 function db_select()
107 {
108 return TRUE;
109 }
110
111 // --------------------------------------------------------------------
112
113 /**
114 * Set client character set
115 *
116 * @access public
117 * @param string
118 * @param string
119 * @return resource
120 */
121 function db_set_charset($charset, $collation)
122 {
123 // @todo - add support if needed
124 return TRUE;
125 }
126
127 // --------------------------------------------------------------------
128
129 /**
130 * Version number query string
131 *
132 * @access public
133 * @return string
134 */
135 function _version()
136 {
137 return ociserverversion($this->conn_id);
138 }
139
140 // --------------------------------------------------------------------
141
142 /**
143 * Execute the query
144 *
145 * @access private called by the base class
146 * @param string an SQL query
147 * @return resource
148 */
149 function _execute($sql)
150 {
151 // oracle must parse the query before it is run. All of the actions with
152 // the query are based on the statement id returned by ociparse
153 $this->stmt_id = FALSE;
154 $this->_set_stmt_id($sql);
155 ocisetprefetch($this->stmt_id, 1000);
156 return @ociexecute($this->stmt_id, $this->_commit);
157 }
158
159 /**
160 * Generate a statement ID
161 *
162 * @access private
163 * @param string an SQL query
164 * @return none
165 */
166 function _set_stmt_id($sql)
167 {
168 if ( ! is_resource($this->stmt_id))
169 {
170 $this->stmt_id = ociparse($this->conn_id, $this->_prep_query($sql));
171 }
172 }
173
174 // --------------------------------------------------------------------
175
176 /**
177 * Prep the query
178 *
179 * If needed, each database adapter can prep the query string
180 *
181 * @access private called by execute()
182 * @param string an SQL query
183 * @return string
184 */
185 function _prep_query($sql)
186 {
187 return $sql;
188 }
189
190 // --------------------------------------------------------------------
191
192 /**
193 * getCursor. Returns a cursor from the datbase
194 *
195 * @access public
196 * @return cursor id
197 */
198 function get_cursor()
199 {
200 $this->curs_id = ocinewcursor($this->conn_id);
201 return $this->curs_id;
202 }
203
204 // --------------------------------------------------------------------
205
206 /**
207 * Stored Procedure. Executes a stored procedure
208 *
209 * @access public
210 * @param package package stored procedure is in
211 * @param procedure stored procedure to execute
212 * @param params array of parameters
213 * @return array
214 *
215 * params array keys
216 *
217 * KEY OPTIONAL NOTES
218 * name no the name of the parameter should be in :<param_name> format
219 * value no the value of the parameter. If this is an OUT or IN OUT parameter,
220 * this should be a reference to a variable
221 * type yes the type of the parameter
222 * length yes the max size of the parameter
223 */
224 function stored_procedure($package, $procedure, $params)
225 {
226 if ($package == '' OR $procedure == '' OR ! is_array($params))
227 {
228 if ($this->db_debug)
229 {
230 log_message('error', 'Invalid query: '.$package.'.'.$procedure);
231 return $this->display_error('db_invalid_query');
232 }
233 return FALSE;
234 }
235
236 // build the query string
237 $sql = "begin $package.$procedure(";
238
239 $have_cursor = FALSE;
240 foreach($params as $param)
241 {
242 $sql .= $param['name'] . ",";
243
244 if (array_key_exists('type', $param) && ($param['type'] == OCI_B_CURSOR))
245 {
246 $have_cursor = TRUE;
247 }
248 }
249 $sql = trim($sql, ",") . "); end;";
250
251 $this->stmt_id = FALSE;
252 $this->_set_stmt_id($sql);
253 $this->_bind_params($params);
254 $this->query($sql, FALSE, $have_cursor);
255 }
256
257 // --------------------------------------------------------------------
258
259 /**
260 * Bind parameters
261 *
262 * @access private
263 * @return none
264 */
265 function _bind_params($params)
266 {
267 if ( ! is_array($params) OR ! is_resource($this->stmt_id))
268 {
269 return;
270 }
271
272 foreach ($params as $param)
273 {
274 foreach (array('name', 'value', 'type', 'length') as $val)
275 {
276 if ( ! isset($param[$val]))
277 {
278 $param[$val] = '';
279 }
280 }
281
282 ocibindbyname($this->stmt_id, $param['name'], $param['value'], $param['length'], $param['type']);
283 }
284 }
285
286 // --------------------------------------------------------------------
287
288 /**
289 * Begin Transaction
290 *
291 * @access public
292 * @return bool
293 */
294 function trans_begin($test_mode = FALSE)
295 {
296 if ( ! $this->trans_enabled)
297 {
298 return TRUE;
299 }
300
301 // When transactions are nested we only begin/commit/rollback the outermost ones
302 if ($this->_trans_depth > 0)
303 {
304 return TRUE;
305 }
306
307 // Reset the transaction failure flag.
308 // If the $test_mode flag is set to TRUE transactions will be rolled back
309 // even if the queries produce a successful result.
310 $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
311
312 $this->_commit = OCI_DEFAULT;
313 return TRUE;
314 }
315
316 // --------------------------------------------------------------------
317
318 /**
319 * Commit Transaction
320 *
321 * @access public
322 * @return bool
323 */
324 function trans_commit()
325 {
326 if ( ! $this->trans_enabled)
327 {
328 return TRUE;
329 }
330
331 // When transactions are nested we only begin/commit/rollback the outermost ones
332 if ($this->_trans_depth > 0)
333 {
334 return TRUE;
335 }
336
337 $ret = OCIcommit($this->conn_id);
338 $this->_commit = OCI_COMMIT_ON_SUCCESS;
339 return $ret;
340 }
341
342 // --------------------------------------------------------------------
343
344 /**
345 * Rollback Transaction
346 *
347 * @access public
348 * @return bool
349 */
350 function trans_rollback()
351 {
352 if ( ! $this->trans_enabled)
353 {
354 return TRUE;
355 }
356
357 // When transactions are nested we only begin/commit/rollback the outermost ones
358 if ($this->_trans_depth > 0)
359 {
360 return TRUE;
361 }
362
363 $ret = OCIrollback($this->conn_id);
364 $this->_commit = OCI_COMMIT_ON_SUCCESS;
365 return $ret;
366 }
367
368 // --------------------------------------------------------------------
369
370 /**
371 * Escape String
372 *
373 * @access public
374 * @param string
Derek Jonese4ed5832009-02-20 21:44:59 +0000375 * @param bool whether or not the string will be used in a LIKE condition
Derek Allard2067d1a2008-11-13 22:59:24 +0000376 * @return string
377 */
Derek Jonese4ed5832009-02-20 21:44:59 +0000378 function escape_str($str, $like = FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000379 {
Derek Jonese4ed5832009-02-20 21:44:59 +0000380 if (is_array($str))
381 {
382 foreach($str as $key => $val)
383 {
384 $str[$key] = $this->escape_str($val, $like);
385 }
386
387 return $str;
388 }
389
Derek Allard2067d1a2008-11-13 22:59:24 +0000390 // Access the CI object
391 $CI =& get_instance();
392
Derek Jonese4ed5832009-02-20 21:44:59 +0000393 $str = $CI->input->_remove_invisible_characters($str);
394
395 // escape LIKE condition wildcards
396 if ($like === TRUE)
397 {
398 $str = str_replace( array('%', '_', $this->_like_escape_chr),
399 array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr),
400 $str);
401 }
402
403 return $str;
Derek Allard2067d1a2008-11-13 22:59:24 +0000404 }
405
406 // --------------------------------------------------------------------
407
408 /**
409 * Affected Rows
410 *
411 * @access public
412 * @return integer
413 */
414 function affected_rows()
415 {
416 return @ocirowcount($this->stmt_id);
417 }
418
419 // --------------------------------------------------------------------
420
421 /**
422 * Insert ID
423 *
424 * @access public
425 * @return integer
426 */
427 function insert_id()
428 {
429 // not supported in oracle
430 return $this->display_error('db_unsupported_function');
431 }
432
433 // --------------------------------------------------------------------
434
435 /**
436 * "Count All" query
437 *
438 * Generates a platform-specific query string that counts all records in
439 * the specified database
440 *
441 * @access public
442 * @param string
443 * @return string
444 */
445 function count_all($table = '')
446 {
447 if ($table == '')
Derek Allarde37ab382009-02-03 16:13:57 +0000448 {
449 return 0;
450 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000451
Derek Allarde37ab382009-02-03 16:13:57 +0000452 $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE));
Derek Allard2067d1a2008-11-13 22:59:24 +0000453
454 if ($query == FALSE)
Derek Allarde37ab382009-02-03 16:13:57 +0000455 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000456 return 0;
Derek Allarde37ab382009-02-03 16:13:57 +0000457 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000458
459 $row = $query->row();
Derek Allarde37ab382009-02-03 16:13:57 +0000460 return (int) $row->numrows;
Derek Allard2067d1a2008-11-13 22:59:24 +0000461 }
462
463 // --------------------------------------------------------------------
464
465 /**
466 * Show table query
467 *
468 * Generates a platform-specific query string so that the table names can be fetched
469 *
470 * @access private
471 * @param boolean
472 * @return string
473 */
474 function _list_tables($prefix_limit = FALSE)
475 {
476 $sql = "SELECT TABLE_NAME FROM ALL_TABLES";
477
478 if ($prefix_limit !== FALSE AND $this->dbprefix != '')
479 {
480 $sql .= " WHERE TABLE_NAME LIKE '".$this->dbprefix."%'";
481 }
482
483 return $sql;
484 }
485
486 // --------------------------------------------------------------------
487
488 /**
489 * Show column query
490 *
491 * Generates a platform-specific query string so that the column names can be fetched
492 *
493 * @access public
494 * @param string the table name
495 * @return string
496 */
497 function _list_columns($table = '')
498 {
499 return "SELECT COLUMN_NAME FROM all_tab_columns WHERE table_name = '$table'";
500 }
501
502 // --------------------------------------------------------------------
503
504 /**
505 * Field data query
506 *
507 * Generates a platform-specific query so that the column data can be retrieved
508 *
509 * @access public
510 * @param string the table name
511 * @return object
512 */
513 function _field_data($table)
514 {
515 return "SELECT * FROM ".$table." where rownum = 1";
516 }
517
518 // --------------------------------------------------------------------
519
520 /**
521 * The error message string
522 *
523 * @access private
524 * @return string
525 */
526 function _error_message()
527 {
528 $error = ocierror($this->conn_id);
529 return $error['message'];
530 }
531
532 // --------------------------------------------------------------------
533
534 /**
535 * The error message number
536 *
537 * @access private
538 * @return integer
539 */
540 function _error_number()
541 {
542 $error = ocierror($this->conn_id);
543 return $error['code'];
544 }
545
546 // --------------------------------------------------------------------
547
548 /**
549 * Escape the SQL Identifiers
550 *
551 * This function escapes column and table names
552 *
553 * @access private
554 * @param string
555 * @return string
556 */
557 function _escape_identifiers($item)
558 {
559 if ($this->_escape_char == '')
560 {
561 return $item;
562 }
563
564 foreach ($this->_reserved_identifiers as $id)
565 {
566 if (strpos($item, '.'.$id) !== FALSE)
567 {
568 $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item);
569
570 // remove duplicates if the user already included the escape
571 return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
572 }
573 }
574
575 if (strpos($item, '.') !== FALSE)
576 {
577 $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char;
578 }
579 else
580 {
581 $str = $this->_escape_char.$item.$this->_escape_char;
582 }
583
584 // remove duplicates if the user already included the escape
585 return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
586 }
587
588 // --------------------------------------------------------------------
589
590 /**
591 * From Tables
592 *
593 * This function implicitly groups FROM tables so there is no confusion
594 * about operator precedence in harmony with SQL standards
595 *
596 * @access public
597 * @param type
598 * @return type
599 */
600 function _from_tables($tables)
601 {
602 if ( ! is_array($tables))
603 {
604 $tables = array($tables);
605 }
606
607 return implode(', ', $tables);
608 }
609
610 // --------------------------------------------------------------------
611
612 /**
613 * Insert statement
614 *
615 * Generates a platform-specific insert string from the supplied data
616 *
617 * @access public
618 * @param string the table name
619 * @param array the insert keys
620 * @param array the insert values
621 * @return string
622 */
623 function _insert($table, $keys, $values)
624 {
625 return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
626 }
627
628 // --------------------------------------------------------------------
629
630 /**
631 * Update statement
632 *
633 * Generates a platform-specific update string from the supplied data
634 *
635 * @access public
636 * @param string the table name
637 * @param array the update data
638 * @param array the where clause
639 * @param array the orderby clause
640 * @param array the limit clause
641 * @return string
642 */
643 function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
644 {
645 foreach($values as $key => $val)
646 {
647 $valstr[] = $key." = ".$val;
648 }
649
650 $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
651
652 $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
653
654 $sql = "UPDATE ".$table." SET ".implode(', ', $valstr);
655
656 $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
657
658 $sql .= $orderby.$limit;
659
660 return $sql;
661 }
662
663 // --------------------------------------------------------------------
664
665 /**
666 * Truncate statement
667 *
668 * Generates a platform-specific truncate string from the supplied data
669 * If the database does not support the truncate() command
670 * This function maps to "DELETE FROM table"
671 *
672 * @access public
673 * @param string the table name
674 * @return string
675 */
676 function _truncate($table)
677 {
678 return "TRUNCATE TABLE ".$table;
679 }
680
681 // --------------------------------------------------------------------
682
683 /**
684 * Delete statement
685 *
686 * Generates a platform-specific delete string from the supplied data
687 *
688 * @access public
689 * @param string the table name
690 * @param array the where clause
691 * @param string the limit clause
692 * @return string
693 */
694 function _delete($table, $where = array(), $like = array(), $limit = FALSE)
695 {
696 $conditions = '';
697
698 if (count($where) > 0 OR count($like) > 0)
699 {
700 $conditions = "\nWHERE ";
701 $conditions .= implode("\n", $this->ar_where);
702
703 if (count($where) > 0 && count($like) > 0)
704 {
705 $conditions .= " AND ";
706 }
707 $conditions .= implode("\n", $like);
708 }
709
710 $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
711
712 return "DELETE FROM ".$table.$conditions.$limit;
713 }
714
715 // --------------------------------------------------------------------
716
717 /**
718 * Limit string
719 *
720 * Generates a platform-specific LIMIT clause
721 *
722 * @access public
723 * @param string the sql query string
724 * @param integer the number of rows to limit the query to
725 * @param integer the offset value
726 * @return string
727 */
728 function _limit($sql, $limit, $offset)
729 {
730 $limit = $offset + $limit;
731 $newsql = "SELECT * FROM (select inner_query.*, rownum rnum FROM ($sql) inner_query WHERE rownum < $limit)";
732
733 if ($offset != 0)
734 {
735 $newsql .= " WHERE rnum >= $offset";
736 }
737
738 // remember that we used limits
739 $this->limit_used = TRUE;
740
741 return $newsql;
742 }
743
744 // --------------------------------------------------------------------
745
746 /**
747 * Close DB Connection
748 *
749 * @access public
750 * @param resource
751 * @return void
752 */
753 function _close($conn_id)
754 {
755 @ocilogoff($conn_id);
756 }
757
758
759}
760
761
762
763/* End of file oci8_driver.php */
Derek Jonesa3ffbbb2008-05-11 18:18:29 +0000764/* Location: ./system/database/drivers/oci8/oci8_driver.php */