/** * Overrides SelectQuery::orderBy(). * * PostgreSQL adheres strictly to the SQL-92 standard and requires that when * using DISTINCT or GROUP BY conditions, fields and expressions that are * ordered on also need to be selected. This is a best effort implementation * to handle the cases that can be automated by adding the field if it is not * yet selected. * * @code * $query = db_select('example', 'e'); * $query->join('example_revision', 'er', 'e.vid = er.vid'); * $query * ->distinct() * ->fields('e') * ->orderBy('timestamp'); * @endcode * * In this query, it is not possible (without relying on the schema) to know * whether timestamp belongs to example_revision and needs to be added or * belongs to node and is already selected. Queries like this will need to be * corrected in the original query by adding an explicit call to * SelectQuery::addField() or SelectQuery::fields(). * * Since this has a small performance impact, both by the additional * processing in this function and in the database that needs to return the * additional fields, this is done as an override instead of implementing it * directly in SelectQuery::orderBy(). */ public function orderBy($field, $direction = 'ASC') { // Call parent function to order on this. $return = parent::orderBy($field, $direction); if ($this->hasTag('entity_query')) { return $return; } // If there is a table alias specified, split it up. if (strpos($field, '.') !== FALSE) { list($table, $table_field) = explode('.', $field); } // Figure out if the field has already been added. foreach ($this->fields as $existing_field) { if (!empty($table)) { // If table alias is given, check if field and table exists. if ($existing_field['table'] == $table && $existing_field['field'] == $table_field) { return $return; } } else { // If there is no table, simply check if the field exists as a field or // an aliased field. if ($existing_field['alias'] == $field) { return $return; } } } // Also check expression aliases. foreach ($this->expressions as $expression) { if ($expression['alias'] == $field) { return $return; } } // If a table loads all fields, it can not be added again. It would // result in an ambigious alias error because that field would be loaded // twice: Once through table_alias.* and once directly. If the field // actually belongs to a different table, it must be added manually. foreach ($this->tables as $table) { if (!empty($table['all_fields'])) { return $return; } } // If $field contains an characters which are not allowed in a field name // it is considered an expression, these can't be handeld automatically // either. if ($this->connection->escapeField($field) != $field) { return $return; } // This is a case that can be handled automatically, add the field. $this->addField(NULL, $field); return $return; }
/** * {@inheritdoc} */ public function fields($table_alias, array $fields = array()) { if (!$fields) { $table = $this->tables[$table_alias]['table']; if (!empty($this->databaseContents[$table])) { $fields = array_keys(reset($this->databaseContents[$table])); } else { throw new \Exception(SafeMarkup::format('All fields on empty table @table is not supported.', array('@table' => $table))); } } return parent::fields($table_alias, $fields); }
/** * Tests that fields passed for ordering get escaped properly. */ public function testFieldEscaping() { $this->query->orderBy('x; DROP table node; --'); $sql = $this->query->__toString(); $this->assertStringEndsWith('ORDER BY xDROPtablenode ASC', $sql, 'Order by field is escaped correctly.'); }
/** * {@inheritdoc} */ public function execute() { $this->connection->addSavepoint(); try { $result = parent::execute(); } catch (\Exception $e) { $this->connection->rollbackSavepoint(); throw $e; } $this->connection->releaseSavepoint(); return $result; }
/** * Override for SelectQuery::compile(). * * Detect when this query is prepared for use in a sub-query. */ public function compile(DatabaseConnection $connection, DatabasePlaceholderInterface $queryPlaceholder) { $this->inSubQuery = $queryPlaceholder != $this; return parent::compile($connection, $queryPlaceholder); }