This commit is contained in:
origami11@yandex.ru 2023-04-13 11:35:07 +03:00
commit 7163158baf

View file

@ -1,227 +1,236 @@
<?php <?php
namespace ctiso\Database; namespace ctiso\Database;
use ctiso\Database, use ctiso\Database,
ctiso\Tools\SQLStatementExtractor, ctiso\Tools\SQLStatementExtractor,
ctiso\Path, ctiso\Path,
Exception; Exception;
class Manager class Manager
{ {
public $db/*: Database*/; public $db/*: Database*/;
function __construct(Database $db) { function __construct(Database $db) {
$this->db = $db; $this->db = $db;
} }
public function ExecuteAction($action/*: array*/, $db_file = "") { public function ExecuteAction($action/*: array*/, $db_file = "") {
switch($action["type"]) { switch($action["type"]) {
case "dropTable": case "dropTable":
$this->DropTableQuery($action["table_name"], true); $this->DropTableQuery($action["table_name"], true);
break; break;
case "createTable": case "createTable":
$constraints = isset($action["constraints"]) ? $action["constraints"] : NULL; $constraints = isset($action["constraints"]) ? $action["constraints"] : NULL;
$this->CreateTableQuery($action["table_name"], $action["fields"], $constraints); $this->CreateTableQuery($action["table_name"], $action["fields"], $constraints);
break; break;
case "addColumn": case "addColumn":
$this->AddColumn($action["table_name"], $action["column_name"], $action["field"]); $this->AddColumn($action["table_name"], $action["column_name"], $action["field"]);
break; break;
case "insert": case "insert":
$this->db->insertQuery($action["table_name"], $action["values"]); $this->db->insertQuery($action["table_name"], $action["values"]);
break; break;
case "alterReference": case "alterReference":
$this->AlterReference($action["table"], $action["column"], $action["refTable"], $action["refColumn"]); $this->AlterReference($action["table"], $action["column"], $action["refTable"], $action["refColumn"]);
break; break;
case "renameColumn": case "renameColumn":
$this->RenameColumn($action["table"], $action["old_name"], $action["new_name"]); $this->RenameColumn($action["table"], $action["old_name"], $action["new_name"]);
break; break;
case "executeFile": case "createView":
if ($this->db->isPostgres() && isset($action["pgsql"])) { $this->recreateView($action["view"], $action["select"]);
$file = $action["pgsql"]; break;
} else { case "executeFile":
$file = $action["source"]; if ($this->db->isPostgres() && isset($action["pgsql"])) {
} $file = $action["pgsql"];
} else {
$file = $action["source"];
}
$stmtList = SQLStatementExtractor::extractFile(Path::join(dirname($db_file), $file)); $stmtList = SQLStatementExtractor::extractFile(Path::join(dirname($db_file), $file));
foreach($stmtList as $stmt) { foreach($stmtList as $stmt) {
$this->db->executeQuery($stmt); $this->db->executeQuery($stmt);
} }
break; break;
default: default:
throw new Exception("unknown action ". $action["type"] . PHP_EOL); throw new Exception("unknown action ". $action["type"] . PHP_EOL);
} }
} }
public function DropTableQuery($table, $cascade=false) { //Дропает и создаёт SQL VIEW
$statement = "DROP TABLE IF EXISTS ".$table; public function recreateView($viewName, $selectStatement) {
if ($this->db->isPostgres()&&$cascade) { $this->db->query("DROP VIEW ".$viewName);
$statement = $statement." CASCADE"; $this->db->query("CREATE VIEW ".$viewName." AS ".$selectStatement);
} }
$this->db->query($statement);
}
public function AlterReference($table,$column,$refTable,$refColumn) { public function DropTableQuery($table, $cascade=false) {
$this->db->query("ALTER TABLE ".$table." ADD CONSTRAINT ".$table."_".$column."fk"." FOREIGN KEY (".$column.") REFERENCES ".$refTable." (".$refColumn.")"); $statement = "DROP TABLE IF EXISTS ".$table;
} if ($this->db->isPostgres()&&$cascade) {
$statement = $statement." CASCADE";
}
$this->db->query($statement);
}
//Извлечение информации о полях таблицы public function AlterReference($table,$column,$refTable,$refColumn) {
public function TableInfo($table) { $this->db->query("ALTER TABLE ".$table." ADD CONSTRAINT ".$table."_".$column."fk"." FOREIGN KEY (".$column.") REFERENCES ".$refTable." (".$refColumn.")");
$pg = $this->db->isPostgres(); }
if ($pg) {
throw new Exception("Not implemented for postgres");
} else {
$results = $this->db->fetchAllArray("PRAGMA table_info(".$table.");");
if (empty($results)) {
return null;
}
$fields = [];
foreach ($results as $result) {
$fields[$result["name"]] = [
"type"=> $result["type"],
"not_null"=> boolval($result["notnull"]),
"constraint"=> ((boolean) $result["pk"]) ? "PRIMARY KEY" : null
];
}
return $fields;
}
}
public function RenameColumn($table, $old_name, $new_name) { //Извлечение информации о полях таблицы
$pg = $this->db->isPostgres(); public function TableInfo($table) {
if ($pg) { $pg = $this->db->isPostgres();
$this->db->query("ALTER TABLE ".$table." RENAME COLUMN ".$old_name." TO ".$new_name); if ($pg) {
} else { throw new Exception("Not implemented for postgres");
$tmp_table = "tmp_" . $table; } else {
$this->DropTableQuery($tmp_table); $results = $this->db->fetchAllArray("PRAGMA table_info(".$table.");");
$table_info = $this->TableInfo($table); if (empty($results)) {
return null;
}
$fields = [];
foreach ($results as $result) {
$fields[$result["name"]] = [
"type"=> $result["type"],
"not_null"=> boolval($result["notnull"]),
"constraint"=> ((boolean) $result["pk"]) ? "PRIMARY KEY" : null
];
}
return $fields;
}
}
if (isset($table_info[$new_name])) { public function RenameColumn($table, $old_name, $new_name) {
return; $pg = $this->db->isPostgres();
} if ($pg) {
$this->db->query("ALTER TABLE ".$table." RENAME COLUMN ".$old_name." TO ".$new_name);
} else {
$tmp_table = "tmp_" . $table;
$this->DropTableQuery($tmp_table);
$table_info = $this->TableInfo($table);
$data/*: array*/ = $this->DumpTable($table); if (isset($table_info[$new_name])) {
return;
}
$this->db->query("ALTER TABLE ".$table." RENAME TO ".$tmp_table.";"); $data/*: array*/ = $this->DumpTable($table);
$table_info[$new_name] = $table_info[$old_name];
unset($table_info[$old_name]);
$this->CreateTableQuery($table,$table_info,null);
foreach ($data as $row) { $this->db->query("ALTER TABLE ".$table." RENAME TO ".$tmp_table.";");
$values = $row['values']; $table_info[$new_name] = $table_info[$old_name];
$values[$new_name] = $values[$old_name]; unset($table_info[$old_name]);
unset($values[$old_name]); $this->CreateTableQuery($table,$table_info,null);
$this->db->insertQuery($table, $values);
}
$this->DropTableQuery($tmp_table);
}
}
//Обновление ключа serial после ручной вставки foreach ($data as $row) {
public function UpdateSerial($table,$column) { $values = $row['values'];
$this->db->query("SELECT setval(pg_get_serial_sequence('".$table."', '".$column."'), coalesce(max(".$column."),0) + 1, false) FROM ".$table); $values[$new_name] = $values[$old_name];
} unset($values[$old_name]);
$this->db->insertQuery($table, $values);
}
$this->DropTableQuery($tmp_table);
}
}
public function Column_Definition($name,$data,$pg){ //Обновление ключа serial после ручной вставки
$constraint = isset($data['constraint'])?" ".$data['constraint']:""; public function UpdateSerial($table,$column) {
$references = ""; $this->db->query("SELECT setval(pg_get_serial_sequence('".$table."', '".$column."'), coalesce(max(".$column."),0) + 1, false) FROM ".$table);
if (isset($data['references'])) { }
$references = " REFERENCES ".$data['references'];
}
if (isset($data["not_null"]) && $data["not_null"])
$constraint .=" NOT NULL";
$type = $data['type'];
if (!$pg) {
if (strtolower($type)=="serial")
$type = "integer";
//if (strtolower($type)=="boolean")
// $type = "integer";
}
return $name." ".$type.$references.$constraint;
}
public function AddColumn($table_name,$column_name,$field){ public function Column_Definition($name,$data,$pg){
$pg = $this->db->isPostgres(); $constraint = isset($data['constraint'])?" ".$data['constraint']:"";
$q = "ALTER TABLE ".$table_name." ADD COLUMN ". $references = "";
$this->Column_Definition($column_name, $field, $pg); if (isset($data['references'])) {
$this->db->query($q); $references = " REFERENCES ".$data['references'];
} }
if (isset($data["not_null"]) && $data["not_null"])
$constraint .=" NOT NULL";
$type = $data['type'];
if (!$pg) {
if (strtolower($type)=="serial")
$type = "integer";
//if (strtolower($type)=="boolean")
// $type = "integer";
}
return $name." ".$type.$references.$constraint;
}
function getConstraintDef($c/*: array*/) { public function AddColumn($table_name,$column_name,$field){
if ($c['type'] == 'unique') { $pg = $this->db->isPostgres();
return "UNIQUE(" . implode(", ", $c['fields']) . ")"; $q = "ALTER TABLE ".$table_name." ADD COLUMN ".
} $this->Column_Definition($column_name, $field, $pg);
return ""; $this->db->query($q);
}
function getConstraintDef($c/*: array*/) {
if ($c['type'] == 'unique') {
return "UNIQUE(" . implode(", ", $c['fields']) . ")";
}
return "";
}
//CreateTableQuery('users',['id'=>['type'=>'integer','constraint'=>'PRIMARY KEY']])
public function CreateTableQuery($table, $fields, $constraints) {
$pg = $this->db->isPostgres();
if ($constraints) {
if (is_array($constraints)) {
$constraints = $this->getConstraintDef($constraints);
}
$constraints = ", " . $constraints;
} }
//CreateTableQuery('users',['id'=>['type'=>'integer','constraint'=>'PRIMARY KEY']]) $statement = "CREATE TABLE $table (" . implode(",",
public function CreateTableQuery($table, $fields, $constraints) { array_map(function($name,$data) use ($pg) {
$pg = $this->db->isPostgres(); return $this->Column_Definition($name,$data,$pg);
if ($constraints) { }, array_keys($fields), array_values($fields))
if (is_array($constraints)) { ) . " " . $constraints . ")";
$constraints = $this->getConstraintDef($constraints); $this->db->query($statement);
}
public function DumpTable($table_name) {
$pg = $this->db->isPostgres();
$result/*: array*/ = array();
$data/*: array*/ = $this->db->fetchAllArray("SELECT * FROM ".$table_name.";");
if (!$pg) {
$table_fields = $this->TableInfo($table_name);
foreach ($table_fields as $name => $value) {
$type = strtolower($value['type']);
if ($type == "boolean") {
foreach ($data as &$row) {
$row/*: array*/ = $row;
if (isset($row[$name])) {
$row[$name] = boolval($row[$name]);
} }
$constraints = ", " . $constraints; }
} }
}
}
foreach ($data as $r) {
$result[] = array(
"type" => "insert",
"table_name" => $table_name,
"values" => $r
);
}
return $result;
}
$statement = "CREATE TABLE $table (" . implode(",", public function GetAllTableNames() {
array_map(function($name,$data) use ($pg) { $result = [];
return $this->Column_Definition($name,$data,$pg); if ($this->db->isPostgres()) {
}, array_keys($fields), array_values($fields)) $query = "SELECT table_name as name FROM information_schema.tables WHERE table_schema='public'";
) . " " . $constraints . ")"; } else {
$this->db->query($statement); $query = "SELECT * FROM sqlite_master WHERE type='table'";
} }
$tables = $this->db->fetchAllArray($query);
foreach ($tables as $table) {
$result[] = $table['name'];
}
return $result;
}
public function DumpTable($table_name) { public function DumpInserts() {
$pg = $this->db->isPostgres(); $table_names = $this->GetAllTableNames();
$result = array();
$result/*: array*/ = array(); foreach ($table_names as $table_name) {
$data/*: array*/ = $this->db->fetchAllArray("SELECT * FROM ".$table_name.";"); $result = array_merge($result, $this->DumpTable($table_name));
}
if (!$pg) { return $result;
$table_fields = $this->TableInfo($table_name); }
foreach ($table_fields as $name => $value) { }
$type = strtolower($value['type']);
if ($type == "boolean") {
foreach ($data as &$row) {
$row/*: array*/ = $row;
if (isset($row[$name])) {
$row[$name] = boolval($row[$name]);
}
}
}
}
}
foreach ($data as $r) {
$result[] = array(
"type" => "insert",
"table_name" => $table_name,
"values" => $r
);
}
return $result;
}
public function GetAllTableNames() {
$result = [];
if ($this->db->isPostgres()) {
$query = "SELECT table_name as name FROM information_schema.tables WHERE table_schema='public'";
} else {
$query = "SELECT * FROM sqlite_master WHERE type='table'";
}
$tables = $this->db->fetchAllArray($query);
foreach ($tables as $table) {
$result[] = $table['name'];
}
return $result;
}
public function DumpInserts() {
$table_names = $this->GetAllTableNames();
$result = array();
foreach ($table_names as $table_name) {
$result = array_merge($result, $this->DumpTable($table_name));
}
return $result;
}
}