Add sqlMigrate tool
git-svn-id: https://svn.fournier38.fr/svn/ProgSVN/trunk@3855 bf3deb0d-5f1a-0410-827f-c0cc1f45334c
This commit is contained in:
340
tools/sqlMigrate.php
Executable file
340
tools/sqlMigrate.php
Executable file
@@ -0,0 +1,340 @@
|
||||
#!/usr/bin/php
|
||||
<?php
|
||||
// SQL Migrate
|
||||
|
||||
/** SQL Migrate
|
||||
* This tool allow to migrate the data from one SQL database to another.
|
||||
* The DB engine can also be different.
|
||||
*
|
||||
* You must have the source DB and create a target DB with all the destination
|
||||
* tables available.
|
||||
* You must pass one parameter : the configuration file where all the
|
||||
* informations will be stored. The file will be created if it doesn't exists.
|
||||
*/
|
||||
|
||||
require_once ("domframework/dblayeroo.php");
|
||||
require_once ("domframework/getopts.php");
|
||||
require_once ("domframework/config.php");
|
||||
|
||||
class configuration extends \config
|
||||
{
|
||||
function __construct ()
|
||||
{
|
||||
$this->default = array (
|
||||
"db" => array (
|
||||
"srcDSN" => "",
|
||||
"srcUser" => null,
|
||||
"srcPassword" => null,
|
||||
"dstDSN" => "",
|
||||
"dstUser" => null,
|
||||
"dstPassword" => null,
|
||||
),
|
||||
"tables" => array (
|
||||
),
|
||||
"tablesRel" => array (
|
||||
),
|
||||
"fields" => array (
|
||||
),
|
||||
"tablesOrder" => array (
|
||||
),
|
||||
"deleteContent" => null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class main
|
||||
{
|
||||
public function __construct ()
|
||||
{
|
||||
global $argv;
|
||||
if (! key_exists (1, $argv))
|
||||
throw new Exception ("No configuration file provided");
|
||||
if (! file_exists ($argv[1]))
|
||||
touch ($argv[1]);
|
||||
$config = new configuration ();
|
||||
$config->confFile = $argv[1];
|
||||
$db = $config->get ("db");
|
||||
$tables = $config->get ("tables");
|
||||
$tablesRel = $config->get ("tablesRel");
|
||||
$fields = $config->get ("fields");
|
||||
$tablesOrder = $config->get ("tablesOrder");
|
||||
$deleteContent = $config->get ("deleteContent");
|
||||
if ($db["srcDSN"] === "" || $db["dstDSN"] === "")
|
||||
$step = 0;
|
||||
elseif (empty ($tables))
|
||||
$step = 1;
|
||||
elseif (empty ($tablesRel))
|
||||
$step = 2;
|
||||
elseif (empty ($fields))
|
||||
$step = 3;
|
||||
elseif (empty ($tablesOrder))
|
||||
$step = 4;
|
||||
elseif ($deleteContent === null)
|
||||
$step = 5;
|
||||
else
|
||||
$step = 6;
|
||||
|
||||
// TODO : Overload here the steps with the getopts
|
||||
if ($step === 0)
|
||||
{
|
||||
// Step 0: ask the connection parameters
|
||||
echo "#### Entering step 0 : ask the connection parameters\n";
|
||||
$srcDSN = $this->ask ("Enter SRC DSN");
|
||||
$srcUser = $this->ask ("Enter SRC User");
|
||||
$srcPassword = $this->ask ("Enter SRC Password");
|
||||
$dstDSN = $this->ask ("Enter DST DSN");
|
||||
$dstUser = $this->ask ("Enter DST User");
|
||||
$dstPassword = $this->ask ("Enter DST Password");
|
||||
if ($srcUser === "")
|
||||
{
|
||||
$srcUser = null;
|
||||
$srcPassword = null;
|
||||
}
|
||||
if ($dstUser === "")
|
||||
{
|
||||
$dstUser = null;
|
||||
$dstPassword = null;
|
||||
}
|
||||
$value = array (
|
||||
"srcDSN" => $srcDSN,
|
||||
"srcUser" => $srcUser,
|
||||
"srcPassword" => $srcPassword,
|
||||
"dstDSN" => $dstDSN,
|
||||
"dstUser" => $dstUser,
|
||||
"dstPassword" => $dstPassword,);
|
||||
$config->set ("db", $value);
|
||||
$db = $config->get ("db");
|
||||
$step++;
|
||||
}
|
||||
if ($step === 1)
|
||||
{
|
||||
echo "#### Entering step 1: read the existing databases schemes\n";
|
||||
$srcDB = new dblayeroo ($db["srcDSN"], $db["srcUser"],
|
||||
$db["srcPassword"]);
|
||||
$srcTables = $srcDB->listTables ();
|
||||
$dstDB = new dblayeroo ($db["dstDSN"], $db["dstUser"],
|
||||
$db["dstPassword"]);
|
||||
$dstTables = $dstDB->listTables ();
|
||||
$value = array (
|
||||
"srcTables" => $srcTables,
|
||||
"dstTables" => $dstTables,
|
||||
);
|
||||
$config->set ("tables", $value);
|
||||
$step++;
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
echo "#### Connect to the defined databases\n";
|
||||
$srcDB = new dblayeroo ($db["srcDSN"], $db["srcUser"],
|
||||
$db["srcPassword"]);
|
||||
$dstDB = new dblayeroo ($db["dstDSN"], $db["dstUser"],
|
||||
$db["dstPassword"]);
|
||||
echo "\n";
|
||||
|
||||
if ($step == 2)
|
||||
{
|
||||
echo "#### Entering step 2: ask the relations between tables\n";
|
||||
$tables = $config->get ("tables");
|
||||
echo "# For each destination table, please provide the source table\n";
|
||||
sort ($tables["srcTables"]);
|
||||
echo "# Allowed source tables : ". implode (", ", $tables["srcTables"]).
|
||||
"\n";
|
||||
$value = array ();
|
||||
natsort ($tables["dstTables"]);
|
||||
foreach ($tables["dstTables"] as $table)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
$value[$table] = $this->ask (
|
||||
"Destination Table '$table' filled by source table");
|
||||
if (in_array ($value[$table], $tables["srcTables"]) ||
|
||||
$value[$table] === "")
|
||||
continue 2;
|
||||
$this->error ("The table '".$value[$table]." doesn't exists");
|
||||
echo "# Allowed source tables : ".
|
||||
implode (", ", $tables["srcTables"])."\n";
|
||||
}
|
||||
}
|
||||
$config->set ("tablesRel", $value);
|
||||
$step++;
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
$tablesRel = $config->get ("tablesRel");
|
||||
if ($step === 3)
|
||||
{
|
||||
echo "#### Entering step 3: ask the relation between fields\n";
|
||||
echo "# For each destination table, if a source table is provided,\n".
|
||||
"# for each field, provide the source SQL request (can be field,\n".
|
||||
"# CONCAT(), text between simple quotes, null...)\n";
|
||||
$value = array ();
|
||||
foreach ($tablesRel as $dstTable=>$srcTable)
|
||||
{
|
||||
echo "- Destination Table '$dstTable': ";
|
||||
if ($srcTable === "")
|
||||
{
|
||||
echo "No source table. SKIPPED\n";
|
||||
continue;
|
||||
}
|
||||
echo "\n";
|
||||
$dstSchema = $dstDB->getTableSchema ($dstTable);
|
||||
$srcSchema = $srcDB->getTableSchema ($srcTable);
|
||||
echo " List of the allowed fields: ".
|
||||
implode (", ", array_keys ($srcSchema["fields"]))."\n";
|
||||
foreach (array_keys ($dstSchema["fields"]) as $field)
|
||||
{
|
||||
if (! key_exists ($dstTable, $value))
|
||||
$value[$dstTable] = array ();
|
||||
if (! key_exists ($field, $value[$dstTable]))
|
||||
$value[$dstTable][$field] = "";
|
||||
while (1)
|
||||
{
|
||||
$value[$dstTable][$field] = $this->ask (
|
||||
"Destination Table '$dstTable' field '$field'");
|
||||
if (trim ($value[$dstTable][$field]) !== "")
|
||||
continue 2;
|
||||
// TODO : Check if the field is provided (think about function)
|
||||
$this->error ("The field source must be defined !");
|
||||
}
|
||||
}
|
||||
$config->set ("fields", $value);
|
||||
}
|
||||
$step++;
|
||||
echo "\n";
|
||||
}
|
||||
$fields = $config->get ("fields");
|
||||
if ($step === 4)
|
||||
{
|
||||
echo "#### Entering step 4: Order the tables by foreign keys\n";
|
||||
echo "# The tables with foreign keys constraint must be populated \n".
|
||||
"# after the tables depending of them\n";
|
||||
$tablesToOrder = array ();
|
||||
foreach ($tablesRel as $dstTable=>$srcTable)
|
||||
{
|
||||
if ($srcTable !== "")
|
||||
$tablesToOrder[] = $dstTable;
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
echo "# Here are the tables to order: ". implode (",", $tablesToOrder).
|
||||
"\n";
|
||||
$tablesOrder = $this->ask ("Order the tables, separated by commas");
|
||||
$tablesOrder = explode (",", $tablesOrder);
|
||||
if (count ($tablesToOrder) === count ($tablesOrder))
|
||||
break;
|
||||
$this->error ("Not all the tables are ordered");
|
||||
}
|
||||
$config->set ("tablesOrder", $tablesOrder);
|
||||
$step++;
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
$tablesOrder = $config->get ("tablesOrder");
|
||||
if ($step === 5)
|
||||
{
|
||||
if ($deleteContent === null)
|
||||
{
|
||||
echo "# The content of the destination tables can be deleted before\n".
|
||||
"# adding the source data.\n";
|
||||
echo "# Attention: the order is important if there is foreign keys!\n".
|
||||
"# Remove the most constraint tables before deleting the less\n".
|
||||
"# constraint ones\n";
|
||||
echo "# List of the destination tables: ".
|
||||
implode (",", array_keys (array_diff ($tablesRel, array ("")))).
|
||||
"\n";
|
||||
$deleteContent = $this->ask ("Get the list of the tables to be ".
|
||||
"cleaned, separated by comas");
|
||||
$config->set ("deleteContent", $deleteContent);
|
||||
$deleteContent = $config->get ("deleteContent");
|
||||
}
|
||||
}
|
||||
if ($deleteContent !== "")
|
||||
{
|
||||
echo "#### Entering step 5: delete tables content\n";
|
||||
foreach (explode (",", $deleteContent) as $table)
|
||||
{
|
||||
$table = trim ($table);
|
||||
echo "- Delete content of table '$table'\n";
|
||||
$dstDB->table ($table)->unique (array ())->delete ()->execute ();
|
||||
}
|
||||
$step = 6;
|
||||
echo "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "#### Entering step5: Skipped delete tables content\n";
|
||||
$step = 6;
|
||||
}
|
||||
if ($step === 6)
|
||||
{
|
||||
echo "#### Entering step 6: populate the destination database with the ".
|
||||
"configured data\n";
|
||||
foreach ($tablesOrder as $dstTable)
|
||||
{
|
||||
echo "- Populate table $dstTable:\n";
|
||||
// Create the source SQL request. Add the alias name of the destination
|
||||
// table to allow to import directely in the destination
|
||||
$sql = "SELECT ";
|
||||
$i = 0;
|
||||
foreach ($fields[$dstTable] as $key=>$val)
|
||||
{
|
||||
if ($i > 0)
|
||||
$sql .= ",";
|
||||
$sql .= "$val AS $key";
|
||||
$i++;
|
||||
}
|
||||
$sql .= " FROM ".$tablesRel[$dstTable];
|
||||
echo " $sql\n";
|
||||
echo " ";
|
||||
// Define all the variables needed to insert
|
||||
$dstSchema = $dstDB->getTableSchema ($dstTable);
|
||||
$dstDB->table ($dstTable);
|
||||
$dstDB->fields ($dstSchema["fields"]);
|
||||
$dstDB->primary ($dstSchema["primary"]);
|
||||
foreach ($srcDB->directQuery ($sql) as $row)
|
||||
{
|
||||
echo ".";
|
||||
//print_r ($row);
|
||||
$dstDB->clearRequest ();
|
||||
$dstDB->insert ()
|
||||
->setValues ($row)
|
||||
->execute ();
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
$step++;
|
||||
}
|
||||
// Last step
|
||||
echo "#### Entering step $step: End of process\n";
|
||||
}
|
||||
|
||||
/** Ask a question to the user and return the answer
|
||||
* @param string $question the question message
|
||||
* @return string The answer
|
||||
*/
|
||||
public function ask ($question)
|
||||
{
|
||||
echo "$question: ";
|
||||
$fp = fopen ("php://stdin", "r");
|
||||
$rc = trim (fgets ($fp));
|
||||
return $rc;
|
||||
}
|
||||
|
||||
/** Display an error to the user
|
||||
* @param string $msg The error message to display
|
||||
*/
|
||||
public function error ($msg)
|
||||
{
|
||||
file_put_contents ("php://stderr", "ERROR: ".$msg."\n");
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$main = new \main ();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
file_put_contents ("php://stderr", $e->getMessage ()."\n");
|
||||
exit (4);
|
||||
}
|
||||
Reference in New Issue
Block a user