public function SelectQuery_sqlsrv::preExecute in Drupal driver for SQL Server and SQL Azure 7.2
Same name and namespace in other branches
- 7.3 sqlsrv/select.inc \SelectQuery_sqlsrv::preExecute()
- 7 sqlsrv/select.inc \SelectQuery_sqlsrv::preExecute()
Override for SelectQuery::preExecute().
Ensure that all the fields in ORDER BY and GROUP BY are part of the main query.
Overrides SelectQuery::preExecute
File
- sqlsrv/
select.inc, line 37
Class
Code
public function preExecute(SelectQueryInterface $query = NULL) {
// If no query object is passed in, use $this.
if (!isset($query)) {
$query = $this;
}
// Only execute this once.
if ($this
->isPrepared()) {
return TRUE;
}
// Execute standard pre-execution first.
parent::preExecute($query);
if ($this->distinct || $this->group) {
// When the query is DISTINCT or contains GROUP BY fields, all the fields
// in the GROUP BY and ORDER BY clauses must appear in the returned
// columns.
$columns = $this->order + array_flip($this->group);
$counter = 0;
foreach ($columns as $field => $dummy) {
$found = FALSE;
foreach ($this->fields as $f) {
if (!isset($f['table']) || !isset($f['field'])) {
continue;
}
$alias = "{$f['table']}.{$f['field']}";
if ($alias == $field) {
$found = TRUE;
break;
}
}
if (!isset($this->fields[$field]) && !isset($this->expressions[$field]) && !$found) {
$alias = '_field_' . $counter++;
$this
->addExpression($field, $alias, array(), FALSE, FALSE);
$this->queryOptions['sqlsrv_drop_columns'][] = $alias;
}
}
// The other way round is also true, if using aggregates, all the fields in the SELECT
// must be present in the GROUP BY.
if (!empty($this->group)) {
foreach ($this->fields as $field) {
$spec = $field['table'] . '.' . $field['field'];
$alias = $field['alias'];
if (!isset($this->group[$spec]) && !isset($this->group[$alias])) {
$this->group[$spec] = $spec;
}
}
}
// More over, GROUP BY columns cannot use aliases, so expand them to
// their full expressions.
foreach ($this->group as $key => &$group_field) {
// Expand an alias on a field.
if (isset($this->fields[$group_field])) {
$field = $this->fields[$group_field];
$group_field = (isset($field['table']) ? $this->connection
->escapeTable($field['table']) . '.' : '') . $this->connection
->escapeField($field['field']);
}
else {
if (isset($this->expressions[$group_field])) {
$expression = $this->expressions[$group_field];
$group_field = $expression['expression'];
// If the expression has arguments, we now
// have duplicate placeholders. Run as insecure.
if (is_array($expression['arguments'])) {
$this->queryOptions['insecure'] = TRUE;
}
}
}
}
}
// Verify type bindings in the conditions, and throw the Exception
// now to prevent a bug in MSSQLPDO where transactions are f**** UP
// when the driver throws a PDOException().
// @see https://github.com/Azure/msphpsql/issues/50
//
// TODO: Remove when the issue is fixed in the PDO driver.
if (DatabaseUtils::GetConfigBoolean('MSSQL_VERIFY_NUMERIC_BINDINGS')) {
foreach ($this->where
->conditions() as $condition) {
if (!isset($condition['field']) || !is_string($condition['field'])) {
continue;
}
// Make sure we have a valid $table.$field format.
$parts = explode('.', $condition['field']);
if (count($parts) !== 2) {
continue;
}
list($table, $field_alias) = $parts;
// Fin the real field name if this was an alias.
$fields = $this
->getFields();
$field = $field_alias;
if (isset($fields[$field_alias])) {
$field = $fields[$field_alias]['field'];
}
// Get the real table name.
$tables = $this
->getTables();
if (!isset($tables[$table])) {
continue;
}
$real_table = $tables[$table]['table'];
/** @var DatabaseSchema_sqlsrv **/
$schema = $this->connection
->schema();
$spec = $schema
->queryColumnInformation($real_table);
if (!isset($spec['columns'][$field])) {
continue;
}
// If no operator defined SKIP
if (!isset($condition['operator'])) {
continue;
}
// Only do this validation for very specific operators (note that IS NULL is an operator itself)
if (!in_array($condition['operator'], array(
'=',
'<',
'>',
'IN',
'<>',
'>=',
'<=',
))) {
continue;
}
$col_spec = $spec['columns'][$field];
$values = $condition['value'];
if (!is_array($values)) {
$values = array(
$values,
);
}
foreach ($values as $value) {
if (!is_numeric($value) && !is_bool($value)) {
if (in_array($col_spec['type'], array(
'int',
'bigint',
))) {
// This is anyways going to throw an exception when running the query against the PDO driver.
// Only trigger the exception if inside a transaction, to maximize backwards compatibility.
if ($this->connection
->inTransaction()) {
$type = gettype($value);
$exception = new \PDOException("Invalid type binding for column ({$col_spec['type']}){$real_table}.{$col_spec['name']} and value ({$type})[{$value}]");
throw $exception;
}
}
}
}
}
}
return $this->prepared;
}