db = $db; } public function ExecuteAction(/*.array.*/$action, $db_file = "") { switch($action["type"]) { case "dropTable": $this->DropTableQuery($action["table_name"], true); break; case "createTable": $constraints = isset($action["constraints"]) ? $action["constraints"] : NULL; $this->CreateTableQuery($action["table_name"], $action["fields"], $constraints); break; case "addColumn": $this->AddColumn($action["table_name"], $action["column_name"], $action["field"]); break; case "insert": $this->db->insertQuery($action["table_name"], $action["values"]); break; case "alterReference": $this->AlterReference($action["table"], $action["column"], $action["refTable"], $action["refColumn"]); break; case "renameColumn": $this->RenameColumn($action["table"], $action["old_name"], $action["new_name"]); break; case "executeFile": if ($this->db->isPostgres() && isset($action["pgsql"])) { $file = $action["pgsql"]; } else { $file = $action["source"]; } $stmtList = Tools_SQLStatementExtractor::extractFile(Path::join(dirname($db_file), $file)); foreach($stmtList as $stmt) { $this->db->executeQuery($stmt); } break; default: throw new Exception("unknown action ". $action["type"] . PHP_EOL); } } public function DropTableQuery($table, $cascade=false) { $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) { $this->db->query("ALTER TABLE ".$table." ADD CONSTRAINT ".$table."_".$column."fk"." FOREIGN KEY (".$column.") REFERENCES ".$refTable." (".$refColumn.")"); } //Извлечение информации о полях таблицы public function TableInfo($table) { $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(); 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); if (isset($table_info[$new_name])) { return; } /*.array.*/$data = $this->DumpTable($table); $this->db->query("ALTER TABLE ".$table." RENAME TO ".$tmp_table.";"); $table_info[$new_name] = $table_info[$old_name]; unset($table_info[$old_name]); $this->CreateTableQuery($table,$table_info,null); foreach ($data as $row) { $values = $row['values']; $values[$new_name] = $values[$old_name]; unset($values[$old_name]); $this->db->insertQuery($table, $values); } $this->DropTableQuery($tmp_table); } } //Обновление ключа serial после ручной вставки public function UpdateSerial($table,$column) { $this->db->query("SELECT setval(pg_get_serial_sequence('".$table."', '".$column."'), coalesce(max(".$column."),0) + 1, false) FROM ".$table); } public function Column_Definition($name,$data,$pg){ $constraint = isset($data['constraint'])?" ".$data['constraint']:""; $references = ""; 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){ $pg = $this->db->isPostgres(); $q = "ALTER TABLE ".$table_name." ADD COLUMN ". $this->Column_Definition($column_name, $field, $pg); $this->db->query($q); } function getConstraintDef($c) { 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; } $statement = "CREATE TABLE $table (" . implode(",", array_map(function($name,$data) use ($pg) { return $this->Column_Definition($name,$data,$pg); }, array_keys($fields), array_values($fields)) ) . " " . $constraints . ")"; $this->db->query($statement); } public function DumpTable($table_name) { $pg = $this->db->isPostgres(); /*.array.*/$result = array(); /*.array.*/$data = $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) { /*.array.*/$row = $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; } }