mirror of
https://github.com/dg/dibi.git
synced 2025-08-04 13:17:58 +02:00
removed MsSqlDriver (is not available in PHP7)
This commit is contained in:
@@ -106,22 +106,6 @@ try {
|
||||
echo "</p>\n";
|
||||
|
||||
|
||||
// connects to MS SQL
|
||||
echo '<p>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 "</p>\n";
|
||||
|
||||
|
||||
// connects to SQLSRV
|
||||
echo '<p>Connecting to Microsoft SQL Server: ';
|
||||
try {
|
||||
|
@@ -1,360 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Dibi\Drivers;
|
||||
|
||||
use Dibi;
|
||||
|
||||
|
||||
/**
|
||||
* The dibi driver for MS SQL database.
|
||||
*
|
||||
* Driver options:
|
||||
* - host => 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;
|
||||
}
|
||||
}
|
@@ -1,199 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" - smart database abstraction layer.
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Dibi\Drivers;
|
||||
|
||||
use Dibi;
|
||||
|
||||
|
||||
/**
|
||||
* The dibi reflector for MS SQL databases.
|
||||
* @internal
|
||||
*/
|
||||
class MsSqlReflector implements Dibi\Reflector
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
/** @var Dibi\Driver */
|
||||
private $driver;
|
||||
|
||||
|
||||
public function __construct(Dibi\Driver $driver)
|
||||
{
|
||||
$this->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);
|
||||
}
|
||||
}
|
@@ -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
|
||||
|
@@ -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')",
|
||||
]),
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user