10 defined(
'JPATH_PLATFORM') or die;
28 public $name =
'sqlsrv';
39 protected $nameQuote =
'[]';
48 protected $nullDate =
'1900-01-01 00:00:00';
54 protected static $dbMinimum =
'10.50.1600.1';
63 public static function isSupported()
65 return (function_exists(
'sqlsrv_connect'));
75 public function __construct($options)
78 $options[
'host'] = (isset($options[
'host'])) ? $options[
'host'] :
'localhost';
79 $options[
'user'] = (isset($options[
'user'])) ? $options[
'user'] :
'';
80 $options[
'password'] = (isset($options[
'password'])) ? $options[
'password'] :
'';
81 $options[
'database'] = (isset($options[
'database'])) ? $options[
'database'] :
'';
82 $options[
'select'] = (isset($options[
'select'])) ? (
bool) $options[
'select'] :
true;
85 parent::__construct($options);
93 public function __destruct()
106 public function connect()
108 if ($this->connection)
115 'Database' => $this->options[
'database'],
116 'uid' => $this->options[
'user'],
117 'pwd' => $this->options[
'password'],
118 'CharacterSet' =>
'UTF-8',
119 'ReturnDatesAsStrings' =>
true);
122 if (!function_exists(
'sqlsrv_connect'))
124 throw new RuntimeException(
'PHP extension sqlsrv_connect is not available.');
128 if (!($this->connection = @ sqlsrv_connect($this->options[
'host'], $config)))
130 throw new RuntimeException(
'Database sqlsrv_connect failed');
134 sqlsrv_configure(
'WarningsReturnAsErrors', 0);
137 if ($this->options[
'select'] && !empty($this->options[
'database']))
139 $this->select($this->options[
'database']);
150 public function disconnect()
153 if (is_resource($this->connection))
155 foreach ($this->disconnectHandlers as $h)
157 call_user_func_array($h, array( &$this));
160 sqlsrv_close($this->connection);
163 $this->connection = null;
175 protected function getTableConstraints($tableName)
179 $query = $this->getQuery(
true);
182 'SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = ' . $query->quote($tableName)
185 return $this->loadColumn();
199 protected function renameConstraints($constraints = array(), $prefix = null, $backup = null)
203 foreach ($constraints as $constraint)
205 $this->setQuery(
'sp_rename ' . $constraint .
',' . str_replace($prefix, $backup, $constraint));
223 public function escape($text, $extra =
false)
225 $result = addslashes($text);
226 $result = str_replace(
"\'",
"''", $result);
227 $result = str_replace(
'\"',
'"', $result);
228 $result = str_replace(
'\/',
'/', $result);
233 $result = str_replace(
'_',
'[_]', $result);
246 public function connected()
262 public function dropTable($tableName, $ifExists =
true)
266 $query = $this->getQuery(
true);
271 'IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = ' . $query->quote($tableName) .
') DROP TABLE ' . $tableName
276 $this->setQuery(
'DROP TABLE ' . $tableName);
291 public function getAffectedRows()
295 return sqlsrv_rows_affected($this->cursor);
305 public function getCollation()
308 return 'MSSQL UTF-8 (UCS2)';
320 public function getNumRows($cursor = null)
324 return sqlsrv_num_rows($cursor ? $cursor : $this->cursor);
338 public function getTableColumns($table, $typeOnly =
true)
342 $table_temp = $this->replacePrefix((
string) $table);
346 'SELECT column_name as Field, data_type as Type, is_nullable as \'Null\', column_default as \'Default\'' .
347 ' FROM information_schema.columns WHERE table_name = ' . $this->quote($table_temp)
349 $fields = $this->loadObjectList();
354 foreach ($fields as $field)
356 $result[$field->Field] = preg_replace(
"/[(0-9)]/",
'', $field->Type);
362 foreach ($fields as $field)
364 $result[$field->Field] = $field;
383 public function getTableCreate($tables)
400 public function getTableKeys($table)
416 public function getTableList()
421 $this->setQuery(
'SELECT name FROM ' . $this->getDatabase() .
'.sys.Tables WHERE type = \'U\';');
422 $tables = $this->loadColumn();
434 public function getVersion()
438 $version = sqlsrv_server_info($this->connection);
440 return $version[
'SQLServerVersion'];
455 public function insertObject($table, &$object, $key = null)
459 $statement =
'INSERT INTO ' . $this->quoteName($table) .
' (%s) VALUES (%s)';
461 foreach (get_object_vars($object) as $k => $v)
464 if (is_array($v) or is_object($v) or $v === null)
469 if (!$this->checkFieldExists($table, $k))
480 if ($k == $key && $key == 0)
485 $fields[] = $this->quoteName($k);
486 $values[] = $this->Quote($v);
489 $this->setQuery(sprintf($statement, implode(
',', $fields), implode(
',', $values)));
491 if (!$this->execute())
496 $id = $this->insertid();
513 public function insertid()
518 $this->setQuery(
'SELECT @@IDENTITY');
520 return (
int) $this->loadResult();
531 public function loadResult()
536 if (!($cursor = $this->execute()))
542 if ($row = sqlsrv_fetch_array($cursor, SQLSRV_FETCH_NUMERIC))
548 $this->freeResult($cursor);
551 $ret = stripslashes($ret);
565 public function execute()
569 if (!is_resource($this->connection))
572 throw new RuntimeException($this->errorMsg, $this->errorNum);
576 $query = $this->replacePrefix((
string) $this->sql);
578 if (!($this->sql instanceof
JDatabaseQuery) && ($this->limit > 0 || $this->offset > 0))
580 $query = $this->limit($query, $this->limit, $this->offset);
588 $this->errorMsg =
'';
594 $this->log[] = $query;
598 $this->timings[] = microtime(
true);
602 if (strncmp(ltrim(strtoupper($query)),
'SELECT', strlen(
'SELECT')) == 0)
604 $array = array(
'Scrollable' => SQLSRV_CURSOR_KEYSET);
612 $this->cursor = @sqlsrv_query($this->connection, $query, array(), $array);
616 $this->timings[] = microtime(
true);
617 if (defined(
'DEBUG_BACKTRACE_IGNORE_ARGS'))
619 $this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
623 $this->callStacks[] = debug_backtrace();
631 if (!$this->connected())
636 $this->connection = null;
640 catch (RuntimeException $e)
643 $errors = sqlsrv_errors();
644 $this->errorNum = $errors[0][
'SQLSTATE'];
645 $this->errorMsg = $errors[0][
'message'] .
'SQL=' . $query;
649 throw new RuntimeException($this->errorMsg, $this->errorNum);
653 return $this->execute();
659 $errors = sqlsrv_errors();
660 $this->errorNum = $errors[0][
'SQLSTATE'];
661 $this->errorMsg = $errors[0][
'message'] .
'SQL=' . $query;
665 throw new RuntimeException($this->errorMsg, $this->errorNum);
669 return $this->cursor;
683 public function replacePrefix($query, $prefix =
'#__')
688 $query = trim($query);
691 while ($startPos < $n)
693 $ip = strpos($query, $prefix, $startPos);
700 $j = strpos($query,
"N'", $startPos);
701 $k = strpos($query,
'"', $startPos);
703 if (($k !==
false) && (($k < $j) || ($j ===
false)))
718 $literal .= str_replace($prefix, $this->tablePrefix, substr($query, $startPos, $j - $startPos));
731 $k = strpos($query, $quoteChar, $j);
741 while ($l >= 0 && $query{$l} ==
'\\')
744 $escaped = !$escaped;
762 $literal .= substr($query, $startPos, $k - $startPos + 1);
768 $literal .= substr($query, $startPos, $n - $startPos);
784 public function select($database)
793 if (!sqlsrv_query($this->connection,
'USE ' . $database, null, array(
'scrollable' => SQLSRV_CURSOR_STATIC)))
795 throw new RuntimeException(
'Could not connect to database');
808 public function setUTF()
823 public function transactionCommit($toSavepoint =
false)
827 if (!$toSavepoint || $this->transactionDepth <= 1)
829 if ($this->setQuery(
'COMMIT TRANSACTION')->execute())
831 $this->transactionDepth = 0;
837 $this->transactionDepth--;
850 public function transactionRollback($toSavepoint =
false)
854 if (!$toSavepoint || $this->transactionDepth <= 1)
856 if ($this->setQuery(
'ROLLBACK TRANSACTION')->execute())
858 $this->transactionDepth = 0;
864 $savepoint =
'SP_' . ($this->transactionDepth - 1);
865 $this->setQuery(
'ROLLBACK TRANSACTION ' . $this->quoteName($savepoint));
867 if ($this->execute())
869 $this->transactionDepth--;
883 public function transactionStart($asSavepoint =
false)
887 if (!$asSavepoint || !$this->transactionDepth)
889 if ($this->setQuery(
'BEGIN TRANSACTION')->execute())
891 $this->transactionDepth = 1;
897 $savepoint =
'SP_' . $this->transactionDepth;
898 $this->setQuery(
'BEGIN TRANSACTION ' . $this->quoteName($savepoint));
900 if ($this->execute())
902 $this->transactionDepth++;
915 protected function fetchArray($cursor = null)
917 return sqlsrv_fetch_array($cursor ? $cursor : $this->cursor, SQLSRV_FETCH_NUMERIC);
929 protected function fetchAssoc($cursor = null)
931 return sqlsrv_fetch_array($cursor ? $cursor : $this->cursor, SQLSRV_FETCH_ASSOC);
944 protected function fetchObject($cursor = null, $class =
'stdClass')
946 return sqlsrv_fetch_object($cursor ? $cursor : $this->cursor, $class);
958 protected function freeResult($cursor = null)
960 sqlsrv_free_stmt($cursor ? $cursor : $this->cursor);
973 protected function checkFieldExists($table, $field)
977 $table = $this->replacePrefix((
string) $table);
978 $query =
"SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '$table' AND COLUMN_NAME = '$field'" .
979 " ORDER BY ORDINAL_POSITION";
980 $this->setQuery($query);
982 if ($this->loadResult())
1003 protected function limit($query, $limit, $offset)
1005 if ($limit == 0 && $offset == 0)
1010 $start = $offset + 1;
1011 $end = $offset + $limit;
1013 $orderBy = stristr($query,
'ORDER BY');
1015 if (is_null($orderBy) || empty($orderBy))
1017 $orderBy =
'ORDER BY (select 0)';
1020 $query = str_ireplace($orderBy,
'', $query);
1022 $rowNumberText =
', ROW_NUMBER() OVER (' . $orderBy .
') AS RowNumber FROM ';
1024 $query = preg_replace(
'/\sFROM\s/i', $rowNumberText, $query, 1);
1025 $query =
'SELECT * FROM (' . $query .
') _myResults WHERE RowNumber BETWEEN ' . $start .
' AND ' . $end;
1043 public function renameTable($oldTable, $newTable, $backup = null, $prefix = null)
1045 $constraints = array();
1047 if (!is_null($prefix) && !is_null($backup))
1049 $constraints = $this->getTableConstraints($oldTable);
1051 if (!empty($constraints))
1053 $this->renameConstraints($constraints, $prefix, $backup);
1056 $this->setQuery(
"sp_rename '" . $oldTable .
"', '" . $newTable .
"'");
1058 return $this->execute();
1071 public function lockTable($tableName)
1084 public function unlockTables()