blob: d0e1092e63312be7e418074fa43e582452f5d9b7 [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
Derek Allard694b5b82007-12-18 15:58:03 +000040
41 /**
Derek Allardd2df9bc2007-04-15 17:41:17 +000042 * Non-persistent database connection
43 *
44 * @access private called by the base class
45 * @return resource
46 */
47 function db_connect()
48 {
49 return @mssql_connect($this->hostname, $this->username, $this->password);
50 }
51
52 // --------------------------------------------------------------------
53
54 /**
55 * Persistent database connection
56 *
57 * @access private called by the base class
58 * @return resource
59 */
60 function db_pconnect()
61 {
62 return @mssql_pconnect($this->hostname, $this->username, $this->password);
63 }
64
65 // --------------------------------------------------------------------
66
67 /**
68 * Select the database
69 *
70 * @access private called by the base class
71 * @return resource
72 */
73 function db_select()
74 {
75 return @mssql_select_db($this->database, $this->conn_id);
76 }
77
78 // --------------------------------------------------------------------
79
80 /**
Derek Allard39b622d2008-01-16 21:10:09 +000081 * Set client character set
82 *
83 * @access public
84 * @param string
85 * @param string
86 * @return resource
87 */
88 function db_set_charset($charset, $collation)
89 {
Rick Ellisff734012008-09-30 20:38:12 +000090 // @todo - add support if needed
Derek Allard39b622d2008-01-16 21:10:09 +000091 return TRUE;
92 }
93
94 // --------------------------------------------------------------------
95
96 /**
Derek Allardd2df9bc2007-04-15 17:41:17 +000097 * Execute the query
98 *
99 * @access private called by the base class
100 * @param string an SQL query
101 * @return resource
102 */
103 function _execute($sql)
104 {
105 $sql = $this->_prep_query($sql);
106 return @mssql_query($sql, $this->conn_id);
107 }
108
109 // --------------------------------------------------------------------
110
111 /**
112 * Prep the query
113 *
114 * If needed, each database adapter can prep the query string
115 *
116 * @access private called by execute()
117 * @param string an SQL query
118 * @return string
119 */
120 function _prep_query($sql)
121 {
122 return $sql;
123 }
124
125 // --------------------------------------------------------------------
126
127 /**
128 * Begin Transaction
129 *
130 * @access public
131 * @return bool
132 */
133 function trans_begin($test_mode = FALSE)
134 {
Derek Jones0b59f272008-05-13 04:22:33 +0000135 if ( ! $this->trans_enabled)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000136 {
137 return TRUE;
138 }
139
140 // When transactions are nested we only begin/commit/rollback the outermost ones
141 if ($this->_trans_depth > 0)
142 {
143 return TRUE;
144 }
145
146 // Reset the transaction failure flag.
147 // If the $test_mode flag is set to TRUE transactions will be rolled back
148 // even if the queries produce a successful result.
149 $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
150
151 $this->simple_query('BEGIN TRAN');
152 return TRUE;
153 }
154
155 // --------------------------------------------------------------------
156
157 /**
158 * Commit Transaction
159 *
160 * @access public
161 * @return bool
162 */
163 function trans_commit()
164 {
Derek Jones0b59f272008-05-13 04:22:33 +0000165 if ( ! $this->trans_enabled)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000166 {
167 return TRUE;
168 }
169
170 // When transactions are nested we only begin/commit/rollback the outermost ones
171 if ($this->_trans_depth > 0)
172 {
173 return TRUE;
174 }
175
176 $this->simple_query('COMMIT TRAN');
177 return TRUE;
178 }
179
180 // --------------------------------------------------------------------
181
182 /**
183 * Rollback Transaction
184 *
185 * @access public
186 * @return bool
187 */
188 function trans_rollback()
189 {
Derek Jones0b59f272008-05-13 04:22:33 +0000190 if ( ! $this->trans_enabled)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000191 {
192 return TRUE;
193 }
194
195 // When transactions are nested we only begin/commit/rollback the outermost ones
196 if ($this->_trans_depth > 0)
197 {
198 return TRUE;
199 }
200
201 $this->simple_query('ROLLBACK TRAN');
202 return TRUE;
203 }
204
205 // --------------------------------------------------------------------
206
207 /**
208 * Escape String
209 *
210 * @access public
211 * @param string
212 * @return string
213 */
214 function escape_str($str)
Rick Ellis06a2e742008-10-07 01:04:15 +0000215 {
216 // Access the CI object
217 $CI->get_instance();
218
Derek Allardd2df9bc2007-04-15 17:41:17 +0000219 // Escape single quotes
Rick Ellis06a2e742008-10-07 01:04:15 +0000220 return str_replace("'", "''", $CI->input->_remove_invisible_characters($str));
Derek Allardd2df9bc2007-04-15 17:41:17 +0000221 }
222
223 // --------------------------------------------------------------------
224
225 /**
226 * Affected Rows
227 *
228 * @access public
229 * @return integer
230 */
231 function affected_rows()
232 {
233 return @mssql_rows_affected($this->conn_id);
234 }
235
236 // --------------------------------------------------------------------
237
238 /**
Rick Ellis1db17b52007-06-11 05:33:21 +0000239 * Insert ID
240 *
241 * Returns the last id created in the Identity column.
242 *
243 * @access public
244 * @return integer
245 */
Derek Allardd2df9bc2007-04-15 17:41:17 +0000246 function insert_id()
247 {
Rick Ellis1db17b52007-06-11 05:33:21 +0000248 $ver = self::_parse_major_version($this->version());
249 $sql = ($ver >= 8 ? "SELECT SCOPE_IDENTITY() AS last_id" : "SELECT @@IDENTITY AS last_id");
250 $query = $this->query($sql);
251 $row = $query->row();
252 return $row->last_id;
253 }
254
255 // --------------------------------------------------------------------
256
257 /**
258 * Parse major version
259 *
260 * Grabs the major version number from the
261 * database server version string passed in.
262 *
263 * @access private
264 * @param string $version
265 * @return int16 major version number
266 */
267 function _parse_major_version($version)
268 {
269 preg_match('/([0-9]+)\.([0-9]+)\.([0-9]+)/', $version, $ver_info);
270 return $ver_info[1]; // return the major version b/c that's all we're interested in.
271 }
272
273 // --------------------------------------------------------------------
274
275 /**
276 * Version number query string
277 *
278 * @access public
279 * @return string
280 */
281 function _version()
282 {
283 return "SELECT @@VERSION AS ver";
Derek Allardd2df9bc2007-04-15 17:41:17 +0000284 }
285
286 // --------------------------------------------------------------------
287
288 /**
289 * "Count All" query
290 *
291 * Generates a platform-specific query string that counts all records in
292 * the specified database
293 *
294 * @access public
295 * @param string
296 * @return string
297 */
298 function count_all($table = '')
299 {
300 if ($table == '')
301 return '0';
302
Derek Allardf6cd45c2008-01-18 14:31:51 +0000303 $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows'). " FROM " . $this->_protect_identifiers($this->dbprefix.$table));
304
Derek Allardd2df9bc2007-04-15 17:41:17 +0000305 if ($query->num_rows() == 0)
306 return '0';
307
308 $row = $query->row();
309 return $row->numrows;
310 }
311
312 // --------------------------------------------------------------------
313
314 /**
315 * List table query
316 *
317 * Generates a platform-specific query string so that the table names can be fetched
318 *
319 * @access private
Derek Allard39b622d2008-01-16 21:10:09 +0000320 * @param boolean
Derek Allardd2df9bc2007-04-15 17:41:17 +0000321 * @return string
322 */
Derek Allard39b622d2008-01-16 21:10:09 +0000323 function _list_tables($prefix_limit = FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000324 {
Derek Allard39b622d2008-01-16 21:10:09 +0000325 $sql = "SELECT name FROM sysobjects WHERE type = 'U' ORDER BY name";
326
327 // for future compatibility
328 if ($prefix_limit !== FALSE AND $this->dbprefix != '')
329 {
330 //$sql .= " LIKE '".$this->dbprefix."%'";
331 return FALSE; // not currently supported
332 }
333
334 return $sql;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000335 }
336
337 // --------------------------------------------------------------------
338
339 /**
340 * List column query
341 *
342 * Generates a platform-specific query string so that the column names can be fetched
343 *
344 * @access private
345 * @param string the table name
346 * @return string
347 */
348 function _list_columns($table = '')
349 {
350 return "SELECT * FROM INFORMATION_SCHEMA.Columns WHERE TABLE_NAME = '".$this->_escape_table($table)."'";
351 }
352
353 // --------------------------------------------------------------------
354
355 /**
356 * Field data query
357 *
358 * Generates a platform-specific query so that the column data can be retrieved
359 *
360 * @access public
361 * @param string the table name
362 * @return object
363 */
364 function _field_data($table)
365 {
366 return "SELECT TOP 1 * FROM ".$this->_escape_table($table);
367 }
368
369 // --------------------------------------------------------------------
370
371 /**
372 * The error message string
373 *
374 * @access private
375 * @return string
376 */
377 function _error_message()
378 {
379 // Are errros even supported in MS SQL?
380 return '';
381 }
382
383 // --------------------------------------------------------------------
384
385 /**
386 * The error message number
387 *
388 * @access private
389 * @return integer
390 */
391 function _error_number()
392 {
393 // Are error numbers supported?
394 return '';
395 }
Rick Ellis52dc8ca2008-09-30 19:53:52 +0000396
397 // --------------------------------------------------------------------
398
399 /**
400 * Escape Column Name
401 *
402 * This function adds backticks around supplied column name
403 *
404 * @access private
405 * @param string the column name
406 * @return string
407 */
408 function _escape_column($column)
409 {
410 // Not necessary with MS SQL so we simply return the value
411 return $column;
412 }
413
Derek Allardd2df9bc2007-04-15 17:41:17 +0000414 // --------------------------------------------------------------------
415
416 /**
417 * Escape Table Name
418 *
419 * This function adds backticks if the table name has a period
420 * in it. Some DBs will get cranky unless periods are escaped
421 *
422 * @access private
423 * @param string the table name
424 * @return string
425 */
426 function _escape_table($table)
427 {
Rick Ellis52dc8ca2008-09-30 19:53:52 +0000428 // Not necessary with MS SQL so we simply return the value
Derek Allardd2df9bc2007-04-15 17:41:17 +0000429 return $table;
430 }
431
432 // --------------------------------------------------------------------
433
434 /**
Derek Allard39b622d2008-01-16 21:10:09 +0000435 * Protect Identifiers
436 *
437 * This function adds backticks if appropriate based on db type
438 *
439 * @access private
440 * @param mixed the item(s)
441 * @param boolean should spaces be backticked
442 * @param boolean only affect the first word
443 * @return mixed the item with backticks
444 */
Derek Allard15648132008-02-10 21:46:18 +0000445 function _protect_identifiers($item, $first_word_only = FALSE)
Derek Allard39b622d2008-01-16 21:10:09 +0000446 {
Derek Allard15648132008-02-10 21:46:18 +0000447 if (is_array($item))
Derek Allard9b3e7b52008-02-04 23:20:34 +0000448 {
Derek Allard15648132008-02-10 21:46:18 +0000449 $escaped_array = array();
450
451 foreach($item as $k=>$v)
452 {
453 $escaped_array[$this->_protect_identifiers($k)] = $this->_protect_identifiers($v, $first_word_only);
454 }
455
456 return $escaped_array;
457 }
458
459 // This function may get "item1 item2" as a string, and so
460 // we may need ""item1" "item2"" and not ""item1 item2""
461 if (ctype_alnum($item) === FALSE)
462 {
463 if (strpos($item, '.') !== FALSE)
464 {
465 $aliased_tables = implode(".",$this->ar_aliased_tables).'.';
466 $table_name = substr($item, 0, strpos($item, '.')+1);
467 $item = (strpos($aliased_tables, $table_name) !== FALSE) ? $item = $item : $this->dbprefix.$item;
468 }
469
470 // This function may get "field >= 1", and need it to return ""field" >= 1"
471 $lbound = ($first_word_only === TRUE) ? '' : '|\s|\(';
472
Rick Ellis218839a2008-10-07 01:15:48 +0000473 $item = preg_replace('/(^'.$lbound.')([\w\d\-\_]+?)(\s|\)|$)/iS', '$1$2$3', $item);
Derek Allard15648132008-02-10 21:46:18 +0000474 }
475 else
476 {
Rick Ellis218839a2008-10-07 01:15:48 +0000477 return $item;
Derek Allard9b3e7b52008-02-04 23:20:34 +0000478 }
479
Derek Allard9a4d1da2008-02-25 14:18:38 +0000480 $exceptions = array('AS', '/', '-', '%', '+', '*', 'OR', 'IS');
Derek Allard15648132008-02-10 21:46:18 +0000481
482 foreach ($exceptions as $exception)
483 {
484
485 if (stristr($item, " \"{$exception}\" ") !== FALSE)
486 {
487 $item = preg_replace('/ "('.preg_quote($exception).')" /i', ' $1 ', $item);
488 }
489 }
Derek Allard39b622d2008-01-16 21:10:09 +0000490 return $item;
491 }
492
493 // --------------------------------------------------------------------
494
495 /**
Derek Jonesc6ad0232008-01-29 18:44:54 +0000496 * From Tables
497 *
498 * This function implicitly groups FROM tables so there is no confusion
499 * about operator precedence in harmony with SQL standards
500 *
501 * @access public
502 * @param type
503 * @return type
504 */
505 function _from_tables($tables)
506 {
Derek Jones0b59f272008-05-13 04:22:33 +0000507 if ( ! is_array($tables))
Derek Jonesc6ad0232008-01-29 18:44:54 +0000508 {
509 $tables = array($tables);
510 }
511
512 return implode(', ', $tables);
513 }
514
515 // --------------------------------------------------------------------
516
517 /**
Derek Allardd2df9bc2007-04-15 17:41:17 +0000518 * Insert statement
519 *
520 * Generates a platform-specific insert string from the supplied data
521 *
522 * @access public
523 * @param string the table name
524 * @param array the insert keys
525 * @param array the insert values
526 * @return string
527 */
528 function _insert($table, $keys, $values)
529 {
530 return "INSERT INTO ".$this->_escape_table($table)." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
531 }
532
533 // --------------------------------------------------------------------
534
535 /**
536 * Update statement
537 *
538 * Generates a platform-specific update string from the supplied data
539 *
540 * @access public
541 * @param string the table name
542 * @param array the update data
543 * @param array the where clause
Derek Allard39b622d2008-01-16 21:10:09 +0000544 * @param array the orderby clause
545 * @param array the limit clause
Derek Allardd2df9bc2007-04-15 17:41:17 +0000546 * @return string
547 */
Derek Allard39b622d2008-01-16 21:10:09 +0000548 function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000549 {
550 foreach($values as $key => $val)
551 {
552 $valstr[] = $key." = ".$val;
553 }
Derek Allardda6d2402007-12-19 14:49:29 +0000554
Derek Jones0b59f272008-05-13 04:22:33 +0000555 $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
Derek Allard39b622d2008-01-16 21:10:09 +0000556
557 $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
Derek Allardd2df9bc2007-04-15 17:41:17 +0000558
Derek Allard32cf7eb2008-02-05 16:03:50 +0000559 $sql = "UPDATE ".$this->_escape_table($table)." SET ".implode(', ', $valstr);
560 $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
561 $sql .= $orderby.$limit;
562
563 return $sql;
Derek Allard39b622d2008-01-16 21:10:09 +0000564 }
565
566
567 // --------------------------------------------------------------------
568
569 /**
570 * Truncate statement
571 *
572 * Generates a platform-specific truncate string from the supplied data
573 * If the database does not support the truncate() command
574 * This function maps to "DELETE FROM table"
575 *
576 * @access public
577 * @param string the table name
578 * @return string
579 */
580 function _truncate($table)
581 {
582 return "TRUNCATE ".$this->_escape_table($table);
Derek Allardd2df9bc2007-04-15 17:41:17 +0000583 }
584
585 // --------------------------------------------------------------------
586
587 /**
588 * Delete statement
589 *
590 * Generates a platform-specific delete string from the supplied data
591 *
592 * @access public
593 * @param string the table name
594 * @param array the where clause
Derek Allard39b622d2008-01-16 21:10:09 +0000595 * @param string the limit clause
Derek Allardd2df9bc2007-04-15 17:41:17 +0000596 * @return string
597 */
Derek Allard39b622d2008-01-16 21:10:09 +0000598 function _delete($table, $where = array(), $like = array(), $limit = FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000599 {
Derek Allard39b622d2008-01-16 21:10:09 +0000600 $conditions = '';
601
Derek Jones0b59f272008-05-13 04:22:33 +0000602 if (count($where) > 0 OR count($like) > 0)
Derek Allard39b622d2008-01-16 21:10:09 +0000603 {
604 $conditions = "\nWHERE ";
605 $conditions .= implode("\n", $this->ar_where);
606
607 if (count($where) > 0 && count($like) > 0)
608 {
609 $conditions .= " AND ";
610 }
611 $conditions .= implode("\n", $like);
612 }
613
Derek Jones0b59f272008-05-13 04:22:33 +0000614 $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
Derek Allarde77d77c2007-12-19 15:01:55 +0000615
Derek Allard39b622d2008-01-16 21:10:09 +0000616 return "DELETE FROM ".$table.$conditions.$limit;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000617 }
618
619 // --------------------------------------------------------------------
620
621 /**
622 * Limit string
623 *
624 * Generates a platform-specific LIMIT clause
625 *
626 * @access public
627 * @param string the sql query string
628 * @param integer the number of rows to limit the query to
629 * @param integer the offset value
630 * @return string
631 */
632 function _limit($sql, $limit, $offset)
633 {
634 $i = $limit + $offset;
635
636 return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$i.' ', $sql);
637 }
638
639 // --------------------------------------------------------------------
640
641 /**
642 * Close DB Connection
643 *
644 * @access public
645 * @param resource
646 * @return void
647 */
648 function _close($conn_id)
649 {
650 @mssql_close($conn_id);
651 }
652
653}
654
655
Derek Jones0b59f272008-05-13 04:22:33 +0000656
657/* End of file mssql_driver.php */
Derek Jonesa3ffbbb2008-05-11 18:18:29 +0000658/* Location: ./system/database/drivers/mssql/mssql_driver.php */