diff --git a/symmetric/symmetric-client/src/main/java/org/jumpmind/symmetric/DbExport.java b/symmetric/symmetric-client/src/main/java/org/jumpmind/symmetric/DbExport.java new file mode 100644 index 0000000000..2e01ded97b --- /dev/null +++ b/symmetric/symmetric-client/src/main/java/org/jumpmind/symmetric/DbExport.java @@ -0,0 +1,273 @@ +/* + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU Lesser General Public License (the + * "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jumpmind.symmetric; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; + +import javax.sql.DataSource; + +import org.jumpmind.db.io.DatabaseIO; +import org.jumpmind.db.model.Column; +import org.jumpmind.db.model.Database; +import org.jumpmind.db.model.Table; +import org.jumpmind.db.platform.DatabasePlatformSettings; +import org.jumpmind.db.platform.IDatabasePlatform; +import org.jumpmind.db.platform.JdbcDatabasePlatformFactory; +import org.jumpmind.db.platform.mysql.MySqlDatabasePlatform; +import org.jumpmind.db.sql.DmlStatement; +import org.jumpmind.db.sql.DmlStatement.DmlType; +import org.jumpmind.db.sql.ISqlRowMapper; +import org.jumpmind.db.sql.ISqlTemplate; +import org.jumpmind.db.sql.Row; +import org.jumpmind.symmetric.csv.CsvWriter; + +/** + * Export the structure and data from database tables to file. + */ +public class DbExport { + + public enum Format { SQL, CSV, XML }; + + public enum Compatible { DB2, DERBY, FIREBIRD, H2, HSQLDB, HSQLDB2, INFORMIX, INTERBASE, MSSQL, MYSQL, ORACLE, POSTGRESQL, SYBASE }; + + private Format format = Format.SQL; + + private Compatible compatible = Compatible.ORACLE; + + private boolean addDropTable; + + private boolean noCreateInfo; + + private boolean noData; + + private boolean comments; + + private String catalog; + + private String schema; + + private IDatabasePlatform platform; + + public DbExport(IDatabasePlatform platform) { + this.platform = platform; + } + + public DbExport(DataSource dataSource) { + platform = JdbcDatabasePlatformFactory.createNewPlatformInstance(dataSource, null); + } + + public String exportTables() throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + exportTables(output); + output.close(); + return output.toString(); + } + + public String exportTables(String[] tableNames) throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + exportTables(output, tableNames); + output.close(); + return output.toString(); + } + + public String exportTables(Table[] tables) throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + exportTables(output, tables); + output.close(); + return output.toString(); + } + + public void exportTables(OutputStream output) throws IOException { + Database database = platform.readDatabase(getDefaultCatalog(), getDefaultSchema(), null); + exportTables(output, database.getTables()); + } + + public void exportTables(OutputStream output, String[] tableNames) throws IOException { + ArrayList tableList = new ArrayList
(); + + for (String tableName : tableNames) { + Table table = platform.readTableFromDatabase(getDefaultCatalog(), getDefaultSchema(), tableName); + if (table != null) { + tableList.add(table); + } else { + throw new RuntimeException("Cannot find table " + tableName + " in catalog " + getDefaultCatalog() + + " and schema " + getDefaultSchema()); + } + } + exportTables(output, tableList.toArray(new Table[tableList.size()])); + } + + public void exportTables(OutputStream output, Table[] tables) throws IOException { + final Writer writer = new OutputStreamWriter(output); + final CsvWriter csvWriter = new CsvWriter(writer, ','); + final ISqlTemplate sqlTemplate = platform.getSqlTemplate(); + + IDatabasePlatform target = new MySqlDatabasePlatform(null, new DatabasePlatformSettings()); + SimpleDateFormat df = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss"); + csvWriter.setEscapeMode(CsvWriter.ESCAPE_MODE_BACKSLASH); + writeComment(writer, "SymmetricDS " + Version.version() + " " + DbExport.class.getSimpleName()); + writeComment(writer, "Catalog: " + getDefaultCatalog()); + writeComment(writer, "Schema: " + getDefaultSchema()); + writeComment(writer, "Started on " + df.format(new Date())); + + for (Table table : tables) { + writeComment(writer, "Table: " + table.getName()); + + if (! noCreateInfo) { + Database db = new Database(); + db.addTable(table); + if (format == Format.SQL) { + writer.write(target.getDdlBuilder().createTables(db, addDropTable)); + } else if (format == Format.XML) { + new DatabaseIO().write(db, output); + } else if (format == Format.CSV) { + csvWriter.writeRecord(table.getColumnNames()); + } + } + + if (! noData) { + DmlStatement stmt = platform.createDmlStatement(DmlType.SELECT_ALL, table); + final Column[] columns = table.getColumns(); + + sqlTemplate.queryForObject(stmt.getSql(), new ISqlRowMapper() { + public Object mapRow(Row row) { + String[] values = platform.getStringValues(columns, row); + if (format == Format.CSV) { + try { + csvWriter.writeRecord(values, true); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else if (format == Format.SQL) { + + } else if (format == Format.XML){ + + } + return values; + } + }); + } + } + + writeComment(writer, "Completed on " + df.format(new Date())); + output.flush(); + csvWriter.flush(); + } + + protected void writeComment(Writer writer, String commentStr) throws IOException { + if (comments) { + if (format == Format.CSV) { + writer.write("# " + commentStr + "\n"); + } else if (format == Format.XML) { + writer.write("\n"); + } else if (format == Format.SQL) { + writer.write("-- SymmetricDS " + Version.version() + "\n--\n"); + } + writer.flush(); + } + } + + public String getDefaultCatalog() { + if (catalog != null) { + return catalog; + } + return platform.getDefaultCatalog(); + } + + public String getDefaultSchema() { + if (schema != null) { + return schema; + } + return platform.getDefaultSchema(); + } + + public Format getFormat() { + return format; + } + + public void setFormat(Format format) { + this.format = format; + } + + public Compatible getCompatible() { + return compatible; + } + + public void setCompatible(Compatible compatible) { + this.compatible = compatible; + } + + public boolean isAddDropTable() { + return addDropTable; + } + + public void setAddDropTable(boolean addDropTable) { + this.addDropTable = addDropTable; + } + + public boolean isNoCreateInfo() { + return noCreateInfo; + } + + public void setNoCreateInfo(boolean noCreateInfo) { + this.noCreateInfo = noCreateInfo; + } + + public boolean isNoData() { + return noData; + } + + public void setNoData(boolean noData) { + this.noData = noData; + } + + public boolean isComments() { + return comments; + } + + public void setComments(boolean comments) { + this.comments = comments; + } + + public String getCatalog() { + return catalog; + } + + public void setCatalog(String catalog) { + this.catalog = catalog; + } + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } +} diff --git a/symmetric/symmetric-client/src/main/java/org/jumpmind/symmetric/DbExportCommand.java b/symmetric/symmetric-client/src/main/java/org/jumpmind/symmetric/DbExportCommand.java new file mode 100644 index 0000000000..cb72e6a02c --- /dev/null +++ b/symmetric/symmetric-client/src/main/java/org/jumpmind/symmetric/DbExportCommand.java @@ -0,0 +1,103 @@ +/* + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU Lesser General Public License (the + * "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jumpmind.symmetric; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Options; +import org.jumpmind.symmetric.DbExport.Compatible; +import org.jumpmind.symmetric.DbExport.Format; + +/** + * Export the structure and data from database tables to file. + */ +public class DbExportCommand extends AbstractCommandLauncher { + + private static final String OPTION_FORMAT = "format"; + + private static final String OPTION_COMPATIBLE = "compatible"; + + private static final String OPTION_ADD_DROP_TABLE = "add-drop-table"; + + private static final String OPTION_NO_CREATE_INFO = "no-create-info"; + + private static final String OPTION_NO_DATA = "no-data"; + + private static final String OPTION_COMMENTS = "comments"; + + public DbExportCommand() { + super("dbexport", "DbExport.Option."); + } + + public static void main(String[] args) { + new DbExportCommand().execute(args); + } + + protected void printHelp(Options options) { + System.out.println(commandName + " version " + Version.version()); + System.out.println("Export the structure and data from database tables to file.\n"); + super.printHelp(options); + } + + @Override + protected void buildOptions(Options options) { + super.buildOptions(options); + addOption(options, null, OPTION_FORMAT, true); + addOption(options, null, OPTION_COMPATIBLE, true); + addOption(options, null, OPTION_ADD_DROP_TABLE, false); + addOption(options, null, OPTION_NO_CREATE_INFO, false); + addOption(options, null, OPTION_NO_DATA, false); + addOption(options, "i", OPTION_COMMENTS, false); + } + + @Override + protected boolean executeOptions(CommandLine line) throws Exception { + DbExport dbExport = new DbExport(getDatabasePlatform()); + + if (line.hasOption(OPTION_FORMAT)) { + dbExport.setFormat(Format.valueOf(line.getOptionValue(OPTION_FORMAT).toUpperCase())); + } + if (line.hasOption(OPTION_COMPATIBLE)) { + dbExport.setCompatible(Compatible.valueOf(line.getOptionValue(OPTION_COMPATIBLE).toUpperCase())); + } + if (line.hasOption(OPTION_ADD_DROP_TABLE)) { + dbExport.setAddDropTable(true); + } + if (line.hasOption(OPTION_NO_CREATE_INFO)) { + dbExport.setNoCreateInfo(true); + } + if (line.hasOption(OPTION_NO_DATA)) { + dbExport.setNoData(true); + } + if (line.hasOption(OPTION_COMMENTS)) { + dbExport.setComments(true); + } + + String[] args = line.getArgs(); + if (args.length == 0) { + dbExport.exportTables(System.out); + } else { + dbExport.exportTables(System.out, args); + } + return true; + } + +} diff --git a/symmetric/symmetric-client/src/main/resources/symmetric-messages.properties b/symmetric/symmetric-client/src/main/resources/symmetric-messages.properties index a5922c74aa..3bea95ebb0 100644 --- a/symmetric/symmetric-client/src/main/resources/symmetric-messages.properties +++ b/symmetric/symmetric-client/src/main/resources/symmetric-messages.properties @@ -39,9 +39,9 @@ SymAdmin.Option.generate-triggers-always=Run the sync triggers process even if t SymAdmin.Option.encrypt=Encrypts the given text for use with db.user and db.password properties. SymAdmin.Option.create-war=Generate a web archive that can be deployed to a web server like Tomcat. The name of the output file must be provided. If a properties file is designated, it will be renamed and packaged as symmetric.properties. Other than the optional properties file, a war is made up of the contents of the web directory and the conf directory of the standalone installation. -DbDump.Option.compatible=Change dump to be compatible with given database: db2, derby, firebird, greenplum, h2, hsqldb, hsqldb2, informix, interbase, mssql, mysql, oracle, postgresql, sybase. -DbDump.Option.add-drop-table=Add drop table commands to output. -DbDump.Option.no-create-info=Do not write statements to create tables. -DbDump.Option.no-data=Do not write statements to insert into tables. -DbDump.Option.comments=Write informational comments. -DbDump.Option.format=Output format: SQL, CSV, or XML. +DbExport.Option.compatible=Change export to be compatible with given database: db2, derby, firebird, greenplum, h2, hsqldb, hsqldb2, informix, interbase, mssql, mysql, oracle, postgresql, sybase. +DbExport.Option.add-drop-table=Add drop table commands to output. +DbExport.Option.no-create-info=Do not write statements to create tables. +DbExport.Option.no-data=Do not write statements to insert into tables. +DbExport.Option.comments=Write informational comments. +DbExport.Option.format=Output format: SQL, CSV, or XML.