sql expression (lots of todo)

This commit is contained in:
Roman 2021-04-08 19:08:05 +02:00
parent 43d9a65def
commit 4582ca0f9f
13 changed files with 187 additions and 31 deletions

43
cli.php

@ -5,6 +5,10 @@ include_once 'core/constants.php';
use Configuration\Configuration; use Configuration\Configuration;
use Configuration\DatabaseScript; use Configuration\DatabaseScript;
use Driver\SQL\Column\Column;
use Driver\SQL\Condition\Compare;
use Driver\SQL\Condition\CondIn;
use Driver\SQL\Expression\DateAdd;
use Objects\ConnectionData; use Objects\ConnectionData;
use Objects\User; use Objects\User;
@ -155,6 +159,45 @@ function handleDatabase(array $argv) {
proc_close($process); proc_close($process);
} }
} else if ($action === "clean") {
$user = getUser();
$sql = $user->getSQL();
printLine("Deleting user related data older than 90 days...");
// 1st: Select all related tables and entities
$tables = [];
$res = $sql->select("entityId", "tableName")
->from("EntityLog")
->where(new Compare($sql->now(), new DateAdd(new Column("modified"), new Column("lifetime"), "DAY"), ">="))
->execute();
$success = ($res !== false);
if (!$success) {
_exit("Error querying data: " . $sql->getLastError());
}
foreach ($res as $row) {
$tableName = $row["tableName"];
$uid = $row["entityId"];
if (!isset($tables[$tableName])) {
$tables[$tableName] = [];
}
$tables[$tableName][] = $uid;
}
// 2nd: delete!
foreach ($tables as $table => $uids) {
$success = $sql->delete($table)
->where(new CondIn("uid", $uids))
->execute();
if (!$success) {
printLine("Error deleting data: " . $sql->getLastError());
}
}
printLine("Done!");
} else { } else {
_exit("Usage: cli.php db <migrate|import|export> [options...]"); _exit("Usage: cli.php db <migrate|import|export> [options...]");
} }

@ -38,12 +38,23 @@ class log extends DatabaseScript {
->exec(array( ->exec(array(
$sql->update("EntityLog") $sql->update("EntityLog")
->set("modified", $sql->now()) ->set("modified", $sql->now())
->where(new Compare("entityId",new CurrentColumn("uid"))) ->where(new Compare("entityId", new CurrentColumn("uid")))
->where(new Compare("tableName",new CurrentTable())) ->where(new Compare("tableName", new CurrentTable()))
));
$deleteProcedure = $sql->createProcedure("DeleteEntityLog")
->param(new CurrentTable())
->param(new IntColumn("uid"))
->returns(new Trigger())
->exec(array(
$sql->delete("EntityLog")
->where(new Compare("entityId", new CurrentColumn("uid")))
->where(new Compare("tableName", new CurrentTable()))
)); ));
$queries[] = $insertProcedure; $queries[] = $insertProcedure;
$queries[] = $updateProcedure; $queries[] = $updateProcedure;
$queries[] = $deleteProcedure;
$tables = ["ContactRequest"]; $tables = ["ContactRequest"];
foreach ($tables as $table) { foreach ($tables as $table) {
@ -55,6 +66,10 @@ class log extends DatabaseScript {
$queries[] = $sql->createTrigger("${table}_trg_update") $queries[] = $sql->createTrigger("${table}_trg_update")
->after()->update($table) ->after()->update($table)
->exec($updateProcedure); ->exec($updateProcedure);
$queries[] = $sql->createTrigger("${table}_trg_delete")
->after()->delete($table)
->exec($deleteProcedure);
} }
return $queries; return $queries;

@ -2,7 +2,9 @@
namespace Driver\SQL\Column; namespace Driver\SQL\Column;
class Column { use Driver\SQL\Expression\Expression;
class Column extends Expression {
private string $name; private string $name;
private bool $nullable; private bool $nullable;

@ -5,16 +5,16 @@ namespace Driver\SQL\Condition;
class Compare extends Condition { class Compare extends Condition {
private string $operator; private string $operator;
private string $column; private $lhs;
private $value; private $value;
public function __construct(string $col, $val, string $operator = '=') { public function __construct($col, $val, string $operator = '=') {
$this->operator = $operator; $this->operator = $operator;
$this->column = $col; $this->lhs = $col;
$this->value = $val; $this->value = $val;
} }
public function getColumn(): string { return $this->column; } public function getLHS() { return $this->lhs; }
public function getValue() { return $this->value; } public function getValue() { return $this->value; }
public function getOperator(): string { return $this->operator; } public function getOperator(): string { return $this->operator; }

@ -2,6 +2,8 @@
namespace Driver\SQL\Condition; namespace Driver\SQL\Condition;
abstract class Condition { use Driver\SQL\Expression\Expression;
abstract class Condition extends Expression {
} }

@ -0,0 +1,7 @@
<?php
namespace Driver\SQL\Expression;
class CurrentTimeStamp extends Expression {
}

@ -0,0 +1,23 @@
<?php
namespace Driver\SQL\Expression;
use Driver\SQL\SQL;
class DateAdd extends Expression {
private Expression $lhs;
private Expression $rhs;
private string $unit;
public function __construct(Expression $lhs, Expression $rhs, string $unit) {
$this->lhs = $lhs;
$this->rhs = $rhs;
$this->unit = $unit;
}
public function getLHS(): Expression { return $this->lhs; }
public function getRHS(): Expression { return $this->rhs; }
public function getUnit(): string { return $this->unit; }
}

@ -0,0 +1,9 @@
<?php
namespace Driver\SQL\Expression;
use Driver\SQL\SQL;
abstract class Expression {
}

@ -2,7 +2,9 @@
namespace Driver\SQL; namespace Driver\SQL;
class Keyword { use Driver\SQL\Expression\Expression;
class Keyword extends Expression {
private string $value; private string $value;

@ -16,6 +16,9 @@ use Driver\SQL\Column\JsonColumn;
use Driver\SQL\Condition\CondRegex; use Driver\SQL\Condition\CondRegex;
use Driver\SQL\Expression\Add; use Driver\SQL\Expression\Add;
use Driver\SQL\Expression\CurrentTimeStamp;
use Driver\SQL\Expression\DateAdd;
use Driver\SQL\Expression\Expression;
use Driver\SQL\Query\CreateProcedure; use Driver\SQL\Query\CreateProcedure;
use Driver\SQL\Query\CreateTrigger; use Driver\SQL\Query\CreateTrigger;
use Driver\SQL\Query\Query; use Driver\SQL\Query\Query;
@ -192,7 +195,7 @@ class MySQL extends SQL {
$columnName = $this->columnName($value->getName()); $columnName = $this->columnName($value->getName());
$updateValues[] = "$leftColumn=VALUES($columnName)"; $updateValues[] = "$leftColumn=VALUES($columnName)";
} else if($value instanceof Add) { } else if($value instanceof Add) {
$columnName = $this->columnName($value->getColumn()); $columnName = $this->columnName($value->getLHS());
$operator = $value->getOperator(); $operator = $value->getOperator();
$value = $value->getValue(); $value = $value->getValue();
$updateValues[] = "$leftColumn=$columnName$operator" . $this->addValue($value, $params); $updateValues[] = "$leftColumn=$columnName$operator" . $this->addValue($value, $params);
@ -278,6 +281,8 @@ class MySQL extends SQL {
return "NULL"; return "NULL";
} else if($value instanceof Keyword) { } else if($value instanceof Keyword) {
return $value->getValue(); return $value->getValue();
} else if ($value instanceof CurrentTimeStamp) {
return "CURRENT_TIMESTAMP";
} else { } else {
$str = addslashes($value); $str = addslashes($value);
return "'$str'"; return "'$str'";
@ -291,6 +296,8 @@ class MySQL extends SQL {
return $val->getName(); return $val->getName();
} else if ($val instanceof Column) { } else if ($val instanceof Column) {
return $this->columnName($val->getName()); return $this->columnName($val->getName());
} else if ($val instanceof Expression) {
return $this->createExpression($val, $params);
} else { } else {
$params[] = $val; $params[] = $val;
return "?"; return "?";
@ -329,10 +336,6 @@ class MySQL extends SQL {
} }
} }
public function currentTimestamp(): Keyword {
return new Keyword("NOW()");
}
public function getStatus() { public function getStatus() {
return mysqli_stat($this->connection); return mysqli_stat($this->connection);
} }
@ -397,4 +400,17 @@ class MySQL extends SQL {
return $query; return $query;
} }
protected function createExpression(Expression $exp, array &$params) {
if ($exp instanceof DateAdd) {
$lhs = $this->addValue($exp->getLHS(), $params);
$rhs = $this->addValue($exp->getRHS(), $params);
$unit = $exp->getUnit();
return "DATE_ADD($lhs, INTERVAL $rhs $unit)";
} else if ($exp instanceof CurrentTimeStamp) {
return "NOW()";
} else {
return parent::createExpression($exp, $params);
}
}
} }

@ -16,6 +16,9 @@ use Driver\SQL\Column\JsonColumn;
use Driver\SQL\Condition\CondRegex; use Driver\SQL\Condition\CondRegex;
use Driver\SQL\Expression\Add; use Driver\SQL\Expression\Add;
use Driver\SQL\Expression\CurrentTimeStamp;
use Driver\SQL\Expression\DateAdd;
use Driver\SQL\Expression\Expression;
use Driver\SQL\Query\CreateProcedure; use Driver\SQL\Query\CreateProcedure;
use Driver\SQL\Query\CreateTrigger; use Driver\SQL\Query\CreateTrigger;
use Driver\SQL\Query\Query; use Driver\SQL\Query\Query;
@ -151,7 +154,7 @@ class PostgreSQL extends SQL {
$columnName = $this->columnName($value->getName()); $columnName = $this->columnName($value->getName());
$updateValues[] = "$leftColumn=EXCLUDED.$columnName"; $updateValues[] = "$leftColumn=EXCLUDED.$columnName";
} else if ($value instanceof Add) { } else if ($value instanceof Add) {
$columnName = $this->columnName($value->getColumn()); $columnName = $this->columnName($value->getLHS());
$operator = $value->getOperator(); $operator = $value->getOperator();
$value = $value->getValue(); $value = $value->getValue();
$updateValues[] = "$leftColumn=$columnName$operator" . $this->addValue($value, $params); $updateValues[] = "$leftColumn=$columnName$operator" . $this->addValue($value, $params);
@ -257,8 +260,10 @@ class PostgreSQL extends SQL {
return $value ? "TRUE" : "FALSE"; return $value ? "TRUE" : "FALSE";
} else if(is_null($value)) { } else if(is_null($value)) {
return "NULL"; return "NULL";
} else if($value instanceof Keyword) { } else if ($value instanceof Keyword) {
return $value->getValue(); return $value->getValue();
} else if ($value instanceof CurrentTimeStamp) {
return "CURRENT_TIMESTAMP";
} else { } else {
$str = str_replace("'", "''", $value); $str = str_replace("'", "''", $value);
return "'$str'"; return "'$str'";
@ -274,6 +279,8 @@ class PostgreSQL extends SQL {
return "NEW." . $this->columnName($val->getName()); return "NEW." . $this->columnName($val->getName());
} else if ($val instanceof Column) { } else if ($val instanceof Column) {
return $this->columnName($val->getName()); return $this->columnName($val->getName());
} else if ($val instanceof Expression) {
return $this->createExpression($val, $params);
} else { } else {
$params[] = is_bool($val) ? ($val ? "TRUE" : "FALSE") : $val; $params[] = is_bool($val) ? ($val ? "TRUE" : "FALSE") : $val;
return '$' . count($params); return '$' . count($params);
@ -312,11 +319,6 @@ class PostgreSQL extends SQL {
} }
} }
// Special Keywords and functions
public function currentTimestamp(): Keyword {
return new Keyword("CURRENT_TIMESTAMP");
}
public function getStatus() { public function getStatus() {
$version = pg_version($this->connection)["client"] ?? "??"; $version = pg_version($this->connection)["client"] ?? "??";
$status = pg_connection_status($this->connection); $status = pg_connection_status($this->connection);
@ -410,4 +412,24 @@ class PostgreSQL extends SQL {
return $query; return $query;
} }
protected function createExpression(Expression $exp, array &$params) {
if ($exp instanceof DateAdd) {
$lhs = $this->addValue($exp->getLHS(), $params);
$rhs = $this->addValue($exp->getRHS(), $params);
$unit = $exp->getUnit();
if ($exp->getRHS() instanceof Column) {
$rhs = "$rhs * INTERVAL '1 $unit'";
} else {
$rhs = "$rhs $unit";
}
return "$lhs + $rhs";
} else if ($exp instanceof CurrentTimeStamp) {
return "CURRENT_TIMESTAMP";
} else {
return parent::createExpression($exp, $params);
}
}
} }

@ -2,9 +2,10 @@
namespace Driver\SQL\Query; namespace Driver\SQL\Query;
use Driver\SQL\Expression\Expression;
use Driver\SQL\SQL; use Driver\SQL\SQL;
abstract class Query { abstract class Query extends Expression {
protected SQL $sql; protected SQL $sql;
public bool $dump; public bool $dump;
@ -23,6 +24,4 @@ abstract class Query {
public function execute() { public function execute() {
return $this->sql->executeQuery($this); return $this->sql->executeQuery($this);
} }
public abstract function build(array &$params): ?string;
} }

@ -15,6 +15,9 @@ use Driver\SQL\Constraint\Constraint;
use \Driver\SQL\Constraint\Unique; use \Driver\SQL\Constraint\Unique;
use \Driver\SQL\Constraint\PrimaryKey; use \Driver\SQL\Constraint\PrimaryKey;
use \Driver\SQL\Constraint\ForeignKey; use \Driver\SQL\Constraint\ForeignKey;
use Driver\SQL\Expression\CurrentTimeStamp;
use Driver\SQL\Expression\DateAdd;
use Driver\SQL\Expression\Expression;
use Driver\SQL\Query\AlterTable; use Driver\SQL\Query\AlterTable;
use Driver\SQL\Query\CreateProcedure; use Driver\SQL\Query\CreateProcedure;
use Driver\SQL\Query\CreateTable; use Driver\SQL\Query\CreateTable;
@ -205,8 +208,8 @@ abstract class SQL {
public abstract function columnName($col): string; public abstract function columnName($col): string;
// Special Keywords and functions // Special Keywords and functions
public function now(): Keyword { return $this->currentTimestamp(); } public function now(): CurrentTimeStamp { return new CurrentTimeStamp(); }
public abstract function currentTimestamp(): Keyword; public function currentTimestamp(): CurrentTimeStamp { return new CurrentTimeStamp(); }
public function count($col = NULL): Keyword { public function count($col = NULL): Keyword {
if (is_null($col)) { if (is_null($col)) {
@ -243,19 +246,23 @@ abstract class SQL {
} }
return "(" . implode(" OR ", $conditions) . ")"; return "(" . implode(" OR ", $conditions) . ")";
} else if ($condition instanceof Compare) { } else if ($condition instanceof Compare) {
$column = $this->columnName($condition->getColumn()); $lhs = $condition->getLHS();
$lhs = ($lhs instanceof Expression ?
$this->createExpression($lhs, $params) :
$this->columnName($lhs));
$value = $condition->getValue(); $value = $condition->getValue();
$operator = $condition->getOperator(); $operator = $condition->getOperator();
if ($value === null) { if ($value === null) {
if ($operator === "=") { if ($operator === "=") {
return "$column IS NULL"; return "$lhs IS NULL";
} else if ($operator === "!=") { } else if ($operator === "!=") {
return "$column IS NOT NULL"; return "$lhs IS NOT NULL";
} }
} }
return $column . $operator . $this->addValue($value, $params); return $lhs . $operator . $this->addValue($value, $params);
} else if ($condition instanceof CondBool) { } else if ($condition instanceof CondBool) {
return $this->columnName($condition->getValue()); return $this->columnName($condition->getValue());
} else if (is_array($condition)) { } else if (is_array($condition)) {
@ -306,7 +313,16 @@ abstract class SQL {
return $this->columnName($condition->getColumn()) . " IS NULL"; return $this->columnName($condition->getColumn()) . " IS NULL";
} else { } else {
$this->lastError = "Unsupported condition type: " . get_class($condition); $this->lastError = "Unsupported condition type: " . get_class($condition);
return false; 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;
} }
} }