260 lines
7.8 KiB
PHP
260 lines
7.8 KiB
PHP
<?php
|
|
|
|
namespace Ulmus\Adapter;
|
|
|
|
use Ulmus\Pdo;
|
|
|
|
use Ulmus\Exception\AdapterConfigurationException;
|
|
|
|
use Ulmus\Ulmus;
|
|
|
|
use Ulmus\Migration\FieldDefinition;
|
|
use Ulmus\{ConnectionAdapter,
|
|
Entity\InformationSchema\Table,
|
|
Migration\MigrateInterface,
|
|
Migration\SqlMigrationTrait,
|
|
Repository,
|
|
QueryBuilder};
|
|
|
|
class MsSQL implements AdapterInterface, MigrateInterface, SqlAdapterInterface {
|
|
use SqlAdapterTrait, SqlMigrationTrait;
|
|
|
|
const ALLOWED_ATTRIBUTES = [
|
|
'default', 'primary_key', 'auto_increment',
|
|
];
|
|
|
|
const DSN_PREFIX = "sqlsrv";
|
|
|
|
public int $port;
|
|
|
|
public int $transactionIsolation;
|
|
|
|
public int $connectionPooling = 0;
|
|
|
|
public bool $encrypt;
|
|
|
|
public bool $multipleActiveResultSets;
|
|
|
|
public bool $traceOn;
|
|
|
|
public string $app;
|
|
|
|
public string $server;
|
|
|
|
public string $database;
|
|
|
|
public string $username;
|
|
|
|
public string $password;
|
|
|
|
public string $traceFile;
|
|
|
|
public string $failoverPartner;
|
|
|
|
public string $wsid;
|
|
|
|
public function __construct(
|
|
?string $server = null,
|
|
?string $database = null,
|
|
#[\SensitiveParameter]
|
|
?string $username = null,
|
|
#[\SensitiveParameter]
|
|
?string $password = null,
|
|
?int $port = null
|
|
) {
|
|
if ($server) {
|
|
$this->server = $server;
|
|
}
|
|
|
|
if ($database) {
|
|
$this->database = $database;
|
|
}
|
|
|
|
if ($port) {
|
|
$this->port = $port;
|
|
}
|
|
|
|
if ($username) {
|
|
$this->username = $username;
|
|
}
|
|
|
|
if ($password) {
|
|
$this->password = $password;
|
|
}
|
|
}
|
|
|
|
public function connect() : Pdo
|
|
{
|
|
try {
|
|
$pdo = new Pdo\Mssql($this->buildDataSourceName(), $this->username, $this->password);
|
|
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
|
$pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
|
|
$pdo->setAttribute(\PDO::SQLSRV_ATTR_ENCODING, \PDO::SQLSRV_ENCODING_UTF8);
|
|
}
|
|
catch(\PDOException $ex){
|
|
throw $ex;
|
|
}
|
|
finally {
|
|
$this->password = str_repeat('*', random_int(8,16));
|
|
}
|
|
|
|
return $pdo;
|
|
}
|
|
|
|
public function buildDataSourceName() : string
|
|
{
|
|
$parts[] = "Server={$this->server}" . ( isset($this->port) ? ",{$this->port}" : "" );
|
|
$parts[] = "Database={$this->database}";
|
|
$parts[] = "ConnectionPooling={$this->connectionPooling}";
|
|
|
|
if ( $this->app ?? false ) {
|
|
$parts[] = "APP={$this->app}";
|
|
}
|
|
|
|
if ( $this->encrypt ?? false ) {
|
|
$parts[] = "Encrypt=1";
|
|
}
|
|
else {
|
|
$parts[] = "Encrypt=0";
|
|
}
|
|
|
|
if ( $this->failoverPartner ?? false ) {
|
|
$parts[] = "Failover_Partner={$this->failoverPartner}";
|
|
}
|
|
|
|
if ( $this->loginTimeout ?? false ) {
|
|
$parts[] = "LoginTimeout={$this->loginTimeout}";
|
|
}
|
|
|
|
if ( $this->multipleActiveResultSets ?? false ) {
|
|
$parts[] = "MultipleActiveResultSets=1";
|
|
}
|
|
|
|
if ( $this->quotedId ?? false ) {
|
|
$parts[] = "QuotedId=1";
|
|
}
|
|
|
|
if ( $this->traceFile ?? false ) {
|
|
$parts[] = "TraceFile={$this->traceFile}";
|
|
}
|
|
|
|
if ( $this->traceOn ?? false ) {
|
|
$parts[] = "TraceOn=1";
|
|
}
|
|
|
|
if ( $this->transactionIsolation ?? false ) {
|
|
$parts[] = "TransactionIsolation={$this->transactionIsolation}";
|
|
}
|
|
|
|
if ( $this->trustServerCertificate ?? false ) {
|
|
$parts[] = "TrustServerCertificate=yes";
|
|
}
|
|
|
|
if ( $this->WSID ?? false ) {
|
|
$parts[] = "WSID={$this->wsid}";
|
|
}
|
|
|
|
return static::DSN_PREFIX . ":" . implode(';', $parts);
|
|
}
|
|
|
|
public function setup(array $configuration) : void
|
|
{
|
|
$configuration = array_change_key_case($configuration, \CASE_LOWER);
|
|
|
|
if ( false === ( $this->server = $configuration['server'] ?? false ) ) {
|
|
throw new AdapterConfigurationException("Your `server` setting is missing. It is a mandatory parameter for this driver.");
|
|
}
|
|
elseif ( false === ( $this->database = $configuration['database'] ?? false ) ) {
|
|
throw new AdapterConfigurationException("Your `database` setting is missing. The adapter won't connect without it.");
|
|
}
|
|
elseif ( false === ( $this->username = $configuration['username'] ?? false ) ) {
|
|
throw new AdapterConfigurationException("Your `username` is missing from your configuration array");
|
|
}
|
|
elseif ( false === ( $this->password = $configuration['password'] ?? false ) ) {
|
|
throw new AdapterConfigurationException("Your `password` is missing from your configuration array");
|
|
}
|
|
|
|
if ( false !== ( $configuration['app'] ?? false ) ) {
|
|
$this->app = $configuration['app'];
|
|
}
|
|
|
|
if ( false !== ( $configuration['port'] ?? false ) ) {
|
|
$this->port = $configuration['port'];
|
|
}
|
|
|
|
if ( false !== ( $configuration['failover_partner'] ?? false ) ) {
|
|
$this->failoverPartner = $configuration['failover_partner'];
|
|
}
|
|
|
|
if ( false !== ( $configuration['wsid'] ?? false ) ) {
|
|
$this->wsid = $configuration['wsid'];
|
|
}
|
|
|
|
if ( false !== ( $configuration['transaction_isolation'] ?? false ) ) {
|
|
$this->transactionIsolation = $configuration['transaction_isolation'];
|
|
}
|
|
|
|
if ( false !== ( $configuration['connection_pooling'] ?? false ) ) {
|
|
$this->connectionPooling = $configuration['connection_pooling'];
|
|
}
|
|
|
|
if ( false !== ( $configuration['encrypt'] ?? false ) ) {
|
|
$this->encrypt = $configuration['encrypt'];
|
|
}
|
|
|
|
if ( false !== ( $configuration['multiple_active_result_sets'] ?? false ) ) {
|
|
$this->multipleActiveResultSets = $configuration['multiple_active_result_sets'];
|
|
}
|
|
|
|
if ( false !== ( $configuration['trace_on'] ?? false ) ) {
|
|
$this->traceOn = $configuration['trace_on'];
|
|
}
|
|
}
|
|
|
|
public function mapFieldType(FieldDefinition $field, bool $typeOnly = false) : string
|
|
{
|
|
$mapper = new MsSQLFieldMapper($field);
|
|
|
|
return $typeOnly ? $mapper->type : $mapper->render();
|
|
}
|
|
|
|
public static function escapeIdentifier(string $segment, int $type) : string
|
|
{
|
|
switch($type) {
|
|
default:
|
|
case static::IDENTIFIER_SCHEMA:
|
|
case static::IDENTIFIER_DATABASE:
|
|
case static::IDENTIFIER_TABLE:
|
|
case static::IDENTIFIER_FIELD:
|
|
return "[" . trim(str_replace(["[", "]"], [ "[[", "]]" ], $segment), '[]') . "]";
|
|
|
|
case static::IDENTIFIER_VALUE:
|
|
return "'$segment'";
|
|
}
|
|
}
|
|
|
|
public function defaultEngine(): ? string
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public function repositoryClass() : string
|
|
{
|
|
return Repository\MssqlRepository::class;
|
|
}
|
|
|
|
public function queryBuilderClass() : string
|
|
{
|
|
return QueryBuilder\Sql\MssqlQueryBuilder::class;
|
|
}
|
|
|
|
public function schemaTable(ConnectionAdapter $adapter, $databaseName, string $tableName) : null|object
|
|
{
|
|
return \Ulmus\Entity\Mssql\Table::repository(Repository::DEFAULT_ALIAS, $adapter)
|
|
->select(\Ulmus\Common\Sql::raw('this.*'))
|
|
->where($this->escapeIdentifier('table_schema', AdapterInterface::IDENTIFIER_FIELD), $databaseName)
|
|
->or($this->escapeIdentifier('table_catalog', AdapterInterface::IDENTIFIER_FIELD), $databaseName)
|
|
->loadOneFromField($this->escapeIdentifier('table_name', AdapterInterface::IDENTIFIER_FIELD), $tableName);
|
|
}
|
|
}
|