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

364 lines
11 KiB
PHP
Raw Normal View History

2020-04-02 00:02:51 +02:00
<?php
namespace Driver\SQL;
2020-04-03 17:39:58 +02:00
use Driver\SQL\Column\Column;
use Driver\SQL\Condition\Compare;
use Driver\SQL\Condition\CondBool;
use Driver\SQL\Condition\CondIn;
2020-06-26 14:58:17 +02:00
use Driver\SQL\Condition\Condition;
2020-06-24 16:09:04 +02:00
use Driver\SQL\Condition\CondKeyword;
2020-06-26 14:58:17 +02:00
use Driver\SQL\Condition\CondNot;
2021-01-13 01:36:04 +01:00
use Driver\Sql\Condition\CondNull;
2020-04-03 17:39:58 +02:00
use Driver\SQL\Condition\CondOr;
use Driver\SQL\Constraint\Constraint;
2020-04-03 14:46:29 +02:00
use \Driver\SQL\Constraint\Unique;
use \Driver\SQL\Constraint\PrimaryKey;
use \Driver\SQL\Constraint\ForeignKey;
2021-04-08 19:08:05 +02:00
use Driver\SQL\Expression\CurrentTimeStamp;
use Driver\SQL\Expression\DateAdd;
use Driver\SQL\Expression\Expression;
2021-04-03 16:23:30 +02:00
use Driver\SQL\Query\AlterTable;
2021-04-08 18:29:47 +02:00
use Driver\SQL\Query\CreateProcedure;
2020-04-03 17:39:58 +02:00
use Driver\SQL\Query\CreateTable;
2021-04-08 18:29:47 +02:00
use Driver\SQL\Query\CreateTrigger;
2020-04-03 17:39:58 +02:00
use Driver\SQL\Query\Delete;
2021-01-07 14:59:36 +01:00
use Driver\SQL\Query\Drop;
2020-04-03 17:39:58 +02:00
use Driver\SQL\Query\Insert;
2020-06-26 23:32:45 +02:00
use Driver\SQL\Query\Query;
2020-04-03 17:39:58 +02:00
use Driver\SQL\Query\Select;
use Driver\SQL\Query\Truncate;
use Driver\SQL\Query\Update;
use Driver\SQL\Strategy\CascadeStrategy;
use Driver\SQL\Strategy\SetDefaultStrategy;
use Driver\SQL\Strategy\SetNullStrategy;
use Driver\SQL\Strategy\Strategy;
use Objects\ConnectionData;
2020-04-03 14:46:29 +02:00
2020-04-02 00:02:51 +02:00
abstract class SQL {
2020-04-03 17:39:58 +02:00
protected string $lastError;
2020-04-02 00:02:51 +02:00
protected $connection;
2020-04-03 17:39:58 +02:00
protected ConnectionData $connectionData;
protected int $lastInsertId;
2020-04-02 00:02:51 +02:00
2020-04-02 01:48:46 +02:00
public function __construct($connectionData) {
2020-04-02 00:02:51 +02:00
$this->connection = NULL;
2020-04-03 14:46:29 +02:00
$this->lastError = 'Unknown Error';
2020-04-02 00:02:51 +02:00
$this->connectionData = $connectionData;
$this->lastInsertId = 0;
}
2021-04-08 18:29:47 +02:00
public function isConnected(): bool {
2020-04-02 00:02:51 +02:00
return !is_null($this->connection);
}
2021-04-08 18:29:47 +02:00
public function getLastError(): string {
2020-04-02 00:02:51 +02:00
return trim($this->lastError);
}
2021-04-08 18:29:47 +02:00
public function createTable($tableName): CreateTable {
2020-06-26 23:32:45 +02:00
return new CreateTable($this, $tableName);
2020-04-02 00:02:51 +02:00
}
2021-04-08 18:29:47 +02:00
public function insert($tableName, $columns=array()): Insert {
2020-06-26 23:32:45 +02:00
return new Insert($this, $tableName, $columns);
2020-04-02 00:02:51 +02:00
}
2021-04-08 18:29:47 +02:00
public function select(...$columNames): Select {
2020-06-26 23:32:45 +02:00
return new Select($this, $columNames);
2020-04-02 00:02:51 +02:00
}
2021-04-08 18:29:47 +02:00
public function truncate($table): Truncate {
2020-06-26 23:32:45 +02:00
return new Truncate($this, $table);
2020-04-02 00:02:51 +02:00
}
2021-04-08 18:29:47 +02:00
public function delete($table): Delete {
2020-06-26 23:32:45 +02:00
return new Delete($this, $table);
2020-04-02 00:02:51 +02:00
}
2021-04-08 18:29:47 +02:00
public function update($table): Update {
2020-06-26 23:32:45 +02:00
return new Update($this, $table);
2020-04-02 00:02:51 +02:00
}
2021-04-08 18:29:47 +02:00
public function drop(string $table): Drop {
2021-01-07 14:59:36 +01:00
return new Drop($this, $table);
}
2021-04-08 18:29:47 +02:00
public function alterTable($tableName): AlterTable {
2021-04-03 16:23:30 +02:00
return new AlterTable($this, $tableName);
}
2021-04-08 18:29:47 +02:00
public function createTrigger($triggerName): CreateTrigger {
return new CreateTrigger($this, $triggerName);
}
public function createProcedure(string $procName): CreateProcedure {
return new CreateProcedure($this, $procName);
}
2020-04-02 01:48:46 +02:00
// ####################
// ### ABSTRACT METHODS
// ####################
// Misc
public abstract function checkRequirements();
public abstract function getDriverName();
2021-04-08 18:29:47 +02:00
// Connection Management
2020-04-02 01:48:46 +02:00
public abstract function connect();
public abstract function disconnect();
2021-04-08 18:29:47 +02:00
public function executeQuery(Query $query, bool $fetchResult = false) {
2020-04-03 17:39:58 +02:00
2021-04-08 18:29:47 +02:00
$parameters = [];
$queryStr = $query->build($parameters);
2020-04-03 17:39:58 +02:00
2021-04-08 18:29:47 +02:00
if($query->dump) {
var_dump($queryStr);
var_dump($parameters);
2020-04-03 17:39:58 +02:00
}
2021-04-08 18:29:47 +02:00
if ($queryStr === null) {
2020-04-03 17:39:58 +02:00
return false;
}
2021-04-08 18:29:47 +02:00
$res = $this->execute($queryStr, $parameters, $fetchResult);
2020-04-03 17:39:58 +02:00
$success = ($res !== FALSE);
2021-04-08 18:29:47 +02:00
// fetch generated serial ids for Insert statements
$generatedColumn = ($query instanceof Insert ? $query->getReturning() : null);
if($success && $fetchResult && $generatedColumn) {
$this->fetchReturning($res, $generatedColumn);
2020-04-03 14:46:29 +02:00
}
2021-04-08 18:29:47 +02:00
return $fetchResult ? $res : $success;
2020-04-03 14:46:29 +02:00
}
2021-04-08 18:29:47 +02:00
public function getWhereClause($conditions, &$params): string {
2020-04-03 14:46:29 +02:00
if (!$conditions) {
return "";
} else {
return " WHERE " . $this->buildCondition($conditions, $params);
}
}
2021-04-08 18:29:47 +02:00
public function getConstraintDefinition(Constraint $constraint): ?string {
2020-04-03 17:39:58 +02:00
$columnName = $this->columnName($constraint->getColumnNames());
2020-04-03 14:46:29 +02:00
if ($constraint instanceof PrimaryKey) {
return "PRIMARY KEY ($columnName)";
} else if ($constraint instanceof Unique) {
return "UNIQUE ($columnName)";
} else if ($constraint instanceof ForeignKey) {
$refTable = $this->tableName($constraint->getReferencedTable());
$refColumn = $this->columnName($constraint->getReferencedColumn());
$strategy = $constraint->onDelete();
$code = "FOREIGN KEY ($columnName) REFERENCES $refTable ($refColumn)";
if ($strategy instanceof SetDefaultStrategy) {
$code .= " ON DELETE SET DEFAULT";
} else if($strategy instanceof SetNullStrategy) {
$code .= " ON DELETE SET NULL";
} else if($strategy instanceof CascadeStrategy) {
$code .= " ON DELETE CASCADE";
}
return $code;
} else {
2020-04-03 17:39:58 +02:00
$this->lastError = "Unsupported constraint type: " . get_class($constraint);
2021-04-08 18:29:47 +02:00
return null;
2020-04-03 14:46:29 +02:00
}
}
2021-04-08 18:29:47 +02:00
protected abstract function fetchReturning($res, string $returningCol);
public abstract function getColumnDefinition(Column $column): ?string;
public abstract function getOnDuplicateStrategy(?Strategy $strategy, &$params): ?string;
public abstract function createTriggerBody(CreateTrigger $trigger): ?string;
public abstract function getProcedureHead(CreateProcedure $procedure): ?string;
public abstract function getColumnType(Column $column): ?string;
public function getProcedureTail(): string { return ""; }
public function getReturning(?string $columns): string { return ""; }
public function getProcedureBody(CreateProcedure $procedure): string {
$statements = "";
foreach ($procedure->getStatements() as $statement) {
$statements .= $this->buildUnsafe($statement) . ";";
}
return $statements;
2020-04-03 17:39:58 +02:00
}
2021-04-08 18:29:47 +02:00
protected function getUnsafeValue($value): ?string {
if (is_string($value) || is_numeric($value) || is_bool($value)) {
return "'" . addslashes("$value") . "'"; // unsafe operation here...
} else if ($value instanceof Column) {
return $this->columnName($value);
} else if ($value === null) {
return "NULL";
} else {
$this->lastError = "Cannot create unsafe value of type: " . gettype($value);
return null;
}
}
2020-04-03 17:39:58 +02:00
2020-04-02 00:02:51 +02:00
protected abstract function getValueDefinition($val);
2021-04-08 18:29:47 +02:00
public abstract function addValue($val, &$params = NULL);
protected abstract function buildUnsafe(Query $statement): string;
2020-04-02 00:02:51 +02:00
2021-04-08 18:29:47 +02:00
public abstract function tableName($table): string;
public abstract function columnName($col): string;
2020-04-02 15:08:14 +02:00
2020-04-02 01:48:46 +02:00
// Special Keywords and functions
2021-04-08 19:08:05 +02:00
public function now(): CurrentTimeStamp { return new CurrentTimeStamp(); }
public function currentTimestamp(): CurrentTimeStamp { return new CurrentTimeStamp(); }
2020-04-02 21:19:06 +02:00
2021-04-08 18:29:47 +02:00
public function count($col = NULL): Keyword {
2020-04-02 21:19:06 +02:00
if (is_null($col)) {
return new Keyword("COUNT(*) AS count");
2020-07-18 12:51:36 +02:00
} else if($col instanceof Keyword) {
return new Keyword("COUNT(" . $col->getValue() . ") AS count");
2020-04-02 21:19:06 +02:00
} else {
2020-06-15 21:14:59 +02:00
$countCol = strtolower(str_replace(".","_", $col)) . "_count";
2020-04-02 21:19:06 +02:00
$col = $this->columnName($col);
2020-06-15 21:14:59 +02:00
return new Keyword("COUNT($col) AS $countCol");
2020-04-02 21:19:06 +02:00
}
}
2021-04-08 18:29:47 +02:00
public function sum($col): Keyword {
2020-06-17 23:50:08 +02:00
$sumCol = strtolower(str_replace(".","_", $col)) . "_sum";
$col = $this->columnName($col);
return new Keyword("SUM($col) AS $sumCol");
}
2021-04-08 18:29:47 +02:00
public function distinct($col): Keyword {
2020-04-02 21:19:06 +02:00
$col = $this->columnName($col);
return new Keyword("DISTINCT($col)");
}
2020-04-02 00:02:51 +02:00
2020-04-02 01:48:46 +02:00
// Statements
protected abstract function execute($query, $values=NULL, $returnValues=false);
2020-04-02 00:02:51 +02:00
2020-04-02 01:48:46 +02:00
protected function buildCondition($condition, &$params) {
2020-04-04 01:15:59 +02:00
2020-04-03 17:39:58 +02:00
if ($condition instanceof CondOr) {
2020-04-02 01:48:46 +02:00
$conditions = array();
foreach($condition->getConditions() as $cond) {
$conditions[] = $this->buildCondition($cond, $params);
}
return "(" . implode(" OR ", $conditions) . ")";
2020-04-03 17:39:58 +02:00
} else if ($condition instanceof Compare) {
2021-04-08 19:08:05 +02:00
$lhs = $condition->getLHS();
$lhs = ($lhs instanceof Expression ?
$this->createExpression($lhs, $params) :
$this->columnName($lhs));
2020-04-02 01:48:46 +02:00
$value = $condition->getValue();
$operator = $condition->getOperator();
2021-03-31 13:58:56 +02:00
if ($value === null) {
if ($operator === "=") {
2021-04-08 19:08:05 +02:00
return "$lhs IS NULL";
2021-03-31 13:58:56 +02:00
} else if ($operator === "!=") {
2021-04-08 19:08:05 +02:00
return "$lhs IS NOT NULL";
2021-03-31 13:58:56 +02:00
}
}
2021-04-08 19:08:05 +02:00
return $lhs . $operator . $this->addValue($value, $params);
2020-04-03 17:39:58 +02:00
} else if ($condition instanceof CondBool) {
2020-04-02 15:08:14 +02:00
return $this->columnName($condition->getValue());
2020-04-02 01:48:46 +02:00
} else if (is_array($condition)) {
2020-04-04 01:15:59 +02:00
if (count($condition) === 1) {
2020-04-02 01:48:46 +02:00
return $this->buildCondition($condition[0], $params);
} else {
$conditions = array();
2020-06-19 14:12:07 +02:00
foreach ($condition as $cond) {
2020-04-02 01:48:46 +02:00
$conditions[] = $this->buildCondition($cond, $params);
}
return implode(" AND ", $conditions);
}
} else if($condition instanceof CondIn) {
2020-06-26 14:58:17 +02:00
2020-06-26 23:32:45 +02:00
$expression = $condition->getExpression();
if (is_array($expression)) {
$values = array();
foreach ($expression as $value) {
$values[] = $this->addValue($value, $params);
}
2020-06-26 14:58:17 +02:00
2020-06-26 23:32:45 +02:00
$values = implode(",", $values);
} else if($expression instanceof Select) {
2021-04-08 18:29:47 +02:00
$values = $expression->build($params);
2020-06-26 23:32:45 +02:00
} else {
$this->lastError = "Unsupported in-expression value: " . get_class($condition);
return false;
}
return $this->columnName($condition->getColumn()) . " IN ($values)";
2020-06-24 16:09:04 +02:00
} else if($condition instanceof CondKeyword) {
$left = $condition->getLeftExp();
$right = $condition->getRightExp();
$keyword = $condition->getKeyword();
$left = ($left instanceof Column) ? $this->columnName($left->getName()) : $this->addValue($left, $params);
$right = ($right instanceof Column) ? $this->columnName($right->getName()) : $this->addValue($right, $params);
return "$left $keyword $right ";
2020-06-26 14:58:17 +02:00
} else if($condition instanceof CondNot) {
$expression = $condition->getExpression();
if ($expression instanceof Condition) {
$expression = $this->buildCondition($expression, $params);
} else {
$expression = $this->columnName($expression);
}
return "NOT $expression";
2021-01-13 01:36:04 +01:00
} else if($condition instanceof CondNull) {
return $this->columnName($condition->getColumn()) . " IS NULL";
2020-04-03 17:39:58 +02:00
} else {
$this->lastError = "Unsupported condition type: " . get_class($condition);
2021-04-08 19:08:05 +02:00
return null;
}
}
protected function createExpression(Expression $exp, array &$params) {
if ($exp instanceof Column) {
return $this->columnName($exp);
} else {
$this->lastError = "Unsupported expression type: " . get_class($exp);
return null;
2020-04-02 00:02:51 +02:00
}
}
2020-04-02 01:48:46 +02:00
public function setLastError($str) {
$this->lastError = $str;
}
2020-04-02 00:02:51 +02:00
2021-04-08 18:29:47 +02:00
public function getLastInsertId(): int {
2020-04-02 00:02:51 +02:00
return $this->lastInsertId;
}
public function close() {
2020-04-02 01:48:46 +02:00
$this->disconnect();
$this->connection = NULL;
2020-04-02 00:02:51 +02:00
}
2020-04-03 17:39:58 +02:00
public static function createConnection(ConnectionData $connectionData) {
2020-04-02 00:02:51 +02:00
$type = $connectionData->getProperty("type");
if ($type === "mysql") {
$sql = new MySQL($connectionData);
2020-04-02 01:48:46 +02:00
} else if ($type === "postgres") {
$sql = new PostgreSQL($connectionData);
2020-04-02 00:02:51 +02:00
} else {
return "Unknown database type";
}
2020-04-02 01:48:46 +02:00
if ($sql->checkRequirements()) {
$sql->connect();
}
2020-04-02 00:02:51 +02:00
return $sql;
}
2020-06-19 13:13:13 +02:00
public abstract function getStatus();
2020-06-25 16:54:58 +02:00
public function parseBool($val) : bool {
2020-06-25 21:53:33 +02:00
return in_array($val, array(true, 1, '1', 't', 'true', 'TRUE'), true);
2020-06-25 16:54:58 +02:00
}
2020-04-03 17:39:58 +02:00
}