blob: 1d32792ce8efc72b548fde1ea4051d59c0139b9e [file] [log] [blame]
Alex Bilbie84445d02011-03-10 16:43:39 +00001<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
2/**
3 * CodeIgniter
4 *
5 * An open source application development framework for PHP 5.1.6 or newer
6 *
7 * @package CodeIgniter
8 * @author ExpressionEngine Dev Team
9 * @copyright Copyright (c) 2008 - 2011, 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/**
Alex Bilbie01ab0042011-03-18 19:24:30 +000019 * SQLSRV Database Adapter Class
Alex Bilbie84445d02011-03-10 16:43:39 +000020 *
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 */
Alex Bilbie01ab0042011-03-18 19:24:30 +000031class CI_DB_sqlsrv_driver extends CI_DB {
Alex Bilbie84445d02011-03-10 16:43:39 +000032
33 var $dbdriver = 'sqlsrv';
34
35 // The character used for escaping
36 var $_escape_char = '';
37
38 // clause and character used for LIKE escape sequences
39 var $_like_escape_str = " ESCAPE '%s' ";
40 var $_like_escape_chr = '!';
41
42 /**
43 * The syntax to count rows is slightly different across different
44 * database engines, so this string appears in each driver and is
45 * used for the count_all() and count_all_results() functions.
46 */
47 var $_count_string = "SELECT COUNT(*) AS ";
48 var $_random_keyword = ' ASC'; // not currently supported
49
50 /**
51 * Non-persistent database connection
52 *
53 * @access private called by the base class
54 * @return resource
55 */
Alex Bilbie3a43c7a2011-03-18 19:48:04 +000056 function db_connect($pooling = false)
Alex Bilbie84445d02011-03-10 16:43:39 +000057 {
Alex Bilbie3a43c7a2011-03-18 19:48:04 +000058 // Check for a UTF-8 charset being passed as CI's default 'utf8'.
59 $character_set = (0 === strcasecmp('utf8', $this->char_set)) ? 'UTF-8' : $this->char_set;
60
61 $connection = array(
62 'UID' => empty($this->username) ? '' : $this->username,
63 'PWD' => empty($this->password) ? '' : $this->password,
64 'Database' => $this->database,
65 'ConnectionPooling' => $pooling ? 1 : 0,
66 'CharacterSet' => $character_set,
67 'ReturnDatesAsStrings' => 1
68 );
69
70 // If the username and password are both empty, assume this is a
71 // 'Windows Authentication Mode' connection.
72 if(empty($connection['UID']) && empty($connection['PWD'])) {
73 unset($connection['UID'], $connection['PWD']);
Alex Bilbie84445d02011-03-10 16:43:39 +000074 }
75
Alex Bilbie3a43c7a2011-03-18 19:48:04 +000076 return sqlsrv_connect($this->hostname, $connection);
Alex Bilbie84445d02011-03-10 16:43:39 +000077 }
78
79 // --------------------------------------------------------------------
80
81 /**
82 * Persistent database connection
83 *
84 * @access private called by the base class
85 * @return resource
86 */
87 function db_pconnect()
88 {
Alex Bilbie3a43c7a2011-03-18 19:48:04 +000089 $this->db_connect(TRUE);
Alex Bilbie84445d02011-03-10 16:43:39 +000090 }
91
92 // --------------------------------------------------------------------
93
94 /**
95 * Reconnect
96 *
97 * Keep / reestablish the db connection if no queries have been
98 * sent for a length of time exceeding the server's idle timeout
99 *
100 * @access public
101 * @return void
102 */
103 function reconnect()
104 {
105 // not implemented in MSSQL
106 }
107
108 // --------------------------------------------------------------------
109
110 /**
111 * Select the database
112 *
113 * @access private called by the base class
114 * @return resource
115 */
116 function db_select()
117 {
Alex Bilbie5336ee22011-03-12 23:35:29 +0000118 return $this->_execute('USE ' . $this->database);
Alex Bilbie84445d02011-03-10 16:43:39 +0000119 }
120
121 // --------------------------------------------------------------------
122
123 /**
124 * Set client character set
125 *
126 * @access public
127 * @param string
128 * @param string
129 * @return resource
130 */
131 function db_set_charset($charset, $collation)
132 {
133 // @todo - add support if needed
134 return TRUE;
135 }
136
137 // --------------------------------------------------------------------
138
139 /**
140 * Execute the query
141 *
142 * @access private called by the base class
143 * @param string an SQL query
144 * @return resource
145 */
146 function _execute($sql)
147 {
148 $sql = $this->_prep_query($sql);
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000149 return sqlsrv_query($this->conn_id, $sql, null, array(
150 'Scrollable' => SQLSRV_CURSOR_STATIC,
151 'SendStreamParamsAtExec' => true
152 ));
Alex Bilbie84445d02011-03-10 16:43:39 +0000153 }
154
155 // --------------------------------------------------------------------
156
157 /**
158 * Prep the query
159 *
160 * If needed, each database adapter can prep the query string
161 *
162 * @access private called by execute()
163 * @param string an SQL query
164 * @return string
165 */
166 function _prep_query($sql)
167 {
168 return $sql;
169 }
170
171 // --------------------------------------------------------------------
172
173 /**
174 * Begin Transaction
175 *
176 * @access public
177 * @return bool
178 */
179 function trans_begin($test_mode = FALSE)
180 {
181 if ( ! $this->trans_enabled)
182 {
183 return TRUE;
184 }
185
186 // When transactions are nested we only begin/commit/rollback the outermost ones
187 if ($this->_trans_depth > 0)
188 {
189 return TRUE;
190 }
191
192 // Reset the transaction failure flag.
193 // If the $test_mode flag is set to TRUE transactions will be rolled back
194 // even if the queries produce a successful result.
195 $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
196
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000197 return sqlsrv_begin_transaction($this->conn_id);
Alex Bilbie84445d02011-03-10 16:43:39 +0000198 }
199
200 // --------------------------------------------------------------------
201
202 /**
203 * Commit Transaction
204 *
205 * @access public
206 * @return bool
207 */
208 function trans_commit()
209 {
210 if ( ! $this->trans_enabled)
211 {
212 return TRUE;
213 }
214
215 // When transactions are nested we only begin/commit/rollback the outermost ones
216 if ($this->_trans_depth > 0)
217 {
218 return TRUE;
219 }
220
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000221 return sqlsrv_commit($this->conn_id);
Alex Bilbie84445d02011-03-10 16:43:39 +0000222 }
223
224 // --------------------------------------------------------------------
225
226 /**
227 * Rollback Transaction
228 *
229 * @access public
230 * @return bool
231 */
232 function trans_rollback()
233 {
234 if ( ! $this->trans_enabled)
235 {
236 return TRUE;
237 }
238
239 // When transactions are nested we only begin/commit/rollback the outermost ones
240 if ($this->_trans_depth > 0)
241 {
242 return TRUE;
243 }
244
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000245 return sqlsrv_rollback($this->conn_id);
Alex Bilbie84445d02011-03-10 16:43:39 +0000246 }
247
248 // --------------------------------------------------------------------
249
250 /**
251 * Escape String
252 *
253 * @access public
254 * @param string
255 * @param bool whether or not the string will be used in a LIKE condition
256 * @return string
257 */
258 function escape_str($str, $like = FALSE)
259 {
Alex Bilbie84445d02011-03-10 16:43:39 +0000260 // Escape single quotes
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000261 return str_replace("'", "''", $str);
Alex Bilbie84445d02011-03-10 16:43:39 +0000262 }
263
264 // --------------------------------------------------------------------
265
266 /**
267 * Affected Rows
268 *
269 * @access public
270 * @return integer
271 */
272 function affected_rows()
273 {
Alex Bilbie56e20402011-03-12 23:43:54 +0000274 return @sqlrv_rows_affected($this->conn_id);
Alex Bilbie84445d02011-03-10 16:43:39 +0000275 }
276
277 // --------------------------------------------------------------------
278
279 /**
280 * Insert ID
281 *
282 * Returns the last id created in the Identity column.
283 *
284 * @access public
285 * @return integer
286 */
287 function insert_id()
288 {
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000289 return $this->query('select @@IDENTITY as insert_id')->row('insert_id');
Alex Bilbie84445d02011-03-10 16:43:39 +0000290 }
291
292 // --------------------------------------------------------------------
293
294 /**
295 * Parse major version
296 *
297 * Grabs the major version number from the
298 * database server version string passed in.
299 *
300 * @access private
301 * @param string $version
302 * @return int16 major version number
303 */
304 function _parse_major_version($version)
305 {
306 preg_match('/([0-9]+)\.([0-9]+)\.([0-9]+)/', $version, $ver_info);
307 return $ver_info[1]; // return the major version b/c that's all we're interested in.
308 }
309
310 // --------------------------------------------------------------------
311
312 /**
313 * Version number query string
314 *
315 * @access public
316 * @return string
317 */
318 function _version()
319 {
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000320 $info = sqlsrv_server_info($this->conn_id);
321 return sprintf("select '%s' as ver", $info['SQLServerVersion']);
Alex Bilbie84445d02011-03-10 16:43:39 +0000322 }
323
324 // --------------------------------------------------------------------
325
326 /**
327 * "Count All" query
328 *
329 * Generates a platform-specific query string that counts all records in
330 * the specified database
331 *
332 * @access public
333 * @param string
334 * @return string
335 */
336 function count_all($table = '')
337 {
338 if ($table == '')
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000339 return '0';
340
341 $query = $this->query("SELECT COUNT(*) AS numrows FROM " . $this->dbprefix . $table);
342
Alex Bilbie84445d02011-03-10 16:43:39 +0000343 if ($query->num_rows() == 0)
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000344 return '0';
Alex Bilbie84445d02011-03-10 16:43:39 +0000345
346 $row = $query->row();
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000347 return $row->numrows;
Alex Bilbie84445d02011-03-10 16:43:39 +0000348 }
349
350 // --------------------------------------------------------------------
351
352 /**
353 * List table query
354 *
355 * Generates a platform-specific query string so that the table names can be fetched
356 *
357 * @access private
358 * @param boolean
359 * @return string
360 */
361 function _list_tables($prefix_limit = FALSE)
362 {
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000363 return "SELECT name FROM sysobjects WHERE type = 'U' ORDER BY name";
Alex Bilbie84445d02011-03-10 16:43:39 +0000364 }
365
366 // --------------------------------------------------------------------
367
368 /**
369 * List column query
370 *
371 * Generates a platform-specific query string so that the column names can be fetched
372 *
373 * @access private
374 * @param string the table name
375 * @return string
376 */
377 function _list_columns($table = '')
378 {
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000379 return "SELECT * FROM INFORMATION_SCHEMA.Columns WHERE TABLE_NAME = '".$this->_escape_table($table)."'";
Alex Bilbie84445d02011-03-10 16:43:39 +0000380 }
381
382 // --------------------------------------------------------------------
383
384 /**
385 * Field data query
386 *
387 * Generates a platform-specific query so that the column data can be retrieved
388 *
389 * @access public
390 * @param string the table name
391 * @return object
392 */
393 function _field_data($table)
394 {
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000395 return "SELECT TOP 1 * FROM " . $this->_escape_table($table);
Alex Bilbie84445d02011-03-10 16:43:39 +0000396 }
397
398 // --------------------------------------------------------------------
399
400 /**
401 * The error message string
402 *
403 * @access private
404 * @return string
405 */
406 function _error_message()
407 {
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000408 $error = array_shift(sqlsrv_errors());
409 return !empty($error['message']) ? $error['message'] : null;
Alex Bilbie84445d02011-03-10 16:43:39 +0000410 }
411
412 // --------------------------------------------------------------------
413
414 /**
415 * The error message number
416 *
417 * @access private
418 * @return integer
419 */
420 function _error_number()
421 {
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000422 $error = array_shift(sqlsrv_errors());
423 return isset($error['SQLSTATE']) ? $error['SQLSTATE'] : null;
Alex Bilbie84445d02011-03-10 16:43:39 +0000424 }
425
426 // --------------------------------------------------------------------
427
428 /**
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000429 * Escape Table Name
430 *
431 * This function adds backticks if the table name has a period
432 * in it. Some DBs will get cranky unless periods are escaped
433 *
434 * @access private
435 * @param string the table name
436 * @return string
437 */
438 function _escape_table($table)
439 {
440 return $table;
441 }
442
443
444 /**
Alex Bilbie84445d02011-03-10 16:43:39 +0000445 * Escape the SQL Identifiers
446 *
447 * This function escapes column and table names
448 *
449 * @access private
450 * @param string
451 * @return string
452 */
453 function _escape_identifiers($item)
454 {
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000455 return $item;
Alex Bilbie84445d02011-03-10 16:43:39 +0000456 }
457
458 // --------------------------------------------------------------------
459
460 /**
461 * From Tables
462 *
463 * This function implicitly groups FROM tables so there is no confusion
464 * about operator precedence in harmony with SQL standards
465 *
466 * @access public
467 * @param type
468 * @return type
469 */
470 function _from_tables($tables)
471 {
472 if ( ! is_array($tables))
473 {
474 $tables = array($tables);
475 }
476
477 return implode(', ', $tables);
478 }
479
480 // --------------------------------------------------------------------
481
482 /**
483 * Insert statement
484 *
485 * Generates a platform-specific insert string from the supplied data
486 *
487 * @access public
488 * @param string the table name
489 * @param array the insert keys
490 * @param array the insert values
491 * @return string
492 */
493 function _insert($table, $keys, $values)
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000494 {
495 return "INSERT INTO ".$this->_escape_table($table)." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
Alex Bilbie84445d02011-03-10 16:43:39 +0000496 }
497
498 // --------------------------------------------------------------------
499
500 /**
501 * Update statement
502 *
503 * Generates a platform-specific update string from the supplied data
504 *
505 * @access public
506 * @param string the table name
507 * @param array the update data
508 * @param array the where clause
509 * @param array the orderby clause
510 * @param array the limit clause
511 * @return string
512 */
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000513 function _update($table, $values, $where)
Alex Bilbie84445d02011-03-10 16:43:39 +0000514 {
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000515 foreach($values as $key => $val)
Alex Bilbie84445d02011-03-10 16:43:39 +0000516 {
517 $valstr[] = $key." = ".$val;
518 }
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000519
520 return "UPDATE ".$this->_escape_table($table)." SET ".implode(', ', $valstr)." WHERE ".implode(" ", $where);
Alex Bilbie84445d02011-03-10 16:43:39 +0000521 }
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000522
Alex Bilbie84445d02011-03-10 16:43:39 +0000523 // --------------------------------------------------------------------
524
525 /**
526 * Truncate statement
527 *
528 * Generates a platform-specific truncate string from the supplied data
529 * If the database does not support the truncate() command
530 * This function maps to "DELETE FROM table"
531 *
532 * @access public
533 * @param string the table name
534 * @return string
535 */
536 function _truncate($table)
537 {
538 return "TRUNCATE ".$table;
539 }
540
541 // --------------------------------------------------------------------
542
543 /**
544 * Delete statement
545 *
546 * Generates a platform-specific delete string from the supplied data
547 *
548 * @access public
549 * @param string the table name
550 * @param array the where clause
551 * @param string the limit clause
552 * @return string
553 */
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000554 function _delete($table, $where)
Alex Bilbie84445d02011-03-10 16:43:39 +0000555 {
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000556 return "DELETE FROM ".$this->_escape_table($table)." WHERE ".implode(" ", $where);
Alex Bilbie84445d02011-03-10 16:43:39 +0000557 }
558
559 // --------------------------------------------------------------------
560
561 /**
562 * Limit string
563 *
564 * Generates a platform-specific LIMIT clause
565 *
566 * @access public
567 * @param string the sql query string
568 * @param integer the number of rows to limit the query to
569 * @param integer the offset value
570 * @return string
571 */
572 function _limit($sql, $limit, $offset)
573 {
574 $i = $limit + $offset;
Alex Bilbie3a43c7a2011-03-18 19:48:04 +0000575
576 return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$i.' ', $sql);
Alex Bilbie84445d02011-03-10 16:43:39 +0000577 }
578
579 // --------------------------------------------------------------------
580
581 /**
582 * Close DB Connection
583 *
584 * @access public
585 * @param resource
586 * @return void
587 */
588 function _close($conn_id)
589 {
590 @sqlsrv_close($conn_id);
591 }
592
593}
594
595
596
597/* End of file mssql_driver.php */
598/* Location: ./system/database/drivers/mssql/mssql_driver.php */