mirror of
https://github.com/codeigniter4/CodeIgniter4.git
synced 2025-02-20 11:44:28 +08:00
Finalize SQLSRV schema support for 4.2
This commit is contained in:
parent
bd15c27f93
commit
61c01ed40b
@ -170,6 +170,16 @@ class Builder extends BaseBuilder
|
||||
return $this->keyPermission ? $this->addIdentity($fullTableName, $statement) : $statement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert batch statement
|
||||
*
|
||||
* Generates a platform-specific insert string from the supplied data.
|
||||
*/
|
||||
protected function _insertBatch(string $table, array $keys, array $values): string
|
||||
{
|
||||
return 'INSERT ' . $this->compileIgnore('insert') . 'INTO ' . $this->getFullName($table) . ' (' . implode(', ', $keys) . ') VALUES ' . implode(', ', $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a platform-specific update string from the supplied data
|
||||
*/
|
||||
@ -183,12 +193,48 @@ class Builder extends BaseBuilder
|
||||
|
||||
$fullTableName = $this->getFullName($table);
|
||||
|
||||
$statement = 'UPDATE ' . (empty($this->QBLimit) ? '' : 'TOP(' . $this->QBLimit . ') ') . $fullTableName . ' SET '
|
||||
. implode(', ', $valstr) . $this->compileWhereHaving('QBWhere') . $this->compileOrderBy();
|
||||
$statement = sprintf('UPDATE %s%s SET ', empty($this->QBLimit) ? '' : 'TOP(' . $this->QBLimit . ') ', $fullTableName);
|
||||
|
||||
$statement .= implode(', ', $valstr)
|
||||
. $this->compileWhereHaving('QBWhere')
|
||||
. $this->compileOrderBy();
|
||||
|
||||
return $this->keyPermission ? $this->addIdentity($fullTableName, $statement) : $statement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update_Batch statement
|
||||
*
|
||||
* Generates a platform-specific batch update string from the supplied data
|
||||
*/
|
||||
protected function _updateBatch(string $table, array $values, string $index): string
|
||||
{
|
||||
$ids = [];
|
||||
$final = [];
|
||||
|
||||
foreach ($values as $val) {
|
||||
$ids[] = $val[$index];
|
||||
|
||||
foreach (array_keys($val) as $field) {
|
||||
if ($field !== $index) {
|
||||
$final[$field][] = 'WHEN ' . $index . ' = ' . $val[$index] . ' THEN ' . $val[$field];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$cases = '';
|
||||
|
||||
foreach ($final as $k => $v) {
|
||||
$cases .= $k . " = CASE \n"
|
||||
. implode("\n", $v) . "\n"
|
||||
. 'ELSE ' . $k . ' END, ';
|
||||
}
|
||||
|
||||
$this->where($index . ' IN(' . implode(',', $ids) . ')', null, false);
|
||||
|
||||
return 'UPDATE ' . $this->compileIgnore('update') . ' ' . $this->getFullName($table) . ' SET ' . substr($cases, 0, -2) . $this->compileWhereHaving('QBWhere');
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments a numeric column by the specified value.
|
||||
*
|
||||
@ -203,6 +249,7 @@ class Builder extends BaseBuilder
|
||||
} else {
|
||||
$values = [$column => "{$column} + {$value}"];
|
||||
}
|
||||
|
||||
$sql = $this->_update($this->QBFrom[0], $values);
|
||||
|
||||
return $this->db->query($sql, $this->binds, false);
|
||||
@ -222,6 +269,7 @@ class Builder extends BaseBuilder
|
||||
} else {
|
||||
$values = [$column => "{$column} + {$value}"];
|
||||
}
|
||||
|
||||
$sql = $this->_update($this->QBFrom[0], $values);
|
||||
|
||||
return $this->db->query($sql, $this->binds, false);
|
||||
@ -304,9 +352,10 @@ class Builder extends BaseBuilder
|
||||
return $sql;
|
||||
}
|
||||
|
||||
$this->db->simpleQuery('SET IDENTITY_INSERT ' . $this->db->escapeIdentifiers($table) . ' ON');
|
||||
$this->db->simpleQuery('SET IDENTITY_INSERT ' . $this->getFullName($table) . ' ON');
|
||||
|
||||
$result = $this->db->query($sql, $this->binds, false);
|
||||
$this->db->simpleQuery('SET IDENTITY_INSERT ' . $this->db->escapeIdentifiers($table) . ' OFF');
|
||||
$this->db->simpleQuery('SET IDENTITY_INSERT ' . $this->getFullName($table) . ' OFF');
|
||||
|
||||
return $result;
|
||||
}
|
||||
@ -410,6 +459,40 @@ class Builder extends BaseBuilder
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* "Count All" query
|
||||
*
|
||||
* Generates a platform-specific query string that counts all records in
|
||||
* the particular table
|
||||
*
|
||||
* @param bool $reset Are we want to clear query builder values?
|
||||
*
|
||||
* @return int|string when $test = true
|
||||
*/
|
||||
public function countAll(bool $reset = true)
|
||||
{
|
||||
$table = $this->QBFrom[0];
|
||||
|
||||
$sql = $this->countString . $this->db->escapeIdentifiers('numrows') . ' FROM ' . $this->getFullName($table);
|
||||
|
||||
if ($this->testMode) {
|
||||
return $sql;
|
||||
}
|
||||
|
||||
$query = $this->db->query($sql, null, false);
|
||||
if (empty($query->getResult())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$query = $query->getRow();
|
||||
|
||||
if ($reset === true) {
|
||||
$this->resetSelect();
|
||||
}
|
||||
|
||||
return (int) $query->numrows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete statement
|
||||
*/
|
||||
@ -504,9 +587,10 @@ class Builder extends BaseBuilder
|
||||
}
|
||||
|
||||
$sql .= $this->compileWhereHaving('QBWhere')
|
||||
. $this->compileGroupBy()
|
||||
. $this->compileWhereHaving('QBHaving')
|
||||
. $this->compileOrderBy(); // ORDER BY
|
||||
. $this->compileGroupBy()
|
||||
. $this->compileWhereHaving('QBHaving')
|
||||
. $this->compileOrderBy(); // ORDER BY
|
||||
|
||||
// LIMIT
|
||||
if ($this->QBLimit) {
|
||||
$sql = $this->_limit($sql . "\n");
|
||||
|
@ -215,7 +215,7 @@ class Connection extends BaseConnection
|
||||
*/
|
||||
protected function _indexData(string $table): array
|
||||
{
|
||||
$sql = 'EXEC sp_helpindex ' . $this->escape($table);
|
||||
$sql = 'EXEC sp_helpindex ' . $this->escape($this->schema . '.' . $table);
|
||||
|
||||
if (($query = $this->query($sql)) === false) {
|
||||
throw new DatabaseException(lang('Database.failGetIndexData'));
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace CodeIgniter\Database\SQLSRV;
|
||||
|
||||
use CodeIgniter\Database\BaseConnection;
|
||||
use CodeIgniter\Database\Forge as BaseForge;
|
||||
|
||||
/**
|
||||
@ -23,7 +24,7 @@ class Forge extends BaseForge
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $dropConstraintStr = 'ALTER TABLE %s DROP CONSTRAINT %s';
|
||||
protected $dropConstraintStr;
|
||||
|
||||
/**
|
||||
* CREATE DATABASE IF statement
|
||||
@ -61,7 +62,7 @@ class Forge extends BaseForge
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $renameTableStr = 'EXEC sp_rename %s , %s ;';
|
||||
protected $renameTableStr;
|
||||
|
||||
/**
|
||||
* UNSIGNED support
|
||||
@ -80,21 +81,32 @@ class Forge extends BaseForge
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $createTableIfStr = "IF NOT EXISTS (SELECT * FROM sysobjects WHERE ID = object_id(N'%s') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)\nCREATE TABLE";
|
||||
protected $createTableIfStr;
|
||||
|
||||
/**
|
||||
* CREATE TABLE statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $createTableStr = "%s %s (%s\n) ";
|
||||
protected $createTableStr;
|
||||
|
||||
/**
|
||||
* DROP TABLE IF statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_drop_table_if = "IF EXISTS (SELECT * FROM sysobjects WHERE ID = object_id(N'%s') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)\nDROP TABLE";
|
||||
public function __construct(BaseConnection $db)
|
||||
{
|
||||
parent::__construct($db);
|
||||
|
||||
$this->createTableIfStr = 'IF NOT EXISTS'
|
||||
. '(SELECT t.name, s.name as schema_name, t.type_desc '
|
||||
. 'FROM sys.tables t '
|
||||
. 'INNER JOIN sys.schemas s on s.schema_id = t.schema_id '
|
||||
. "WHERE s.name=N'" . $this->db->schema . "' "
|
||||
. "AND t.name=REPLACE(N'%s', '\"', '') "
|
||||
. "AND t.type_desc='USER_TABLE')\nCREATE TABLE ";
|
||||
|
||||
$this->createTableStr = '%s ' . $this->db->escapeIdentifiers($this->db->schema) . ".%s (%s\n) ";
|
||||
$this->renameTableStr = 'EXEC sp_rename [' . $this->db->escapeIdentifiers($this->db->schema) . '.%s] , %s ;';
|
||||
|
||||
$this->dropConstraintStr = 'ALTER TABLE ' . $this->db->escapeIdentifiers($this->db->schema) . '.%s DROP CONSTRAINT %s';
|
||||
}
|
||||
|
||||
/**
|
||||
* CREATE TABLE attributes
|
||||
@ -111,9 +123,6 @@ class Forge extends BaseForge
|
||||
*/
|
||||
protected function _alterTable(string $alterType, string $table, $field)
|
||||
{
|
||||
if ($alterType === 'ADD') {
|
||||
return parent::_alterTable($alterType, $table, $field);
|
||||
}
|
||||
|
||||
// Handle DROP here
|
||||
if ($alterType === 'DROP') {
|
||||
@ -133,7 +142,7 @@ class Forge extends BaseForge
|
||||
}
|
||||
}
|
||||
|
||||
$sql = 'ALTER TABLE [' . $table . '] DROP ';
|
||||
$sql = 'ALTER TABLE ' . $this->db->escapeIdentifiers($this->db->schema) . '.' . $this->db->escapeIdentifiers($table) . ' DROP ';
|
||||
|
||||
$fields = array_map(static function ($item) {
|
||||
return 'COLUMN [' . trim($item) . ']';
|
||||
@ -142,10 +151,19 @@ class Forge extends BaseForge
|
||||
return $sql . implode(',', $fields);
|
||||
}
|
||||
|
||||
$sql = 'ALTER TABLE ' . $this->db->escapeIdentifiers($table);
|
||||
$sql = 'ALTER TABLE ' . $this->db->escapeIdentifiers($this->db->schema) . '.' . $this->db->escapeIdentifiers($table);
|
||||
$sql .= ($alterType === 'ADD') ? 'ADD ' : ' ';
|
||||
|
||||
$sqls = [];
|
||||
|
||||
if ($alterType === 'ADD') {
|
||||
foreach ($field as $data) {
|
||||
$sqls[] = $sql . ($data['_literal'] !== false ? $data['_literal'] : $this->_processColumn($data));
|
||||
}
|
||||
|
||||
return $sqls;
|
||||
}
|
||||
|
||||
foreach ($field as $data) {
|
||||
if ($data['_literal'] !== false) {
|
||||
return false;
|
||||
@ -198,6 +216,46 @@ class Forge extends BaseForge
|
||||
return $this->db->simpleQuery($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process indexes
|
||||
*
|
||||
* @return array|string
|
||||
*/
|
||||
protected function _processIndexes(string $table)
|
||||
{
|
||||
$sqls = [];
|
||||
|
||||
for ($i = 0, $c = count($this->keys); $i < $c; $i++) {
|
||||
$this->keys[$i] = (array) $this->keys[$i];
|
||||
|
||||
for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++) {
|
||||
if (! isset($this->fields[$this->keys[$i][$i2]])) {
|
||||
unset($this->keys[$i][$i2]);
|
||||
}
|
||||
}
|
||||
|
||||
if (count($this->keys[$i]) <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array($i, $this->uniqueKeys, true)) {
|
||||
$sqls[] = 'ALTER TABLE '
|
||||
. $this->db->escapeIdentifiers($this->db->schema) . '.' . $this->db->escapeIdentifiers($table)
|
||||
. ' ADD CONSTRAINT ' . $this->db->escapeIdentifiers($table . '_' . implode('_', $this->keys[$i]))
|
||||
. ' UNIQUE (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i])) . ');';
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$sqls[] = 'CREATE INDEX '
|
||||
. $this->db->escapeIdentifiers($table . '_' . implode('_', $this->keys[$i]))
|
||||
. ' ON ' . $this->db->escapeIdentifiers($this->db->schema) . '.' . $this->db->escapeIdentifiers($table)
|
||||
. ' (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i])) . ');';
|
||||
}
|
||||
|
||||
return $sqls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process column
|
||||
*/
|
||||
@ -229,7 +287,7 @@ class Forge extends BaseForge
|
||||
|
||||
$sql .= ",\n\t CONSTRAINT " . $this->db->escapeIdentifiers($nameIndex)
|
||||
. ' FOREIGN KEY (' . $this->db->escapeIdentifiers($field) . ') '
|
||||
. ' REFERENCES ' . $this->db->escapeIdentifiers($this->db->getPrefix() . $fkey['table']) . ' (' . $this->db->escapeIdentifiers($fkey['field']) . ')';
|
||||
. ' REFERENCES ' . $this->db->escapeIdentifiers($this->db->schema) . '.' . $this->db->escapeIdentifiers($this->db->getPrefix() . $fkey['table']) . ' (' . $this->db->escapeIdentifiers($fkey['field']) . ')';
|
||||
|
||||
if ($fkey['onDelete'] !== false && in_array($fkey['onDelete'], $allowActions, true)) {
|
||||
$sql .= ' ON DELETE ' . $fkey['onDelete'];
|
||||
@ -245,8 +303,6 @@ class Forge extends BaseForge
|
||||
|
||||
/**
|
||||
* Process primary keys
|
||||
*
|
||||
* @param string $table Table name
|
||||
*/
|
||||
protected function _processPrimaryKeys(string $table): string
|
||||
{
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace CodeIgniter\Database\SQLSRV;
|
||||
|
||||
use CodeIgniter\Database\BaseUtils;
|
||||
use CodeIgniter\Database\ConnectionInterface;
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
|
||||
/**
|
||||
@ -33,6 +34,13 @@ class Utils extends BaseUtils
|
||||
*/
|
||||
protected $optimizeTable = 'ALTER INDEX all ON %s REORGANIZE';
|
||||
|
||||
public function __construct(ConnectionInterface &$db)
|
||||
{
|
||||
parent::__construct($db);
|
||||
|
||||
$this->optimizeTable = 'ALTER INDEX all ON ' . $this->db->schema . '.%s REORGANIZE';
|
||||
}
|
||||
|
||||
/**
|
||||
* Platform dependent version of the backup function.
|
||||
*
|
||||
|
@ -50,7 +50,7 @@ final class PreparedQueryTest extends CIUnitTestCase
|
||||
|
||||
if ($this->db->DBDriver === 'SQLSRV') {
|
||||
$database = $this->db->getDatabase();
|
||||
$expected = "INSERT INTO {$ec}{$database}{$ec}.{$ec}dbo{$ec}.{$ec}{$pre}user{$ec} ({$ec}name{$ec},{$ec}email{$ec}) VALUES ({$placeholders})";
|
||||
$expected = "INSERT INTO {$ec}{$database}{$ec}.{$ec}{$this->db->schema}{$ec}.{$ec}{$pre}user{$ec} ({$ec}name{$ec},{$ec}email{$ec}) VALUES ({$placeholders})";
|
||||
} else {
|
||||
$expected = "INSERT INTO {$ec}{$pre}user{$ec} ({$ec}name{$ec}, {$ec}email{$ec}) VALUES ({$placeholders})";
|
||||
}
|
||||
@ -107,6 +107,10 @@ final class PreparedQueryTest extends CIUnitTestCase
|
||||
$query = $this->db->prepare(static function ($db) {
|
||||
$sql = "INSERT INTO {$db->DBPrefix}user (name, email, country) VALUES (?, ?, ?)";
|
||||
|
||||
if ($db->DBDriver === 'SQLSRV') {
|
||||
$sql = "INSERT INTO {$db->schema}.{$db->DBPrefix}user (name, email, country) VALUES (?, ?, ?)";
|
||||
}
|
||||
|
||||
return (new Query($db))->setQuery($sql);
|
||||
});
|
||||
|
||||
|
@ -93,7 +93,7 @@ final class MigrationRunnerTest extends CIUnitTestCase
|
||||
];
|
||||
|
||||
if ($this->db->DBDriver === 'SQLSRV') {
|
||||
$this->db->simpleQuery('SET IDENTITY_INSERT ' . $this->db->prefixTable('migrations') . ' ON');
|
||||
$this->db->simpleQuery('SET IDENTITY_INSERT ' . $this->db->escapeIdentifiers($this->db->schema) . '.' . $this->db->prefixTable('migrations') . ' ON');
|
||||
}
|
||||
|
||||
$this->hasInDatabase('migrations', $expected);
|
||||
@ -110,8 +110,7 @@ final class MigrationRunnerTest extends CIUnitTestCase
|
||||
$this->assertSame($expected, $history);
|
||||
|
||||
if ($this->db->DBDriver === 'SQLSRV') {
|
||||
$this->db->simpleQuery('SET IDENTITY_INSERT ' . $this->db->prefixTable('migrations') . ' OFF');
|
||||
|
||||
$this->db->simpleQuery('SET IDENTITY_INSERT ' . $this->db->escapeIdentifiers($this->db->schema) . '.' . $this->db->prefixTable('migrations') . ' OFF');
|
||||
$db = $this->getPrivateProperty($runner, 'db');
|
||||
$db->table('migrations')->delete(['id' => 4]);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user