View source
<?php
class InsertQuery_sqlsrv extends InsertQuery {
protected $use_output = TRUE;
public function __construct($connection, $table, array $options = []) {
global $conf;
if (isset($conf['MSSQL_INSERT_DISABLE_OUTPUT']) && $conf['MSSQL_INSERT_DISABLE_OUTPUT'] === TRUE) {
$this->use_output = FALSE;
}
if (!isset($options['return'])) {
$options['return'] = Database::RETURN_NULL;
}
parent::__construct($connection, $table, $options);
}
public function execute() {
if (!$this
->preExecute()) {
return NULL;
}
$columnInformation = $this->connection
->schema()
->queryColumnInformation($this->table);
$this->setIdentity = !empty($columnInformation['identity']) && in_array($columnInformation['identity'], $this->insertFields);
$identity = !empty($columnInformation['identity']) ? $columnInformation['identity'] : NULL;
$options = $this->queryOptions;
if (!empty($this->fromQuery)) {
$this->insertValues = array();
$stmt = $this->connection
->prepareQuery((string) $this);
$arguments = $this->fromQuery
->getArguments();
DatabaseUtils::BindArguments($stmt, $arguments);
$this->connection
->query($stmt, array(), $options);
if ($this->use_output) {
try {
return $stmt
->fetchColumn(0);
} catch (\PDOException $e) {
return NULL;
}
}
else {
return $this->connection
->lastInsertId();
}
}
if (empty($this->fromQuery) && (empty($this->insertFields) || empty($this->insertValues))) {
$this->insertValues = array();
$stmt = $this->connection
->prepareQuery((string) $this);
$this->connection
->query($stmt, array(), $options);
if ($this->use_output) {
try {
return $stmt
->fetchColumn(0);
} catch (\PDOException $e) {
return NULL;
}
}
else {
return $this->connection
->lastInsertId();
}
}
$transaction = NULL;
$batch_size = 200;
$batch = array_splice($this->insertValues, 0, $batch_size);
if (empty($this->queryOptions['sqlsrv_skip_transactions']) && !empty($this->insertValues)) {
$transaction = $this->connection
->startTransaction('', DatabaseTransactionSettings::GetBetterDefaults());
}
while (!empty($batch)) {
$query = (string) $this
->__toString2(count($batch));
$stmt = $this->connection
->prepareQuery($query);
$blobs = array();
$max_placeholder = 0;
foreach ($batch as $insert_index => $insert_values) {
$values = array_combine($this->insertFields, $insert_values);
DatabaseUtils::BindValues($stmt, $values, $blobs, ':db_insert', $columnInformation, $max_placeholder, $insert_index);
}
$this->connection
->query($stmt, [], array_merge($options, [
'fetch' => PDO::FETCH_ASSOC,
]));
if ($this->use_output) {
foreach ($stmt as $insert) {
try {
$this->inserted_keys[] = $insert[$identity];
} catch (\Exception $e) {
$this->inserted_keys[] = NULL;
}
}
}
else {
$this->inserted_keys[] = $this->connection
->lastInsertId();
}
$batch = array_splice($this->insertValues, 0, $batch_size);
}
if ($transaction) {
$transaction
->commit();
}
$this->insertValues = array();
return empty($this->inserted_keys) ? NULL : end($this->inserted_keys);
}
public $inserted_keys = array();
public function __toString() {
return $this
->__toString2(1);
}
private function __toString2($batch_size) {
if ($batch_size > 250) {
throw new Exception("MSSQL Native Batch Insert limited to 250.");
}
$columnInformation = $this->connection
->schema()
->queryColumnInformation($this->table);
$prefix = $this->connection
->makeComment($this->comments);
$output = NULL;
if (!empty($this->setIdentity)) {
$prefix .= 'SET IDENTITY_INSERT {' . $this->table . '} ON;';
}
if (isset($columnInformation['identities']) && !empty($columnInformation['identities'])) {
$identities = array_keys($columnInformation['identities']);
$identity = reset($identities);
$output = "OUTPUT (Inserted.{$identity})";
}
else {
$output = "OUTPUT ('')";
}
if ($this->use_output === FALSE) {
$output = '';
}
if (!empty($this->fromQuery)) {
if (empty($this->insertFields)) {
return $prefix . "INSERT INTO {{$this->table}} {$output}" . $this->fromQuery;
}
else {
$fields_csv = implode(', ', $this->connection
->quoteIdentifiers($this->insertFields));
return $prefix . "INSERT INTO {{$this->table}} ({$fields_csv}) {$output} " . $this->fromQuery;
}
}
if (empty($this->insertFields)) {
return $prefix . "INSERT INTO {{$this->table}} {$output} DEFAULT VALUES";
}
$placeholders = array();
$field_count = count($this->insertFields);
for ($j = 0; $j < $batch_size; $j++) {
$batch_placeholders = array();
for ($i = 0; $i < $field_count; ++$i) {
$batch_placeholders[] = ':db_insert' . ($field_count * $j + $i);
}
$placeholders[] = '(' . implode(', ', $batch_placeholders) . ')';
}
$sql = $prefix . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $this->connection
->quoteIdentifiers($this->insertFields)) . ') ' . $output . ' VALUES ' . PHP_EOL;
$sql .= implode(', ', $placeholders) . PHP_EOL;
return $sql;
}
}
class UpdateQuery_sqlsrv extends UpdateQuery {
public function execute() {
$options = $this->queryOptions;
$columnInformation = $this->connection
->schema()
->queryColumnInformation($this->table);
if (isset($columnInformation['identity']) && isset($this->fields[$columnInformation['identity']])) {
unset($this->fields[$columnInformation['identity']]);
}
$stmt = $this->connection
->prepareQuery((string) $this);
$fields = $this->fields;
DatabaseUtils::BindExpressions($stmt, $this->expressionFields, $fields);
$blobs = array();
DatabaseUtils::BindValues($stmt, $fields, $blobs, ':db_update_placeholder_', $columnInformation);
if (count($this->condition)) {
$this->condition
->compile($this->connection, $this);
$arguments = $this->condition
->arguments();
DatabaseUtils::BindArguments($stmt, $arguments);
}
$options = $this->queryOptions;
$options['already_prepared'] = TRUE;
$this->connection
->query($stmt, array(), $options);
return $stmt
->rowCount();
}
public function __toString() {
$prefix = $this->connection
->makeComment($this->comments);
$fields = $this->fields;
$update_fields = array();
foreach ($this->expressionFields as $field => $data) {
$update_fields[] = $this->connection
->quoteIdentifier($field) . '=' . $data['expression'];
unset($fields[$field]);
}
$max_placeholder = 0;
foreach ($fields as $field => $value) {
$update_fields[] = $this->connection
->quoteIdentifier($field) . '=:db_update_placeholder_' . $max_placeholder++;
}
$query = $prefix . 'UPDATE {' . $this->connection
->escapeTable($this->table) . '} SET ' . implode(', ', $update_fields);
if (count($this->condition)) {
$this->condition
->compile($this->connection, $this);
$query .= "\nWHERE " . $this->condition;
}
return $query;
}
}
class TruncateQuery_sqlsrv extends TruncateQuery {
public function __toString() {
$prefix = $this->connection
->makeComment($this->comments);
return $prefix . 'TRUNCATE TABLE {' . $this->connection
->escapeTable($this->table) . '} ';
}
}
class MergeQuery_sqlsrv extends MergeQuery {
public function execute() {
if (!count($this->condition)) {
throw new InvalidMergeQueryException(t('Invalid merge query: no conditions'));
}
$options = $this->queryOptions;
$blobs = array();
$columnInformation = $this->connection
->schema()
->queryColumnInformation($this->table);
$this->setIdentity = !empty($columnInformation['identity']) && in_array($columnInformation['identity'], array_keys($this->insertFields));
$max_placeholder = 0;
$stmt = $this->connection
->prepareQuery((string) $this);
$arguments = $this->condition
->arguments();
DatabaseUtils::BindArguments($stmt, $arguments);
$fields = $this->updateFields;
DatabaseUtils::BindExpressions($stmt, $this->expressionFields, $fields);
DatabaseUtils::BindValues($stmt, $fields, $blobs, ':db_merge_placeholder_', $columnInformation, $max_placeholder);
DatabaseUtils::BindValues($stmt, $this->insertFields, $blobs, ':db_merge_placeholder_', $columnInformation, $max_placeholder);
try {
$this->connection
->query($stmt, [], $options);
} catch (\PDOException $e) {
if (in_array((string) $e
->getCode(), [
'23000',
])) {
$this->connection
->query($stmt, [], $options);
}
else {
throw $e;
}
}
$result = NULL;
foreach ($stmt as $value) {
$result = $value->{'$action'};
}
switch ($result) {
case 'UPDATE':
return static::STATUS_UPDATE;
case 'INSERT':
return static::STATUS_INSERT;
}
}
public function __toString() {
$max_placeholder = 0;
$max_placeholder_conditions = 0;
$query = array();
if (!empty($this->setIdentity)) {
$query[] = 'SET IDENTITY_INSERT {' . $this->table . '} ON;';
}
$query[] = 'MERGE INTO {' . $this->table . '} _target';
$this->condition
->compile($this->connection, $this);
$key_conditions = array();
$template_item = array();
$conditions = $this
->conditions();
unset($conditions['#conjunction']);
foreach ($conditions as $condition) {
$key_conditions[] = '_target.' . $this->connection
->escapeField($condition['field']) . ' = ' . '_source.' . $this->connection
->escapeField($condition['field']);
$template_item[] = ':db_condition_placeholder_' . $max_placeholder_conditions++ . ' AS ' . $this->connection
->escapeField($condition['field']);
}
$query[] = 'USING (SELECT ' . implode(', ', $template_item) . ') _source ' . PHP_EOL . 'ON ' . implode(' AND ', $key_conditions);
$fields = $this->updateFields;
$update_fields = array();
foreach ($this->expressionFields as $field => $data) {
$update_fields[] = $field . '=' . $data['expression'];
unset($fields[$field]);
}
foreach ($fields as $field => $value) {
$update_fields[] = $this->connection
->quoteIdentifier($field) . '=:db_merge_placeholder_' . $max_placeholder++;
}
if (!empty($update_fields)) {
$query[] = 'WHEN MATCHED THEN UPDATE SET ' . implode(', ', $update_fields);
}
if ($this->insertFields) {
$placeholders = array();
for ($i = 0; $i < count($this->insertFields); ++$i) {
$placeholders[] = ':db_merge_placeholder_' . $max_placeholder++;
}
$query[] = 'WHEN NOT MATCHED THEN INSERT (' . implode(', ', $this->connection
->quoteIdentifiers(array_keys($this->insertFields))) . ') VALUES (' . implode(', ', $placeholders) . ')';
}
else {
$query[] = 'WHEN NOT MATCHED THEN INSERT DEFAULT VALUES';
}
$query[] = 'OUTPUT $action;';
return implode(PHP_EOL, $query);
}
}