/** * Build a condition to match a table name against a standard information_schema. * * MySQL uses databases like schemas rather than catalogs so when we build * a condition to query the information_schema.tables, we set the default * database as the schema unless specified otherwise, and exclude table_catalog * from the condition criteria. */ protected function buildTableNameCondition($table_name, $operator = '=', $add_prefix = TRUE) { $info = $this->connection->getConnectionOptions(); $table_info = $this->getPrefixInfo($table_name, $add_prefix); $condition = new DatabaseCondition('AND'); $condition->condition('table_schema', $table_info['database']); $condition->condition('table_name', $table_info['table'], $operator); return $condition; }
/** * Implements PHP magic __toString method to convert the query to a string. * * @return string * The prepared statement. */ public function __toString() { // Create a sanitized comment string to prepend to the query. $comments = $this->connection->makeComment($this->comments); // Expressions take priority over literal fields, so we process those first // and remove any literal fields that conflict. $fields = $this->fields; $update_fields = array(); foreach ($this->expressionFields as $field => $data) { $update_fields[] = $field . '=' . $data['expression']; unset($fields[$field]); } $max_placeholder = 0; foreach ($fields as $field => $value) { $update_fields[] = $field . '=:db_update_placeholder_' . $max_placeholder++; } $query = $comments . 'UPDATE {' . $this->connection->escapeTable($this->table) . '} SET ' . implode(', ', $update_fields); if (count($this->condition)) { $this->condition->compile($this->connection, $this); // There is an implicit string cast on $this->condition. $query .= "\nWHERE " . $this->condition; } return $query; }
/** * Parses the search query into SQL conditions. * * Sets up the following variables: * - $this->keys * - $this->words * - $this->conditions * - $this->simple * - $this->matches */ protected function parseSearchExpression() { // Matches words optionally prefixed by a - sign. A word in this case is // something between two spaces, optionally quoted. preg_match_all('/ (-?)("[^"]+"|[^" ]+)/i', ' ' . $this->searchExpression, $keywords, PREG_SET_ORDER); if (count($keywords) == 0) { return; } // Classify tokens. $or = FALSE; $limit_combinations = \Drupal::config('search.settings')->get('and_or_limit'); // The first search expression does not count as AND. $and_count = -1; $or_count = 0; foreach ($keywords as $match) { if ($or_count && $and_count + $or_count >= $limit_combinations) { // Ignore all further search expressions to prevent Denial-of-Service // attacks using a high number of AND/OR combinations. $this->status |= SearchQuery::EXPRESSIONS_IGNORED; break; } $phrase = FALSE; // Strip off phrase quotes. if ($match[2][0] == '"') { $match[2] = substr($match[2], 1, -1); $phrase = TRUE; $this->simple = FALSE; } // Simplify keyword according to indexing rules and external // preprocessors. Use same process as during search indexing, so it // will match search index. $words = search_simplify($match[2]); // Re-explode in case simplification added more words, except when // matching a phrase. $words = $phrase ? array($words) : preg_split('/ /', $words, -1, PREG_SPLIT_NO_EMPTY); // Negative matches. if ($match[1] == '-') { $this->keys['negative'] = array_merge($this->keys['negative'], $words); } elseif ($match[2] == 'OR' && count($this->keys['positive'])) { $last = array_pop($this->keys['positive']); // Starting a new OR? if (!is_array($last)) { $last = array($last); } $this->keys['positive'][] = $last; $or = TRUE; $or_count++; continue; } elseif ($match[2] == 'AND' || $match[2] == 'and') { continue; } else { if ($match[2] == 'or') { // Lower-case "or" instead of "OR" is a warning condition. $this->status |= SearchQuery::LOWER_CASE_OR; } if ($or) { // Add to last element (which is an array). $this->keys['positive'][count($this->keys['positive']) - 1] = array_merge($this->keys['positive'][count($this->keys['positive']) - 1], $words); } else { $this->keys['positive'] = array_merge($this->keys['positive'], $words); $and_count++; } } $or = FALSE; } // Convert keywords into SQL statements. $simple_and = FALSE; $simple_or = FALSE; // Positive matches. foreach ($this->keys['positive'] as $key) { // Group of ORed terms. if (is_array($key) && count($key)) { $simple_or = TRUE; $any = FALSE; $queryor = db_or(); foreach ($key as $or) { list($num_new_scores) = $this->parseWord($or); $any |= $num_new_scores; $queryor->condition('d.data', "% {$or} %", 'LIKE'); } if (count($queryor)) { $this->conditions->condition($queryor); // A group of OR keywords only needs to match once. $this->matches += $any > 0; } } else { $simple_and = TRUE; list($num_new_scores, $num_valid_words) = $this->parseWord($key); $this->conditions->condition('d.data', "% {$key} %", 'LIKE'); if (!$num_valid_words) { $this->simple = FALSE; } // Each AND keyword needs to match at least once. $this->matches += $num_new_scores; } } if ($simple_and && $simple_or) { $this->simple = FALSE; } // Negative matches. foreach ($this->keys['negative'] as $key) { $this->conditions->condition('d.data', "% {$key} %", 'NOT LIKE'); $this->simple = FALSE; } }
public function execute() { if (!empty($this->queryOptions['sqlite_return_matched_rows'])) { return parent::execute(); } // Get the fields used in the update query, and remove those that are already // in the condition. $fields = $this->expressionFields + $this->fields; $this->removeFieldsInCondition($fields, $this->condition); // Add the inverse of the fields to the condition. $condition = new DatabaseCondition('OR'); foreach ($fields as $field => $data) { if (is_array($data)) { // The field is an expression. $condition->where($field . ' <> ' . $data['expression']); $condition->isNull($field); } elseif (!isset($data)) { // The field will be set to NULL. $condition->isNull($field); } else { $condition->condition($field, $data, '<>'); $condition->isNull($field); } } if (count($condition)) { $condition->compile($this->connection, $this); $this->condition->where((string) $condition, $condition->arguments()); } return parent::execute(); }
/** * {@inheritdoc} */ public function preloadPathAlias($sources, $langcode) { // VERY IMPORTANT PIECE OF DOCUMENTATION, BECAUSE CORE DOES NOT // DOCUMENT IT VERY WELL: // - the query inverse all the orders 'pid' and 'language' compared // to the original ::lookupPathAlias() method // - smart little bitches, it seems they didn't know how to write it // correctly in SQL (and neither do I actually) - so they rely on // the fetchAllKeyed() method, which iterates in order on the rows // making them squashing the previously fetched one $query = $this->db->select('url_alias', 'u')->fields('u', ['source', 'alias']); $condition = new \DatabaseCondition('OR'); foreach ($sources as $source) { // See the queries above. Use LIKE for case-insensitive matching. $condition->condition('u.source', $this->db->escapeLike($source), 'LIKE'); } $query->condition($condition); if (LanguageInterface::LANGCODE_NOT_SPECIFIED === $langcode) { $langcodeList = [$langcode]; } else { $langcodeList = [$langcode, LanguageInterface::LANGCODE_NOT_SPECIFIED]; // !!! condition here is inversed from the lookup*() methods if (LanguageInterface::LANGCODE_NOT_SPECIFIED > $langcode) { $query->orderBy('u.language', 'DESC'); } else { $query->orderBy('u.language', 'ASC'); } } return $query->orderBy('u.pid', 'ASC')->condition('u.language', $langcodeList)->execute()->fetchAllKeyed(); }
public function condition($field, $value = null, $operator = null) { Internal\condition_hook($field, $value, $operator); return parent::condition($field, $value, $operator); }
public function __toString() { // Create a comments string to prepend to the query. $comments = !empty($this->comments) ? '/* ' . implode('; ', $this->comments) . ' */ ' : ''; // SELECT $query = $comments . 'SELECT '; if ($this->distinct) { $query .= 'DISTINCT '; } // FIELDS and EXPRESSIONS $fields = array(); foreach ($this->tables as $alias => $table) { if (!empty($table['all_fields'])) { $fields[] = $this->connection->escapeTable($alias) . '.*'; } } foreach ($this->fields as $alias => $field) { // Always use the AS keyword for field aliases, as some // databases require it (e.g., PostgreSQL). $fields[] = (isset($field['table']) ? $this->connection->escapeTable($field['table']) . '.' : '') . $this->connection->escapeField($field['field']) . ' AS ' . $this->connection->escapeAlias($field['alias']); } foreach ($this->expressions as $alias => $expression) { $fields[] = $expression['expression'] . ' AS ' . $this->connection->escapeAlias($expression['alias']); } $query .= implode(', ', $fields); // FROM - We presume all queries have a FROM, as any query that doesn't won't need the query builder anyway. $query .= "\nFROM "; foreach ($this->tables as $alias => $table) { $query .= "\n"; if (isset($table['join type'])) { $query .= $table['join type'] . ' JOIN '; } // If the table is a subquery, compile it and integrate it into this query. if ($table['table'] instanceof SelectQueryInterface) { // Run preparation steps on this sub-query before converting to string. $subquery = $table['table']; $subquery->preExecute(); $table_string = '(' . (string) $subquery . ')'; } else { $table_string = '{' . $this->connection->escapeTable($table['table']) . '}'; } // Don't use the AS keyword for table aliases, as some // databases don't support it (e.g., Oracle). $query .= $table_string . ' ' . $this->connection->escapeTable($table['alias']); if (!empty($table['condition'])) { $query .= ' ON ' . $table['condition']; } } // WHERE if (count($this->where)) { // The following line will not generate placeholders correctly if there // is a subquery. Fortunately, it is also called from getArguments() first // so it's not a problem in practice... unless you try to call __toString() // before calling getArguments(). That is a problem that we will have to // fix in Drupal 8, because it requires more refactoring than we are // able to do in Drupal 7. // @todo Move away from __toString() For SelectQuery compilation at least. $this->where->compile($this->connection, $this); // There is an implicit string cast on $this->condition. $query .= "\nWHERE " . $this->where; } // GROUP BY if ($this->group) { $query .= "\nGROUP BY " . implode(', ', $this->group); } // HAVING if (count($this->having)) { $this->having->compile($this->connection, $this); // There is an implicit string cast on $this->having. $query .= "\nHAVING " . $this->having; } // ORDER BY if ($this->order) { $query .= "\nORDER BY "; $fields = array(); foreach ($this->order as $field => $direction) { $fields[] = $field . ' ' . $direction; } $query .= implode(', ', $fields); } // RANGE // There is no universal SQL standard for handling range or limit clauses. // Fortunately, all core-supported databases use the same range syntax. // Databases that need a different syntax can override this method and // do whatever alternate logic they need to. if (!empty($this->range)) { $query .= "\nLIMIT " . (int) $this->range['length'] . " OFFSET " . (int) $this->range['start']; } // UNION is a little odd, as the select queries to combine are passed into // this query, but syntactically they all end up on the same level. if ($this->union) { foreach ($this->union as $union) { $query .= ' ' . $union['type'] . ' ' . (string) $union['query']; } } if ($this->forUpdate) { $query .= ' FOR UPDATE'; } return $query; }