blob: 99f7d57bd302edff07d8a874614e900bc2f917ef [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 {
158 if ( ! is_resource($this->stmt_id))
159 {
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 {
257 if ( ! is_array($params) OR ! is_resource($this->stmt_id))
258 {
259 return;
260 }
261
262 foreach ($params as $param)
263 {
264 foreach (array('name', 'value', 'type', 'length') as $val)
265 {
266 if ( ! isset($param[$val]))
267 {
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 {
286 if ( ! $this->trans_enabled)
287 {
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 {
316 if ( ! $this->trans_enabled)
317 {
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 {
342 if ( ! $this->trans_enabled)
343 {
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 {
524 if (stristr($table, '.'))
525 {
526 $table = preg_replace("/\./", "`.`", $table);
527 }
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
559 // 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 {
562 // This function may get "field >= 1", and need it to return "`field` >= 1"
Derek Allard61579382008-01-16 22:22:42 +0000563 $lbound = ($first_word_only === TRUE) ? '' : '|\s|\(';
Derek Allard39b622d2008-01-16 21:10:09 +0000564
Derek Allard61579382008-01-16 22:22:42 +0000565 $item = preg_replace('/(^'.$lbound.')([\w\d\-\_]+?)(\s|\)|$)/iS', '$1`$2`$3', $item);
566 }
567 else
568 {
569 return "`{$item}`";
Derek Allard39b622d2008-01-16 21:10:09 +0000570 }
571
572 $exceptions = array('AS', '/', '-', '%', '+', '*');
573
574 foreach ($exceptions as $exception)
575 {
Derek Allard61579382008-01-16 22:22:42 +0000576
Derek Allard39b622d2008-01-16 21:10:09 +0000577 if (stristr($item, " `{$exception}` ") !== FALSE)
578 {
579 $item = preg_replace('/ `('.preg_quote($exception).')` /i', ' $1 ', $item);
580 }
581 }
Derek Allard39b622d2008-01-16 21:10:09 +0000582 return $item;
583 }
584
585 // --------------------------------------------------------------------
586
587 /**
Derek Allardd2df9bc2007-04-15 17:41:17 +0000588 * Insert statement
589 *
590 * Generates a platform-specific insert string from the supplied data
591 *
592 * @access public
593 * @param string the table name
594 * @param array the insert keys
595 * @param array the insert values
596 * @return string
597 */
598 function _insert($table, $keys, $values)
599 {
600 return "INSERT INTO ".$this->_escape_table($table)." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
601 }
602
603 // --------------------------------------------------------------------
604
605 /**
606 * Update statement
607 *
608 * Generates a platform-specific update string from the supplied data
609 *
Derek Allard39b622d2008-01-16 21:10:09 +0000610 * @access public
611 * @param string the table name
612 * @param array the update data
613 * @param array the where clause
614 * @param array the orderby clause
615 * @param array the limit clause
616 * @return string
Derek Allardd2df9bc2007-04-15 17:41:17 +0000617 */
Derek Allard39b622d2008-01-16 21:10:09 +0000618 function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000619 {
620 foreach($values as $key => $val)
621 {
622 $valstr[] = $key." = ".$val;
623 }
Derek Allardda6d2402007-12-19 14:49:29 +0000624
625 $limit = (!$limit) ? '' : ' LIMIT '.$limit;
Derek Allard39b622d2008-01-16 21:10:09 +0000626
627 $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
Derek Allardda6d2402007-12-19 14:49:29 +0000628
Derek Allard39b622d2008-01-16 21:10:09 +0000629 return "UPDATE ".$this->_escape_table($table)." SET ".implode(', ', $valstr)." WHERE ".implode(" ", $where).$orderby.$limit;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000630 }
631
632 // --------------------------------------------------------------------
633
634 /**
Derek Allard39b622d2008-01-16 21:10:09 +0000635 * Truncate statement
636 *
637 * Generates a platform-specific truncate string from the supplied data
638 * If the database does not support the truncate() command
639 * This function maps to "DELETE FROM table"
640 *
641 * @access public
642 * @param string the table name
643 * @return string
644 */
645 function _truncate($table)
646 {
647 return "TRUNCATE TABLE ".$this->_escape_table($table);
648 }
649
650 // --------------------------------------------------------------------
651
652 /**
Derek Allardd2df9bc2007-04-15 17:41:17 +0000653 * Delete statement
654 *
655 * Generates a platform-specific delete string from the supplied data
656 *
Derek Allard39b622d2008-01-16 21:10:09 +0000657 * @access public
658 * @param string the table name
659 * @param array the where clause
660 * @param string the limit clause
661 * @return string
662 */
663 function _delete($table, $where = array(), $like = array(), $limit = FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000664 {
Derek Allard39b622d2008-01-16 21:10:09 +0000665 $conditions = '';
666
667 if (count($where) > 0 || count($like) > 0)
668 {
669 $conditions = "\nWHERE ";
670 $conditions .= implode("\n", $this->ar_where);
671
672 if (count($where) > 0 && count($like) > 0)
673 {
674 $conditions .= " AND ";
675 }
676 $conditions .= implode("\n", $like);
677 }
678
Derek Allarde77d77c2007-12-19 15:01:55 +0000679 $limit = (!$limit) ? '' : ' LIMIT '.$limit;
680
Derek Allard39b622d2008-01-16 21:10:09 +0000681 return "DELETE FROM ".$table.$conditions.$limit;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000682 }
683
684 // --------------------------------------------------------------------
685
686 /**
687 * Limit string
688 *
689 * Generates a platform-specific LIMIT clause
690 *
691 * @access public
692 * @param string the sql query string
693 * @param integer the number of rows to limit the query to
694 * @param integer the offset value
695 * @return string
696 */
697 function _limit($sql, $limit, $offset)
698 {
699 $limit = $offset + $limit;
700 $newsql = "SELECT * FROM (select inner_query.*, rownum rnum FROM ($sql) inner_query WHERE rownum < $limit)";
701
702 if ($offset != 0)
703 {
704 $newsql .= " WHERE rnum >= $offset";
705 }
706
707 // remember that we used limits
708 $this->limit_used = TRUE;
709
710 return $newsql;
711 }
712
713 // --------------------------------------------------------------------
714
715 /**
716 * Close DB Connection
717 *
718 * @access public
719 * @param resource
720 * @return void
721 */
722 function _close($conn_id)
723 {
724 @ocilogoff($conn_id);
725 }
726
727
728}
729
730
admin99e1b292006-09-24 18:12:43 +0000731?>