Commit 60f36e89 authored by Alex Pott's avatar Alex Pott
Browse files

Issue #1600670 by mradcliffe, bendiy, bzrudi71, andypost, daffie, stefan.r,...

Issue #1600670 by mradcliffe, bendiy, bzrudi71, andypost, daffie, stefan.r, devpreview: Cannot query Postgres database that has column names with capital letters
parent ce22a3c3
Loading
Loading
Loading
Loading
+58 −0
Original line number Diff line number Diff line
@@ -186,6 +186,64 @@ public function queryTemporary($query, array $args = array(), array $options = a
    return $tablename;
  }

  /**
   * {@inheritdoc}
   */
  public function escapeField($field) {
    $escaped = parent::escapeField($field);

    // Remove any invalid start character.
    $escaped = preg_replace('/^[^A-Za-z0-9_]/', '', $escaped);

    // The pgsql database driver does not support field names that contain
    // periods (supported by PostgreSQL server) because this method may be
    // called by a field with a table alias as part of SQL conditions or
    // order by statements. This will consider a period as a table alias
    // identifier, and split the string at the first period.
    if (preg_match('/^([A-Za-z0-9_]+)"?[.]"?([A-Za-z0-9_.]+)/', $escaped, $parts)) {
      $table = $parts[1];
      $column = $parts[2];

      // Use escape alias because escapeField may contain multiple periods that
      // need to be escaped.
      $escaped = $this->escapeTable($table) . '.' . $this->escapeAlias($column);
    }
    elseif (preg_match('/[A-Z]/', $escaped)) {
      // Quote the field name for case-sensitivity.
      $escaped = '"' . $escaped . '"';
    }

    return $escaped;
  }

  /**
   * {@inheritdoc}
   */
  public function escapeAlias($field) {
    $escaped = preg_replace('/[^A-Za-z0-9_]+/', '', $field);

    // Escape the alias in quotes for case-sensitivity.
    if (preg_match('/[A-Z]/', $escaped)) {
      $escaped = '"' . $escaped . '"';
    }

    return $escaped;
  }

  /**
   * {@inheritdoc}
   */
  public function escapeTable($table) {
    $escaped = parent::escapeTable($table);

    // Quote identifier to make it case-sensitive.
    if (preg_match('/[A-Z]/', $escaped)) {
      $escaped = '"' . $escaped . '"';
    }

    return $escaped;
  }

  public function driver() {
    return 'pgsql';
  }
+2 −1
Original line number Diff line number Diff line
@@ -262,7 +262,8 @@ protected function createTableSql($name, $table) {
   *    The field specification, as per the schema data structure format.
   */
  protected function createFieldSql($name, $spec) {
    $sql = $name . ' ' . $spec['pgsql_type'];
    // The PostgreSQL server converts names into lowercase, unless quoted.
    $sql = '"' . $name . '" ' . $spec['pgsql_type'];

    if (isset($spec['type']) && $spec['type'] == 'serial') {
      unset($spec['not null']);
+26 −1
Original line number Diff line number Diff line
@@ -82,7 +82,7 @@ public function orderBy($field, $direction = 'ASC') {

    // Also check expression aliases.
    foreach ($this->expressions as $expression) {
      if ($expression['alias'] == $field) {
      if ($expression['alias'] == $this->connection->escapeAlias($field)) {
        return $return;
      }
    }
@@ -109,6 +109,31 @@ public function orderBy($field, $direction = 'ASC') {
    return $return;
  }

  /**
   * {@inheritdoc}
   */
  public function addExpression($expression, $alias = NULL, $arguments = array()) {
    if (empty($alias)) {
      $alias = 'expression';
    }

    // This implements counting in the same manner as the parent method.
    $alias_candidate = $alias;
    $count = 2;
    while (!empty($this->expressions[$alias_candidate])) {
      $alias_candidate = $alias . '_' . $count++;
    }
    $alias = $alias_candidate;

    $this->expressions[$alias] = array(
      'expression' => $expression,
      'alias' => $this->connection->escapeAlias($alias_candidate),
      'arguments' => $arguments,
    );

    return $alias;
  }

  /**
   * {@inheritdoc}
   */
+4 −4
Original line number Diff line number Diff line
@@ -39,10 +39,10 @@ function testSimpleComment() {
    $records = $result->fetchAll();

    $query = (string) $query;
    $expected = "/* Testing query comments */ SELECT test.name AS name, test.age AS age\nFROM \n{test} test";
    $expected = "/* Testing query comments */";

    $this->assertEqual(count($records), 4, 'Returned the correct number of rows.');
    $this->assertEqual($query, $expected, 'The flattened query contains the comment string.');
    $this->assertNotIdentical(FALSE, strpos($query, $expected), 'The flattened query contains the comment string.');
  }

  /**
@@ -57,10 +57,10 @@ function testVulnerableComment() {
    $records = $result->fetchAll();

    $query = (string) $query;
    $expected = "/* Testing query comments SELECT nid FROM {node}; -- */ SELECT test.name AS name, test.age AS age\nFROM \n{test} test";
    $expected = "/* Testing query comments SELECT nid FROM {node}; -- */";

    $this->assertEqual(count($records), 4, 'Returned the correct number of rows.');
    $this->assertEqual($query, $expected, 'The flattened query contains the sanitised comment string.');
    $this->assertNotIdentical(FALSE, strpos($query, $expected), 'The flattened query contains the sanitised comment string.');
  }

  /**
+3 −6
Original line number Diff line number Diff line
@@ -139,15 +139,12 @@ function testSubSelectUpdate() {
    $query = db_update('test')
      ->expression('age', $subselect)
      ->condition('name', 'Ringo');
    // Save the query for a __toString test later.
    $string_test = $query;
    $query->execute();
    // Save the number of rows that updated for assertion later.
    $num_updated = $query->execute();
    $after_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField();
    $expected_age = $select->execute()->fetchField();
    $this->assertEqual($after_age, $expected_age);
    // Replace whitespace with a single space.
    $query_string = preg_replace('/\s+/', ' ', $string_test);
    $this->assertIdentical('UPDATE {test} SET age= (SELECT MAX(priority) + :increment AS max_priority FROM {test_task} t) WHERE (name = :db_condition_placeholder_0)', trim($query_string));
    $this->assertEqual(1, $num_updated, t('Expected 1 row to be updated in subselect update query.'));
  }

}
Loading