/* * #! * Ontopia Engine * #- * Copyright (C) 2001 - 2013 The Ontopia Project * #- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 net.ontopia.persistence.rdbms; import java.io.IOException; import java.io.Writer; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import net.ontopia.utils.OntopiaRuntimeException; import net.ontopia.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * INTERNAL: Class that generates DDL statements for the generic * database platform. */ public class GenericSQLProducer { // Define a logging category. static Logger log = LoggerFactory.getLogger(GenericSQLProducer.class.getName()); protected Project project; protected String[] platforms = new String[] { "generic" }; public GenericSQLProducer(Project project) { this.project = project; } public GenericSQLProducer(Project project, String[] platforms) { this.project = project; this.platforms = platforms; } /** * INTERNAL: Create the DDL statement(s) to create the database schema. */ public void writeCreate(Writer writer) throws IOException { // Create table Iterator<Table> iter = project.getTables().iterator(); while (iter.hasNext()) { Table table = iter.next(); outputStatements(createStatement(table, new ArrayList<String>()), writer); writer.write("\n"); } // Primary keys iter = project.getTables().iterator(); while (iter.hasNext()) { Table table = iter.next(); outputStatements(addPrimaryKeys(table, new ArrayList<String>()), writer); } // Foreign keys if (supportsForeignKeys()) { iter = project.getTables().iterator(); boolean fkeys = iter.hasNext(); if (fkeys) writer.write("\n/* \n"); while (iter.hasNext()) { Table table = iter.next(); int counter = 0; Iterator<Column> iter2 = table.getColumns().iterator(); while (iter2.hasNext()) { Column col = iter2.next(); if (col.getReferencedTable() != null) { counter++; String keyname = "FK_" + table.getName() + "_" + counter; outputStatements(addForeignKey(table, col, keyname, new ArrayList<String>()), writer); } } } if (fkeys) writer.write("*/\n"); } // Indexes iter = project.getTables().iterator(); if (iter.hasNext()) writer.write("\n"); while (iter.hasNext()) { Table table = iter.next(); if (table.getIndexes() != null) outputStatements(createIndexes(table, new ArrayList<String>()), writer); } // Actions List<String> actions = project.getCreateActions(platforms); if (!actions.isEmpty()) { writer.write("\n"); outputStatements(actions, writer); writer.write("\n"); } } public void executeCreate(Connection conn) throws IOException, SQLException { List<String> statements = new ArrayList<String>(); // Create table Iterator<Table> iter = project.getTables().iterator(); while (iter.hasNext()) { Table table = iter.next(); createStatement(table, statements); } // Primary keys iter = project.getTables().iterator(); while (iter.hasNext()) { Table table = iter.next(); addPrimaryKeys(table, statements); } //! // Foreign keys //! if (supportsForeignKeys()) { //! iter = project.getTables().iterator(); //! boolean fkeys = iter.hasNext(); //! //! if (fkeys) writer.write("\n/* \n"); //! //! while (iter.hasNext()) { //! Table table = (Table)iter.next(); //! int counter = 0; //! Iterator iter2 = table.getColumns().iterator(); //! while (iter2.hasNext()) { //! Column col = (Column)iter2.next(); //! if (col.getReferencedTable() != null) { //! counter++; //! String keyname = "FK_" + table.getName() + "_" + counter; //! outputStatements(addForeignKey(table, col, keyname, new ArrayList()), writer); //! } //! } //! } //! //! if (fkeys) writer.write("\n*/\n"); //! } // Indexes iter = project.getTables().iterator(); while (iter.hasNext()) { Table table = iter.next(); if (table.getIndexes() != null) createIndexes(table, statements); } // Actions statements.addAll(project.getCreateActions(platforms)); // Execute the statements executeStatements(statements, conn); } /** * INTERNAL: Create the DDL statement(s) to drop the database schema. */ public void writeDrop(Writer writer) throws IOException { // Actions List<String> actions = project.getDropActions(platforms); if (!actions.isEmpty()) { writer.write("\n"); outputStatements(actions, writer); writer.write("\n"); } Iterator<Table> iter; // Foreign keys if (supportsForeignKeys()) { iter = project.getTables().iterator(); boolean fkeys = iter.hasNext(); if (fkeys) writer.write("\n/* \n"); while (iter.hasNext()) { Table table = iter.next(); int counter = 0; Iterator<Column> iter2 = table.getColumns().iterator(); while (iter2.hasNext()) { Column col = iter2.next(); if (col.getReferencedTable() != null) { counter++; String keyname = "FK_" + table.getName() + "_" + counter; outputStatements(dropConstraint(table, col, keyname, new ArrayList<String>()), writer); } } } if (fkeys) writer.write("*/\n"); } // drop table iter = project.getTables().iterator(); while (iter.hasNext()) { Table table = iter.next(); outputStatements(dropStatement(table, new ArrayList<String>()), writer); } } public void executeDrop(Connection conn) throws IOException, SQLException { List<String> statements = new ArrayList<String>(); // Actions statements.addAll(project.getDropActions(platforms)); // drop table Iterator<Table> iter = project.getTables().iterator(); while (iter.hasNext()) { Table table = iter.next(); dropStatement(table, statements); } // Execute the statements executeStatements(statements, conn); } /** * INTERNAL: Generate the DDL statement(s) to create the specified table. */ protected List<String> createStatement(Table table, List<String> statements) throws IOException { String[] pkeys = table.getPrimaryKeys(); // Create table StringBuilder sb = new StringBuilder(); sb.append("create table "); sb.append(table.getName()); sb.append(" (\n"); Iterator<Column> iter = table.getColumns().iterator(); while (iter.hasNext()) { Column col = iter.next(); DataType type = project.getDataTypeByName(col.getType(), platforms); if (type == null) throw new OntopiaRuntimeException("Unknown datatype: '" + col.getType() + "'"); sb.append(" "); sb.append(col.getName()); sb.append(" "); sb.append(type.getType()); sb.append((type.isVariable() ? "(" + type.getSize() + ")" : "")); sb.append((!col.isNullable() ? " not null" : (supportsNullInColumnDefinition() ? " null" : ""))); if (pkeys != null || iter.hasNext()) sb.append(","); sb.append("\n"); } // Primary keys if (pkeys != null) { sb.append(" constraint " + getPrimaryKeyName(table) + " primary key ("); sb.append(StringUtils.join(pkeys, ", ")); sb.append(")\n"); } sb.append(")"); statements.add(sb.toString()); return statements; } protected String getPrimaryKeyName(Table table) { return table.getName() + "_pkey"; } protected String getIndexName(Index index) { return index.getName(); } protected boolean supportsNullInColumnDefinition() { return true; } /** * INTERNAL: Generate the DDL statement(s) to drop the specified table. */ protected List<String> dropStatement(Table table, List<String> statements) throws IOException { StringBuilder sb = new StringBuilder(); sb.append("drop table "); sb.append(table.getName()); statements.add(sb.toString()); return statements; } /** * INTERNAL: Generate the DDL statement(s) to add primary keys for * the specified table. This method should only be implemented if * primary keys need to be created by a separate statement. */ protected List<String> addPrimaryKeys(Table table, List<String> statements) throws IOException { // Ignore, since we do this in the createStatement method. return statements; } /** * INTERNAL: Generate the DDL statement(s) to add foreigns keys for * the specified column. This method should only be implemented if * foreign keys need to be created by a separate statement. */ protected List<String> addForeignKey(Table table, Column col, String keyname, List<String> statements) throws IOException { StringBuilder sb = new StringBuilder(); sb.append("alter table "); sb.append(table.getName()); sb.append(" add constraint "); sb.append(keyname); sb.append(" foreign key ("); sb.append(col.getName()); sb.append(") references "); sb.append(col.getReferencedTable()); sb.append(" ("); sb.append(col.getReferencedColumn()); sb.append(") deferrable initially deferred"); statements.add(sb.toString()); return statements; } /** * INTERNAL: Generate the DDL statement(s) to drop foreigns keys for * the specified column. This method should only be implemented if * foreign keys need to be created by a separate statement. */ protected List<String> dropConstraint(Table table, Column col, String keyname, List<String> statements) throws IOException { StringBuilder sb = new StringBuilder(); sb.append("alter table "); sb.append(table.getName()); sb.append(" drop constraint "); sb.append(keyname); statements.add(sb.toString()); return statements; } /** * INTERNAL: Generate the DDL statement(s) to create indexes for the * specified table. */ protected List<String> createIndexes(Table table, List<String> statements) throws IOException { List<Index> indexes = table.getIndexes(); for (int i=0; i < indexes.size(); i++) { Index index = indexes.get(i); StringBuilder sb = new StringBuilder(); sb.append("create index "); sb.append(getIndexName(index)); sb.append(" on "); sb.append(table.getName()); sb.append("("); sb.append(StringUtils.join(index.getColumns(), ", ")); sb.append(")"); statements.add(sb.toString()); } return statements; } // -- utility methods protected void outputStatements(List<String> statements, Writer writer) throws IOException { Iterator<String> iter = statements.iterator(); while (iter.hasNext()) { writer.write(iter.next()); writer.write(";\n"); } } protected void executeStatements(List<String> statements, Connection conn) throws IOException, SQLException { // Execute the statements Iterator<String> iter = statements.iterator(); while (iter.hasNext()) { Statement stm = conn.createStatement(); String sql = iter.next(); log.info("Executing sql statements generated by " + getClass().getName()); if (log.isDebugEnabled()) log.debug("Executing: " + sql); stm.executeUpdate(sql); } } // -- flags protected boolean supportsForeignKeys() { return false; } }