diff --git a/examples/connecting-to-databases.php b/examples/connecting-to-databases.php index cfc5b6a6..0c113b3e 100644 --- a/examples/connecting-to-databases.php +++ b/examples/connecting-to-databases.php @@ -106,22 +106,6 @@ try { echo "
\n"; -// connects to MS SQL -echo 'Connecting to MS SQL: '; -try { - dibi::connect([ - 'driver' => 'mssql', - 'host' => 'localhost', - 'username' => 'root', - 'password' => 'xxx', - ]); - echo 'OK'; -} catch (Dibi\Exception $e) { - echo get_class($e), ': ', $e->getMessage(), "\n"; -} -echo "
\n"; - - // connects to SQLSRV echo 'Connecting to Microsoft SQL Server: '; try { diff --git a/src/Dibi/Drivers/MsSqlDriver.php b/src/Dibi/Drivers/MsSqlDriver.php deleted file mode 100644 index c4a8e37e..00000000 --- a/src/Dibi/Drivers/MsSqlDriver.php +++ /dev/null @@ -1,360 +0,0 @@ - the MS SQL server host name. It can also include a port number (hostname:port) - * - username (or user) - * - password (or pass) - * - database => the database name to select - * - persistent (bool) => try to find a persistent link? - * - resource (resource) => existing connection resource - * - lazy, profiler, result, substitutes, ... => see Dibi\Connection options - */ -class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver -{ - use Dibi\Strict; - - /** @var resource|null */ - private $connection; - - /** @var resource|null */ - private $resultSet; - - /** @var bool */ - private $autoFree = true; - - - /** - * @throws Dibi\NotSupportedException - */ - public function __construct() - { - if (!extension_loaded('mssql')) { - throw new Dibi\NotSupportedException("PHP extension 'mssql' is not loaded."); - } - } - - - /** - * Connects to a database. - * @throws Dibi\Exception - */ - public function connect(array &$config): void - { - if (isset($config['resource'])) { - $this->connection = $config['resource']; - } elseif (empty($config['persistent'])) { - $this->connection = @mssql_connect($config['host'], $config['username'], $config['password'], true); // intentionally @ - } else { - $this->connection = @mssql_pconnect($config['host'], $config['username'], $config['password']); // intentionally @ - } - - if (!is_resource($this->connection)) { - throw new Dibi\DriverException("Can't connect to DB."); - } - - if (isset($config['database']) && !@mssql_select_db($this->escapeIdentifier($config['database']), $this->connection)) { // intentionally @ - throw new Dibi\DriverException("Can't select DB '$config[database]'."); - } - } - - - /** - * Disconnects from a database. - */ - public function disconnect(): void - { - @mssql_close($this->connection); // @ - connection can be already disconnected - } - - - /** - * Executes the SQL query. - * @throws Dibi\DriverException - */ - public function query(string $sql): ?Dibi\ResultDriver - { - $res = @mssql_query($sql, $this->connection); // intentionally @ - - if ($res === false) { - throw new Dibi\DriverException(mssql_get_last_message(), 0, $sql); - - } elseif (is_resource($res)) { - return $this->createResultDriver($res); - } - return null; - } - - - /** - * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. - */ - public function getAffectedRows(): ?int - { - return Dibi\Helpers::false2Null(mssql_rows_affected($this->connection)); - } - - - /** - * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. - */ - public function getInsertId(?string $sequence): ?int - { - $res = mssql_query('SELECT @@IDENTITY', $this->connection); - if (is_resource($res)) { - $row = mssql_fetch_row($res); - return $row[0]; - } - return null; - } - - - /** - * Begins a transaction (if supported). - * @throws Dibi\DriverException - */ - public function begin(string $savepoint = null): void - { - $this->query('BEGIN TRANSACTION'); - } - - - /** - * Commits statements in a transaction. - * @throws Dibi\DriverException - */ - public function commit(string $savepoint = null): void - { - $this->query('COMMIT'); - } - - - /** - * Rollback changes in a transaction. - * @throws Dibi\DriverException - */ - public function rollback(string $savepoint = null): void - { - $this->query('ROLLBACK'); - } - - - /** - * Returns the connection resource. - * @return resource|null - */ - public function getResource() - { - return is_resource($this->connection) ? $this->connection : null; - } - - - /** - * Returns the connection reflector. - */ - public function getReflector(): Dibi\Reflector - { - return new MsSqlReflector($this); - } - - - /** - * Result set driver factory. - * @param resource $resource - */ - public function createResultDriver($resource): Dibi\ResultDriver - { - $res = clone $this; - $res->resultSet = $resource; - return $res; - } - - - /********************* SQL ****************d*g**/ - - - /** - * Encodes data for use in a SQL statement. - */ - public function escapeText(string $value): string - { - return "'" . str_replace("'", "''", $value) . "'"; - } - - - public function escapeBinary(string $value): string - { - return "'" . str_replace("'", "''", $value) . "'"; - } - - - public function escapeIdentifier(string $value): string - { - // @see https://msdn.microsoft.com/en-us/library/ms176027.aspx - return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']'; - } - - - public function escapeBool(bool $value): string - { - return $value ? '1' : '0'; - } - - - /** - * @param \DateTimeInterface|string|int $value - */ - public function escapeDate($value): string - { - if (!$value instanceof \DateTimeInterface) { - $value = new Dibi\DateTime($value); - } - return $value->format("'Y-m-d'"); - } - - - /** - * @param \DateTimeInterface|string|int $value - */ - public function escapeDateTime($value): string - { - if (!$value instanceof \DateTimeInterface) { - $value = new Dibi\DateTime($value); - } - return 'CONVERT(DATETIME2(7), ' . $value->format("'Y-m-d H:i:s.u'") . ')'; - } - - - /** - * Encodes string for use in a LIKE statement. - */ - public function escapeLike(string $value, int $pos): string - { - $value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']); - return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); - } - - - /** - * Decodes data from result set. - */ - public function unescapeBinary(string $value): string - { - return $value; - } - - - /** - * Injects LIMIT/OFFSET to the SQL query. - */ - public function applyLimit(string &$sql, ?int $limit, ?int $offset): void - { - if ($offset) { - throw new Dibi\NotSupportedException('Offset is not supported by this database.'); - - } elseif ($limit < 0) { - throw new Dibi\NotSupportedException('Negative offset or limit.'); - - } elseif ($limit !== null) { - $sql = 'SELECT TOP ' . $limit . ' * FROM (' . $sql . ') t'; - } - } - - - /********************* result set ****************d*g**/ - - - /** - * Automatically frees the resources allocated for this result set. - */ - public function __destruct() - { - if ($this->autoFree && $this->getResultResource()) { - $this->free(); - } - } - - - /** - * Returns the number of rows in a result set. - */ - public function getRowCount(): int - { - return mssql_num_rows($this->resultSet); - } - - - /** - * Fetches the row at current position and moves the internal cursor to the next position. - * @param bool $assoc true for associative array, false for numeric - */ - public function fetch(bool $assoc): ?array - { - return Dibi\Helpers::false2Null(mssql_fetch_array($this->resultSet, $assoc ? MSSQL_ASSOC : MSSQL_NUM)); - } - - - /** - * Moves cursor position without fetching row. - * @return bool true on success, false if unable to seek to specified record - */ - public function seek(int $row): bool - { - return mssql_data_seek($this->resultSet, $row); - } - - - /** - * Frees the resources allocated for this result set. - */ - public function free(): void - { - mssql_free_result($this->resultSet); - $this->resultSet = null; - } - - - /** - * Returns metadata for all columns in a result set. - */ - public function getResultColumns(): array - { - $count = mssql_num_fields($this->resultSet); - $columns = []; - for ($i = 0; $i < $count; $i++) { - $row = (array) mssql_fetch_field($this->resultSet, $i); - $columns[] = [ - 'name' => $row['name'], - 'fullname' => $row['column_source'] ? $row['column_source'] . '.' . $row['name'] : $row['name'], - 'table' => $row['column_source'], - 'nativetype' => $row['type'], - ]; - } - return $columns; - } - - - /** - * Returns the result set resource. - * @return resource|null - */ - public function getResultResource() - { - $this->autoFree = false; - return is_resource($this->resultSet) ? $this->resultSet : null; - } -} diff --git a/src/Dibi/Drivers/MsSqlReflector.php b/src/Dibi/Drivers/MsSqlReflector.php deleted file mode 100644 index dcdd7c60..00000000 --- a/src/Dibi/Drivers/MsSqlReflector.php +++ /dev/null @@ -1,199 +0,0 @@ -driver = $driver; - } - - - /** - * Returns list of tables. - */ - public function getTables(): array - { - $res = $this->driver->query(' - SELECT TABLE_NAME, TABLE_TYPE - FROM INFORMATION_SCHEMA.TABLES - '); - $tables = []; - while ($row = $res->fetch(false)) { - $tables[] = [ - 'name' => $row[0], - 'view' => isset($row[1]) && $row[1] === 'VIEW', - ]; - } - return $tables; - } - - - /** - * Returns count of rows in a table - */ - public function getTableCount(string $table, bool $fallback = true): ?int - { - if (empty($table)) { - return null; - } - $result = $this->driver->query(" - SELECT MAX(rowcnt) - FROM sys.sysindexes - WHERE id=OBJECT_ID({$this->driver->escapeIdentifier($table)}) - "); - $row = $result->fetch(false); - - if (!is_array($row) || count($row) < 1) { - if ($fallback) { - $row = $this->driver->query("SELECT COUNT(*) FROM {$this->driver->escapeIdentifier($table)}")->fetch(false); - $count = Dibi\Helpers::intVal($row[0]); - } else { - $count = null; - } - } else { - $count = Dibi\Helpers::intVal($row[0]); - } - - return $count; - } - - - /** - * Returns metadata for all columns in a table. - */ - public function getColumns(string $table): array - { - $res = $this->driver->query(" - SELECT * FROM - INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_NAME = {$this->driver->escapeText($table)} - ORDER BY TABLE_NAME, ORDINAL_POSITION - "); - $columns = []; - while ($row = $res->fetch(true)) { - static $size_cols = [ - 'DATETIME' => 'DATETIME_PRECISION', - 'DECIMAL' => 'NUMERIC_PRECISION', - 'CHAR' => 'CHARACTER_MAXIMUM_LENGTH', - 'NCHAR' => 'CHARACTER_OCTET_LENGTH', - 'NVARCHAR' => 'CHARACTER_OCTET_LENGTH', - 'VARCHAR' => 'CHARACTER_OCTET_LENGTH', - ]; - - $type = strtoupper($row['DATA_TYPE']); - $columns[] = [ - 'name' => $row['COLUMN_NAME'], - 'table' => $table, - 'nativetype' => $type, - 'size' => $row[$size_cols[$type]] ?? null, - 'nullable' => $row['IS_NULLABLE'] === 'YES', - 'default' => $row['COLUMN_DEFAULT'], - 'autoincrement' => false, - 'vendor' => $row, - ]; - } - - return $columns; - } - - - /** - * Returns metadata for all indexes in a table. - */ - public function getIndexes(string $table): array - { - $res = $this->driver->query( - "SELECT ind.name index_name, ind.index_id, ic.index_column_id, - col.name column_name, ind.is_unique, ind.is_primary_key - FROM sys.indexes ind - INNER JOIN sys.index_columns ic ON - (ind.object_id = ic.object_id AND ind.index_id = ic.index_id) - INNER JOIN sys.columns col ON - (ic.object_id = col.object_id and ic.column_id = col.column_id) - INNER JOIN sys.tables t ON - (ind.object_id = t.object_id) - WHERE t.name = {$this->driver->escapeText($table)} - AND t.is_ms_shipped = 0 - ORDER BY - t.name, ind.name, ind.index_id, ic.index_column_id - "); - - $indexes = []; - while ($row = $res->fetch(true)) { - $index_name = $row['index_name']; - - if (!isset($indexes[$index_name])) { - $indexes[$index_name] = []; - $indexes[$index_name]['name'] = $index_name; - $indexes[$index_name]['unique'] = (bool) $row['is_unique']; - $indexes[$index_name]['primary'] = (bool) $row['is_primary_key']; - $indexes[$index_name]['columns'] = []; - } - $indexes[$index_name]['columns'][] = $row['column_name']; - } - - return array_values($indexes); - } - - - /** - * Returns metadata for all foreign keys in a table. - */ - public function getForeignKeys(string $table): array - { - $res = $this->driver->query(" - SELECT f.name AS foreign_key, - OBJECT_NAME(f.parent_object_id) AS table_name, - COL_NAME(fc.parent_object_id, - fc.parent_column_id) AS column_name, - OBJECT_NAME (f.referenced_object_id) AS reference_table_name, - COL_NAME(fc.referenced_object_id, - fc.referenced_column_id) AS reference_column_name, - fc.* - FROM sys.foreign_keys AS f - INNER JOIN sys.foreign_key_columns AS fc - ON f.OBJECT_ID = fc.constraint_object_id - WHERE OBJECT_NAME(f.parent_object_id) = {$this->driver->escapeText($table)} - "); - - $keys = []; - while ($row = $res->fetch(true)) { - $key_name = $row['foreign_key']; - - if (!isset($keys[$key_name])) { - $keys[$key_name]['name'] = $row['foreign_key']; // foreign key name - $keys[$key_name]['local'] = [$row['column_name']]; // local columns - $keys[$key_name]['table'] = $row['reference_table_name']; // referenced table - $keys[$key_name]['foreign'] = [$row['reference_column_name']]; // referenced columns - $keys[$key_name]['onDelete'] = false; - $keys[$key_name]['onUpdate'] = false; - } else { - $keys[$key_name]['local'][] = $row['column_name']; // local columns - $keys[$key_name]['foreign'][] = $row['reference_column_name']; // referenced columns - } - } - return array_values($keys); - } -} diff --git a/tests/databases.sample.ini b/tests/databases.sample.ini index 40670576..07a9d219 100644 --- a/tests/databases.sample.ini +++ b/tests/databases.sample.ini @@ -49,13 +49,6 @@ username = password = system = odbc -[mssql] -driver = mssql -host = 127.0.0.1 -username = dibi -password = -system = mssql - [mssql pdo] driver = pdo host = mssql:host=127.0.0.1;dbname=dibi_test diff --git a/tests/dibi/Translator.phpt b/tests/dibi/Translator.phpt index 8c7415e5..4bdf1a77 100644 --- a/tests/dibi/Translator.phpt +++ b/tests/dibi/Translator.phpt @@ -183,7 +183,6 @@ if ($config['system'] === 'odbc') { Assert::same( reformat([ 'odbc' => 'INSERT INTO test ([a2], [a4], [b1], [b2], [b3], [b4], [b5], [b6], [b7], [b8], [b9], [c1]) VALUES (#09/26/1212 00:00:00.000000#, #12/31/1969 22:13:20.000000#, #09/26/1212#, #09/26/1212 00:00:00.000000#, #12/31/1969#, #12/31/1969 22:13:20.000000#, #09/26/1212 00:00:00.000000#, #09/26/1212#, #09/26/1212 00:00:00.000000#, NULL, NULL, #09/26/1212 16:51:34.012400#)', - 'mssql' => "INSERT INTO test ([a2], [a4], [b1], [b2], [b3], [b4], [b5], [b6], [b7], [b8], [b9], [c1]) VALUES (CONVERT(DATETIME2(7), '1212-09-26 00:00:00.000000'), CONVERT(DATETIME2(7), '1969-12-31 22:13:20.000000'), '1212-09-26', CONVERT(DATETIME2(7), '1212-09-26 00:00:00.000000'), '1969-12-31', CONVERT(DATETIME2(7), '1969-12-31 22:13:20.000000'), CONVERT(DATETIME2(7), '1212-09-26 00:00:00.000000'), '1212-09-26', CONVERT(DATETIME2(7), '1212-09-26 00:00:00.000000'), NULL, NULL, CONVERT(DATETIME2(7), '1212-09-26 16:51:34.012400'))", 'sqlsrv' => "INSERT INTO test ([a2], [a4], [b1], [b2], [b3], [b4], [b5], [b6], [b7], [b8], [b9], [c1]) VALUES (CONVERT(DATETIME2(7), '1212-09-26 00:00:00.000000'), CONVERT(DATETIME2(7), '1969-12-31 22:13:20.000000'), '1212-09-26', CONVERT(DATETIME2(7), '1212-09-26 00:00:00.000000'), '1969-12-31', CONVERT(DATETIME2(7), '1969-12-31 22:13:20.000000'), CONVERT(DATETIME2(7), '1212-09-26 00:00:00.000000'), '1212-09-26', CONVERT(DATETIME2(7), '1212-09-26 00:00:00.000000'), NULL, NULL, CONVERT(DATETIME2(7), '1212-09-26 16:51:34.012400'))", "INSERT INTO test ([a2], [a4], [b1], [b2], [b3], [b4], [b5], [b6], [b7], [b8], [b9], [c1]) VALUES ('1212-09-26 00:00:00.000000', '1969-12-31 22:13:20.000000', '1212-09-26', '1212-09-26 00:00:00.000000', '1969-12-31', '1969-12-31 22:13:20.000000', '1212-09-26 00:00:00.000000', '1212-09-26', '1212-09-26 00:00:00.000000', NULL, NULL, '1212-09-26 16:51:34.012400')", ]), diff --git a/tests/php-win.ini b/tests/php-win.ini index e4942e32..97e76131 100644 --- a/tests/php-win.ini +++ b/tests/php-win.ini @@ -1,6 +1,5 @@ [PHP] extension_dir = "./ext" -;extension=php_mssql.dll extension=php_mysqli.dll ;extension=php_oci8.dll ;extension=php_oci8_11g.dll