blob: 8fc20449ffd11fe54872680b9b8f4b4c78c49a7c [file] [log] [blame]
Derek Allardd2df9bc2007-04-15 17:41:17 +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 *
Derek Allard3d879d52008-01-18 19:41:32 +00007 * @package CodeIgniter
8 * @author ExpressionEngine Dev Team
Derek Allardd2df9bc2007-04-15 17:41:17 +00009 * @copyright Copyright (c) 2006, EllisLab, Inc.
Derek Jones7a9193a2008-01-21 18:39:20 +000010 * @license http://codeigniter.com/user_guide/license.html
11 * @link http://codeigniter.com
Derek Allard3d879d52008-01-18 19:41:32 +000012 * @since Version 1.0
Derek Allardd2df9bc2007-04-15 17:41:17 +000013 * @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 *
Derek Allard3d879d52008-01-18 19:41:32 +000025 * @package CodeIgniter
Derek Allardd2df9bc2007-04-15 17:41:17 +000026 * @subpackage Drivers
27 * @category Database
Derek Allard3d879d52008-01-18 19:41:32 +000028 * @author ExpressionEngine Dev Team
Derek Jones7a9193a2008-01-21 18:39:20 +000029 * @link http://codeigniter.com/user_guide/database/
Derek Allardd2df9bc2007-04-15 17:41:17 +000030 */
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
Derek Allard694b5b82007-12-18 15:58:03 +000046 /**
47 * The syntax to count rows is slightly different across different
48 * database engines, so this string appears in each driver and is
49 * used for the count_all() and count_all_results() functions.
50 */
Derek Allard39b622d2008-01-16 21:10:09 +000051 var $_count_string = "SELECT COUNT(1) AS ";
52 var $_random_keyword = ' ASC'; // not currently supported
Derek Allard694b5b82007-12-18 15:58:03 +000053
Derek Allardd2df9bc2007-04-15 17:41:17 +000054 // Set "auto commit" by default
55 var $_commit = OCI_COMMIT_ON_SUCCESS;
56
57 // need to track statement id and cursor id
58 var $stmt_id;
59 var $curs_id;
60
61 // if we use a limit, we will add a field that will
62 // throw off num_fields later
63 var $limit_used;
64
65 /**
66 * Non-persistent database connection
67 *
68 * @access private called by the base class
69 * @return resource
70 */
71 function db_connect()
72 {
73 return @ocilogon($this->username, $this->password, $this->hostname);
74 }
75
76 // --------------------------------------------------------------------
77
78 /**
79 * Persistent database connection
80 *
81 * @access private called by the base class
82 * @return resource
83 */
84 function db_pconnect()
85 {
86 return @ociplogon($this->username, $this->password, $this->hostname);
87 }
88
89 // --------------------------------------------------------------------
90
91 /**
92 * Select the database
93 *
94 * @access private called by the base class
95 * @return resource
96 */
97 function db_select()
98 {
99 return TRUE;
100 }
101
102 // --------------------------------------------------------------------
103
104 /**
Derek Allard39b622d2008-01-16 21:10:09 +0000105 * Set client character set
106 *
107 * @access public
108 * @param string
109 * @param string
110 * @return resource
111 */
112 function db_set_charset($charset, $collation)
113 {
114 // TODO - add support if needed
115 return TRUE;
116 }
117
118 // --------------------------------------------------------------------
119
120 /**
Derek Allardd2df9bc2007-04-15 17:41:17 +0000121 * Version number query string
122 *
123 * @access public
124 * @return string
125 */
126 function _version()
127 {
128 return ociserverversion($this->conn_id);
129 }
130
131 // --------------------------------------------------------------------
132
133 /**
134 * Execute the query
135 *
136 * @access private called by the base class
137 * @param string an SQL query
138 * @return resource
139 */
140 function _execute($sql)
141 {
142 // oracle must parse the query before it is run. All of the actions with
143 // the query are based on the statement id returned by ociparse
144 $this->_set_stmt_id($sql);
145 ocisetprefetch($this->stmt_id, 1000);
146 return @ociexecute($this->stmt_id, $this->_commit);
147 }
148
149 /**
150 * Generate a statement ID
151 *
152 * @access private
153 * @param string an SQL query
154 * @return none
155 */
156 function _set_stmt_id($sql)
157 {
Derek Allard73274992008-05-05 16:39:18 +0000158 if (! is_resource($this->stmt_id))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000159 {
160 $this->stmt_id = ociparse($this->conn_id, $this->_prep_query($sql));
161 }
162 }
163
164 // --------------------------------------------------------------------
165
166 /**
167 * Prep the query
168 *
169 * If needed, each database adapter can prep the query string
170 *
171 * @access private called by execute()
172 * @param string an SQL query
173 * @return string
174 */
175 function _prep_query($sql)
176 {
177 return $sql;
178 }
179
180 // --------------------------------------------------------------------
181
182 /**
183 * getCursor. Returns a cursor from the datbase
184 *
185 * @access public
186 * @return cursor id
187 */
188 function get_cursor()
189 {
190 $this->curs_id = ocinewcursor($this->conn_id);
191 return $this->curs_id;
192 }
193
194 // --------------------------------------------------------------------
195
196 /**
197 * Stored Procedure. Executes a stored procedure
198 *
199 * @access public
200 * @param package package stored procedure is in
201 * @param procedure stored procedure to execute
202 * @param params array of parameters
203 * @return array
204 *
205 * params array keys
206 *
207 * KEY OPTIONAL NOTES
208 * name no the name of the parameter should be in :<param_name> format
209 * value no the value of the parameter. If this is an OUT or IN OUT parameter,
210 * this should be a reference to a variable
211 * type yes the type of the parameter
212 * length yes the max size of the parameter
213 */
214 function stored_procedure($package, $procedure, $params)
215 {
216 if ($package == '' OR $procedure == '' OR ! is_array($params))
217 {
218 if ($this->db_debug)
219 {
220 log_message('error', 'Invalid query: '.$package.'.'.$procedure);
221 return $this->display_error('db_invalid_query');
222 }
223 return FALSE;
224 }
225
226 // build the query string
227 $sql = "begin $package.$procedure(";
228
229 $have_cursor = FALSE;
230 foreach($params as $param)
231 {
232 $sql .= $param['name'] . ",";
233
234 if (array_key_exists('type', $param) && ($param['type'] == OCI_B_CURSOR))
235 {
236 $have_cursor = TRUE;
237 }
238 }
239 $sql = trim($sql, ",") . "); end;";
240
241 $this->stmt_id = FALSE;
242 $this->_set_stmt_id($sql);
243 $this->_bind_params($params);
244 $this->query($sql, FALSE, $have_cursor);
245 }
246
247 // --------------------------------------------------------------------
248
249 /**
250 * Bind parameters
251 *
252 * @access private
253 * @return none
254 */
255 function _bind_params($params)
256 {
Derek Allard73274992008-05-05 16:39:18 +0000257 if (! is_array($params) OR ! is_resource($this->stmt_id))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000258 {
259 return;
260 }
261
262 foreach ($params as $param)
263 {
264 foreach (array('name', 'value', 'type', 'length') as $val)
265 {
Derek Allard73274992008-05-05 16:39:18 +0000266 if (! isset($param[$val]))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000267 {
268 $param[$val] = '';
269 }
270 }
271
272 ocibindbyname($this->stmt_id, $param['name'], $param['value'], $param['length'], $param['type']);
273 }
274 }
275
276 // --------------------------------------------------------------------
277
278 /**
279 * Begin Transaction
280 *
281 * @access public
282 * @return bool
283 */
284 function trans_begin($test_mode = FALSE)
285 {
Derek Allard73274992008-05-05 16:39:18 +0000286 if (! $this->trans_enabled)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000287 {
288 return TRUE;
289 }
290
291 // When transactions are nested we only begin/commit/rollback the outermost ones
292 if ($this->_trans_depth > 0)
293 {
294 return TRUE;
295 }
296
297 // Reset the transaction failure flag.
298 // If the $test_mode flag is set to TRUE transactions will be rolled back
299 // even if the queries produce a successful result.
300 $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
301
302 $this->_commit = OCI_DEFAULT;
303 return TRUE;
304 }
305
306 // --------------------------------------------------------------------
307
308 /**
309 * Commit Transaction
310 *
311 * @access public
312 * @return bool
313 */
314 function trans_commit()
315 {
Derek Allard73274992008-05-05 16:39:18 +0000316 if (! $this->trans_enabled)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000317 {
318 return TRUE;
319 }
320
321 // When transactions are nested we only begin/commit/rollback the outermost ones
322 if ($this->_trans_depth > 0)
323 {
324 return TRUE;
325 }
326
327 $ret = OCIcommit($this->conn_id);
328 $this->_commit = OCI_COMMIT_ON_SUCCESS;
329 return $ret;
330 }
331
332 // --------------------------------------------------------------------
333
334 /**
335 * Rollback Transaction
336 *
337 * @access public
338 * @return bool
339 */
340 function trans_rollback()
341 {
Derek Allard73274992008-05-05 16:39:18 +0000342 if (! $this->trans_enabled)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000343 {
344 return TRUE;
345 }
346
347 // When transactions are nested we only begin/commit/rollback the outermost ones
348 if ($this->_trans_depth > 0)
349 {
350 return TRUE;
351 }
352
353 $ret = OCIrollback($this->conn_id);
354 $this->_commit = OCI_COMMIT_ON_SUCCESS;
355 return $ret;
356 }
357
358 // --------------------------------------------------------------------
359
360 /**
361 * Escape String
362 *
363 * @access public
364 * @param string
365 * @return string
366 */
367 function escape_str($str)
368 {
369 return $str;
370 }
371
372 // --------------------------------------------------------------------
373
374 /**
375 * Affected Rows
376 *
377 * @access public
378 * @return integer
379 */
380 function affected_rows()
381 {
382 return @ocirowcount($this->stmt_id);
383 }
384
385 // --------------------------------------------------------------------
386
387 /**
388 * Insert ID
389 *
390 * @access public
391 * @return integer
392 */
393 function insert_id()
394 {
395 // not supported in oracle
Derek Allarddb708af2008-01-23 17:18:41 +0000396 return $this->display_error('db_unsupported_function');
Derek Allardd2df9bc2007-04-15 17:41:17 +0000397 }
398
399 // --------------------------------------------------------------------
400
401 /**
402 * "Count All" query
403 *
404 * Generates a platform-specific query string that counts all records in
405 * the specified database
406 *
407 * @access public
408 * @param string
409 * @return string
410 */
411 function count_all($table = '')
412 {
413 if ($table == '')
414 return '0';
415
Derek Allardf6cd45c2008-01-18 14:31:51 +0000416 $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows'). " FROM " . $this->_protect_identifiers($this->dbprefix.$table));
Derek Allardd2df9bc2007-04-15 17:41:17 +0000417
418 if ($query == FALSE)
419 {
420 return 0;
421 }
422
423 $row = $query->row();
424 return $row->NUMROWS;
425 }
426
427 // --------------------------------------------------------------------
428
429 /**
430 * Show table query
431 *
432 * Generates a platform-specific query string so that the table names can be fetched
433 *
434 * @access private
Derek Allard39b622d2008-01-16 21:10:09 +0000435 * @param boolean
Derek Allardd2df9bc2007-04-15 17:41:17 +0000436 * @return string
437 */
Derek Allard39b622d2008-01-16 21:10:09 +0000438 function _list_tables($prefix_limit = FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000439 {
Derek Allard39b622d2008-01-16 21:10:09 +0000440 $sql = "SELECT TABLE_NAME FROM ALL_TABLES";
441
442 if ($prefix_limit !== FALSE AND $this->dbprefix != '')
443 {
444 $sql .= " WHERE TABLE_NAME LIKE '".$this->dbprefix."%'";
445 }
446
447 return $sql;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000448 }
449
450 // --------------------------------------------------------------------
451
452 /**
453 * Show column query
454 *
455 * Generates a platform-specific query string so that the column names can be fetched
456 *
457 * @access public
458 * @param string the table name
459 * @return string
460 */
461 function _list_columns($table = '')
462 {
463 return "SELECT COLUMN_NAME FROM all_tab_columns WHERE table_name = '$table'";
464 }
465
466 // --------------------------------------------------------------------
467
468 /**
469 * Field data query
470 *
471 * Generates a platform-specific query so that the column data can be retrieved
472 *
473 * @access public
474 * @param string the table name
475 * @return object
476 */
477 function _field_data($table)
478 {
479 return "SELECT * FROM ".$this->_escape_table($table)." where rownum = 1";
480 }
481
482 // --------------------------------------------------------------------
483
484 /**
485 * The error message string
486 *
487 * @access private
488 * @return string
489 */
490 function _error_message()
491 {
492 $error = ocierror($this->conn_id);
493 return $error['message'];
494 }
495
496 // --------------------------------------------------------------------
497
498 /**
499 * The error message number
500 *
501 * @access private
502 * @return integer
503 */
504 function _error_number()
505 {
506 $error = ocierror($this->conn_id);
507 return $error['code'];
508 }
509
510 // --------------------------------------------------------------------
511
512 /**
513 * Escape Table Name
514 *
515 * This function adds backticks if the table name has a period
516 * in it. Some DBs will get cranky unless periods are escaped
517 *
518 * @access private
519 * @param string the table name
520 * @return string
521 */
522 function _escape_table($table)
523 {
Derek Allardc0743382008-02-11 05:54:44 +0000524 if (strpos($table, '.') !== FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000525 {
Derek Allardc0743382008-02-11 05:54:44 +0000526 $table = '"' . str_replace('.', '"."', $table) . '"';
Derek Allardd2df9bc2007-04-15 17:41:17 +0000527 }
528
529 return $table;
530 }
531
532 // --------------------------------------------------------------------
533
534 /**
Derek Allard39b622d2008-01-16 21:10:09 +0000535 * Protect Identifiers
536 *
537 * This function adds backticks if appropriate based on db type
538 *
539 * @access private
540 * @param mixed the item to escape
541 * @param boolean only affect the first word
542 * @return mixed the item with backticks
543 */
544 function _protect_identifiers($item, $first_word_only = FALSE)
545 {
546 if (is_array($item))
547 {
548 $escaped_array = array();
549
550 foreach($item as $k=>$v)
551 {
552 $escaped_array[$this->_protect_identifiers($k)] = $this->_protect_identifiers($v, $first_word_only);
553 }
554
555 return $escaped_array;
556 }
557
558 // This function may get "item1 item2" as a string, and so
Derek Allard15648132008-02-10 21:46:18 +0000559 // we may need ""item1" "item2"" and not ""item1 item2""
Derek Allard61579382008-01-16 22:22:42 +0000560 if (ctype_alnum($item) === FALSE)
Derek Allard39b622d2008-01-16 21:10:09 +0000561 {
Derek Allard9b3e7b52008-02-04 23:20:34 +0000562 if (strpos($item, '.') !== FALSE)
563 {
564 $aliased_tables = implode(".",$this->ar_aliased_tables).'.';
565 $table_name = substr($item, 0, strpos($item, '.')+1);
566 $item = (strpos($aliased_tables, $table_name) !== FALSE) ? $item = $item : $this->dbprefix.$item;
567 }
568
Derek Allard15648132008-02-10 21:46:18 +0000569 // This function may get "field >= 1", and need it to return ""field" >= 1"
Derek Allard61579382008-01-16 22:22:42 +0000570 $lbound = ($first_word_only === TRUE) ? '' : '|\s|\(';
Derek Allard39b622d2008-01-16 21:10:09 +0000571
Derek Allard15648132008-02-10 21:46:18 +0000572 $item = preg_replace('/(^'.$lbound.')([\w\d\-\_]+?)(\s|\)|$)/iS', '$1"$2"$3', $item);
Derek Allard61579382008-01-16 22:22:42 +0000573 }
574 else
575 {
Derek Allard15648132008-02-10 21:46:18 +0000576 return "\"{$item}\"";
Derek Allard39b622d2008-01-16 21:10:09 +0000577 }
578
Derek Allard9a4d1da2008-02-25 14:18:38 +0000579 $exceptions = array('AS', '/', '-', '%', '+', '*', 'OR', 'IS');
Derek Allard39b622d2008-01-16 21:10:09 +0000580
581 foreach ($exceptions as $exception)
582 {
Derek Allard61579382008-01-16 22:22:42 +0000583
Derek Allard15648132008-02-10 21:46:18 +0000584 if (stristr($item, " \"{$exception}\" ") !== FALSE)
Derek Allard39b622d2008-01-16 21:10:09 +0000585 {
Derek Allard15648132008-02-10 21:46:18 +0000586 $item = preg_replace('/ "('.preg_quote($exception).')" /i', ' $1 ', $item);
Derek Allard39b622d2008-01-16 21:10:09 +0000587 }
588 }
Derek Allard39b622d2008-01-16 21:10:09 +0000589 return $item;
590 }
591
592 // --------------------------------------------------------------------
593
594 /**
Derek Jonesc6ad0232008-01-29 18:44:54 +0000595 * From Tables
596 *
597 * This function implicitly groups FROM tables so there is no confusion
598 * about operator precedence in harmony with SQL standards
599 *
600 * @access public
601 * @param type
602 * @return type
603 */
604 function _from_tables($tables)
605 {
606 if (! is_array($tables))
607 {
608 $tables = array($tables);
609 }
610
Derek Allard15648132008-02-10 21:46:18 +0000611 return implode(', ', $tables);
Derek Jonesc6ad0232008-01-29 18:44:54 +0000612 }
613
614 // --------------------------------------------------------------------
615
616 /**
Derek Allardd2df9bc2007-04-15 17:41:17 +0000617 * Insert statement
618 *
619 * Generates a platform-specific insert string from the supplied data
620 *
621 * @access public
622 * @param string the table name
623 * @param array the insert keys
624 * @param array the insert values
625 * @return string
626 */
627 function _insert($table, $keys, $values)
628 {
629 return "INSERT INTO ".$this->_escape_table($table)." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
630 }
631
632 // --------------------------------------------------------------------
633
634 /**
635 * Update statement
636 *
637 * Generates a platform-specific update string from the supplied data
638 *
Derek Allard39b622d2008-01-16 21:10:09 +0000639 * @access public
640 * @param string the table name
641 * @param array the update data
642 * @param array the where clause
643 * @param array the orderby clause
644 * @param array the limit clause
645 * @return string
Derek Allardd2df9bc2007-04-15 17:41:17 +0000646 */
Derek Allard39b622d2008-01-16 21:10:09 +0000647 function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000648 {
649 foreach($values as $key => $val)
650 {
651 $valstr[] = $key." = ".$val;
652 }
Derek Allardda6d2402007-12-19 14:49:29 +0000653
654 $limit = (!$limit) ? '' : ' LIMIT '.$limit;
Derek Allard39b622d2008-01-16 21:10:09 +0000655
656 $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
Derek Allardda6d2402007-12-19 14:49:29 +0000657
Derek Allard32cf7eb2008-02-05 16:03:50 +0000658 $sql = "UPDATE ".$this->_escape_table($table)." SET ".implode(', ', $valstr);
659 $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
660 $sql .= $orderby.$limit;
661
662 return $sql;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000663 }
664
665 // --------------------------------------------------------------------
666
667 /**
Derek Allard39b622d2008-01-16 21:10:09 +0000668 * Truncate statement
669 *
670 * Generates a platform-specific truncate string from the supplied data
671 * If the database does not support the truncate() command
672 * This function maps to "DELETE FROM table"
673 *
674 * @access public
675 * @param string the table name
676 * @return string
677 */
678 function _truncate($table)
679 {
680 return "TRUNCATE TABLE ".$this->_escape_table($table);
681 }
682
683 // --------------------------------------------------------------------
684
685 /**
Derek Allardd2df9bc2007-04-15 17:41:17 +0000686 * Delete statement
687 *
688 * Generates a platform-specific delete string from the supplied data
689 *
Derek Allard39b622d2008-01-16 21:10:09 +0000690 * @access public
691 * @param string the table name
692 * @param array the where clause
693 * @param string the limit clause
694 * @return string
695 */
696 function _delete($table, $where = array(), $like = array(), $limit = FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000697 {
Derek Allard39b622d2008-01-16 21:10:09 +0000698 $conditions = '';
699
700 if (count($where) > 0 || count($like) > 0)
701 {
702 $conditions = "\nWHERE ";
703 $conditions .= implode("\n", $this->ar_where);
704
705 if (count($where) > 0 && count($like) > 0)
706 {
707 $conditions .= " AND ";
708 }
709 $conditions .= implode("\n", $like);
710 }
711
Derek Allarde77d77c2007-12-19 15:01:55 +0000712 $limit = (!$limit) ? '' : ' LIMIT '.$limit;
713
Derek Allard39b622d2008-01-16 21:10:09 +0000714 return "DELETE FROM ".$table.$conditions.$limit;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000715 }
716
717 // --------------------------------------------------------------------
718
719 /**
720 * Limit string
721 *
722 * Generates a platform-specific LIMIT clause
723 *
724 * @access public
725 * @param string the sql query string
726 * @param integer the number of rows to limit the query to
727 * @param integer the offset value
728 * @return string
729 */
730 function _limit($sql, $limit, $offset)
731 {
732 $limit = $offset + $limit;
733 $newsql = "SELECT * FROM (select inner_query.*, rownum rnum FROM ($sql) inner_query WHERE rownum < $limit)";
734
735 if ($offset != 0)
736 {
737 $newsql .= " WHERE rnum >= $offset";
738 }
739
740 // remember that we used limits
741 $this->limit_used = TRUE;
742
743 return $newsql;
744 }
745
746 // --------------------------------------------------------------------
747
748 /**
749 * Close DB Connection
750 *
751 * @access public
752 * @param resource
753 * @return void
754 */
755 function _close($conn_id)
756 {
757 @ocilogoff($conn_id);
758 }
759
760
761}
762
763
admin99e1b292006-09-24 18:12:43 +0000764?>