Index: includes/acp/acp_database.php
===================================================================
--- includes/acp/acp_database.php (revision 10488)
+++ includes/acp/acp_database.php (working copy)
@@ -109,6 +109,7 @@
case 'mssql':
case 'mssql_odbc':
+ case 'mssqlnative':
$extractor = new mssql_extractor($download, $store, $format, $filename, $time);
break;
@@ -138,6 +139,7 @@
case 'mssql':
case 'mssql_odbc':
+ case 'mssqlnative':
$extractor->flush('TRUNCATE TABLE ' . $table_name . "GO\n");
break;
@@ -1509,6 +1511,10 @@
{
$this->write_data_mssql($table_name);
}
+ else if($db->sql_layer === 'mssqlnative')
+ {
+ $this->write_data_mssqlnative($table_name);
+ }
else
{
$this->write_data_odbc($table_name);
@@ -1608,7 +1614,103 @@
}
$this->flush($sql_data);
}
+
+ function write_data_mssqlnative($table_name)
+ {
+ global $db;
+ $ary_type = $ary_name = $meta_array = array();
+ $ident_set = false;
+ $sql_data = '';
+ // Grab all of the data from current table.
+ $sql = "SELECT * FROM $table_name";
+ $result = $db->sql_query($sql);
+
+ $retrieved_data = $db->mssqlnative_num_rows($result);
+
+ $meta_array = sqlsrv_field_metadata($result);
+ $i_num_fields = sqlsrv_num_fields($result);
+
+ for ($i = 0; $i < $i_num_fields; $i++)
+ {
+ $info = $db->mssqlnative_fieldInfo($table_name, $meta_array[$i]['Name']);
+ $ary_type[$i] = $info->type();
+ $ary_name[$i] = $info->name();
+ }
+
+ if ($retrieved_data)
+ {
+ $sql = "SELECT 1 as has_identity
+ FROM INFORMATION_SCHEMA.COLUMNS
+ WHERE COLUMNPROPERTY(object_id('$table_name'), COLUMN_NAME, 'IsIdentity') = 1";
+ $result2 = $db->sql_query($sql);
+ $row2 = $db->sql_fetchrow($result2);
+
+ if (!empty($row2['has_identity']))
+ {
+ $sql_data .= "\nSET IDENTITY_INSERT $table_name ON\nGO\n";
+ $ident_set = true;
+ }
+ $db->sql_freeresult($result2);
+ }
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $schema_vals = $schema_fields = array();
+
+ // Build the SQL statement to recreate the data.
+ for ($i = 0; $i < $i_num_fields; $i++)
+ {
+ $str_val = $row[$ary_name[$i]];
+
+ if (preg_match('#char|text|bool|varbinary#i', $ary_type[$i]))
+ {
+ $str_quote = '';
+ $str_empty = "''";
+ $str_val = sanitize_data_mssql(str_replace("'", "''", $str_val));
+ }
+ else if (preg_match('#date|timestamp#i', $ary_type[$i]))
+ {
+ if (empty($str_val))
+ {
+ $str_quote = '';
+ }
+ else
+ {
+ $str_quote = "'";
+ }
+ }
+ else
+ {
+ $str_quote = '';
+ $str_empty = 'NULL';
+ }
+
+ if (empty($str_val) && $str_val !== '0' && !(is_int($str_val) || is_float($str_val)))
+ {
+ $str_val = $str_empty;
+ }
+
+ $schema_vals[$i] = $str_quote . $str_val . $str_quote;
+ $schema_fields[$i] = $ary_name[$i];
+ }
+
+ // Take the ordered fields and their associated data and build it
+ // into a valid sql statement to recreate that field in the data.
+ $sql_data .= "INSERT INTO $table_name (" . implode(', ', $schema_fields) . ') VALUES (' . implode(', ', $schema_vals) . ");\nGO\n";
+
+ $this->flush($sql_data);
+ $sql_data = '';
+ }
+ $db->sql_freeresult($result);
+
+ if ($retrieved_data && $ident_set)
+ {
+ $sql_data .= "\nSET IDENTITY_INSERT $table_name OFF\nGO\n";
+ }
+ $this->flush($sql_data);
+ }
+
function write_data_odbc($table_name)
{
global $db;
Index: includes/cache.php
===================================================================
--- includes/cache.php (revision 10488)
+++ includes/cache.php (working copy)
@@ -299,6 +299,7 @@
{
case 'mssql':
case 'mssql_odbc':
+ case 'mssqlnative':
$sql = 'SELECT user_id, bot_agent, bot_ip
FROM ' . BOTS_TABLE . '
WHERE bot_active = 1
Index: includes/db/db_tools.php
===================================================================
--- includes/db/db_tools.php (revision 10488)
+++ includes/db/db_tools.php (working copy)
@@ -159,7 +159,37 @@
'VCHAR_CI' => '[varchar] (255)',
'VARBINARY' => '[varchar] (255)',
),
-
+
+ 'mssqlnative' => array(
+ 'INT:' => '[int]',
+ 'BINT' => '[float]',
+ 'UINT' => '[int]',
+ 'UINT:' => '[int]',
+ 'TINT:' => '[int]',
+ 'USINT' => '[int]',
+ 'BOOL' => '[int]',
+ 'VCHAR' => '[varchar] (255)',
+ 'VCHAR:' => '[varchar] (%d)',
+ 'CHAR:' => '[char] (%d)',
+ 'XSTEXT' => '[varchar] (1000)',
+ 'STEXT' => '[varchar] (3000)',
+ 'TEXT' => '[varchar] (8000)',
+ 'MTEXT' => '[text]',
+ 'XSTEXT_UNI'=> '[varchar] (100)',
+ 'STEXT_UNI' => '[varchar] (255)',
+ 'TEXT_UNI' => '[varchar] (4000)',
+ 'MTEXT_UNI' => '[text]',
+ 'TIMESTAMP' => '[int]',
+ 'DECIMAL' => '[float]',
+ 'DECIMAL:' => '[float]',
+ 'PDECIMAL' => '[float]',
+ 'PDECIMAL:' => '[float]',
+ 'VCHAR_UNI' => '[varchar] (255)',
+ 'VCHAR_UNI:'=> '[varchar] (%d)',
+ 'VCHAR_CI' => '[varchar] (255)',
+ 'VARBINARY' => '[varchar] (255)',
+ ),
+
'oracle' => array(
'INT:' => 'number(%d)',
'BINT' => 'number(20)',
@@ -261,7 +291,7 @@
* A list of supported DBMS. We change this class to support more DBMS, the DBMS itself only need to follow some rules.
* @var array
*/
- var $supported_dbms = array('firebird', 'mssql', 'mysql_40', 'mysql_41', 'oracle', 'postgres', 'sqlite');
+ var $supported_dbms = array('firebird', 'mssql', 'mssqlnative', 'mysql_40', 'mysql_41', 'oracle', 'postgres', 'sqlite');
/**
* This is set to true if user only wants to return the 'to-be-executed' SQL statement(s) (as an array).
@@ -306,6 +336,10 @@
case 'mssql_odbc':
$this->sql_layer = 'mssql';
break;
+
+ case 'mssqlnative':
+ $this->sql_layer = 'mssqlnative';
+ break;
default:
$this->sql_layer = $this->db->sql_layer;
@@ -368,6 +402,7 @@
switch ($this->sql_layer)
{
case 'mssql':
+ case 'mssqlnative':
$table_sql = 'CREATE TABLE [' . $table_name . '] (' . "\n";
break;
@@ -386,6 +421,7 @@
switch ($this->sql_layer)
{
case 'mssql':
+ case 'mssqlnative':
$columns[] = "\t [{$column_name}] " . $prepared_column['column_type_sql_default'];
break;
@@ -425,6 +461,7 @@
break;
case 'mssql':
+ case 'mssqlnative':
$table_sql .= "\n) ON [PRIMARY]" . (($create_textimage) ? ' TEXTIMAGE_ON [PRIMARY]' : '');
$statements[] = $table_sql;
break;
@@ -453,6 +490,7 @@
case 'firebird':
case 'mssql':
+ case 'mssqlnative':
// We need the data here
$old_return_statements = $this->return_statements;
$this->return_statements = true;
@@ -970,6 +1008,7 @@
// same deal with PostgreSQL, we must perform more complex operations than
// we technically could
case 'mssql':
+ case 'mssqlnative':
$sql = "SELECT c.name
FROM syscolumns c
LEFT JOIN sysobjects o ON c.id = o.id
@@ -1187,6 +1226,7 @@
break;
case 'mssql':
+ case 'mssqlnative':
$sql .= " {$column_type} ";
$sql_default = " {$column_type} ";
@@ -1335,6 +1375,7 @@
break;
case 'mssql':
+ case 'mssqlnative':
$statements[] = 'ALTER TABLE [' . $table_name . '] ADD [' . $column_name . '] ' . $column_data['column_type_sql_default'];
break;
@@ -1455,6 +1496,7 @@
break;
case 'mssql':
+ case 'mssqlnative':
$statements[] = 'ALTER TABLE [' . $table_name . '] DROP COLUMN [' . $column_name . ']';
break;
@@ -1549,6 +1591,7 @@
switch ($this->sql_layer)
{
case 'mssql':
+ case 'mssqlnative':
$statements[] = 'DROP INDEX ' . $table_name . '.' . $index_name;
break;
@@ -1652,6 +1695,7 @@
break;
case 'mssql':
+ case 'mssqlnative':
$sql = "ALTER TABLE [{$table_name}] WITH NOCHECK ADD ";
$sql .= "CONSTRAINT [PK_{$table_name}] PRIMARY KEY CLUSTERED (";
$sql .= '[' . implode("],\n\t\t[", $column) . ']';
@@ -1745,6 +1789,7 @@
break;
case 'mssql':
+ case 'mssqlnative':
$statements[] = 'CREATE UNIQUE INDEX ' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ') ON [PRIMARY]';
break;
}
@@ -1774,6 +1819,7 @@
break;
case 'mssql':
+ case 'mssqlnative':
$statements[] = 'CREATE INDEX ' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ') ON [PRIMARY]';
break;
}
@@ -1791,7 +1837,7 @@
{
$index_array = array();
- if ($this->sql_layer == 'mssql')
+ if ($this->sql_layer == 'mssql' || $this->sql_laery == 'mssqlnative')
{
$sql = "EXEC sp_statistics '$table_name'";
$result = $this->db->sql_query($sql);
@@ -1900,6 +1946,7 @@
break;
case 'mssql':
+ case 'mssqlnative':
$statements[] = 'ALTER TABLE [' . $table_name . '] ALTER COLUMN [' . $column_name . '] ' . $column_data['column_type_sql'];
if (!empty($column_data['default']))
Index: includes/db/mssqlnative.php
===================================================================
--- includes/db/mssqlnative.php (revision 0)
+++ includes/db/mssqlnative.php (revision 0)
@@ -0,0 +1,608 @@
+m_cursor = 0;
+ $this->m_rows = array();
+ $this->m_num_fields = sqlsrv_num_fields($queryresult);
+ $this->m_field_meta = sqlsrv_field_metadata($queryresult);
+
+ while($row = sqlsrv_fetch_array($queryresult, SQLSRV_FETCH_ASSOC))
+ {
+ if($row !== null)
+ {
+ foreach($row as $k => $v)
+ {
+ if (is_object($v) && method_exists($v, 'format'))
+ {
+ $row[$k] = $v->format("Y-m-d\TH:i:s\Z");
+ }
+ }
+ $this->m_rows[] = $row;//read results into memory, cursors are not supported
+ }
+ }
+
+ $this->m_row_count = count($this->m_rows);
+ sqlsrv_free_stmt($queryresult);
+ }
+
+ private function array_to_obj($array, &$obj)
+ {
+ foreach ($array as $key => $value)
+ {
+ if (is_array($value))
+ {
+ $obj->$key = new stdClass();
+ array_to_obj($value, $obj->$key);
+ }
+ else
+ {
+ $obj->$key = $value;
+ }
+ }
+ return $obj;
+ }
+
+ public function fetch($mode=SQLSRV_FETCH_BOTH, $object_class = 'stdClass')
+ {
+ if($this->m_cursor >= $this->m_row_count || $this->m_row_count == 0)
+ {
+ return false;
+ }
+
+ $ret = false;
+ $arr_num = array();
+
+ if($mode == SQLSRV_FETCH_NUMERIC || $mode == SQLSRV_FETCH_BOTH)
+ {
+ foreach($this->m_rows[$this->m_cursor] as $key=>$value)
+ {
+ $arr_num[] = $value;
+ }
+ }
+
+ switch($mode)
+ {
+ case SQLSRV_FETCH_ASSOC:
+ $ret = $this->m_rows[$this->m_cursor];
+ break;
+ case SQLSRV_FETCH_NUMERIC:
+ $ret = $arr_num;
+ break;
+ case 'OBJECT':
+ $ret = $this->array_to_obj($this->m_rows[$this->m_cursor], $o = new $object_class);
+ break;
+ case SQLSRV_FETCH_BOTH:
+ default:
+ $ret = $this->m_rows[$this->m_cursor] + $arr_num;
+ break;
+ }
+ $this->m_cursor++;
+ return $ret;
+ }
+
+ public function get($pos, $fld)
+ {
+ return $this->m_rows[$pos][$fld];
+ }
+
+ public function num_rows()
+ {
+ return $this->m_row_count;
+ }
+
+ public function seek($iRow)
+ {
+ $this->m_cursor = min($iRow, $this->m_row_count);
+ }
+
+ public function num_fields()
+ {
+ return $this->m_num_fields;
+ }
+
+ public function field_name($nr)
+ {
+ $arr_keys = array_keys($this->m_rows[0]);
+ return $arr_keys[$nr];
+ }
+
+ public function field_type($nr)
+ {
+ $i=0;
+ $int_type = -1;
+ $str_type = '';
+
+ foreach($this->m_field_meta as $meta)
+ {
+ if($nr==$i)
+ {
+ $int_type = $meta['Type'];
+ break;
+ }
+ $i++;
+ }
+
+ //http://msdn.microsoft.com/en-us/library/cc296183.aspx contains type table
+ switch($int_type)
+ {
+ case SQLSRV_SQLTYPE_BIGINT: $str_type = 'bigint'; break;
+ case SQLSRV_SQLTYPE_BINARY: $str_type = 'binary'; break;
+ case SQLSRV_SQLTYPE_BIT: $str_type = 'bit'; break;
+ case SQLSRV_SQLTYPE_CHAR: $str_type = 'char'; break;
+ case SQLSRV_SQLTYPE_DATETIME: $str_type = 'datetime'; break;
+ case SQLSRV_SQLTYPE_DECIMAL/*($precision, $scale)*/: $str_type = 'decimal'; break;
+ case SQLSRV_SQLTYPE_FLOAT: $str_type = 'float'; break;
+ case SQLSRV_SQLTYPE_IMAGE: $str_type = 'image'; break;
+ case SQLSRV_SQLTYPE_INT: $str_type = 'int'; break;
+ case SQLSRV_SQLTYPE_MONEY: $str_type = 'money'; break;
+ case SQLSRV_SQLTYPE_NCHAR/*($charCount)*/: $str_type = 'nchar'; break;
+ case SQLSRV_SQLTYPE_NUMERIC/*($precision, $scale)*/: $str_type = 'numeric'; break;
+ case SQLSRV_SQLTYPE_NVARCHAR/*($charCount)*/: $str_type = 'nvarchar'; break;
+ case SQLSRV_SQLTYPE_NTEXT: $str_type = 'ntext'; break;
+ case SQLSRV_SQLTYPE_REAL: $str_type = 'real'; break;
+ case SQLSRV_SQLTYPE_SMALLDATETIME: $str_type = 'smalldatetime'; break;
+ case SQLSRV_SQLTYPE_SMALLINT: $str_type = 'smallint'; break;
+ case SQLSRV_SQLTYPE_SMALLMONEY: $str_type = 'smallmoney'; break;
+ case SQLSRV_SQLTYPE_TEXT: $str_type = 'text'; break;
+ case SQLSRV_SQLTYPE_TIMESTAMP: $str_type = 'timestamp'; break;
+ case SQLSRV_SQLTYPE_TINYINT: $str_type = 'tinyint'; break;
+ case SQLSRV_SQLTYPE_UNIQUEIDENTIFIER: $str_type = 'uniqueidentifier'; break;
+ case SQLSRV_SQLTYPE_UDT: $str_type = 'UDT'; break;
+ case SQLSRV_SQLTYPE_VARBINARY/*($byteCount)*/: $str_type = 'varbinary'; break;
+ case SQLSRV_SQLTYPE_VARCHAR/*($charCount)*/: $str_type = 'varchar'; break;
+ case SQLSRV_SQLTYPE_XML: $str_type = 'xml'; break;
+ default: $str_type = $int_type;
+ }
+ return $str_type;
+ }
+
+ public function free()
+ {
+ unset($this->m_rows);
+ return;
+ }
+}
+
+/**
+* @package dbal
+*/
+class dbal_mssqlnative extends dbal
+{
+ var $m_insert_id = NULL;
+ var $last_query_text = '';
+
+ /**
+ * Connect to server
+ */
+ function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
+ {
+ # Test for driver support, to avoid suppressed fatal error
+ if (!function_exists('sqlsrv_connect'))
+ {
+ die('Native MS SQL Server driver for PHP is missing or needs to be updated. Version 1.1 or later is required to install phpBB3. You can download the driver from: http://www.microsoft.com/sqlserver/2005/en/us/PHP-Driver.aspx\n');
+ }
+
+ //set up connection variables
+ $this->persistency = $persistency;
+ $this->user = $sqluser;
+ $this->dbname = $database;
+ $port_delimiter = (defined('PHP_OS') && substr(PHP_OS, 0, 3) === 'WIN') ? ',' : ':';
+ $this->server = $sqlserver . (($port) ? $port_delimiter . $port : '');
+
+ //connect to database
+ error_reporting( E_ALL );
+ $this->db_connect_id = sqlsrv_connect($this->server, array('Database'=>$this->dbname,'UID'=>$this->user,'PWD'=>$sqlpassword));
+
+ if($this->db_connect_id === false)
+ {
+ print_r('Unable to connect to database:');
+ die(print_r(sqlsrv_errors(), true));
+ }
+ return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
+ }
+
+ /**
+ * Version information about used database
+ * @param bool $raw if true, only return the fetched sql_server_version
+ * @return string sql server version
+ */
+ function sql_server_info($raw = false)
+ {
+ global $cache;
+
+ if (empty($cache) || ($this->sql_server_version = $cache->get('mssql_version')) === false)
+ {
+ $arr_server_info = sqlsrv_server_info($this->db_connect_id);
+ $this->sql_server_version = $arr_server_info['SQLServerVersion'];
+
+ if (!empty($cache))
+ {
+ $cache->put('mssql_version', $this->sql_server_version);
+ }
+ }
+
+ if ($raw)
+ {
+ return $this->sql_server_version;
+ }
+
+ return ($this->sql_server_version) ? 'MSSQL
' . $this->sql_server_version : 'MSSQL';
+ }
+
+ /**
+ * SQL Transaction
+ * @access private
+ */
+ function _sql_transaction($status = 'begin')
+ {
+ switch ($status)
+ {
+ case 'begin':
+ return sqlsrv_begin_transaction($this->db_connect_id);
+ break;
+
+ case 'commit':
+ return sqlsrv_commit($this->db_connect_id);
+ break;
+
+ case 'rollback':
+ return sqlsrv_rollback($this->db_connect_id);
+ break;
+ }
+ return true;
+ }
+
+ /**
+ * Base query method
+ *
+ * @param string $query Contains the SQL query which shall be executed
+ * @param int $cache_ttl Either 0 to avoid caching or the time in seconds which the result shall be kept in cache
+ * @return mixed When casted to bool the returned value returns true on success and false on failure
+ *
+ * @access public
+ */
+ function sql_query($query = '', $cache_ttl = 0)
+ {
+ if ($query != '')
+ {
+ global $cache;
+
+ // EXPLAIN only in extra debug mode
+ if (defined('DEBUG_EXTRA'))
+ {
+ $this->sql_report('start', $query);
+ }
+
+ $this->last_query_text = $query;
+ $this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
+ $this->sql_add_num_queries($this->query_result);
+
+ if ($this->query_result === false)
+ {
+ if (($this->query_result = @sqlsrv_query($this->db_connect_id, $query)) === false)
+ {
+ $this->sql_error($query);
+ }
+
+ if (defined('DEBUG_EXTRA'))
+ {
+ $this->sql_report('stop', $query);
+ }
+
+ if ($cache_ttl && method_exists($cache, 'sql_save'))
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ $cache->sql_save($query, $this->query_result, $cache_ttl);
+ }
+ else if (strpos($query, 'SELECT') === 0 && $this->query_result)
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ }
+ }
+ else if (defined('DEBUG_EXTRA'))
+ {
+ $this->sql_report('fromcache', $query);
+ }
+ }
+ else
+ {
+ return false;
+ }
+ return $this->query_result;
+ }
+
+ /**
+ * Build LIMIT query
+ */
+ function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
+ {
+ $this->query_result = false;
+
+ if($offset === false || $offset == 0)
+ {
+ if (strpos($query, "SELECT") === false)
+ {
+ $query = "TOP {$total} " . $query;
+ }
+ else
+ {
+ $query = preg_replace('/SELECT(\s*DISTINCT)?/Dsi', 'SELECT$1 TOP '.$total, $query);
+ }
+ }
+ else
+ {
+ $query = preg_replace('/SELECT(\s*DISTINCT)?/Dsi', 'SELECT$1 TOP(10000000) ', $query);
+ $query = 'SELECT *
+ FROM (SELECT sub2.*, ROW_NUMBER() OVER(ORDER BY sub2.line2) AS line3
+ FROM (SELECT 1 AS line2, sub1.* FROM (' . $query . ') AS sub1) as sub2) AS sub3
+ WHERE line3 BETWEEN ' . ($offset+1) . ' AND ' . ($offset + $total);
+ }
+
+ $result = $this->sql_query($query, $cache_ttl);
+
+ return $result;
+ }
+
+ /**
+ * Return number of affected rows
+ */
+ function sql_affectedrows()
+ {
+ return ($this->db_connect_id) ? @sqlsrv_rows_affected($this->db_connect_id) : false;
+ }
+
+ /**
+ * Fetch current row
+ */
+ function sql_fetchrow($query_id = false)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if (isset($cache->sql_rowset[$query_id]))
+ {
+ return $cache->sql_fetchrow($query_id);
+ }
+
+ if ($query_id === false)
+ {
+ return false;
+ }
+
+ $row = @sqlsrv_fetch_array($query_id, SQLSRV_FETCH_ASSOC);
+
+ // I hope i am able to remove this later... hopefully only a PHP or MSSQL bug
+ if ($row)
+ {
+ foreach ($row as $key => $value)
+ {
+ $row[$key] = ($value === ' ' || $value === NULL) ? '' : $value;
+ }
+ }
+ return $row;
+ }
+
+ /**
+ * Seek to given row number
+ * rownum is zero-based
+ */
+ function sql_rowseek($rownum, &$query_id)
+ {
+ global $cache;
+
+ if (isset($cache->sql_rowset[$query_id]))
+ {
+ return $cache->sql_rowseek($rownum, $query_id);
+ }
+
+ $seek = new result_mssqlnative($query_id);
+ $row = $seek->seek($rownum);
+ return ($row = $seek->fetch()) ? $row : false;
+ }
+
+ /**
+ * Get last inserted id after insert statement
+ */
+ function sql_nextid()
+ {
+ $result_id = @sqlsrv_query($this->db_connect_id, 'SELECT @@IDENTITY');
+
+ if ($result_id !== false)
+ {
+ $row = @sqlsrv_fetch_array($result_id);
+ $id = $row[0];
+ @sqlsrv_free_stmt($result_id);
+ return $id;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Free sql result
+ */
+ function sql_freeresult($query_id = false)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if (isset($cache->sql_rowset[$query_id]))
+ {
+ return $cache->sql_freeresult($query_id);
+ }
+
+ if (isset($this->open_queries[$query_id]))
+ {
+ unset($this->open_queries[$query_id]);
+ return @sqlsrv_free_stmt($query_id);
+ }
+ return false;
+ }
+
+ /**
+ * Escape string used in sql query
+ */
+ function sql_escape($msg)
+ {
+ return str_replace(array("'", "\0"), array("''", ''), $msg);
+ }
+
+ /**
+ * Build LIKE expression
+ * @access private
+ */
+ function _sql_like_expression($expression)
+ {
+ return $expression . " ESCAPE '\\'";
+ }
+
+ /**
+ * return sql error array
+ * @access private
+ */
+ function _sql_error()
+ {
+ $errors = @sqlsrv_errors(SQLSRV_ERR_ERRORS);
+ $error_message = '';
+
+ if($errors != null)
+ {
+ foreach($errors as $error)
+ {
+ $error_message .= "SQLSTATE: ".$error[ 'SQLSTATE']."\n";
+ $error_message .= "code: ".$error[ 'code']."\n";
+ $error_message .= "message: ".$error[ 'message']."\n";
+ }
+ $this->last_error_result = $error_message;
+ $error = $this->last_error_result;
+ }
+ else
+ {
+ $error = (isset($this->last_error_result) && $this->last_error_result) ? $this->last_error_result : array();
+ }
+ return $error;
+ }
+
+ /**
+ * Build db-specific query data
+ * @access private
+ */
+ function _sql_custom_build($stage, $data)
+ {
+ return $data;
+ }
+
+ /**
+ * Close sql connection
+ * @access private
+ */
+ function _sql_close()
+ {
+ return @sqlsrv_close($this->db_connect_id);
+ }
+
+ /**
+ * Build db-specific report
+ * @access private
+ */
+ function _sql_report($mode, $query = '')
+ {
+ switch ($mode)
+ {
+ case 'start':
+ $html_table = false;
+ @sqlsrv_query($this->db_connect_id, 'SET SHOWPLAN_TEXT ON;');
+ if ($result = @sqlsrv_query($this->db_connect_id, $query))
+ {
+ @sqlsrv_next_result($result);
+ while ($row = @sqlsrv_fetch_array($result))
+ {
+ $html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
+ }
+ }
+ @sqlsrv_query($this->db_connect_id, 'SET SHOWPLAN_TEXT OFF;');
+ @sqlsrv_free_stmt($result);
+
+ if ($html_table)
+ {
+ $this->html_hold .= '';
+ }
+ break;
+
+ case 'fromcache':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $result = @sqlsrv_query($this->db_connect_id, $query);
+ while ($void = @sqlsrv_fetch_array($result))
+ {
+ // Take the time spent on parsing rows into account
+ }
+ @sqlsrv_free_stmt($result);
+
+ $splittime = explode(' ', microtime());
+ $splittime = $splittime[0] + $splittime[1];
+
+ $this->sql_report('record_fromcache', $query, $endtime, $splittime);
+
+ break;
+ }
+ }
+
+ /**
+ * Utility method used to retrieve number of rows
+ * Emulates mysql_num_rows
+ * Used in acp_database.php -> write_data_mssqlnative()
+ */
+ function mssqlnative_num_rows($res){
+ if($res !== false){
+ $row = new result_mssqlnative($res);
+ $num_rows = $row->num_rows();
+ return $num_rows;
+ } else {
+ return false;
+ }
+ }
+}
+
+?>
\ No newline at end of file
Index: includes/functions_admin.php
===================================================================
--- includes/functions_admin.php (revision 10488)
+++ includes/functions_admin.php (working copy)
@@ -3041,6 +3041,7 @@
case 'mssql':
case 'mssql_odbc':
+ case 'mssqlnative':
$sql = 'SELECT ((SUM(size) * 8.0) * 1024.0) as dbsize
FROM sysfiles';
$result = $db->sql_query($sql, 7200);
Index: includes/functions_convert.php
===================================================================
--- includes/functions_convert.php (revision 10488)
+++ includes/functions_convert.php (working copy)
@@ -1643,6 +1643,7 @@
case 'mssql':
case 'sqlite':
+ case 'mssqlnative':
$sql = implode(' UNION ALL ', preg_replace('#^(.*?)$#', 'SELECT \1', $sql_subary));
break;
Index: includes/functions_install.php
===================================================================
--- includes/functions_install.php (revision 10488)
+++ includes/functions_install.php (working copy)
@@ -95,6 +95,16 @@
'AVAILABLE' => true,
'2.0.x' => true,
),
+ 'mssqlnative' => array(
+ 'LABEL' => 'MS SQL Server 2005+ [ Native ]',
+ 'SCHEMA' => 'mssql',
+ 'MODULE' => 'sqlsrv',
+ 'DELIM' => 'GO',
+ 'COMMENTS' => 'remove_comments',
+ 'DRIVER' => 'mssqlnative',
+ 'AVAILABLE' => true,
+ '2.0.x' => false,
+ ),
'oracle' => array(
'LABEL' => 'Oracle',
'SCHEMA' => 'oracle',
@@ -220,6 +230,7 @@
case 'mssql':
case 'mssql_odbc':
+ case 'mssqlnative':
$sql = "SELECT name
FROM sysobjects
WHERE type='U'";
@@ -313,6 +324,7 @@
case 'mssql':
case 'mssql_odbc':
+ case 'mssqlnative':
$prefix_length = 90;
break;
Index: includes/message_parser.php
===================================================================
--- includes/message_parser.php (revision 10488)
+++ includes/message_parser.php (working copy)
@@ -1284,6 +1284,7 @@
{
case 'mssql':
case 'mssql_odbc':
+ case 'mssqlnative':
$sql = 'SELECT *
FROM ' . SMILIES_TABLE . '
ORDER BY LEN(code) DESC';
Index: install/database_update.php
===================================================================
--- install/database_update.php (revision 10488)
+++ install/database_update.php (working copy)
@@ -1743,7 +1743,37 @@
'VCHAR_CI' => '[varchar] (255)',
'VARBINARY' => '[varchar] (255)',
),
-
+
+ 'mssqlnative' => array(
+ 'INT:' => '[int]',
+ 'BINT' => '[float]',
+ 'UINT' => '[int]',
+ 'UINT:' => '[int]',
+ 'TINT:' => '[int]',
+ 'USINT' => '[int]',
+ 'BOOL' => '[int]',
+ 'VCHAR' => '[varchar] (255)',
+ 'VCHAR:' => '[varchar] (%d)',
+ 'CHAR:' => '[char] (%d)',
+ 'XSTEXT' => '[varchar] (1000)',
+ 'STEXT' => '[varchar] (3000)',
+ 'TEXT' => '[varchar] (8000)',
+ 'MTEXT' => '[text]',
+ 'XSTEXT_UNI'=> '[varchar] (100)',
+ 'STEXT_UNI' => '[varchar] (255)',
+ 'TEXT_UNI' => '[varchar] (4000)',
+ 'MTEXT_UNI' => '[text]',
+ 'TIMESTAMP' => '[int]',
+ 'DECIMAL' => '[float]',
+ 'DECIMAL:' => '[float]',
+ 'PDECIMAL' => '[float]',
+ 'PDECIMAL:' => '[float]',
+ 'VCHAR_UNI' => '[varchar] (255)',
+ 'VCHAR_UNI:'=> '[varchar] (%d)',
+ 'VCHAR_CI' => '[varchar] (255)',
+ 'VARBINARY' => '[varchar] (255)',
+ ),
+
'oracle' => array(
'INT:' => 'number(%d)',
'BINT' => 'number(20)',
@@ -1845,7 +1875,7 @@
* A list of supported DBMS. We change this class to support more DBMS, the DBMS itself only need to follow some rules.
* @var array
*/
- var $supported_dbms = array('firebird', 'mssql', 'mysql_40', 'mysql_41', 'oracle', 'postgres', 'sqlite');
+ var $supported_dbms = array('firebird', 'mssql', 'mssqlnative', 'mysql_40', 'mysql_41', 'oracle', 'postgres', 'sqlite');
/**
* This is set to true if user only wants to return the 'to-be-executed' SQL statement(s) (as an array).
@@ -1890,6 +1920,10 @@
case 'mssql_odbc':
$this->sql_layer = 'mssql';
break;
+
+ case 'mssqlnative':
+ $this->sql_layer = 'mssqlnative';
+ break;
default:
$this->sql_layer = $this->db->sql_layer;
@@ -2322,6 +2356,7 @@
// same deal with PostgreSQL, we must perform more complex operations than
// we technically could
case 'mssql':
+ case 'mssqlnative':
$sql = "SELECT c.name
FROM syscolumns c
LEFT JOIN sysobjects o ON c.id = o.id
@@ -2425,7 +2460,7 @@
*/
function sql_index_exists($table_name, $index_name)
{
- if ($this->sql_layer == 'mssql')
+ if ($this->sql_layer == 'mssql' || $this->sql_layer == 'mssqlnative')
{
$sql = "EXEC sp_statistics '$table_name'";
$result = $this->db->sql_query($sql);
@@ -2530,7 +2565,7 @@
*/
function sql_unique_index_exists($table_name, $index_name)
{
- if ($this->sql_layer == 'mssql')
+ if ($this->sql_layer == 'mssql' || $this->sql_layer == 'mssqlnative')
{
$sql = "EXEC sp_statistics '$table_name'";
$result = $this->db->sql_query($sql);
@@ -2769,6 +2804,7 @@
break;
case 'mssql':
+ case 'mssqlnative':
$sql .= " {$column_type} ";
$sql_default = " {$column_type} ";
@@ -2918,6 +2954,7 @@
break;
case 'mssql':
+ case 'mssqlnative':
// Does not support AFTER, only through temporary table
$statements[] = 'ALTER TABLE [' . $table_name . '] ADD [' . $column_name . '] ' . $column_data['column_type_sql_default'];
break;
@@ -3042,6 +3079,7 @@
break;
case 'mssql':
+ case 'mssqlnative':
$statements[] = 'ALTER TABLE [' . $table_name . '] DROP COLUMN [' . $column_name . ']';
break;
@@ -3136,6 +3174,7 @@
switch ($this->sql_layer)
{
case 'mssql':
+ case 'mssqlnative':
$statements[] = 'DROP INDEX ' . $table_name . '.' . $index_name;
break;
@@ -3172,6 +3211,7 @@
break;
case 'mssql':
+ case 'mssqlnative':
$sql = "ALTER TABLE [{$table_name}] WITH NOCHECK ADD ";
$sql .= "CONSTRAINT [PK_{$table_name}] PRIMARY KEY CLUSTERED (";
$sql .= '[' . implode("],\n\t\t[", $column) . ']';
@@ -3265,6 +3305,7 @@
break;
case 'mssql':
+ case 'mssqlnative':
$statements[] = 'CREATE UNIQUE INDEX ' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ') ON [PRIMARY]';
break;
}
@@ -3294,6 +3335,7 @@
break;
case 'mssql':
+ case 'mssqlnative':
$statements[] = 'CREATE INDEX ' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ') ON [PRIMARY]';
break;
}
@@ -3326,6 +3368,7 @@
break;
case 'mssql':
+ case 'mssqlnative':
$statements[] = 'ALTER TABLE [' . $table_name . '] ALTER COLUMN [' . $column_name . '] ' . $column_data['column_type_sql'];
if (!empty($column_data['default']))
Index: install/install_convert.php
===================================================================
--- install/install_convert.php (revision 10488)
+++ install/install_convert.php (working copy)
@@ -1248,6 +1248,7 @@
{
case 'mssql':
case 'mssql_odbc':
+ case 'mssqlnative':
$db->sql_query('SET IDENTITY_INSERT ' . $schema['target'] . ' ON');
break;
}
@@ -1375,6 +1376,7 @@
{
case 'mssql':
case 'mssql_odbc':
+ case 'mssqlnative':
$db->sql_query('SET IDENTITY_INSERT ' . $schema['target'] . ' OFF');
break;
Index: install/install_install.php
===================================================================
--- install/install_install.php (revision 10488)
+++ install/install_install.php (working copy)
@@ -1203,6 +1203,7 @@
{
case 'mssql':
case 'mssql_odbc':
+ case 'mssqlnative':
$sql_query = preg_replace('#\# MSSQL IDENTITY (phpbb_[a-z_]+) (ON|OFF) \##s', 'SET IDENTITY_INSERT \1 \2;', $sql_query);
break;
Index: install/schemas/mssql_schema.sql
===================================================================
--- install/schemas/mssql_schema.sql (revision 10488)
+++ install/schemas/mssql_schema.sql (working copy)
@@ -4,9 +4,6 @@
*/
-BEGIN TRANSACTION
-GO
-
/*
Table: 'phpbb_attachments'
*/
@@ -1733,8 +1730,3 @@
) ON [PRIMARY]
GO
-
-
-COMMIT
-GO
-
Index: language/en/install.php
===================================================================
--- language/en/install.php (revision 10488)
+++ language/en/install.php (working copy)
@@ -147,6 +147,7 @@
'DLL_MBSTRING' => 'Multi-byte character support',
'DLL_MSSQL' => 'MSSQL Server 2000+',
'DLL_MSSQL_ODBC' => 'MSSQL Server 2000+ via ODBC',
+ 'DLL_MSSQLNATIVE' => 'MSSQL Server 2005+ [ Native ]',
'DLL_MYSQL' => 'MySQL',
'DLL_MYSQLI' => 'MySQL with MySQLi Extension',
'DLL_ORACLE' => 'Oracle',
@@ -214,6 +215,7 @@