web-base/core/Driver/SQL/PostgreSQL.class.php

324 lines
8.7 KiB
PHP
Raw Normal View History

2020-04-02 01:48:46 +02:00
<?php
namespace Driver\SQL;
use \Api\Parameter\Parameter;
use \Driver\SQL\Column\Column;
use \Driver\SQL\Column\IntColumn;
use \Driver\SQL\Column\SerialColumn;
use \Driver\SQL\Column\StringColumn;
use \Driver\SQL\Column\EnumColumn;
use \Driver\SQL\Column\DateTimeColumn;
use Driver\SQL\Column\BoolColumn;
use Driver\SQL\Column\JsonColumn;
use \Driver\SQL\Strategy\CascadeStrategy;
use \Driver\SQL\Strategy\SetDefaultStrategy;
use \Driver\SQL\Strategy\SetNullStrategy;
use \Driver\SQL\Strategy\UpdateStrategy;
class PostgreSQL extends SQL {
public function __construct($connectionData) {
parent::__construct($connectionData);
}
public function checkRequirements() {
return function_exists('pg_connect');
}
public function getDriverName() {
return 'pgsql';
}
// Connection Managment
public function connect() {
if(!is_null($this->connection)) {
return true;
}
$config = array(
"host" => $this->connectionData->getHost(),
"port" => $this->connectionData->getPort(),
"dbname" => $this->connectionData->getProperty('database', 'public'),
"user" => $this->connectionData->getLogin(),
"password" => $this->connectionData->getPassword()
);
$connectionString = array();
foreach($config as $key => $val) {
if (!empty($val)) {
$connectionString[] = "$key=$val";
}
}
$this->connection = @pg_connect(implode(" ", $connectionString));
if (!$this->connection) {
$this->lastError = "Failed to connect to Database";
$this->connection = NULL;
return false;
}
pg_set_client_encoding($this->connection, $this->connectionData->getProperty('encoding', 'UTF-8'));
return true;
}
public function disconnect() {
if(is_null($this->connection))
return;
pg_close($this->connection);
}
2020-04-02 21:19:06 +02:00
public function getLastError() {
$lastError = parent::getLastError();
if (empty($lastError)) {
$lastError = pg_last_error($this->connection) . " " . pg_last_error($this->connection);
}
return $lastError;
}
2020-04-02 01:48:46 +02:00
protected function execute($query, $values = NULL, $returnValues = false) {
$this->lastError = "";
$stmt_name = uniqid();
$pgParams = array();
if (!is_null($values)) {
foreach($values as $value) {
$paramType = Parameter::parseType($value);
switch($paramType) {
case Parameter::TYPE_DATE:
$value = $value->format("Y-m-d");
break;
case Parameter::TYPE_TIME:
$value = $value->format("H:i:s");
break;
case Parameter::TYPE_DATE_TIME:
$value = $value->format("Y-m-d H:i:s");
break;
default:
break;
}
$pgParams[] = $value;
}
}
$stmt = @pg_prepare($this->connection, $stmt_name, $query);
if ($stmt === FALSE) {
return false;
}
$result = @pg_execute($this->connection, $stmt_name, $pgParams);
if ($result === FALSE) {
return false;
}
if ($returnValues) {
$rows = pg_fetch_all($result);
if ($rows === FALSE) {
if (empty(trim($this->getLastError()))) {
$rows = array();
}
}
return $rows;
} else {
return true;
}
}
// Querybuilder
public function executeInsert($insert) {
2020-04-02 15:08:14 +02:00
$tableName = $this->tableName($insert->getTableName());
2020-04-02 01:48:46 +02:00
$columns = $insert->getColumns();
$rows = $insert->getRows();
$onDuplicateKey = $insert->onDuplicateKey() ?? "";
if (empty($rows)) {
$this->lastError = "No rows to insert given.";
return false;
}
if (is_null($columns) || empty($columns)) {
2020-04-02 15:08:14 +02:00
$columnStr = "";
2020-04-02 01:48:46 +02:00
} else {
2020-04-02 16:16:58 +02:00
$columnStr = " (" . $this->columnName($columns) . ")";
2020-04-02 01:48:46 +02:00
}
$numRows = count($rows);
$parameters = array();
$values = array();
foreach($rows as $row) {
$rowPlaceHolder = array();
foreach($row as $val) {
$rowPlaceHolder[] = $this->addValue($val, $parameters);
}
$values[] = "(" . implode(",", $rowPlaceHolder) . ")";
}
$values = implode(",", $values);
if ($onDuplicateKey) {
2020-04-02 15:08:14 +02:00
/*if ($onDuplicateKey instanceof UpdateStrategy) {
2020-04-02 01:48:46 +02:00
$updateValues = array();
foreach($onDuplicateKey->getValues() as $key => $value) {
if ($value instanceof Column) {
$columnName = $value->getName();
$updateValues[] = "\"$key\"=\"$columnName\"";
} else {
$updateValues[] = "\"$key\"=" . $this->addValue($value, $parameters);
}
}
$onDuplicateKey = " ON CONFLICT DO UPDATE SET " . implode(",", $updateValues);
2020-04-02 15:08:14 +02:00
} else*/ {
2020-04-02 01:48:46 +02:00
$strategy = get_class($onDuplicateKey);
$this->lastError = "ON DUPLICATE Strategy $strategy is not supported yet.";
return false;
}
}
$returningCol = $insert->getReturning();
2020-04-02 15:08:14 +02:00
$returning = $returningCol ? (" RETURNING " . $this->columnName($returningCol)) : "";
2020-04-02 01:48:46 +02:00
2020-04-02 15:08:14 +02:00
$query = "INSERT INTO $tableName$columnStr VALUES$values$onDuplicateKey$returning";
2020-04-02 01:48:46 +02:00
$res = $this->execute($query, $parameters, !empty($returning));
$success = ($res !== FALSE);
if($success && !empty($returning)) {
$this->lastInsertId = $res[0][$returningCol];
}
return $success;
}
// UGLY but.. what should i do?
private function createEnum($enumColumn) {
$typeName = $enumColumn->getName();
if(!endsWith($typeName, "_type")) {
$typeName = "${typeName}_type";
}
$values = array();
foreach($enumColumn->getValues() as $value) {
$values[] = $this->getValueDefinition($value);
}
$values = implode(",", $values);
$query =
"DO $$ BEGIN
CREATE TYPE \"$typeName\" AS ENUM ($values);
EXCEPTION
WHEN duplicate_object THEN null;
END $$;";
$this->execute($query);
return $typeName;
}
protected function getColumnDefinition($column) {
2020-04-02 16:16:58 +02:00
$columnName = $this->columnName($column->getName());
2020-04-02 01:48:46 +02:00
if ($column instanceof StringColumn) {
$maxSize = $column->getMaxSize();
if ($maxSize) {
$type = "VARCHAR($maxSize)";
} else {
$type = "TEXT";
}
} else if($column instanceof SerialColumn) {
$type = "SERIAL";
} else if($column instanceof IntColumn) {
$type = "INTEGER";
} else if($column instanceof DateTimeColumn) {
$type = "TIMESTAMP";
} else if($column instanceof EnumColumn) {
$type = $this->createEnum($column);
} else if($column instanceof BoolColumn) {
$type = "BOOLEAN";
} else if($column instanceof JsonColumn) {
$type = "JSON";
} else {
$this->lastError = "Unsupported Column Type: " . get_class($column);
return NULL;
}
$notNull = $column->notNull() ? " NOT NULL" : "";
$defaultValue = "";
if (!is_null($column->getDefaultValue()) || !$column->notNull()) {
$defaultValue = " DEFAULT " . $this->getValueDefinition($column->getDefaultValue());
}
2020-04-02 16:16:58 +02:00
return "$columnName $type$notNull$defaultValue";
2020-04-02 01:48:46 +02:00
}
protected function getValueDefinition($value) {
if (is_numeric($value)) {
return $value;
} else if(is_bool($value)) {
return $value ? "TRUE" : "FALSE";
} else if(is_null($value)) {
return "NULL";
} else if($value instanceof Keyword) {
return $value->getValue();
} else {
$str = str_replace("'", "''", $value);
return "'$str'";
}
}
protected function addValue($val, &$params) {
if ($val instanceof Keyword) {
return $val->getValue();
} else {
2020-04-02 21:19:06 +02:00
$params[] = is_bool($val) ? ($val ? "TRUE" : "FALSE") : $val;
2020-04-02 01:48:46 +02:00
return '$' . count($params);
}
}
2020-04-02 15:08:14 +02:00
protected function tableName($table) {
2020-04-02 16:16:58 +02:00
if (is_array($table)) {
$tables = array();
foreach($table as $t) $tables[] = $this->tableName($t);
return implode(",", $tables);
} else {
return "\"$table\"";
}
2020-04-02 15:08:14 +02:00
}
protected function columnName($col) {
if ($col instanceof KeyWord) {
return $col->getValue();
2020-04-02 16:16:58 +02:00
} elseif(is_array($col)) {
$columns = array();
foreach($col as $c) $columns[] = $this->columnName($c);
return implode(",", $columns);
2020-04-02 15:08:14 +02:00
} else {
2020-04-02 16:16:58 +02:00
if (($index = strrpos($col, ".")) !== FALSE) {
2020-04-02 15:08:14 +02:00
$tableName = $this->tableName(substr($col, 0, $index));
$columnName = $this->columnName(substr($col, $index + 1));
return "$tableName.$columnName";
2020-04-02 16:16:58 +02:00
} else if(($index = stripos($col, " as ")) !== FALSE) {
$columnName = $this->columnName(trim(substr($col, 0, $index)));
$alias = $this->columnName(trim(substr($col, $index + 4)));
return "$columnName as $alias";
} else {
return "\"$col\"";
2020-04-02 15:08:14 +02:00
}
}
}
2020-04-02 01:48:46 +02:00
// Special Keywords and functions
public function currentTimestamp() {
2020-04-02 15:08:14 +02:00
return new Keyword("CURRENT_TIMESTAMP");
}
2020-04-02 01:48:46 +02:00
}
?>