From 4f857a1cd5cceb272f1b5959566749ef9188f00a Mon Sep 17 00:00:00 2001 From: Vitaliy Filippov Date: Sun, 16 Sep 2018 02:31:52 +0300 Subject: [PATCH] Update Pgsql --- DatabasePdoPgsql.php | 74 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 11 deletions(-) diff --git a/DatabasePdoPgsql.php b/DatabasePdoPgsql.php index 6d2a658..5cb64fd 100644 --- a/DatabasePdoPgsql.php +++ b/DatabasePdoPgsql.php @@ -3,8 +3,8 @@ /** * PDO/PostgreSQL wrapper with (mostly) DatabaseMySQL interface :) * Select builder is inspired by MediaWiki's one. - * Version: 2017-12-13 - * (c) Vitaliy Filippov, 2015-2017 + * Version: 2018-08-12 + * (c) Vitaliy Filippov, 2015-2018 */ if (!defined('MS_HASH')) @@ -155,6 +155,8 @@ class DatabasePdoPgsql implements Database { if ($value === NULL) return "NULL"; + elseif ($value instanceof DatabasePdoPgsql_Fragment) + return $value->bind ? $this->quoteInto($value->text, $value->bind) : $value->text; if (!$this->link) $this->connect(); return $this->link->quote($value); @@ -314,8 +316,6 @@ class DatabasePdoPgsql implements Database $wh[] = "$k IN (" . implode(",", $v) . ")"; } } - elseif (preg_match('/^-?\d+(\.\d+)?$/s', $v)) // int/float - $wh[] = "$k=$v"; elseif ($v !== NULL) $wh[] = "$k=".$this->quote($v); else @@ -344,6 +344,9 @@ class DatabasePdoPgsql implements Database * 'ORDER BY' => array($orderby_field1 => 'ASC', $orderby_field2 => 'DESC') * 'LIMIT' => array($offset, $limit) or array($limit) or just $limit * 'OFFSET' => $offset, for the case when 'LIMIT' is just $limit + * 'DISTINCT' => true + * 'DISTINCT ON' => $fields + * 'HAVING' => $having (like WHERE) */ function select_builder($tables, $fields, $where, $options = NULL) { @@ -357,9 +360,14 @@ class DatabasePdoPgsql implements Database } if (is_array($fields)) { - foreach ($fields as $k => $v) + foreach ($fields as $k => &$v) + { + if ($v instanceof DatabasePdoPgsql_Fragment) + $v = $v->bind ? $this->quoteInto($v->text, $v->bind) : $v->text; if (!ctype_digit("$k")) - $fields[$k] = "$v AS ".$this->quoteId($k); + $v = "$v AS ".$this->quoteId($k); + } + unset($v); $fields = join(",\n ", str_replace("\n", "\n ", $fields)); } $more = NULL; @@ -370,11 +378,18 @@ class DatabasePdoPgsql implements Database $this->calcFoundRows = isset($options['CALC_FOUND_ROWS']) || isset($options['SQL_CALC_FOUND_ROWS']); if ($this->calcFoundRows) $fields .= ",\n COUNT(*) OVER () \"*\""; - $sql = "SELECT $fields\nFROM $tables"; + $sql = "SELECT"; + if (isset($options['DISTINCT ON'])) + $sql .= " DISTINCT ON (".implode(', ', (array)$options['DISTINCT ON']).")"; + elseif (isset($options['DISTINCT'])) + $sql .= " DISTINCT"; + $sql .= " $fields\nFROM $tables"; if ($where) $sql .= "\nWHERE $where"; if (!empty($options['GROUP BY']) && $options['GROUP BY'] !== '0') $sql .= "\nGROUP BY ".$this->order_option($options['GROUP BY']); + if (!empty($options['HAVING'])) + $sql .= "\nHAVING ".$this->where_builder($options['HAVING']); if (!empty($options['ORDER BY']) && $options['ORDER BY'] !== '0') $sql .= "\nORDER BY ".$this->order_option($options['ORDER BY']); $sql .= $this->limit_option($options); @@ -417,7 +432,7 @@ class DatabasePdoPgsql implements Database if (is_array($g) && count($g) != 2) $g = $g[0]; if (is_array($g)) - return " LIMIT ".$g[1]." OFFSET ".$g[0]; + return " LIMIT ".$g[1].($g[0] > 0 ? " OFFSET ".$g[0] : ''); $g = " LIMIT $g"; } if (!empty($options['OFFSET'])) @@ -538,7 +553,7 @@ class DatabasePdoPgsql implements Database $fs = $format & MS_LIST ? PDO::FETCH_NUM : PDO::FETCH_ASSOC; $rows = $format & MS_ROW ? [ $res->fetch($fs) ] : $res->fetchAll($fs); $this->foundRows = reset($rows); - $this->foundRows = end($this->foundRows); + $this->foundRows = $this->foundRows ? end($this->foundRows) : 0; if ($format & MS_COL) foreach ($rows as &$row) $row = reset($row); @@ -567,6 +582,15 @@ class DatabasePdoPgsql implements Database * 'OFFSET' => $offset, for the case when 'LIMIT' is just $limit */ function delete($tables, $where, $options = NULL) + { + $sql = $this->delete_builder($tables, $where, $options); + return $this->query($sql); + } + + /** + * Same as delete(), but only returns SQL text + */ + function delete_builder($tables, $where, $options = NULL) { list($what, $using) = $this->split_using($tables, $where); $more = NULL; @@ -576,7 +600,7 @@ class DatabasePdoPgsql implements Database $where = $this->where_builder($where) ?: '1=1'; $sql .= " WHERE $where"; $sql .= $this->limit_option($options); - return $this->query($sql); + return $sql; } protected function values($rows, $forInsert) @@ -587,6 +611,11 @@ class DatabasePdoPgsql implements Database $key = $rows; $rows = [ $rows ]; } + else + { + foreach ($rows as $r) + $key += $r; + } $key = array_keys($key); foreach ($rows as &$r) { @@ -600,7 +629,9 @@ class DatabasePdoPgsql implements Database foreach ($key as &$k) { if (strpos($k, '"') === false) + { $k = $this->quoteId($k); + } } return array($key, $rows); } @@ -611,6 +642,11 @@ class DatabasePdoPgsql implements Database return new DatabasePdoPgsql_Values($keys, $values, $alias); } + function fragment($text, array $bind = []) + { + return new DatabasePdoPgsql_Fragment($text, $bind); + } + /** * Builds an INSERT query. * @@ -625,12 +661,15 @@ class DatabasePdoPgsql implements Database */ function insert_builder($table, $rows, $action = NULL, $uniqueColumns = NULL, $updateCols = NULL) { + $alias = is_array($table) ? array_keys($table)[0] : NULL; + $table = is_array($table) ? $table[$alias] : $table; if (isset($this->tableNames[$table])) { $table = $this->quoteId($this->tableNames[$table]); } list($keys, $values) = $this->values($rows, true); - $sql = "INSERT INTO $table (".implode(',', $keys).") VALUES (".implode('),(', $values).")"; + $sql = "INSERT INTO $table".($alias ? " AS $alias" : ""). + " (".implode(',', $keys).") VALUES (".implode('),(', $values).")"; if ($action) { $sql .= " ON CONFLICT"; @@ -825,6 +864,8 @@ class DatabasePdoPgsql implements Database class DatabasePdoPgsql_Values { + public $keys, $values, $alias; + function __construct($keys, $values, $alias) { $this->keys = $keys; @@ -837,3 +878,14 @@ class DatabasePdoPgsql_Values return "(VALUES (".implode("),(", $this->values).")) AS ".$this->alias." (".implode(',', $this->keys).")"; } } + +class DatabasePdoPgsql_Fragment +{ + public $text, $bind; + + function __construct($text, $bind) + { + $this->text = $text; + $this->bind = $bind; + } +}