blob: 02b975ad2d81231859c5fc4b2ab3b9d060f3510c [file] [log] [blame]
Derek Jones0b59f272008-05-13 04:22:33 +00001<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
Derek Allardd2df9bc2007-04-15 17:41:17 +00002/**
3 * CodeIgniter
4 *
5 * An open source application development framework for PHP 4.3.2 or newer
6 *
7 * @package CodeIgniter
Derek Allard3d879d52008-01-18 19:41:32 +00008 * @author ExpressionEngine Dev Team
Rick Ellis37b3ecf2008-09-12 23:34:18 +00009 * @copyright Copyright (c) 2008, EllisLab, Inc.
Derek Jones7a9193a2008-01-21 18:39:20 +000010 * @license http://codeigniter.com/user_guide/license.html
11 * @link http://codeigniter.com
Derek Allardd2df9bc2007-04-15 17:41:17 +000012 * @since Version 1.0
13 * @filesource
14 */
15
16// ------------------------------------------------------------------------
17
18/**
19 * MS SQL 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
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 */
31class CI_DB_mssql_driver extends CI_DB {
32
33 /**
Derek Allard694b5b82007-12-18 15:58:03 +000034 * The syntax to count rows is slightly different across different
35 * database engines, so this string appears in each driver and is
36 * used for the count_all() and count_all_results() functions.
37 */
Derek Allard39b622d2008-01-16 21:10:09 +000038 var $_count_string = "SELECT COUNT(*) AS ";
39 var $_random_keyword = ' ASC'; // not currently supported
Rick Ellis5aa8c602008-10-07 01:24:07 +000040 var $dbdriver = 'mssql';
41
Derek Allard694b5b82007-12-18 15:58:03 +000042 /**
Derek Allardd2df9bc2007-04-15 17:41:17 +000043 * Non-persistent database connection
44 *
45 * @access private called by the base class
46 * @return resource
47 */
48 function db_connect()
49 {
50 return @mssql_connect($this->hostname, $this->username, $this->password);
51 }
52
53 // --------------------------------------------------------------------
54
55 /**
56 * Persistent database connection
57 *
58 * @access private called by the base class
59 * @return resource
60 */
61 function db_pconnect()
62 {
63 return @mssql_pconnect($this->hostname, $this->username, $this->password);
64 }
65
66 // --------------------------------------------------------------------
67
68 /**
69 * Select the database
70 *
71 * @access private called by the base class
72 * @return resource
73 */
74 function db_select()
75 {
76 return @mssql_select_db($this->database, $this->conn_id);
77 }
78
79 // --------------------------------------------------------------------
80
81 /**
Derek Allard39b622d2008-01-16 21:10:09 +000082 * Set client character set
83 *
84 * @access public
85 * @param string
86 * @param string
87 * @return resource
88 */
89 function db_set_charset($charset, $collation)
90 {
Rick Ellisff734012008-09-30 20:38:12 +000091 // @todo - add support if needed
Derek Allard39b622d2008-01-16 21:10:09 +000092 return TRUE;
93 }
94
95 // --------------------------------------------------------------------
96
97 /**
Derek Allardd2df9bc2007-04-15 17:41:17 +000098 * Execute the query
99 *
100 * @access private called by the base class
101 * @param string an SQL query
102 * @return resource
103 */
104 function _execute($sql)
105 {
106 $sql = $this->_prep_query($sql);
107 return @mssql_query($sql, $this->conn_id);
108 }
109
110 // --------------------------------------------------------------------
111
112 /**
113 * Prep the query
114 *
115 * If needed, each database adapter can prep the query string
116 *
117 * @access private called by execute()
118 * @param string an SQL query
119 * @return string
120 */
121 function _prep_query($sql)
122 {
123 return $sql;
124 }
125
126 // --------------------------------------------------------------------
127
128 /**
129 * Begin Transaction
130 *
131 * @access public
132 * @return bool
133 */
134 function trans_begin($test_mode = FALSE)
135 {
Derek Jones0b59f272008-05-13 04:22:33 +0000136 if ( ! $this->trans_enabled)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000137 {
138 return TRUE;
139 }
140
141 // When transactions are nested we only begin/commit/rollback the outermost ones
142 if ($this->_trans_depth > 0)
143 {
144 return TRUE;
145 }
146
147 // Reset the transaction failure flag.
148 // If the $test_mode flag is set to TRUE transactions will be rolled back
149 // even if the queries produce a successful result.
150 $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
151
152 $this->simple_query('BEGIN TRAN');
153 return TRUE;
154 }
155
156 // --------------------------------------------------------------------
157
158 /**
159 * Commit Transaction
160 *
161 * @access public
162 * @return bool
163 */
164 function trans_commit()
165 {
Derek Jones0b59f272008-05-13 04:22:33 +0000166 if ( ! $this->trans_enabled)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000167 {
168 return TRUE;
169 }
170
171 // When transactions are nested we only begin/commit/rollback the outermost ones
172 if ($this->_trans_depth > 0)
173 {
174 return TRUE;
175 }
176
177 $this->simple_query('COMMIT TRAN');
178 return TRUE;
179 }
180
181 // --------------------------------------------------------------------
182
183 /**
184 * Rollback Transaction
185 *
186 * @access public
187 * @return bool
188 */
189 function trans_rollback()
190 {
Derek Jones0b59f272008-05-13 04:22:33 +0000191 if ( ! $this->trans_enabled)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000192 {
193 return TRUE;
194 }
195
196 // When transactions are nested we only begin/commit/rollback the outermost ones
197 if ($this->_trans_depth > 0)
198 {
199 return TRUE;
200 }
201
202 $this->simple_query('ROLLBACK TRAN');
203 return TRUE;
204 }
205
206 // --------------------------------------------------------------------
207
208 /**
209 * Escape String
210 *
211 * @access public
212 * @param string
213 * @return string
214 */
215 function escape_str($str)
Rick Ellis06a2e742008-10-07 01:04:15 +0000216 {
217 // Access the CI object
Rick Ellisca86a7c2008-10-07 01:16:57 +0000218 $CI =& get_instance();
Rick Ellis06a2e742008-10-07 01:04:15 +0000219
Derek Allardd2df9bc2007-04-15 17:41:17 +0000220 // Escape single quotes
Rick Ellis06a2e742008-10-07 01:04:15 +0000221 return str_replace("'", "''", $CI->input->_remove_invisible_characters($str));
Derek Allardd2df9bc2007-04-15 17:41:17 +0000222 }
223
224 // --------------------------------------------------------------------
225
226 /**
227 * Affected Rows
228 *
229 * @access public
230 * @return integer
231 */
232 function affected_rows()
233 {
234 return @mssql_rows_affected($this->conn_id);
235 }
236
237 // --------------------------------------------------------------------
238
239 /**
Rick Ellis1db17b52007-06-11 05:33:21 +0000240 * Insert ID
241 *
242 * Returns the last id created in the Identity column.
243 *
244 * @access public
245 * @return integer
246 */
Derek Allardd2df9bc2007-04-15 17:41:17 +0000247 function insert_id()
248 {
Rick Ellis1db17b52007-06-11 05:33:21 +0000249 $ver = self::_parse_major_version($this->version());
250 $sql = ($ver >= 8 ? "SELECT SCOPE_IDENTITY() AS last_id" : "SELECT @@IDENTITY AS last_id");
251 $query = $this->query($sql);
252 $row = $query->row();
253 return $row->last_id;
254 }
255
256 // --------------------------------------------------------------------
257
258 /**
259 * Parse major version
260 *
261 * Grabs the major version number from the
262 * database server version string passed in.
263 *
264 * @access private
265 * @param string $version
266 * @return int16 major version number
267 */
268 function _parse_major_version($version)
269 {
270 preg_match('/([0-9]+)\.([0-9]+)\.([0-9]+)/', $version, $ver_info);
271 return $ver_info[1]; // return the major version b/c that's all we're interested in.
272 }
273
274 // --------------------------------------------------------------------
275
276 /**
277 * Version number query string
278 *
279 * @access public
280 * @return string
281 */
282 function _version()
283 {
284 return "SELECT @@VERSION AS ver";
Derek Allardd2df9bc2007-04-15 17:41:17 +0000285 }
286
287 // --------------------------------------------------------------------
288
289 /**
290 * "Count All" query
291 *
292 * Generates a platform-specific query string that counts all records in
293 * the specified database
294 *
295 * @access public
296 * @param string
297 * @return string
298 */
299 function count_all($table = '')
300 {
301 if ($table == '')
302 return '0';
303
Derek Allardf6cd45c2008-01-18 14:31:51 +0000304 $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows'). " FROM " . $this->_protect_identifiers($this->dbprefix.$table));
305
Derek Allardd2df9bc2007-04-15 17:41:17 +0000306 if ($query->num_rows() == 0)
307 return '0';
308
309 $row = $query->row();
310 return $row->numrows;
311 }
312
313 // --------------------------------------------------------------------
314
315 /**
316 * List table query
317 *
318 * Generates a platform-specific query string so that the table names can be fetched
319 *
320 * @access private
Derek Allard39b622d2008-01-16 21:10:09 +0000321 * @param boolean
Derek Allardd2df9bc2007-04-15 17:41:17 +0000322 * @return string
323 */
Derek Allard39b622d2008-01-16 21:10:09 +0000324 function _list_tables($prefix_limit = FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000325 {
Derek Allard39b622d2008-01-16 21:10:09 +0000326 $sql = "SELECT name FROM sysobjects WHERE type = 'U' ORDER BY name";
327
328 // for future compatibility
329 if ($prefix_limit !== FALSE AND $this->dbprefix != '')
330 {
331 //$sql .= " LIKE '".$this->dbprefix."%'";
332 return FALSE; // not currently supported
333 }
334
335 return $sql;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000336 }
337
338 // --------------------------------------------------------------------
339
340 /**
341 * List column query
342 *
343 * Generates a platform-specific query string so that the column names can be fetched
344 *
345 * @access private
346 * @param string the table name
347 * @return string
348 */
349 function _list_columns($table = '')
350 {
351 return "SELECT * FROM INFORMATION_SCHEMA.Columns WHERE TABLE_NAME = '".$this->_escape_table($table)."'";
352 }
353
354 // --------------------------------------------------------------------
355
356 /**
357 * Field data query
358 *
359 * Generates a platform-specific query so that the column data can be retrieved
360 *
361 * @access public
362 * @param string the table name
363 * @return object
364 */
365 function _field_data($table)
366 {
367 return "SELECT TOP 1 * FROM ".$this->_escape_table($table);
368 }
369
370 // --------------------------------------------------------------------
371
372 /**
373 * The error message string
374 *
375 * @access private
376 * @return string
377 */
378 function _error_message()
379 {
380 // Are errros even supported in MS SQL?
381 return '';
382 }
383
384 // --------------------------------------------------------------------
385
386 /**
387 * The error message number
388 *
389 * @access private
390 * @return integer
391 */
392 function _error_number()
393 {
394 // Are error numbers supported?
395 return '';
396 }
Rick Ellis52dc8ca2008-09-30 19:53:52 +0000397
398 // --------------------------------------------------------------------
399
400 /**
401 * Escape Column Name
402 *
403 * This function adds backticks around supplied column name
404 *
405 * @access private
406 * @param string the column name
407 * @return string
408 */
409 function _escape_column($column)
410 {
411 // Not necessary with MS SQL so we simply return the value
412 return $column;
413 }
414
Derek Allardd2df9bc2007-04-15 17:41:17 +0000415 // --------------------------------------------------------------------
416
417 /**
418 * Escape Table Name
419 *
420 * This function adds backticks if the table name has a period
421 * in it. Some DBs will get cranky unless periods are escaped
422 *
423 * @access private
424 * @param string the table name
425 * @return string
426 */
427 function _escape_table($table)
428 {
Rick Ellis52dc8ca2008-09-30 19:53:52 +0000429 // Not necessary with MS SQL so we simply return the value
Derek Allardd2df9bc2007-04-15 17:41:17 +0000430 return $table;
431 }
432
433 // --------------------------------------------------------------------
434
435 /**
Derek Allard39b622d2008-01-16 21:10:09 +0000436 * Protect Identifiers
437 *
438 * This function adds backticks if appropriate based on db type
439 *
440 * @access private
441 * @param mixed the item(s)
442 * @param boolean should spaces be backticked
443 * @param boolean only affect the first word
444 * @return mixed the item with backticks
445 */
Derek Allard15648132008-02-10 21:46:18 +0000446 function _protect_identifiers($item, $first_word_only = FALSE)
Derek Allard39b622d2008-01-16 21:10:09 +0000447 {
Derek Allard15648132008-02-10 21:46:18 +0000448 if (is_array($item))
Derek Allard9b3e7b52008-02-04 23:20:34 +0000449 {
Derek Allard15648132008-02-10 21:46:18 +0000450 $escaped_array = array();
451
452 foreach($item as $k=>$v)
453 {
454 $escaped_array[$this->_protect_identifiers($k)] = $this->_protect_identifiers($v, $first_word_only);
455 }
456
457 return $escaped_array;
458 }
459
460 // This function may get "item1 item2" as a string, and so
461 // we may need ""item1" "item2"" and not ""item1 item2""
462 if (ctype_alnum($item) === FALSE)
463 {
464 if (strpos($item, '.') !== FALSE)
465 {
466 $aliased_tables = implode(".",$this->ar_aliased_tables).'.';
467 $table_name = substr($item, 0, strpos($item, '.')+1);
468 $item = (strpos($aliased_tables, $table_name) !== FALSE) ? $item = $item : $this->dbprefix.$item;
469 }
470
471 // This function may get "field >= 1", and need it to return ""field" >= 1"
472 $lbound = ($first_word_only === TRUE) ? '' : '|\s|\(';
473
Rick Ellis218839a2008-10-07 01:15:48 +0000474 $item = preg_replace('/(^'.$lbound.')([\w\d\-\_]+?)(\s|\)|$)/iS', '$1$2$3', $item);
Derek Allard15648132008-02-10 21:46:18 +0000475 }
476 else
477 {
Rick Ellis218839a2008-10-07 01:15:48 +0000478 return $item;
Derek Allard9b3e7b52008-02-04 23:20:34 +0000479 }
480
Derek Allard9a4d1da2008-02-25 14:18:38 +0000481 $exceptions = array('AS', '/', '-', '%', '+', '*', 'OR', 'IS');
Derek Allard15648132008-02-10 21:46:18 +0000482
483 foreach ($exceptions as $exception)
484 {
485
486 if (stristr($item, " \"{$exception}\" ") !== FALSE)
487 {
488 $item = preg_replace('/ "('.preg_quote($exception).')" /i', ' $1 ', $item);
489 }
490 }
Derek Allard39b622d2008-01-16 21:10:09 +0000491 return $item;
492 }
493
494 // --------------------------------------------------------------------
495
496 /**
Derek Jonesc6ad0232008-01-29 18:44:54 +0000497 * From Tables
498 *
499 * This function implicitly groups FROM tables so there is no confusion
500 * about operator precedence in harmony with SQL standards
501 *
502 * @access public
503 * @param type
504 * @return type
505 */
506 function _from_tables($tables)
507 {
Derek Jones0b59f272008-05-13 04:22:33 +0000508 if ( ! is_array($tables))
Derek Jonesc6ad0232008-01-29 18:44:54 +0000509 {
510 $tables = array($tables);
511 }
512
513 return implode(', ', $tables);
514 }
515
516 // --------------------------------------------------------------------
517
518 /**
Derek Allardd2df9bc2007-04-15 17:41:17 +0000519 * Insert statement
520 *
521 * Generates a platform-specific insert string from the supplied data
522 *
523 * @access public
524 * @param string the table name
525 * @param array the insert keys
526 * @param array the insert values
527 * @return string
528 */
529 function _insert($table, $keys, $values)
530 {
531 return "INSERT INTO ".$this->_escape_table($table)." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
532 }
533
534 // --------------------------------------------------------------------
535
536 /**
537 * Update statement
538 *
539 * Generates a platform-specific update string from the supplied data
540 *
541 * @access public
542 * @param string the table name
543 * @param array the update data
544 * @param array the where clause
Derek Allard39b622d2008-01-16 21:10:09 +0000545 * @param array the orderby clause
546 * @param array the limit clause
Derek Allardd2df9bc2007-04-15 17:41:17 +0000547 * @return string
548 */
Derek Allard39b622d2008-01-16 21:10:09 +0000549 function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000550 {
551 foreach($values as $key => $val)
552 {
553 $valstr[] = $key." = ".$val;
554 }
Derek Allardda6d2402007-12-19 14:49:29 +0000555
Derek Jones0b59f272008-05-13 04:22:33 +0000556 $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
Derek Allard39b622d2008-01-16 21:10:09 +0000557
558 $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
Derek Allardd2df9bc2007-04-15 17:41:17 +0000559
Derek Allard32cf7eb2008-02-05 16:03:50 +0000560 $sql = "UPDATE ".$this->_escape_table($table)." SET ".implode(', ', $valstr);
561 $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
562 $sql .= $orderby.$limit;
563
564 return $sql;
Derek Allard39b622d2008-01-16 21:10:09 +0000565 }
566
567
568 // --------------------------------------------------------------------
569
570 /**
571 * Truncate statement
572 *
573 * Generates a platform-specific truncate string from the supplied data
574 * If the database does not support the truncate() command
575 * This function maps to "DELETE FROM table"
576 *
577 * @access public
578 * @param string the table name
579 * @return string
580 */
581 function _truncate($table)
582 {
583 return "TRUNCATE ".$this->_escape_table($table);
Derek Allardd2df9bc2007-04-15 17:41:17 +0000584 }
585
586 // --------------------------------------------------------------------
587
588 /**
589 * Delete statement
590 *
591 * Generates a platform-specific delete string from the supplied data
592 *
593 * @access public
594 * @param string the table name
595 * @param array the where clause
Derek Allard39b622d2008-01-16 21:10:09 +0000596 * @param string the limit clause
Derek Allardd2df9bc2007-04-15 17:41:17 +0000597 * @return string
598 */
Derek Allard39b622d2008-01-16 21:10:09 +0000599 function _delete($table, $where = array(), $like = array(), $limit = FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000600 {
Derek Allard39b622d2008-01-16 21:10:09 +0000601 $conditions = '';
602
Derek Jones0b59f272008-05-13 04:22:33 +0000603 if (count($where) > 0 OR count($like) > 0)
Derek Allard39b622d2008-01-16 21:10:09 +0000604 {
605 $conditions = "\nWHERE ";
606 $conditions .= implode("\n", $this->ar_where);
607
608 if (count($where) > 0 && count($like) > 0)
609 {
610 $conditions .= " AND ";
611 }
612 $conditions .= implode("\n", $like);
613 }
614
Derek Jones0b59f272008-05-13 04:22:33 +0000615 $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
Derek Allarde77d77c2007-12-19 15:01:55 +0000616
Derek Allard39b622d2008-01-16 21:10:09 +0000617 return "DELETE FROM ".$table.$conditions.$limit;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000618 }
619
620 // --------------------------------------------------------------------
621
622 /**
623 * Limit string
624 *
625 * Generates a platform-specific LIMIT clause
626 *
627 * @access public
628 * @param string the sql query string
629 * @param integer the number of rows to limit the query to
630 * @param integer the offset value
631 * @return string
632 */
633 function _limit($sql, $limit, $offset)
634 {
635 $i = $limit + $offset;
636
637 return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$i.' ', $sql);
638 }
639
640 // --------------------------------------------------------------------
641
642 /**
643 * Close DB Connection
644 *
645 * @access public
646 * @param resource
647 * @return void
648 */
649 function _close($conn_id)
650 {
651 @mssql_close($conn_id);
652 }
653
654}
655
656
Derek Jones0b59f272008-05-13 04:22:33 +0000657
658/* End of file mssql_driver.php */
Derek Jonesa3ffbbb2008-05-11 18:18:29 +0000659/* Location: ./system/database/drivers/mssql/mssql_driver.php */