diff --git a/Tests/Mysql/MysqlExporterTest.php b/Tests/Mysql/MysqlExporterTest.php
index c099e9a82..98d0db59d 100644
--- a/Tests/Mysql/MysqlExporterTest.php
+++ b/Tests/Mysql/MysqlExporterTest.php
@@ -13,7 +13,7 @@
*
* @since 1.0
*/
-class MysqlExporterText extends TestCase
+class MysqlExporterTest extends TestCase
{
/**
* @var object The mocked database object for use by test methods.
@@ -215,7 +215,7 @@ public function test__toString()
-
+
';
@@ -278,7 +278,7 @@ public function testBuildXml()
-
+
';
@@ -318,7 +318,7 @@ public function testBuildXmlStructure()
' ',
' ',
' ',
+ 'Null="" Index_type="BTREE" Sub_part="" Comment="" />',
' '
)
),
diff --git a/Tests/Pgsql/PgsqlDriverTest.php b/Tests/Pgsql/PgsqlDriverTest.php
index 41a6a6e64..c646e7107 100644
--- a/Tests/Pgsql/PgsqlDriverTest.php
+++ b/Tests/Pgsql/PgsqlDriverTest.php
@@ -360,24 +360,28 @@ public function testGetTableKeys()
$pkey->idxName = 'assets_pkey';
$pkey->isPrimary = true;
$pkey->isUnique = true;
+ $pkey->indKey = '1';
$pkey->Query = 'ALTER TABLE assets ADD PRIMARY KEY (id)';
$asset = new \stdClass;
$asset->idxName = 'idx_asset_name';
$asset->isPrimary = false;
$asset->isUnique = true;
+ $asset->indKey = '6';
$asset->Query = 'CREATE UNIQUE INDEX idx_asset_name ON assets USING btree (name)';
$lftrgt = new \stdClass;
$lftrgt->idxName = 'assets_idx_lft_rgt';
$lftrgt->isPrimary = false;
$lftrgt->isUnique = false;
+ $lftrgt->indKey = '3 4';
$lftrgt->Query = 'CREATE INDEX assets_idx_lft_rgt ON assets USING btree (lft, rgt)';
$id = new \stdClass;
$id->idxName = 'assets_idx_parent_id';
$id->isPrimary = false;
$id->isUnique = false;
+ $id->indKey = '2';
$id->Query = 'CREATE INDEX assets_idx_parent_id ON assets USING btree (parent_id)';
$this->assertThat(self::$driver->getTableKeys('assets'), $this->equalTo(array($pkey, $id, $lftrgt, $asset)), __LINE__);
diff --git a/Tests/Pgsql/PgsqlExporterTest.php b/Tests/Pgsql/PgsqlExporterTest.php
index 645fdce08..dc6f7d0d6 100644
--- a/Tests/Pgsql/PgsqlExporterTest.php
+++ b/Tests/Pgsql/PgsqlExporterTest.php
@@ -48,6 +48,9 @@ protected function setup()
'getTableColumns',
'getTableKeys',
'getTableSequences',
+ 'getSequenceLastValue',
+ 'getSequenceIsCalled',
+ 'getNamesKey',
'getVersion',
'quoteName',
'loadObjectList',
@@ -77,28 +80,28 @@ protected function setup()
'column_name' => 'id',
'type' => 'integer',
'null' => 'NO',
- 'default' => 'nextval(\'jos_dbtest_id_seq\'::regclass)',
+ 'Default' => 'nextval(\'jos_dbtest_id_seq\'::regclass)',
'comments' => '',
),
(object) array(
'column_name' => 'title',
'type' => 'character varying(50)',
'null' => 'NO',
- 'default' => 'NULL',
+ 'Default' => 'NULL',
'comments' => '',
),
(object) array(
'column_name' => 'start_date',
'type' => 'timestamp without time zone',
'null' => 'NO',
- 'default' => 'NULL',
+ 'Default' => 'NULL',
'comments' => '',
),
(object) array(
'column_name' => 'description',
'type' => 'text',
'null' => 'NO',
- 'default' => 'NULL',
+ 'Default' => 'NULL',
'comments' => '',
)
)
@@ -116,12 +119,43 @@ protected function setup()
'idxName' => 'jos_dbtest_pkey',
'isPrimary' => 'TRUE',
'isUnique' => 'TRUE',
+ 'indKey' => '1',
'Query' => 'ALTER TABLE "jos_dbtest" ADD PRIMARY KEY (id)',
)
)
)
);
+ $this->dbo->expects(
+ $this->any()
+ )
+ ->method('getNamesKey')
+ ->will(
+ $this->returnValue(
+ 'id'
+ )
+ );
+
+ $this->dbo->expects(
+ $this->any()
+ )
+ ->method('getSequenceLastValue')
+ ->will(
+ $this->returnValue(
+ '1'
+ )
+ );
+
+ $this->dbo->expects(
+ $this->any()
+ )
+ ->method('getSequenceIsCalled')
+ ->will(
+ $this->returnValue(
+ 'f'
+ )
+ );
+
/* Check if database is at least 9.1.0 */
$this->dbo->expects(
$this->any()
@@ -243,7 +277,7 @@ public function test__toString()
// Set up the export settings.
$instance
->setDbo($this->dbo)
- ->from('jos_test')
+ ->from('jos_dbtest')
->withStructure(true);
/* Depending on which version is running, 9.1.0 or older */
@@ -252,14 +286,14 @@ public function test__toString()
$expecting = '
-
-
+
+
-
+
';
@@ -314,7 +348,7 @@ public function testBuildXml()
// Set up the export settings.
$instance
->setDbo($this->dbo)
- ->from('jos_test')
+ ->from('jos_dbtest')
->withStructure(true);
/* Depending on which version is running, 9.1.0 or older */
@@ -323,14 +357,14 @@ public function testBuildXml()
$expecting = '
-
-
+
+
-
+
';
@@ -359,7 +393,7 @@ public function testBuildXmlStructure()
// Set up the export settings.
$instance
->setDbo($this->dbo)
- ->from('jos_test')
+ ->from('jos_dbtest')
->withStructure(true);
/* Depending on which version is running, 9.1.0 or older */
@@ -369,14 +403,14 @@ public function testBuildXmlStructure()
$instance->buildXmlStructure(),
$this->equalTo(
array(
- ' ',
- ' ',
+ ' ',
+ ' ',
' ',
' ',
' ',
' ',
- ' ',
+ ' ',
' '
)
),
@@ -542,8 +576,8 @@ public function testGetGenericTableName()
$instance->setDbo($this->dbo);
$this->assertThat(
- $instance->getGenericTableName('jos_test'),
- $this->equalTo('#__test'),
+ $instance->getGenericTableName('jos_dbtest'),
+ $this->equalTo('#__dbtest'),
'The testGetGenericTableName should replace the database prefix with #__.'
);
}
diff --git a/Tests/Pgsql/PgsqlImporterTest.php b/Tests/Pgsql/PgsqlImporterTest.php
index 5a212ce07..7d4a2fb91 100644
--- a/Tests/Pgsql/PgsqlImporterTest.php
+++ b/Tests/Pgsql/PgsqlImporterTest.php
@@ -48,6 +48,7 @@ public function setup()
'getTableColumns',
'getTableKeys',
'getTableSequences',
+ 'getSetvalSequenceSql',
'getVersion',
'quote',
'quoteName',
@@ -103,12 +104,14 @@ public function setup()
'Index' => 'jos_dbtest_pkey',
'is_primary' => 'TRUE',
'is_unique' => 'TRUE',
+ 'Key_name' => 'id',
'Query' => 'ALTER TABLE jos_dbtest ADD PRIMARY KEY (id)',
),
(object) array(
'Index' => 'jos_dbtest_idx_name',
'is_primary' => 'FALSE',
'is_unique' => 'FALSE',
+ 'Key_name' => 'name',
'Query' => 'CREATE INDEX jos_dbtest_idx_name ON jos_dbtest USING btree (name)',
)
)
@@ -261,26 +264,27 @@ public function dataGetAlterTableSql()
$f3 = '';
$f2_def = '';
- $k1 = '';
- $k2 = '';
+ $k2 = '';
- $k3 = '';
- $k4 = '';
- $pk = '';
$s1 = '';
$s2 = '';
+ 'Max_Value="9223372036854775807" Last_Value="1" Increment="1" Cycle_option="NO" />';
- $addSequence = 'CREATE SEQUENCE jos_dbtest_title_seq INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 ' .
+ $addSequence = 'CREATE SEQUENCE IF NOT EXISTS jos_dbtest_title_seq INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 ' .
'NO CYCLE OWNED BY "public.jos_dbtest.title"';
+ $setValSequence = 'SELECT setval(\'jos_dbtest_title_seq\', 1, FALSE)';
$changeCol = 'ALTER TABLE "jos_test" ALTER COLUMN "title" TYPE character ' .
"varying(50),\nALTER COLUMN \"title\" SET NOT NULL,\nALTER COLUMN \"title\" SET DEFAULT 'add default'";
- $changeSeq = 'CREATE SEQUENCE jos_dbtest_title_seq INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 ' .
+ $changeSeq = 'CREATE SEQUENCE IF NOT EXISTS jos_dbtest_title_seq INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 ' .
'START 1 NO CYCLE OWNED BY "public.jos_dbtest.title"';
return array(
@@ -317,6 +321,7 @@ public function dataGetAlterTableSql()
new \SimpleXmlElement('' . $s1 . $s2 . $f1 . $f2 . $k1 . $k2 . ''),
array(
$addSequence,
+ $setValSequence,
),
'getAlterTableSQL should add the new sequence.'
),
@@ -371,6 +376,7 @@ public function dataGetAlterTableSql()
new \SimpleXmlElement('' . $s2 . $f1 . $f2 . $k1 . $k2 . ''),
array(
$changeSeq,
+ $setValSequence,
'DROP SEQUENCE "jos_dbtest_id_seq"',),
'getAlterTableSQL should change sequence.'
),
@@ -406,9 +412,9 @@ public function dataGetColumnSql()
{
$sample = array(
'xml-id-field' => '',
- 'xml-title-field' => '',
+ 'xml-title-field' => '',
'xml-title-def' => '',
- 'xml-body-field' => '',);
+ 'xml-body-field' => '',);
return array(
array(
@@ -602,7 +608,7 @@ public function testGetAddColumnSql()
$instance->setDbo($this->dbo);
$sample = array(
- 'xml-title-field' => '',
+ 'xml-title-field' => '',
'xml-title-def' => '',
'xml-int-defnum' => '',);
@@ -662,7 +668,7 @@ public function testGetAddSequenceSql()
new \SimpleXmlElement($xmlIdSeq)
),
$this->equalTo(
- 'CREATE SEQUENCE jos_dbtest_id_seq INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 NO CYCLE OWNED BY "public.jos_dbtest.id"'
+ 'CREATE SEQUENCE IF NOT EXISTS jos_dbtest_id_seq INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 NO CYCLE OWNED BY "public.jos_dbtest.id"'
),
'getAddSequenceSQL did not yield the expected result.'
);
@@ -677,9 +683,9 @@ public function testGetAddSequenceSql()
*/
public function testGetAddIndexSql()
{
- $xmlIndex = '';
- $xmlPrimaryKey = '';
$instance = new PgsqlImporterInspector;
diff --git a/composer.json b/composer.json
index 22990a6a6..f4e4ad28b 100644
--- a/composer.json
+++ b/composer.json
@@ -12,6 +12,9 @@
},
"require-dev": {
"joomla/coding-standards": "~2.0@alpha",
+ "joomla/console": "~2.0",
+ "joomla/archive": "~2.0@dev",
+ "joomla/filesystem": "~2.0@dev",
"joomla/di": "~1.0|~2.0",
"joomla/registry": "^1.4.5|~2.0",
"joomla/test": "~1.2",
@@ -19,6 +22,7 @@
"phpunit/dbunit": "~3.0"
},
"suggest": {
+ "joomla/console": "If you want to use the ExporterCommand and ImporterCommand classes, please install joomla/console:~2.0",
"joomla/di": "To use the Database ServiceProviderInterface objects, install joomla/di.",
"joomla/registry": "To use the Database ServiceProviderInterface objects, install joomla/registry.",
"ext-mysqli": "To connect to a MySQL database via MySQLi",
diff --git a/src/Command/ExportCommand.php b/src/Command/ExportCommand.php
new file mode 100644
index 000000000..3309d0719
--- /dev/null
+++ b/src/Command/ExportCommand.php
@@ -0,0 +1,159 @@
+db = $db;
+
+ parent::__construct();
+ }
+
+ /**
+ * Internal function to execute the command.
+ *
+ * @param InputInterface $input The input to inject into the command.
+ * @param OutputInterface $output The output to inject into the command.
+ *
+ * @return integer The command exit code
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ protected function doExecute(InputInterface $input, OutputInterface $output): int
+ {
+ $symfonyStyle = new SymfonyStyle($input, $output);
+
+ $symfonyStyle->title('Exporting Database');
+
+ $total_time = microtime(true);
+ $date = getDate();
+ $dateFormat = sprintf("%s-%02s-%02s",$date['year'], $date['mon'], $date['mday']);
+
+ $tables = $this->db->getTableList();
+ $prefix = $this->db->getPrefix();
+ $exp = $this->db->getExporter()->withStructure();
+
+ $folderPath = $input->getOption('folder');
+ $tableName = $input->getOption('table');
+ $all = $input->getOption('all');
+ $zip = $input->getOption('zip');
+
+ $zipFile = $folderPath . '/' . 'data_exported_' . $dateFormat . '.zip';
+
+ if (($tableName == null) && ($all == null))
+ {
+ $symfonyStyle->warning("Either the --table or --all option must be specified");
+ return 1;
+ }
+
+ if($tableName)
+ {
+ if (!\in_array($tableName, $tables))
+ {
+ $symfonyStyle->error($tableName . ' does not exist in the database.');
+ return 1;
+ }
+ $tables = array($tableName);
+ }
+
+ if ($zip)
+ {
+ $archive = new Archive;
+ $zipArchive = $archive->getAdapter('zip');
+ }
+
+ foreach ($tables as $table)
+ {
+ if (strpos(substr($table, 0, strlen($prefix)), $prefix) !== false)
+ {
+ $task_i_time = microtime(true);
+ $filename = $folderPath . '/' . $table . '.xml';
+ $symfonyStyle->newLine();
+ $symfonyStyle->text('Exporting ' . $table . '....');
+ $data = (string) $exp->from($table)->withData(true);
+
+ if (file_exists($filename))
+ {
+ File::delete($filename);
+ }
+
+ File::write($filename, $data);
+
+ if ($zip)
+ {
+ $zipFilesArray[] = array('name' => $table . '.xml', 'data' => $data);
+ $zipArchive->create($zipFile, $zipFilesArray);
+ File::delete($filename);
+ }
+
+ $symfonyStyle->text('Exported in ' . round(microtime(true) - $task_i_time, 3));
+ }
+ }
+
+ $symfonyStyle->text('Total time: ' . round(microtime(true) - $total_time, 3));
+ $symfonyStyle->success('Finished Exporting Database');
+
+ return 0;
+ }
+
+ /**
+ * Configure the command.
+ *
+ * @return void
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ protected function configure()
+ {
+ $this->setDescription('Exports the database');
+ $this->addOption('folder', null, InputOption::VALUE_OPTIONAL, 'Dump in folder path', '.');
+ $this->addOption('table', null, InputOption::VALUE_REQUIRED, 'Dump table name');
+ $this->addOption('all', null, InputOption::VALUE_NONE, 'Dump all tables');
+ $this->addOption('zip', null, InputOption::VALUE_NONE, 'Dump in zip format');
+ }
+}
diff --git a/src/Command/ImportCommand.php b/src/Command/ImportCommand.php
new file mode 100644
index 000000000..8df689e55
--- /dev/null
+++ b/src/Command/ImportCommand.php
@@ -0,0 +1,172 @@
+db = $db;
+
+ parent::__construct();
+ }
+
+ /**
+ * Internal function to execute the command.
+ *
+ * @param InputInterface $input The input to inject into the command.
+ * @param OutputInterface $output The output to inject into the command.
+ *
+ * @return integer The command exit code
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ protected function doExecute(InputInterface $input, OutputInterface $output): int
+ {
+ $symfonyStyle = new SymfonyStyle($input, $output);
+
+ $symfonyStyle->title('Importing Database');
+
+ $total_time = microtime(true);
+
+ $folderPath = $input->getOption('folder');
+ $tableName = $input->getOption('table');
+ $all = $input->getOption('all');
+
+ $tables = Folder::files($folderPath, '\.xml$');
+
+ if (($tableName == null) && ($all == null))
+ {
+ $symfonyStyle->warning("Either the --table or --all option must be specified");
+ return 1;
+ }
+
+ if ($tableName)
+ {
+ $tables = array($tableName . '.xml');
+ }
+
+ foreach ($tables as $table)
+ {
+ $task_i_time = microtime(true);
+ $percorso = $folderPath . '/' . $table;
+
+ // Check file
+ if (!file_exists($percorso))
+ {
+ $symfonyStyle->error('Not Found ' . $table . '....');
+ return 1;
+ }
+
+ $table_name = str_replace('.xml', '', $table);
+ $symfonyStyle->text('Importing ' . $table_name . ' from ' . $table);
+
+ try
+ {
+ $imp = $this->db->getImporter()->from(file_get_contents($percorso))->withStructure()->asXml();
+ }
+ catch (\Exception $e)
+ {
+ $symfonyStyle->error('Error on getImporter' . $table . ' ' . $e);
+ return 1;
+ }
+
+ $symfonyStyle->text('Reading data from ' . $table);
+
+ try
+ {
+ $symfonyStyle->text('Drop ' . $table_name);
+ $this->db->dropTable($table_name, true);
+ }
+ catch (ExecutionFailureException $e)
+ {
+ $symfonyStyle->error(' Error in DROP TABLE ' . $table_name . ' ' . $e);
+ return 1;
+ }
+
+ try
+ {
+ $imp->mergeStructure();
+ }
+ catch (\Exception $e)
+ {
+ $symfonyStyle->error('Error on mergeStructure' . $table . ' ' . $e);
+ return 1;
+ }
+
+ $symfonyStyle->text('Checked structure ' . $table);
+
+ try
+ {
+ $imp->importData();
+ }
+ catch (\Exception $e)
+ {
+ $symfonyStyle->error('Error on importData' . $table . ' ' . $e);
+ return 1;
+ }
+ $symfonyStyle->text('Data loaded ' . $table . ' in ' . round(microtime(true) - $task_i_time, 3));
+ $symfonyStyle->newLine();
+ }
+
+ $symfonyStyle->text('Total time: ' . round(microtime(true) - $total_time, 3));
+ }
+
+ /**
+ * Configure the command.
+ *
+ * @return void
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ protected function configure()
+ {
+ $this->setDescription('Import the database');
+ $this->addOption('folder', null, InputOption::VALUE_OPTIONAL, 'Import from folder path', '.');
+ $this->addOption('table', null, InputOption::VALUE_REQUIRED, 'Import table name');
+ $this->addOption('all', null, InputOption::VALUE_NONE, 'Import all files');
+ }
+}
diff --git a/src/DatabaseExporter.php b/src/DatabaseExporter.php
index de0c57b4b..7916fcde0 100644
--- a/src/DatabaseExporter.php
+++ b/src/DatabaseExporter.php
@@ -68,8 +68,9 @@ public function __construct()
// Set up the class defaults:
- // Export with only structure
+ // Export not only structure
$this->withStructure();
+ $this->withData();
// Export as xml.
$this->asXml();
@@ -233,4 +234,89 @@ public function withStructure($setting = true)
return $this;
}
+
+ /**
+ * Sets an internal option to export the data of the input table(s).
+ *
+ * @param boolean $setting True to export the data, false to not.
+ *
+ * @return $this
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function withData($setting = false)
+ {
+ $this->options->withData = (boolean) $setting;
+
+ return $this;
+ }
+
+ /**
+ * Builds the XML data to export.
+ *
+ * @return array An array of XML lines (strings).
+ *
+ * @since __DEPLOY_VERSION__
+ * @throws \Exception if an error occurs.
+ */
+ protected function buildXmlData()
+ {
+ $buffer = array();
+
+ foreach ($this->from as $table)
+ {
+ // Replace the magic prefix if found.
+ $table = $this->getGenericTableName($table);
+
+ // Get the details columns information.
+ $fields = $this->db->getTableColumns($table, false);
+ $colblob = array();
+
+ foreach ($fields as $field)
+ {
+ // Cacth blob for conversion xml
+ if ($field->Type == 'mediumblob')
+ {
+ $colblob[] = $field->Field;
+ }
+ }
+
+ $query = $this->db->getQuery(true);
+ $query->select($query->quoteName(array_keys($fields)))
+ ->from($query->quoteName($table));
+ $this->db->setQuery($query);
+
+ $rows = $this->db->loadObjectList();
+
+ if (!count($rows))
+ {
+ continue;
+ }
+
+ $buffer[] = ' ';
+
+ foreach ($rows as $row)
+ {
+ $buffer[] = ' ';
+
+ foreach ($row as $key => $value)
+ {
+ if (!in_array($key, $colblob))
+ {
+ $buffer[] = ' ' . htmlspecialchars($value, ENT_COMPAT, 'UTF-8') . '';
+ }
+ else
+ {
+ $buffer[] = ' ' . base64_encode($value) . '';
+ }
+ }
+
+ $buffer[] = '
';
+ }
+
+ $buffer[] = ' ';
+ }
+
+ return $buffer;
+ }
}
diff --git a/src/DatabaseImporter.php b/src/DatabaseImporter.php
index effd1a108..d709c6369 100644
--- a/src/DatabaseImporter.php
+++ b/src/DatabaseImporter.php
@@ -218,6 +218,53 @@ protected function getRealTableName($table)
return $table;
}
+ /**
+ * Import the data from the source into the existing tables.
+ *
+ * @return void
+ *
+ * @note Currently only supports XML format.
+ * @since __DEPLOY_VERSION__
+ * @throws \RuntimeException on error.
+ */
+ public function importData()
+ {
+ if ($this->from instanceof \SimpleXMLElement)
+ {
+ $xml = $this->from;
+ }
+ else
+ {
+ $xml = new \SimpleXMLElement($this->from);
+ }
+
+ // Get all the table definitions.
+ $xmlTables = $xml->xpath('database/table_data');
+
+ foreach ($xmlTables as $table)
+ {
+ // Convert the magic prefix into the real table name.
+ $tableName = $this->getRealTableName((string) $table['name']);
+
+ $rows = $table->children();
+
+ foreach ($rows as $row)
+ {
+ if ($row->getName() == 'row')
+ {
+ $entry = new \stdClass;
+
+ foreach ($row->children() as $data)
+ {
+ $entry->{(string) $data['name']} = (string) $data;
+ }
+
+ $this->db->insertObject($tableName, $entry);
+ }
+ }
+ }
+ }
+
/**
* Merges the incoming structure definition with the existing structure.
*
@@ -265,9 +312,16 @@ public function mergeStructure()
{
// This is a new table.
$sql = $this->xmlToCreate($table);
+ $queries = explode(';', (string) $sql);
- $this->db->setQuery((string) $sql);
- $this->db->execute();
+ foreach ($queries as $query)
+ {
+ if (!empty($query))
+ {
+ $this->db->setQuery((string) $query);
+ $this->db->execute();
+ }
+ }
}
}
}
diff --git a/src/Mysql/MysqlDriver.php b/src/Mysql/MysqlDriver.php
index 9713bafcb..89d5b9a89 100644
--- a/src/Mysql/MysqlDriver.php
+++ b/src/Mysql/MysqlDriver.php
@@ -473,6 +473,77 @@ public function renameTable($oldTable, $newTable, $backup = null, $prefix = null
return $this;
}
+ /**
+ * Inserts a row into a table based on an object's properties.
+ *
+ * @param string $table The name of the database table to insert into.
+ * @param object $object A reference to an object whose public properties match the table fields.
+ * @param string $key The name of the primary key. If provided the object property is updated.
+ *
+ * @return boolean
+ *
+ * @since __DEPLOY_VERSION__
+ * @throws \RuntimeException
+ */
+ public function insertObject($table, &$object, $key = null)
+ {
+ $fields = [];
+ $values = [];
+ $tableColumns = $this->getTableColumns($table);
+
+ // Iterate over the object variables to build the query fields and values.
+ foreach (get_object_vars($object) as $k => $v)
+ {
+ // Skip columns that don't exist in the table.
+ if (!array_key_exists($k, $tableColumns))
+ {
+ continue;
+ }
+
+ // Only process non-null scalars.
+ if (\is_array($v) || \is_object($v) || $v === null)
+ {
+ continue;
+ }
+
+ // Ignore any internal fields.
+ if ($k[0] === '_')
+ {
+ continue;
+ }
+
+ // Ignore null datetime fields.
+ if (($tableColumns[$k] == "datetime") && empty($v))
+ {
+ continue;
+ }
+
+ // Prepare and sanitize the fields and values for the database query.
+ $fields[] = $this->quoteName($k);
+ $values[] = $this->quote($v);
+ }
+
+ // Create the base insert statement.
+ $query = $this->getQuery(true)
+ ->insert($this->quoteName($table))
+ ->columns($fields)
+ ->values(implode(',', $values));
+
+ // Set the query and execute the insert.
+ $this->setQuery($query)->execute();
+
+ // Update the primary key if it exists.
+ $id = $this->insertid();
+
+ if ($key && $id && \is_string($key))
+ {
+ $object->$key = $id;
+ }
+
+ return true;
+ }
+
+
/**
* Method to escape a string for usage in an SQL statement.
*
diff --git a/src/Mysql/MysqlExporter.php b/src/Mysql/MysqlExporter.php
index 9201545e1..1f8c95346 100644
--- a/src/Mysql/MysqlExporter.php
+++ b/src/Mysql/MysqlExporter.php
@@ -33,7 +33,15 @@ protected function buildXml()
$buffer[] = '';
$buffer[] = ' ';
- $buffer = array_merge($buffer, $this->buildXmlStructure());
+ if ($this->options->withStructure)
+ {
+ $buffer = array_merge($buffer, $this->buildXmlStructure());
+ }
+
+ if ($this->options->withData)
+ {
+ $buffer = array_merge($buffer, $this->buildXmlData());
+ }
$buffer[] = ' ';
$buffer[] = '';
@@ -76,6 +84,7 @@ protected function buildXmlStructure()
$buffer[] = ' ';
}
diff --git a/src/Mysql/MysqlImporter.php b/src/Mysql/MysqlImporter.php
index 40a8d0208..fb8040d55 100644
--- a/src/Mysql/MysqlImporter.php
+++ b/src/Mysql/MysqlImporter.php
@@ -138,6 +138,7 @@ protected function getAlterTableSql(\SimpleXMLElement $structure)
&& ((string) $newLookup[$name][$i]['Column_name'] === $oldLookup[$name][$i]->Column_name)
&& ((string) $newLookup[$name][$i]['Seq_in_index'] === $oldLookup[$name][$i]->Seq_in_index)
&& ((string) $newLookup[$name][$i]['Collation'] === $oldLookup[$name][$i]->Collation)
+ && ((string) $newLookup[$name][$i]['Sub_part'] === $oldLookup[$name][$i]->Sub_part)
&& ((string) $newLookup[$name][$i]['Index_type'] === $oldLookup[$name][$i]->Index_type));
/*
@@ -155,6 +156,9 @@ protected function getAlterTableSql(\SimpleXMLElement $structure)
echo '
Collation: '.
((string) $newLookup[$name][$i]['Collation'] == $oldLookup[$name][$i]->Collation ? 'Pass' : 'Fail').' '.
(string) $newLookup[$name][$i]['Collation'].' vs '.$oldLookup[$name][$i]->Collation;
+ echo '
Sub_part: '.
+ ((string) $newLookup[$name][$i]['Sub_part'] == $oldLookup[$name][$i]->Sub_part ? 'Pass' : 'Fail').' '.
+ (string) $newLookup[$name][$i]['Sub_part'].' vs '.$oldLookup[$name][$i]->Sub_part;
echo '
Index_type: '.
((string) $newLookup[$name][$i]['Index_type'] == $oldLookup[$name][$i]->Index_type ? 'Pass' : 'Fail').' '.
(string) $newLookup[$name][$i]['Index_type'].' vs '.$oldLookup[$name][$i]->Index_type;
@@ -255,7 +259,15 @@ protected function getColumnSql(\SimpleXMLElement $field)
else
{
// TODO Don't quote numeric values.
- $sql .= ' NOT NULL DEFAULT ' . $this->db->quote($fDefault);
+ if (strpos($fDefault, 'CURRENT') !== false)
+ {
+ $sql .= ' NOT NULL DEFAULT CURRENT_TIMESTAMP()';
+ }
+ else
+ {
+ $sql .= ' NOT NULL DEFAULT ' . $this->db->quote($fDefault);
+ }
+
}
}
else
@@ -356,15 +368,11 @@ protected function getKeyLookup($keys)
*/
protected function getKeySql($columns)
{
- // TODO Error checking on array and element types.
-
$kNonUnique = (string) $columns[0]['Non_unique'];
$kName = (string) $columns[0]['Key_name'];
- $kColumn = (string) $columns[0]['Column_name'];
+ $prefix = '';
- $prefix = '';
-
- if ($kName === 'PRIMARY')
+ if ($kName == 'PRIMARY')
{
$prefix = 'PRIMARY ';
}
@@ -373,22 +381,21 @@ protected function getKeySql($columns)
$prefix = 'UNIQUE ';
}
- $nColumns = \count($columns);
- $kColumns = [];
+ $kColumns = array();
- if ($nColumns === 1)
- {
- $kColumns[] = $this->db->quoteName($kColumn);
- }
- else
+ foreach ($columns as $column)
{
- foreach ($columns as $column)
+ $kLength = '';
+
+ if (!empty($column['Sub_part']))
{
- $kColumns[] = (string) $column['Column_name'];
+ $kLength = '(' . $column['Sub_part'] . ')';
}
+
+ $kColumns[] = $this->db->quoteName((string) $column['Column_name']) . $kLength;
}
- return $prefix . 'KEY ' . ($kName !== 'PRIMARY' ? $this->db->quoteName($kName) : '') . ' (' . implode(',', $kColumns) . ')';
+ return $prefix . 'KEY ' . ($kName != 'PRIMARY' ? $this->db->quoteName($kName) : '') . ' (' . implode(',', $kColumns) . ')';
}
/**
@@ -415,12 +422,14 @@ protected function xmlToCreate(\SimpleXMLElement $table)
foreach ($table->xpath('field') as $field)
{
- $createTableStatement .= $this->getColumnSQL($field) . ', ';
+ $createTableStatement .= $this->getColumnSql($field) . ', ';
}
- foreach ($table->xpath('key') as $key)
+ $newLookup = $this->getKeyLookup($table->xpath('key'));
+
+ foreach ($newLookup as $key)
{
- $createTableStatement .= $this->getKeySQL([$key]) . ', ';
+ $createTableStatement .= $this->getKeySql($key) . ', ';
}
$createTableStatement = rtrim($createTableStatement, ', ');
diff --git a/src/Mysqli/MysqliDriver.php b/src/Mysqli/MysqliDriver.php
index 1a03217cf..d16f9a526 100644
--- a/src/Mysqli/MysqliDriver.php
+++ b/src/Mysqli/MysqliDriver.php
@@ -586,6 +586,76 @@ public function insertid()
return $this->connection->insert_id;
}
+ /**
+ * Inserts a row into a table based on an object's properties.
+ *
+ * @param string $table The name of the database table to insert into.
+ * @param object $object A reference to an object whose public properties match the table fields.
+ * @param string $key The name of the primary key. If provided the object property is updated.
+ *
+ * @return boolean
+ *
+ * @since __DEPLOY_VERSION__
+ * @throws \RuntimeException
+ */
+ public function insertObject($table, &$object, $key = null)
+ {
+ $fields = [];
+ $values = [];
+ $tableColumns = $this->getTableColumns($table);
+
+ // Iterate over the object variables to build the query fields and values.
+ foreach (get_object_vars($object) as $k => $v)
+ {
+ // Skip columns that don't exist in the table.
+ if (!array_key_exists($k, $tableColumns))
+ {
+ continue;
+ }
+
+ // Only process non-null scalars.
+ if (\is_array($v) || \is_object($v) || $v === null)
+ {
+ continue;
+ }
+
+ // Ignore any internal fields.
+ if ($k[0] === '_')
+ {
+ continue;
+ }
+
+ // Ignore null datetime fields.
+ if (($tableColumns[$k] == "datetime") && empty($v))
+ {
+ continue;
+ }
+
+ // Prepare and sanitize the fields and values for the database query.
+ $fields[] = $this->quoteName($k);
+ $values[] = $this->quote($v);
+ }
+
+ // Create the base insert statement.
+ $query = $this->getQuery(true)
+ ->insert($this->quoteName($table))
+ ->columns($fields)
+ ->values(implode(',', $values));
+
+ // Set the query and execute the insert.
+ $this->setQuery($query)->execute();
+
+ // Update the primary key if it exists.
+ $id = $this->insertid();
+
+ if ($key && $id && \is_string($key))
+ {
+ $object->$key = $id;
+ }
+
+ return true;
+ }
+
/**
* Locks a table in the database.
*
diff --git a/src/Mysqli/MysqliExporter.php b/src/Mysqli/MysqliExporter.php
index 9814e6934..2ddcd3353 100644
--- a/src/Mysqli/MysqliExporter.php
+++ b/src/Mysqli/MysqliExporter.php
@@ -33,7 +33,15 @@ protected function buildXml()
$buffer[] = '';
$buffer[] = ' ';
- $buffer = array_merge($buffer, $this->buildXmlStructure());
+ if ($this->options->withStructure)
+ {
+ $buffer = array_merge($buffer, $this->buildXmlStructure());
+ }
+
+ if ($this->options->withData)
+ {
+ $buffer = array_merge($buffer, $this->buildXmlData());
+ }
$buffer[] = ' ';
$buffer[] = '';
@@ -76,6 +84,7 @@ protected function buildXmlStructure()
$buffer[] = ' ';
}
diff --git a/src/Mysqli/MysqliImporter.php b/src/Mysqli/MysqliImporter.php
index f7c50f030..38d7924b2 100644
--- a/src/Mysqli/MysqliImporter.php
+++ b/src/Mysqli/MysqliImporter.php
@@ -66,12 +66,14 @@ protected function xmlToCreate(\SimpleXMLElement $table)
foreach ($table->xpath('field') as $field)
{
- $createTableStatement .= $this->getColumnSQL($field) . ', ';
+ $createTableStatement .= $this->getColumnSql($field) . ', ';
}
- foreach ($table->xpath('key') as $key)
+ $newLookup = $this->getKeyLookup($table->xpath('key'));
+
+ foreach ($newLookup as $key)
{
- $createTableStatement .= $this->getKeySQL([$key]) . ', ';
+ $createTableStatement .= $this->getKeySql($key) . ', ';
}
$createTableStatement = rtrim($createTableStatement, ', ');
@@ -176,6 +178,7 @@ protected function getAlterTableSql(\SimpleXMLElement $structure)
&& ((string) $newLookup[$name][$i]['Column_name'] === $oldLookup[$name][$i]->Column_name)
&& ((string) $newLookup[$name][$i]['Seq_in_index'] === $oldLookup[$name][$i]->Seq_in_index)
&& ((string) $newLookup[$name][$i]['Collation'] === $oldLookup[$name][$i]->Collation)
+ && ((string) $newLookup[$name][$i]['Sub_part'] == $oldLookup[$name][$i]->Sub_part)
&& ((string) $newLookup[$name][$i]['Index_type'] === $oldLookup[$name][$i]->Index_type));
/*
@@ -193,6 +196,9 @@ protected function getAlterTableSql(\SimpleXMLElement $structure)
echo '
Collation: '.
((string) $newLookup[$name][$i]['Collation'] == $oldLookup[$name][$i]->Collation ? 'Pass' : 'Fail').' '.
(string) $newLookup[$name][$i]['Collation'].' vs '.$oldLookup[$name][$i]->Collation;
+ echo '
Sub_part: '.
+ ((string) $newLookup[$name][$i]['Sub_part'] == $oldLookup[$name][$i]->Sub_part ? 'Pass' : 'Fail').' '.
+ (string) $newLookup[$name][$i]['Sub_part'].' vs '.$oldLookup[$name][$i]->Sub_part;
echo '
Index_type: '.
((string) $newLookup[$name][$i]['Index_type'] == $oldLookup[$name][$i]->Index_type ? 'Pass' : 'Fail').' '.
(string) $newLookup[$name][$i]['Index_type'].' vs '.$oldLookup[$name][$i]->Index_type;
@@ -292,7 +298,14 @@ protected function getColumnSql(\SimpleXMLElement $field)
else
{
// TODO Don't quote numeric values.
- $sql .= ' NOT NULL DEFAULT ' . $this->db->quote($fDefault);
+ if (strpos($fDefault, 'CURRENT') !== false)
+ {
+ $sql .= ' NOT NULL DEFAULT CURRENT_TIMESTAMP()';
+ }
+ else
+ {
+ $sql .= ' NOT NULL DEFAULT ' . $this->db->quote($fDefault);
+ }
}
}
else
@@ -392,15 +405,11 @@ protected function getKeyLookup($keys)
*/
protected function getKeySql($columns)
{
- // TODO Error checking on array and element types.
-
$kNonUnique = (string) $columns[0]['Non_unique'];
$kName = (string) $columns[0]['Key_name'];
- $kColumn = (string) $columns[0]['Column_name'];
+ $prefix = '';
- $prefix = '';
-
- if ($kName === 'PRIMARY')
+ if ($kName == 'PRIMARY')
{
$prefix = 'PRIMARY ';
}
@@ -409,21 +418,20 @@ protected function getKeySql($columns)
$prefix = 'UNIQUE ';
}
- $nColumns = \count($columns);
- $kColumns = [];
+ $kColumns = array();
- if ($nColumns === 1)
- {
- $kColumns[] = $this->db->quoteName($kColumn);
- }
- else
+ foreach ($columns as $column)
{
- foreach ($columns as $column)
+ $kLength = '';
+
+ if (!empty($column['Sub_part']))
{
- $kColumns[] = (string) $column['Column_name'];
+ $kLength = '(' . $column['Sub_part'] . ')';
}
+
+ $kColumns[] = $this->db->quoteName((string) $column['Column_name']) . $kLength;
}
- return $prefix . 'KEY ' . ($kName !== 'PRIMARY' ? $this->db->quoteName($kName) : '') . ' (' . implode(',', $kColumns) . ')';
+ return $prefix . 'KEY ' . ($kName != 'PRIMARY' ? $this->db->quoteName($kName) : '') . ' (' . implode(',', $kColumns) . ')';
}
}
diff --git a/src/Pgsql/PgsqlDriver.php b/src/Pgsql/PgsqlDriver.php
index c80cf2683..d1be2d6eb 100644
--- a/src/Pgsql/PgsqlDriver.php
+++ b/src/Pgsql/PgsqlDriver.php
@@ -226,16 +226,6 @@ public function getTableColumns($table, $typeOnly = true)
{
foreach ($fields as $field)
{
- if (stristr(strtolower($field->type), 'character varying'))
- {
- $field->Default = '';
- }
-
- if (stristr(strtolower($field->type), 'text'))
- {
- $field->Default = '';
- }
-
// Do some dirty translation to MySQL output.
// @todo: Come up with and implement a standard across databases.
$result[$field->column_name] = (object) [
@@ -281,12 +271,13 @@ public function getTableKeys($table)
// To check if table exists and prevent SQL injection
$tableList = $this->getTableList();
+ $tableSub = $this->replacePrefix($table);
- if (\in_array($table, $tableList, true))
+ if (\in_array($tableSub, $tableList, true))
{
// Get the details columns information.
$this->setQuery('
- SELECT indexname AS "idxName", indisprimary AS "isPrimary", indisunique AS "isUnique",
+ SELECT indexname AS "idxName", indisprimary AS "isPrimary", indisunique AS "isUnique", indkey AS "indKey",
CASE WHEN indisprimary = true THEN
( SELECT \'ALTER TABLE \' || tablename || \' ADD \' || pg_catalog.pg_get_constraintdef(const.oid, true)
FROM pg_constraint AS const WHERE const.conname= pgClassFirst.relname )
@@ -295,7 +286,7 @@ public function getTableKeys($table)
FROM pg_indexes
LEFT JOIN pg_class AS pgClassFirst ON indexname=pgClassFirst.relname
LEFT JOIN pg_index AS pgIndex ON pgClassFirst.oid=pgIndex.indexrelid
- WHERE tablename=' . $this->quote($table) . ' ORDER BY indkey'
+ WHERE tablename=' . $this->quote($tableSub) . ' ORDER BY indkey'
);
return $this->loadObjectList();
@@ -304,6 +295,40 @@ public function getTableKeys($table)
return [];
}
+ /**
+ * Get the list of column names this index indexes.
+ *
+ * @param string $table The name of the table.
+ * @param string $indKey The list of column numbers for the table
+ *
+ * @return string A list of the column names for the table.
+ *
+ * @since __DEPLOY_VERSION__
+ * @throws \RuntimeException
+ */
+ public function getNamesKey($table, $indKey)
+ {
+ $this->connect();
+
+ $tableSub = $this->replacePrefix($table);
+
+ $tabInd = explode(' ', $indKey);
+ $colNames = array();
+
+ foreach ($tabInd as $numCol)
+ {
+ $query = $this->getQuery(true)
+ ->select('attname')
+ ->from('pg_attribute')
+ ->join('LEFT', 'pg_class ON pg_class.relname=' . $this->quote($tableSub))
+ ->where('attnum=' . $numCol . ' AND attrelid=pg_class.oid');
+ $this->setQuery($query);
+ $colNames[] = $this->loadResult();
+ }
+
+ return implode(', ', $colNames);
+ }
+
/**
* Method to get an array of all tables in the database.
*
@@ -340,8 +365,9 @@ public function getTableSequences($table)
{
// To check if table exists and prevent SQL injection
$tableList = $this->getTableList();
+ $tableSub = $this->replacePrefix($table);
- if (\in_array($table, $tableList, true))
+ if (\in_array($tableSub, $tableList, true))
{
$name = [
's.relname', 'n.nspname', 't.relname', 'a.attname', 'info.data_type',
@@ -361,7 +387,7 @@ public function getTableSequences($table)
->leftJoin('pg_namespace n ON n.oid = t.relnamespace')
->leftJoin('pg_attribute a ON a.attrelid = t.oid AND a.attnum = d.refobjsubid')
->leftJoin('information_schema.sequences AS info ON info.sequence_name = s.relname')
- ->where('s.relkind = ' . $this->quote('S') . ' AND d.deptype = ' . $this->quote('a') . ' AND t.relname = ' . $this->quote($table));
+ ->where('s.relkind = ' . $this->quote('S') . ' AND d.deptype = ' . $this->quote('a') . ' AND t.relname = ' . $this->quote($tableSub));
$this->setQuery($query);
return $this->loadObjectList();
@@ -370,6 +396,52 @@ public function getTableSequences($table)
return [];
}
+ /**
+ * Method to get the last value of a sequence in the database.
+ *
+ * @param string $sequence The name of the sequence.
+ *
+ * @return integer The last value of the sequence.
+ *
+ * @since __DEPLOY_VERSION__
+ * @throws \RuntimeException
+ */
+ public function getSequenceLastValue($sequence)
+ {
+ $this->connect();
+
+ $query = $this->getQuery(true)
+ ->select($this->quoteName('last_value'))
+ ->from($sequence);
+
+ $this->setQuery($query);
+
+ return $this->loadResult();
+ }
+
+ /**
+ * Method to get the is_called attribute of a sequence.
+ *
+ * @param string $sequence The name of the sequence.
+ *
+ * @return boolean The is_called attribute of the sequence.
+ *
+ * @since __DEPLOY_VERSION__
+ * @throws \RuntimeException
+ */
+ public function getSequenceIsCalled($sequence)
+ {
+ $this->connect();
+
+ $query = $this->getQuery(true)
+ ->select($this->quoteName('is_called'))
+ ->from($sequence);
+
+ $this->setQuery($query);
+
+ return $this->loadResult();
+ }
+
/**
* Locks a table in the database.
*
@@ -510,7 +582,7 @@ public function sqlValue($columns, $field_name, $field_value)
break;
case 'date':
- case 'timestamp without time zone':
+ //case 'timestamp without time zone':
if (empty($field_value))
{
$field_value = $this->getNullDate();
@@ -654,6 +726,12 @@ public function insertObject($table, &$object, $key = null)
continue;
}
+ // Ignore null timestamp fields.
+ if (($columns[$k] == "timestamp without time zone") && empty($v))
+ {
+ continue;
+ }
+
// Prepare and sanitize the fields and values for the database query.
$fields[] = $this->quoteName($k);
$values[] = $this->sqlValue($columns, $k, $v);
diff --git a/src/Pgsql/PgsqlExporter.php b/src/Pgsql/PgsqlExporter.php
index 5c6a12242..127cc5a99 100644
--- a/src/Pgsql/PgsqlExporter.php
+++ b/src/Pgsql/PgsqlExporter.php
@@ -33,7 +33,15 @@ protected function buildXml()
$buffer[] = '';
$buffer[] = ' ';
- $buffer = array_merge($buffer, $this->buildXmlStructure());
+ if ($this->options->withStructure)
+ {
+ $buffer = array_merge($buffer, $this->buildXmlStructure());
+ }
+
+ if ($this->options->withData)
+ {
+ $buffer = array_merge($buffer, $this->buildXmlData());
+ }
$buffer[] = ' ';
$buffer[] = '';
@@ -67,24 +75,26 @@ protected function buildXmlStructure()
foreach ($sequences as $sequence)
{
- $buffer[] = ' ';
}
foreach ($fields as $field)
{
$buffer[] = ' default) ? ' Default="' . $field->default . '"' : '') . ' Comments="' . $field->comments . '" />';
+ ' Default="' . $field->Default . '"' . ' Comments="' . $field->comments . '" />';
}
foreach ($keys as $key)
{
- $buffer[] = ' ';
+ $buffer[] = ' Query . '\' />';
}
$buffer[] = ' ';
@@ -93,6 +103,76 @@ protected function buildXmlStructure()
return $buffer;
}
+ /**
+ * Builds the XML data to export.
+ *
+ * @return array An array of XML lines (strings).
+ *
+ * @since __DEPLOY_VERSION__
+ * @throws \Exception if an error occurs.
+ */
+ protected function buildXmlData()
+ {
+ $buffer = array();
+
+ foreach ($this->from as $table)
+ {
+ // Replace the magic prefix if found.
+ $table = $this->getGenericTableName($table);
+
+ // Get the details columns information.
+ $fields = $this->db->getTableColumns($table, false);
+ $colblob = array();
+
+ foreach ($fields as $field)
+ {
+ // Catch blob for xml conversion
+ // PostgreSQL binary large object type
+ if ($field->Type == 'bytea')
+ {
+ $colblob[] = $field->Field;
+ }
+ }
+
+ $query = $this->db->getQuery(true);
+ $query->select($query->quoteName(array_keys($fields)))
+ ->from($query->quoteName($table));
+ $this->db->setQuery($query);
+
+ $rows = $this->db->loadObjectList();
+
+ if (!count($rows))
+ {
+ continue;
+ }
+
+ $buffer[] = ' ';
+
+ foreach ($rows as $row)
+ {
+ $buffer[] = ' ';
+
+ foreach ($row as $key => $value)
+ {
+ if (!in_array($key, $colblob))
+ {
+ $buffer[] = ' ' . htmlspecialchars($value, ENT_COMPAT, 'UTF-8') . '';
+ }
+ else
+ {
+ $buffer[] = ' ' . stream_get_contents($value) . '';
+ }
+ }
+
+ $buffer[] = '
';
+ }
+
+ $buffer[] = ' ';
+ }
+
+ return $buffer;
+ }
+
/**
* Checks if all data and options are in order prior to exporting.
*
diff --git a/src/Pgsql/PgsqlImporter.php b/src/Pgsql/PgsqlImporter.php
index f4617e7b0..ca5af0e1b 100644
--- a/src/Pgsql/PgsqlImporter.php
+++ b/src/Pgsql/PgsqlImporter.php
@@ -107,6 +107,7 @@ protected function getAlterTableSql(\SimpleXMLElement $structure)
if ($change)
{
$alters[] = $this->getChangeSequenceSql($kSeqName, $vSeq);
+ $alters[] = $this->getSetvalSequenceSql($kSeqName, $vSeq);
}
// Unset this field so that what we have left are fields that need to be removed.
@@ -116,6 +117,7 @@ protected function getAlterTableSql(\SimpleXMLElement $structure)
{
// The sequence is new
$alters[] = $this->getAddSequenceSql($newSequenceLook[$kSeqName][0]);
+ $alters[] = $this->getSetvalSequenceSql($newSequenceLook[$kSeqName][0]);
}
}
@@ -262,7 +264,7 @@ protected function getDropSequenceSql($name)
*/
protected function getAddSequenceSql(\SimpleXMLElement $field)
{
- $sql = 'CREATE SEQUENCE ' . (string) $field['Name']
+ $sql = 'CREATE SEQUENCE IF NOT EXISTS ' . (string) $field['Name']
. ' INCREMENT BY ' . (string) $field['Increment'] . ' MINVALUE ' . $field['Min_Value']
. ' MAXVALUE ' . (string) $field['Max_Value'] . ' START ' . (string) $field['Start_Value']
. (((string) $field['Cycle_option'] === 'NO') ? ' NO' : '') . ' CYCLE'
@@ -290,6 +292,22 @@ protected function getChangeSequenceSql(\SimpleXMLElement $field)
return $sql;
}
+ /**
+ * Get the syntax to setval a sequence.
+ *
+ * @param \SimpleXMLElement $field The XML definition for the sequence.
+ *
+ * @return string
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ protected function getSetvalSequenceSql($field)
+ {
+ $is_called = $field['Is_called'] == 't' || $field['Is_called'] == '1' ? 'TRUE' : 'FALSE';
+
+ return 'SELECT setval(\'' . (string) $field['Name'] . '\', ' . (string) $field['Last_Value'] . ', ' . $is_called . ')';
+ }
+
/**
* Get the syntax to alter a column.
*
@@ -376,18 +394,22 @@ protected function getAlterColumnSql($table, \SimpleXMLElement $field)
*/
protected function getColumnSql(\SimpleXMLElement $field)
{
- // TODO Incorporate into parent class and use $this.
- $blobs = ['text', 'smalltext', 'mediumtext', 'largetext'];
-
$fName = (string) $field['Field'];
$fType = (string) $field['Type'];
$fNull = (string) $field['Null'];
- $fDefault = (isset($field['Default']) && $field['Default'] != 'NULL') ?
- preg_match('/^[0-9]$/', $field['Default']) ? $field['Default'] : $this->db->quote((string) $field['Default'])
- : null;
+ if (strpos($field['Default'], '::') != false)
+ {
+ $fDefault = strstr($field['Default'], '::', true);
+ }
+ else
+ {
+ $fDefault = isset($field['Default']) && strlen($field['Default']) != 0 ?
+ preg_match('/^[0-9]$/', $field['Default']) ? $field['Default'] : $this->db->quote((string) $field['Default'])
+ : null;
+ }
- // Note, nextval() as default value means that type field is serial.
+ /* nextval() as default value means that type field is serial */
if (strpos($fDefault, 'nextval') !== false)
{
$sql = $this->db->quoteName($fName) . ' SERIAL';
@@ -396,9 +418,9 @@ protected function getColumnSql(\SimpleXMLElement $field)
{
$sql = $this->db->quoteName($fName) . ' ' . $fType;
- if ($fNull === 'NO')
+ if ($fNull == 'NO')
{
- if ($fDefault === null || \in_array($fType, $blobs, true))
+ if ($fDefault === null)
{
$sql .= ' NOT NULL';
}
@@ -484,6 +506,34 @@ protected function getKeyLookup($keys)
return $lookup;
}
+ /**
+ * Get the SQL syntax to add a unique constraint for a table key.
+ *
+ * @param string $table The table name.
+ * @param array $key The key.
+ *
+ * @return string
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ protected function getAddUniqueSql($table, $key)
+ {
+ if ($key instanceof \SimpleXMLElement)
+ {
+ $kName = (string) $key['Key_name'];
+ $kIndex = (string) $key['Index'];
+ }
+ else
+ {
+ $kName = $key->Key_name;
+ $kIndex = $key->Index;
+ }
+
+ $unique = $kIndex . ' UNIQUE (' . $kName . ')';
+
+ return 'ALTER TABLE ' . $this->db->quoteName($table) . ' ADD CONSTRAINT ' . $unique;
+ }
+
/**
* Get the details list of sequences for a table.
*
@@ -532,7 +582,42 @@ protected function getSeqLookup($sequences)
*/
protected function xmlToCreate(\SimpleXMLElement $table)
{
- // TODO - Implement this
- return '';
+ $existingTables = $this->db->getTableList();
+ $tableName = (string) $table['name'];
+
+ if (in_array($tableName, $existingTables))
+ {
+ throw new \RuntimeException('The table you are trying to create already exists');
+ }
+
+ $createTableStatement = 'CREATE TABLE ' . $this->db->quoteName($tableName) . ' (';
+
+ foreach ($table->xpath('field') as $field)
+ {
+ $createTableStatement .= $this->getColumnSql($field) . ', ';
+ }
+
+ $createTableStatement = rtrim($createTableStatement, ', ');
+ $createTableStatement .= ');';
+
+ foreach ($table->xpath('sequence') as $seq)
+ {
+ $createTableStatement .= $this->getAddSequenceSql($seq) . ';';
+ $createTableStatement .= $this->getSetvalSequenceSql($seq) . ';';
+ }
+
+ foreach ($table->xpath('key') as $key)
+ {
+ if ((($key['is_primary'] == 'f') || ($key['is_primary'] == '')) && (($key['is_unique'] == 't') || ($key['is_unique'] == '1')))
+ {
+ $createTableStatement .= $this->getAddUniqueSql($tableName, $key) . ';';
+ }
+ else
+ {
+ $createTableStatement .= $this->getAddIndexSql($key) . ';';
+ }
+ }
+
+ return $createTableStatement;
}
}