/* * Copyright 2007 - 2017 the original author or authors. * * 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.sf.jailer.entitygraph.local; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStreamWriter; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import net.sf.jailer.ExecutionContext; import net.sf.jailer.configuration.Configuration; import net.sf.jailer.configuration.DBMS; import net.sf.jailer.configuration.LocalDatabaseConfiguration; import net.sf.jailer.database.InlineViewBuilder; import net.sf.jailer.database.InlineViewStyle; import net.sf.jailer.database.LocalDatabase; import net.sf.jailer.database.SQLDialect; import net.sf.jailer.database.Session; import net.sf.jailer.database.Session.ResultSetReader; import net.sf.jailer.database.WorkingTableScope; import net.sf.jailer.database.UpdateTransformer; import net.sf.jailer.datamodel.Association; import net.sf.jailer.datamodel.Column; import net.sf.jailer.datamodel.DataModel; import net.sf.jailer.datamodel.PrimaryKey; import net.sf.jailer.datamodel.PrimaryKeyFactory; import net.sf.jailer.datamodel.RowIdSupport; import net.sf.jailer.datamodel.Table; import net.sf.jailer.ddl.DDLCreator; import net.sf.jailer.entitygraph.EntityGraph; import net.sf.jailer.progress.ProgressListenerRegistry; import net.sf.jailer.util.CellContentConverter; import net.sf.jailer.util.CsvFile; import net.sf.jailer.util.Quoting; import net.sf.jailer.util.SqlUtil; /** * Persistent graph of entities. * Persists the graph in a local database. * * @author Ralf Wisser */ public class LocalEntityGraph extends EntityGraph { /** * For access to the remote database. */ private final Session remoteSession; /** * For access to the local database. */ private final Session localSession; /** * Local database. */ private final LocalDatabase localDatabase; private InlineViewStyle localInlineViewStyle; private InlineViewStyle remoteInlineViewStyle; private abstract class RemoteInlineViewBuilder extends InlineViewBuilder { public RemoteInlineViewBuilder(String name, String columnList) { this(name, columnList, false); } public RemoteInlineViewBuilder(String name, String columnList, boolean allUPK) { super(remoteInlineViewStyle, name, remoteSession, columnList.split(", *")); this.allUPK = allUPK; } private final boolean allUPK; protected String sqlValue(ResultSet resultSet, int i) throws SQLException { Object value = cellContentConverter.getObject(resultSet, i); if (!allUPK && !isUPKColumn(columnNames[i - 1])) { value = cellContentConverter.toSql(value); } else if (value instanceof String && isNUPKColumn(columnNames[i - 1])) { String prefix = remoteSession.dbms.getNcharPrefix(); if (prefix != null && value != null && !value.toString().startsWith(prefix)) { value = prefix + value; } } return (String) value; } @Override protected CellContentConverter createCellContentConverter() { return new CellContentConverter(resultSetMetaData, localSession, localSession.dbms); } } private abstract class LocalInlineViewBuilder extends InlineViewBuilder { public LocalInlineViewBuilder(String name, String columnList) { this(name, columnList, false); } public LocalInlineViewBuilder(String name, String columnList, boolean allUPK) { super(localInlineViewStyle, name, localSession, columnList.split(", *")); this.allUPK = allUPK; this.localDBMSConfiguration = localSession.dbms; } private final boolean allUPK; private final DBMS localDBMSConfiguration; protected String sqlValue(ResultSet resultSet, int i) throws SQLException { String value = cellContentConverter.toSql(cellContentConverter.getObject(resultSet, i)); if (allUPK || isUPKColumn(columnNames[i - 1])) { // value = cellContentConverter.toSql(value); value = "'" + localDBMSConfiguration.convertToStringLiteral(value) + "'"; } return value; } @Override protected CellContentConverter createCellContentConverter() { return new CellContentConverter(resultSetMetaData, remoteSession, remoteSession.dbms); } } private Set<String> upkColumnNames = null; private Set<String> nupkColumnNames = null; private synchronized boolean isUPKColumn(String columnName) { if (upkColumnNames == null) { upkColumnNames = new HashSet<String>(); for (Column c: universalPrimaryKey.getColumns()) { upkColumnNames.add(c.name); } } return upkColumnNames.contains(columnName); } private synchronized boolean isNUPKColumn(String columnName) { if (nupkColumnNames == null) { nupkColumnNames = new HashSet<String>(); String localNPKType = getConfiguration().getLocalNPKType(); for (Column c: universalPrimaryKey.getColumns()) { if (c.type.equalsIgnoreCase(localNPKType)) { nupkColumnNames.add(c.name); } } } return nupkColumnNames.contains(columnName); } private static LocalDatabaseConfiguration localConfiguration = null; private static synchronized LocalDatabaseConfiguration getConfiguration() { if (localConfiguration == null) { localConfiguration = (LocalDatabaseConfiguration) Configuration.getInstance().localEntityGraphConfiguration; if (localConfiguration == null) { localConfiguration = new LocalDatabaseConfiguration(); } } return localConfiguration; } private final RowIdSupport rowIdSupport; private final Quoting quoting; /** * Copy constructor. */ private LocalEntityGraph(int graphID, DataModel dataModel, Session remoteSession, Session localSession, LocalDatabase localDatabase, InlineViewStyle localInlineViewStyle, InlineViewStyle remoteInlineViewStyle, Set<String> upkColumnNames, PrimaryKey universalPrimaryKey, int birthdayOfSubject, Set<String> fieldProcTables, RowIdSupport rowIdSupport, ExecutionContext executionContext) throws SQLException { super(graphID, dataModel, executionContext); this.remoteSession = remoteSession; this.localSession = localSession; this.localDatabase = localDatabase; this.localInlineViewStyle = localInlineViewStyle; this.remoteInlineViewStyle = remoteInlineViewStyle; this.upkColumnNames = upkColumnNames; this.universalPrimaryKey = universalPrimaryKey; this.birthdayOfSubject = birthdayOfSubject; this.fieldProcTables.addAll(fieldProcTables); this.rowIdSupport = rowIdSupport; this.quoting = new Quoting(remoteSession); } /** * Constructor. * * @param remoteSession */ private LocalEntityGraph(int graphID, Session remoteSession, ExecutionContext executionContext) throws IOException, SQLException { super(graphID, new DataModel(new PrimaryKeyFactory() { @Override public PrimaryKey createPrimaryKey(List<Column> columns) { List<Column> localPK = new ArrayList<Column>(columns.size()); for (Column c: columns) { if (c.type.equalsIgnoreCase("nvarchar") || c.type.equalsIgnoreCase("nchar")) { localPK.add(new Column(c.name, getConfiguration().getLocalNPKType(), getConfiguration().getLocalPKLength(), -1)); } else { localPK.add(new Column(c.name, getConfiguration().getLocalPKType(), getConfiguration().getLocalPKLength(), -1)); } } return super.createPrimaryKey(localPK); } }, executionContext.getSourceSchemaMapping(), executionContext), executionContext); this.remoteSession = remoteSession; this.quoting = new Quoting(remoteSession); this.rowIdSupport = new RowIdSupport(getDatamodel(), remoteSession.dbms, getConfiguration().getLocalPKType(), executionContext); this.localDatabase = createLocalDatabase(getConfiguration().getDriver(), getConfiguration().getUrlPattern(), getConfiguration().getUser(), getConfiguration().getPassword(), getConfiguration().getLib()); this.localSession = this.localDatabase.getSession(); this.universalPrimaryKey = rowIdSupport.getUniversalPrimaryKey(); this.localInlineViewStyle = InlineViewStyle.forSession(localSession); this.remoteInlineViewStyle = InlineViewStyle.forSession(remoteSession); new DDLCreator(executionContext).createDDL(getDatamodel(), localSession, WorkingTableScope.GLOBAL, rowIdSupport, null); File fieldProcTablesFile = new File("field-proc-tables.csv"); if (fieldProcTablesFile.exists()) { try { for (CsvFile.Line line: new CsvFile(fieldProcTablesFile).getLines()) { fieldProcTables.add(line.cells.get(0).toLowerCase()); } Session._log.info("tables with field procedures: " + fieldProcTables); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } } /** * Creates a local database and opens a localSession for it. * @param urlparameter * * @return the localSession * @ */ private LocalDatabase createLocalDatabase(String driverClassName, String urlPattern, String user, String password, String jarfile) throws FileNotFoundException, SQLException { try { return new LocalDatabase(driverClassName, urlPattern, user, password, jarfile); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } /** * Closes the graph. Deletes the local database. */ public void close() throws SQLException { localDatabase.shutDown(); } /** * The universal primary key. */ private final PrimaryKey universalPrimaryKey; /** * Birthday of subject rows. */ private int birthdayOfSubject = 0; /** * Sets birthday of subject rows. * * @param birthdayOfSubject birthday of subject rows */ public void setBirthdayOfSubject(int birthdayOfSubject) { this.birthdayOfSubject = birthdayOfSubject; } /** * Creates a new entity-graph. * * @param graphID the unique ID of the graph * @param remoteSession for executing SQL-Statements * @param universalPrimaryKey the universal primary key * @return the newly created entity-graph */ public static LocalEntityGraph create(DataModel dataModel, int graphID, Session remoteSession, ExecutionContext executionContext) throws IOException, SQLException { LocalEntityGraph entityGraph = new LocalEntityGraph(graphID, remoteSession, executionContext); try { entityGraph.localSession.executeUpdate("Insert into " + SQLDialect.dmlTableReference(ENTITY_GRAPH, entityGraph.localSession, executionContext) + "(id, age) values (" + graphID + ", 1)"); } catch (SQLException e) { throw new RuntimeException("Can't find working tables! " + "Run 'jailer.sh create-ddl' " + "and execute the DDL-script first!", e); } return entityGraph; } /** * Copies an entity-graph. * * @param newGraphID the unique ID of the new graph * @param localSession for executing SQL-Statements * @return the newly created entity-graph */ public EntityGraph copy(int newGraphID, Session globalSession) throws SQLException { LocalEntityGraph entityGraph = new LocalEntityGraph(newGraphID, dataModel, remoteSession, localSession, localDatabase, localInlineViewStyle, remoteInlineViewStyle, upkColumnNames, universalPrimaryKey, birthdayOfSubject, fieldProcTables, rowIdSupport, executionContext); entityGraph.setBirthdayOfSubject(birthdayOfSubject); localSession.executeUpdate( "Insert into " + dmlTableReference(ENTITY, localSession) + "(r_entitygraph, " + universalPrimaryKey.columnList(null) + ", birthday, orig_birthday, type) " + "Select " + newGraphID + ", " + universalPrimaryKey.columnList(null) + ", birthday, birthday, type From " + dmlTableReference(ENTITY, localSession) + " Where r_entitygraph=" + graphID + ""); return entityGraph; } /** * Finds an entity-graph. * * @param graphID the unique ID of the graph * @param universalPrimaryKey the universal primary key * @param localSession for executing SQL-Statements * @return the entity-graph */ public EntityGraph find(int graphID, Session localSession, PrimaryKey universalPrimaryKey) throws IOException, SQLException { LocalEntityGraph entityGraph = new LocalEntityGraph(graphID, localSession, executionContext); final boolean[] found = new boolean[1]; found[0] = false; localSession.executeQuery("Select * From " + dmlTableReference(ENTITY_GRAPH, localSession) + "Where id=" + graphID + "", new Session.ResultSetReader() { public void readCurrentRow(ResultSet resultSet) throws SQLException { found[0] = true; } public void close() { } }); if (!found[0]) { throw new RuntimeException("entity-graph " + graphID + " not found"); } return entityGraph; } /** * Gets the age of the graph. * * @return the age of the graph */ public int getAge() throws SQLException { final int[] age = new int[1]; age[0] = -1; localSession.executeQuery("Select age From " + dmlTableReference(ENTITY_GRAPH, localSession) + " Where id=" + graphID + "", new Session.ResultSetReader() { public void readCurrentRow(ResultSet resultSet) throws SQLException { age[0] = resultSet.getInt(1); } public void close() { } }); return age[0]; } /** * Sets the age of the graph. * * @param age the age of the graph */ public void setAge(int age) throws SQLException { localSession.executeUpdate("Update " + dmlTableReference(ENTITY_GRAPH, localSession) + " Set age=" + age + " Where id=" + graphID + ""); } /** * Gets the number of entities in the graph. * * @return the number of entities in the graph */ public long getSize() throws SQLException { final int[] size = new int[1]; size[0] = -1; localSession.executeQuery("Select count(*) From " + dmlTableReference(ENTITY, localSession) + " Where r_entitygraph=" + graphID + " and birthday >= 0", new Session.ResultSetReader() { public void readCurrentRow(ResultSet resultSet) throws SQLException { size[0] = resultSet.getInt(1); } public void close() { } }); return size[0]; } /** * Deletes the graph. */ public void delete() throws SQLException { localSession.executeUpdate("Delete from " + dmlTableReference(DEPENDENCY, localSession) + " Where r_entitygraph=" + graphID + ""); localSession.executeUpdate("Delete from " + dmlTableReference(ENTITY, localSession) + " Where r_entitygraph=" + graphID + ""); localSession.executeUpdate("Delete from " + dmlTableReference(ENTITY_GRAPH, localSession) + " Where id=" + graphID + ""); } /** * Adds entities to the graph. * * @param table the table * @param condition the condition in SQL that the entities must fulfill * @param today the birthday of the new entities * * @return row-count */ public long addEntities(Table table, String condition, int today) throws SQLException { // checkPseudoColumns(table, condition); return addEntities(table, "T", condition, today); } /** * The pseudo-columns $DISTANCE and $IS_SUBJECT are currently not supported * if the "working table scope" "local database" is used. It works with * "global tables" or "temporary tables". */ private void checkPseudoColumns(Table table, String condition) { if (condition != null) { if (!condition.equals(SqlUtil.resolvePseudoColumns(condition, "A", "B", 0, 0, inDeleteMode))) { throw new IllegalArgumentException( "Unsupported use of pseudo-columns in condition:\n\"" + condition + "\"\n(Table " + table.getName() + ")\n\n" + "When generating delete-scripts, the pseudo-columns $DISTANCE and $IS_SUBJECT are currently not supported " + "if the \"working table scope\" is \"local database\". It works with " + "\"global tables\" or \"temporary tables\"."); } } } /** * Resolves an association. Retrieves and adds all entities * associated with an entity born yesterday in the graph * and adds the dependencies. * * @param table the table * @param association the association to resolve * @param today the birthday of the new entities * * @return row-count or -1, if association is ignored */ public long resolveAssociation(final Table table, Association association, final int today) throws SQLException { if (association.getJoinCondition() != null) { final String jc = SqlUtil.resolvePseudoColumns(association.getJoinCondition(), today, birthdayOfSubject, association.reversed, inDeleteMode); final String destAlias; final String sourceAlias; if (association.reversed) { destAlias = "A"; sourceAlias = "B"; } else { destAlias = "B"; sourceAlias = "A"; } Integer associationExplanationIDm = 0; if (explain) { synchronized (explainIdOfAssociation) { associationExplanationIDm = explainIdOfAssociation.get(association); if (associationExplanationIDm == null) { associationExplanationIDm = (nextExplainID++); explainIdOfAssociation.put(association, associationExplanationIDm); } } } final Integer associationExplanationID = associationExplanationIDm; // ---- final Table destination = association.destination; final String condition = "E.r_entitygraph=" + graphID + " and E.birthday = " + (today - 1) + " and E.type=" + typeName(table) + ""; final Table source = association.source; String select; select = "Select " + upkColumnList(source, "E", null) + " From " + dmlTableReference(ENTITY, localSession) + " E" + " Where " + condition; final long[] rc = new long[1]; localSession.executeQuery(select, new RemoteInlineViewBuilder("E", upkColumnList(source, null)) { @Override protected void process(String inlineView) throws SQLException { String select = "Select distinct " + pkList(destination, destAlias) + " From " + inlineView + " join " + quoting.requote(source.getName()) + " " + sourceAlias + " on " + pkEqualsEntityID(source, sourceAlias, "E", "", false) + " join " + quoting.requote(destination.getName()) + " " + destAlias + " on (" + jc + ")"; remoteSession.executeQuery(select, new LocalInlineViewBuilder(destAlias, upkColumnList(destination, null)) { @Override protected void process(String inlineView) throws SQLException { Map<Column, Column> match = upkMatch(destination); StringBuffer sb = new StringBuffer(); for (Column column: universalPrimaryKey.getColumns()) { if (sb.length() > 0) { sb.append(" and "); } Column tableColumn = match.get(column); sb.append("Duplicate." + column.name); if (tableColumn != null) { sb.append("=" + destAlias + "." + column.name); } else { sb.append(" is null"); } } String entityJoinCondition = sb.toString(); String select = "Select " + graphID + " as GRAPH_ID, " + upkColumnList(destination, destAlias, null) + ", " + today + " AS BIRTHDAY, " + typeName(destination) + " AS TYPE" + (source == null || !explain? "" : ", " + associationExplanationID + " AS ASSOCIATION, " + typeName(source) + " AS SOURCE_TYPE, " + upkColumnList(source, "PRE_")) + " From " + inlineView + " left join " + dmlTableReference(ENTITY, localSession) + " Duplicate on Duplicate.r_entitygraph=" + graphID + " and Duplicate.type=" + typeName(destination) + " and " + entityJoinCondition + " Where Duplicate.type is null"; String insert = "Insert into " + dmlTableReference(ENTITY, localSession) + " (r_entitygraph, " + upkColumnList(destination, null) + ", birthday, type" + (source == null || !explain? "" : ", association, PRE_TYPE, " + upkColumnList(source, "PRE_")) + ") " + select; rc[0] += localSession.executeUpdate(insert); totalRowcount += rc[0]; } }); } }); return rc[0]; } return -1; } /** * Adds entities to the graph. * * @param table the table * @param condition the condition in SQL that the entities must fulfill with 'E' as alias for the entity-table * @param joinedTable optional table to join with * @param source optional, the source-table * @param joinCondition optional condition to join with <code>joinedTable</code> * @param joinWithEntity whether to join with entity-table too * @param today the birthday of the new entities * * @return row-count */ private long addEntities(final Table table, final String alias, String condition, final int today) throws SQLException { String select = "Select " + pkList(table, alias) + " From " + quoting.requote(table.getName()) + " " + alias + " Where (" + condition + ")"; final long[] rc = new long[1]; remoteSession.executeQuery(select, new LocalInlineViewBuilder(alias, upkColumnList(table, null)) { @Override protected void process(String inlineView) throws SQLException { Map<Column, Column> match = upkMatch(table); StringBuffer sb = new StringBuffer(); for (Column column: universalPrimaryKey.getColumns()) { if (sb.length() > 0) { sb.append(" and "); } Column tableColumn = match.get(column); sb.append("Duplicate." + column.name); if (tableColumn != null) { sb.append("=" + alias + "." + column.name); } else { sb.append(" is null"); } } String entityJoinCondition = sb.toString(); String select = "Select " + graphID + " as GRAPH_ID, " + upkColumnList(table, alias, null) + ", " + today + " AS BIRTHDAY, " + typeName(table) + " AS TYPE" + " From " + inlineView + " left join " + dmlTableReference(ENTITY, localSession) + " Duplicate on Duplicate.r_entitygraph=" + graphID + " and Duplicate.type=" + typeName(table) + " and " + entityJoinCondition + " Where Duplicate.type is null"; String insert = "Insert into " + dmlTableReference(ENTITY, localSession) + " (r_entitygraph, " + upkColumnList(table, null) + ", birthday, type) " + select; rc[0] += localSession.executeUpdate(insert); totalRowcount += rc[0]; } }); return rc[0]; } /** * Adds dependencies. * * @param from source of dependency * @param fromAlias alias for from-table * @param to destination of dependency * @param toAlias alias for to-table * @param condition condition of dependency * @param aggregationId id of aggregation association (for XML export), 0 if not applicable * @param dependencyId id of dependency */ public void addDependencies(final Table from, final String fromAlias, final Table to, final String toAlias, final String condition, final int aggregationId, final int dependencyId, boolean isAssociationReversed) throws SQLException { checkPseudoColumns(from, condition); String upkColumnList = upkColumnList(from, "E1", null); String select = "Select " + upkColumnList + " From " + dmlTableReference(ENTITY, localSession) + " E1 " + " Where E1.r_entitygraph=" + graphID + " and E1.type=" + typeName(from) + ""; localSession.executeQuery(select, new RemoteInlineViewBuilder("E1", upkColumnList(from, null, null)) { @Override protected void process(String inlineView) throws SQLException { String upkColumnList = upkColumnList(from, "E1", null); String select = "Select " + upkColumnList + ", " + pkList(to, toAlias) + " From " + inlineView + ", " + quoting.requote(from.getName()) + " " + fromAlias + ", " + quoting.requote(to.getName()) + " " + toAlias + " Where (" + condition + ")" + " and " + pkEqualsEntityID(from, fromAlias, "E1", "", false); remoteSession.executeQuery(select, new LocalInlineViewBuilder("E1E2", upkColumnList(from, null, "E1") + ", " + upkColumnList(to, null, "E2"), true) { @Override protected void process(String inlineView) throws SQLException { // (Select 1002 E1PK0, 7934 E1PK1, '2007-01-01' E1PK2, 7934 E2PK0 Union all Map<Column, Column> match = upkMatch(to); StringBuffer sb = new StringBuffer(); for (Column column: universalPrimaryKey.getColumns()) { Column tableColumn = match.get(column); if (tableColumn != null) { if (sb.length() > 0) { sb.append(" and "); } sb.append("E2" + "." + "" + column.name); sb.append("=" + "E1E2" + ".E2" + column.name); } } String pkEqualsEntityID = sb.toString(); String insert = "Insert into " + dmlTableReference(DEPENDENCY, localSession) + "(r_entitygraph, assoc, depend_id, from_type, to_type, " + upkColumnList(from, "FROM_") + ", " + upkColumnList(to, "TO_") + ") " + "Select " + graphID + ", " + aggregationId + ", " + dependencyId + ", " + typeName(from) + ", " + typeName(to) + ", " + upkColumnList(from, "E1E2", "E1") + ", " + upkColumnList(to, "E1E2", "E2") + " From " + inlineView + ", " + dmlTableReference(ENTITY, localSession) + " E2" + " Where E2.r_entitygraph=" + graphID + "" + " and E2.type=" + typeName(to) + "" + " and " + pkEqualsEntityID; totalRowcount += localSession.executeUpdate(insert); } }); } }); } /** * Gets distinct association-ids of all edged. */ public Set<Integer> getDistinctDependencyIDs() throws SQLException { String select = "Select distinct depend_id from " + dmlTableReference(DEPENDENCY, localSession) + " Where r_entitygraph=" + graphID; final Set<Integer> ids = new HashSet<Integer>(); localSession.executeQuery(select, new Session.ResultSetReader() { public void readCurrentRow(ResultSet resultSet) throws SQLException { ids.add(resultSet.getInt(1)); } public void close() { } }); return ids; } /** * Marks all entities of a given table which don't dependent on other entities, * s.t. they can be read and deleted. */ public void markIndependentEntities(Table table) throws SQLException { StringBuffer fromEqualsPK = new StringBuffer(); Map<Column, Column> match = upkMatch(table); for (Column column: universalPrimaryKey.getColumns()) { if (fromEqualsPK.length() > 0) { fromEqualsPK.append(" and "); } if (match.get(column) != null) { fromEqualsPK.append("D.FROM_" + column.name + "=" + dmlTableReference(ENTITY, localSession) + "." + column.name); } else { fromEqualsPK.append("D.FROM_" + column.name + " is null and " + dmlTableReference(ENTITY, localSession) + "." + column.name + " is null"); } } localSession.executeUpdate( "Update " + dmlTableReference(ENTITY, localSession) + " set birthday=0 " + "Where r_entitygraph=" + graphID + " and birthday>0 and " + (table != null? "type=" + typeName(table) + " and " : "") + "not exists (Select * from " + dmlTableReference(DEPENDENCY, localSession) + " D " + "Where D.r_entitygraph=" + graphID + " and D.assoc=0 and D.from_type=" + dmlTableReference(ENTITY, localSession) + ".type and " + fromEqualsPK + ")"); } private Map<Column, Column> upkMatch(Table table) { return universalPrimaryKey.match(rowIdSupport.getPrimaryKey(getDatamodel().getTable(table.getName()))); } /** * Marks all rows which are not target of a dependency. */ public void markRoots(Table table) throws SQLException { StringBuffer toEqualsPK = new StringBuffer(); Map<Column, Column> match = upkMatch(table); for (Column column: universalPrimaryKey.getColumns()) { if (toEqualsPK.length() > 0) { toEqualsPK.append(" and "); } if (match.containsKey(column)) { toEqualsPK.append("D.TO_" + column.name + "=" + dmlTableReference(ENTITY, localSession) + "." + column.name); } else { toEqualsPK.append("D.TO_" + column.name + " is null and " + dmlTableReference(ENTITY, localSession) + "." + column.name + " is null"); } } localSession.executeUpdate( "Update " + dmlTableReference(ENTITY, localSession) + " set birthday=0 " + "Where r_entitygraph=" + graphID + " and birthday>0 and type=" + typeName(table) + " and " + "not exists (Select * from " + dmlTableReference(DEPENDENCY, localSession) + " D " + "Where D.r_entitygraph=" +graphID + " and D.to_type=" + dmlTableReference(ENTITY, localSession) + ".type and " + toEqualsPK + ")"); } /** * Reads all entities of a given table which are marked as independent or as roots. * * @param table the table * @param orderByPK if <code>true</code>, result will be ordered by primary keys */ public void readMarkedEntities(Table table, boolean orderByPK) throws SQLException { Session.ResultSetReader reader = getTransformerFactory().create(table); readMarkedEntities(table, reader, filteredSelectionClause(table), orderByPK); } /** * Reads all entities of a given table which are marked as independent or as roots. * * @param reader for reading the result-set * @param table the table * @param orderByPK if <code>true</code>, result will be ordered by primary keys */ private void readMarkedEntities(final Table table, final Session.ResultSetReader reader, final String selectionSchema, final boolean orderByPK) throws SQLException { String orderBy = ""; String upkColumnList = upkColumnList(table, null, ""); if (orderByPK) { orderBy = " order by " + upkColumnList; } String select = "Select " + upkColumnList + " From " + dmlTableReference(ENTITY, localSession) + " E" + " Where E.birthday=0 and E.r_entitygraph=" + graphID + " and E.type=" + typeName(table) + "" + orderBy; localSession.executeQuery(select, new RemoteInlineViewBuilder("E", upkColumnList) { @Override protected void process(String inlineView) throws SQLException { String orderBy = ""; if (orderByPK) { orderBy = " order by " + rowIdSupport.getPrimaryKey(table).columnList("T.", quoting); } long rc = remoteSession.executeQuery( "Select " + selectionSchema + " From " + inlineView + " join " + quoting.requote(table.getName()) + " T on " + pkEqualsEntityID(table, "T", "E", "", false) + orderBy, reader); ProgressListenerRegistry.getProgressListener().exported(table, rc); } }); } /** * Reads all entities of a given table which are marked as independent or as roots. * * @param reader for reading the result-set * @param table the table * @param orderByPK if <code>true</code>, result will be ordered by primary keys */ public void readMarkedEntities(final Table table, final Session.ResultSetReader reader, final String selectionSchema, final String originalPKAliasPrefix, final boolean orderByPK) throws SQLException { if (originalPKAliasPrefix == null) { readMarkedEntities(table, reader, selectionSchema, orderByPK); return; } String orderBy = ""; String upkColumnList = upkColumnList(table, null, ""); if (orderByPK) { orderBy = " order by " + upkColumnList; } String select = "Select " + upkColumnList + " From " + dmlTableReference(ENTITY, localSession) + " E" + " Where E.birthday=0 and E.r_entitygraph=" + graphID + " and E.type=" + typeName(table) + "" + orderBy; localSession.executeQuery(select, new RemoteInlineViewBuilder("E", upkColumnList) { @Override protected void process(String inlineView) throws SQLException { String orderBy = ""; if (orderByPK) { orderBy = " order by " + rowIdSupport.getPrimaryKey(table).columnList("T.", quoting); } StringBuffer sb = new StringBuffer(); StringBuffer selectOPK = new StringBuffer(); List<Column> pkColumns = rowIdSupport.getPrimaryKey(table).getColumns(); for (int i = 0; i < pkColumns.size(); ++i) { if (i > 0) { sb.append(", "); selectOPK.append(", "); } sb.append(originalPKAliasPrefix + i); selectOPK.append("T." + quoting.requote(pkColumns.get(i).name) + " AS " + originalPKAliasPrefix + i); } String sqlQuery = "Select " + selectionSchema + " From (" + "Select " + selectOPK + ", " + filteredSelectionClause(table) + " From " + inlineView + " join " + quoting.requote(table.getName()) + " T on " + pkEqualsEntityID(table, "T", "E", "", false) + ") T "; long rc = remoteSession.executeQuery( sqlQuery + orderBy, reader); ProgressListenerRegistry.getProgressListener().exported(table, rc); } }); } /** * Unites the graph with another one and deletes the other graph. * * @param graph the graph to be united with this graph */ public void uniteWith(EntityGraph graph) throws SQLException { StringBuffer e1EqualsE2 = new StringBuffer(); for (Column column: universalPrimaryKey.getColumns()) { if (e1EqualsE2.length() > 0) { e1EqualsE2.append(" and "); } e1EqualsE2.append("E1." + column.name + "=E2." + column.name); } localSession.executeUpdate("Update " + dmlTableReference(ENTITY, localSession) + " E1 " + "set E1.r_entitygraph=" + graphID + " " + "Where E1.r_entitygraph=" + graph.graphID + " " + "and not exists(Select * from " + dmlTableReference(ENTITY, localSession) + " E2 Where " + "E2.r_entitygraph=" + graphID + " and " + e1EqualsE2 + ")"); graph.delete(); } /** * Reads all entities of a given table. * * @param table the table * @param orderByPK if <code>true</code>, result will be ordered by primary keys */ public void readEntities(Table table, boolean orderByPK) throws SQLException { Session.ResultSetReader reader = getTransformerFactory().create(table); long rc = readEntities(table, orderByPK, reader); ProgressListenerRegistry.getProgressListener().exported(table, rc); } /** * Reads all entities of a given table. * * @param table the table * @param orderByPK if <code>true</code>, result will be ordered by primary keys */ private long readEntities(final Table table, final boolean orderByPK, final Session.ResultSetReader reader) throws SQLException { String upkColumnList = upkColumnList(table, "E", null); String select = "Select " + upkColumnList + " From " + dmlTableReference(ENTITY, localSession) + " E " + " Where E.birthday>=0 and E.r_entitygraph=" + graphID + " and E.type=" + typeName(table) + ""; if (orderByPK) { select += " order by " + upkColumnList; } final long[] rc = new long[1]; localSession.executeQuery(select, new RemoteInlineViewBuilder("E", upkColumnList(table, null, null)) { @Override protected void process(String inlineView) throws SQLException { String sqlQuery = "Select " + filteredSelectionClause(table) + " From " + inlineView + " join " + quoting.requote(table.getName()) + " T on " + pkEqualsEntityID(table, "T", "E", "", false); if (orderByPK) { String sqlQueryWithOrderBy = sqlQuery + " order by " + rowIdSupport.getPrimaryKey(table).columnList("T.", quoting); rc[0] += remoteSession.executeQuery(sqlQueryWithOrderBy, reader, sqlQuery, null, 0); } else { rc[0] += remoteSession.executeQuery(sqlQuery, reader); } } }); return rc[0]; } /** * Reads some columns of all entities of a given table without using filters. * * @param table the table * @param columns the columns * @param reader to read */ public long readUnfilteredEntityColumns(final Table table, final List<Column> columns, final Session.ResultSetReader reader) throws SQLException { String upkColumnList = upkColumnList(table, "E", null); String select = "Select distinct " + upkColumnList + " From " + dmlTableReference(ENTITY, localSession) + " E " + " Where E.birthday>=0 and E.r_entitygraph=" + graphID + " and E.type=" + typeName(table) + ""; StringBuilder sb = new StringBuilder(); boolean first = true; for (Column c: columns) { if (!first) { sb.append(", "); } sb.append("T." + quoting.requote(c.name)); sb.append(" as " + quoting.requote(c.name)); first = false; } final String columnList = sb.toString(); final long[] rc = new long[1]; localSession.executeQuery(select, new RemoteInlineViewBuilder("E", upkColumnList(table, null, null)) { @Override protected void process(String inlineView) throws SQLException { String sqlQuery = "Select distinct " + columnList + " From " + inlineView + " join " + quoting.requote(table.getName()) + " T on " + pkEqualsEntityID(table, "T", "E", "", false); rc[0] += remoteSession.executeQuery(sqlQuery, reader); } }); return rc[0]; } /** * Updates columns of a table. * * @param table the table * @param columns the columns; */ public void updateEntities(Table table, Set<Column> columns, OutputStreamWriter scriptFileWriter, DBMS targetConfiguration) throws SQLException { Session.ResultSetReader reader = new UpdateTransformer(table, columns, scriptFileWriter, executionContext.getNumberOfEntities(), getTargetSession(), targetConfiguration, importFilterManager, executionContext); readEntities(table, false, reader); } /** * Gets select clause for reading rows of given type * with respect of the column filters. * * @param table the table to read rows from * @return select clause */ private String filteredSelectionClause(Table table) { StringBuilder sb = new StringBuilder(); boolean first = true; for (Column c: table.getSelectionClause(localSession)) { if (!first) { sb.append(", "); } String filterExpression = null; if (c.getFilter() != null && c.getFilter().isApplyAtExport()) { filterExpression = c.getFilterExpression(); } if (filterExpression != null) { if (filterExpression.trim().toLowerCase().startsWith("select")) { sb.append("(" + filterExpression + ")"); } else { sb.append(filterExpression); } } else { sb.append("T." + quoting.requote(c.name)); } sb.append(" as " + quoting.requote(c.name)); first = false; } return sb.toString(); } /** * Deletes all entities which are marked as independent. */ public void deleteIndependentEntities(Table table) throws SQLException { StringBuffer fromEqualsPK = new StringBuffer(); StringBuffer toEqualsPK = new StringBuffer(); Map<Column, Column> match = upkMatch(table); for (Column column: universalPrimaryKey.getColumns()) { if (fromEqualsPK.length() > 0) { fromEqualsPK.append(" and "); } if (match.containsKey(column)) { fromEqualsPK.append(dmlTableReference(DEPENDENCY, localSession) + ".FROM_" + column.name + "=" + column.name); } else { fromEqualsPK.append(dmlTableReference(DEPENDENCY, localSession) + ".FROM_" + column.name + " is null and " + column.name + " is null"); } if (toEqualsPK.length() > 0) { toEqualsPK.append(" and "); } if (match.containsKey(column)) { toEqualsPK.append(dmlTableReference(DEPENDENCY, localSession) + ".TO_" + column.name + "=" + column.name); } else { toEqualsPK.append(dmlTableReference(DEPENDENCY, localSession) + ".TO_" + column.name + " is null and " + column.name + " is null"); } } localSession.executeUpdate( "Delete From " + dmlTableReference(DEPENDENCY, localSession) + " " + "Where " + dmlTableReference(DEPENDENCY, localSession) + ".r_entitygraph=" + graphID + " and assoc=0 and from_type=" + typeName(table) + " and " + "exists (Select * from " + dmlTableReference(ENTITY, localSession) + " E Where " + "E.r_entitygraph=" + graphID + " and " + fromEqualsPK + " and " + dmlTableReference(DEPENDENCY, localSession) + ".from_type=E.type and " + "E.birthday=0)"); localSession.executeUpdate( "Delete From " + dmlTableReference(DEPENDENCY, localSession) + " " + "Where " + dmlTableReference(DEPENDENCY, localSession) + ".r_entitygraph=" + graphID + " and assoc=0 and to_type=" + typeName(table) + " and " + "exists (Select * from " + dmlTableReference(ENTITY, localSession) + " E Where " + "E.r_entitygraph=" + graphID + " and " + toEqualsPK + " and " + dmlTableReference(DEPENDENCY, localSession) + ".to_type=E.type and " + "E.birthday=0)"); localSession.executeUpdate( "Delete From " + dmlTableReference(ENTITY, localSession) + " " + "Where r_entitygraph=" + graphID + " and type=" + typeName(table) + " and " + "birthday=0"); } /** * Deletes all entities from a given table. */ public long deleteEntities(Table table) throws SQLException { return localSession.executeUpdate( "Delete From " + dmlTableReference(ENTITY, localSession) + " " + "Where r_entitygraph=" + graphID + " and " + "type=" + typeName(table)); } /** * Counts the entities of a given table in this graph. * * @param table the table * @return the number of entities from table in this graph */ public long countEntities(Table table) throws SQLException { final long[] count = new long[1]; localSession.executeQuery( "Select count(*) from " + dmlTableReference(ENTITY, localSession) + " E " + "Where E.birthday>=0 and E.r_entitygraph=" + graphID + " and E.type=" + typeName(table) + "", new Session.AbstractResultSetReader() { public void readCurrentRow(ResultSet resultSet) throws SQLException { count[0] = resultSet.getLong(1); } }); return count[0]; } /** * Removes all entities from this graph which are associated with an entity * outside the graph. * * @param deletedEntitiesAreMarked if true, consider entity as deleted if its birthday is negative * @param association the association * @return number of removed entities */ public long removeAssociatedDestinations(final Association association, final boolean deletedEntitiesAreMarked) throws SQLException { final String jc = association.getJoinCondition(); checkPseudoColumns(association.source, jc); if (jc != null) { final String destAlias; final String sourceAlias; if (association.reversed) { destAlias = "A"; sourceAlias = "B"; } else { destAlias = "B"; sourceAlias = "A"; } final int setId = getNextSetId(); final long[] rc = new long[1]; String selectEB = "Select " + upkColumnList(association.destination, "EB", "") + " from " + dmlTableReference(ENTITY, localSession) + " EB " + "Where " + (deletedEntitiesAreMarked? "EB.birthday>=0 and " : "") + "EB.r_entitygraph=" + graphID + " and EB.type=" + typeName(association.destination) + " "; localSession.executeQuery(selectEB, new RemoteInlineViewBuilder("EB", upkColumnList(association.destination, ""), true) { @Override protected void process(String inlineView) throws SQLException { String selectSource = "Select distinct " + upkColumnList(association.destination, "EB", "") + ", " + pkList(association.source, sourceAlias, "") + " from " + inlineView + " " + "join " + quoting.requote(association.destination.getName()) + " " + destAlias + " on "+ pkEqualsEntityID(association.destination, destAlias, "EB", "", false) + " " + "join " + quoting.requote(association.source.getName()) + " " + sourceAlias + " " + " on " + jc; remoteSession.executeQuery(selectSource, new LocalInlineViewBuilder("EBA", upkColumnList(association.destination, null, "EB") + ", " + upkColumnList(association.source, "A"), true) { @Override protected void process(String inlineView) throws SQLException { Map<Column, Column> match = upkMatch(association.source); StringBuffer eBAEqualsEA = new StringBuffer(); for (Column column: universalPrimaryKey.getColumns()) { Column tableColumn = match.get(column); if (tableColumn != null) { if (eBAEqualsEA.length() > 0) { eBAEqualsEA.append(" and "); } eBAEqualsEA.append("EBA.A" + column.name); eBAEqualsEA.append("=EA." + column.name); } } String selectEB = "Select distinct " + setId + ", " + typeName(association.destination) + ", " + upkColumnList(association.destination, "EBA", "EB") + " from " + inlineView + (deletedEntitiesAreMarked? " join " : " left join ") + dmlTableReference(ENTITY, localSession) + " EA" + " on EA.r_entitygraph=" + graphID + " and EA.type=" + typeName(association.source) + "" + " and " + eBAEqualsEA + " Where " + (deletedEntitiesAreMarked? "EA.birthday=-1" : "EA.type is null"); String remove = "Insert into " + dmlTableReference(ENTITY_SET_ELEMENT, localSession) + "(set_id, type, " + upkColumnList(association.destination, null, "") + ") " + selectEB; long rcl = localSession.executeUpdate(remove); totalRowcount += rcl; if (rcl > 0) { match = upkMatch(association.destination); StringBuffer sEqualsE = new StringBuffer(); StringBuffer sEqualsEWoAlias = new StringBuffer(); for (Column column: universalPrimaryKey.getColumns()) { if (sEqualsE.length() > 0) { sEqualsE.append(" and "); } if (sEqualsEWoAlias.length() > 0) { sEqualsEWoAlias.append(" and "); } if (match.containsKey(column)) { sEqualsE.append("S." + column.name + "=E." + column.name); sEqualsEWoAlias.append("S." + column.name + "=" + dmlTableReference(ENTITY, localSession) + "." + column.name); } else { sEqualsE.append("S." + column.name + " is null and E." + column.name + " is null"); sEqualsEWoAlias.append("S." + column.name + " is null and " + dmlTableReference(ENTITY, localSession) + "." + column.name + " is null"); } } remove = "Update " + dmlTableReference(ENTITY, localSession) + " E set E.birthday=-1 Where E.r_entitygraph=" + graphID + " and E.type=" + typeName(association.destination) + " " + "and exists (Select * from " + dmlTableReference(ENTITY_SET_ELEMENT, localSession) + " S where S.set_id=" + setId + " and E.type=S.type and " + sEqualsE + ") " + "and E.birthday<>-1"; boolean silent = localSession.getSilent(); try { localSession.setSilent(true); long r = localSession.executeUpdate(remove); rc[0] += r; } catch (SQLException e) { // postgreSQL Session._log.debug("failed, retry without alias (" + e.getMessage() + ")"); remove = "Update " + dmlTableReference(ENTITY, localSession) + " set birthday=-1 Where " + dmlTableReference(ENTITY, localSession) + ".r_entitygraph=" + graphID + " and " + dmlTableReference(ENTITY, localSession) + ".type=" + typeName(association.destination) + " " + "and exists (Select * from " + dmlTableReference(ENTITY_SET_ELEMENT, localSession) + " S where S.set_id=" + setId + " and " + dmlTableReference(ENTITY, localSession) + ".type=S.type and " + sEqualsEWoAlias + ") " + "and " + dmlTableReference(ENTITY, localSession) + ".birthday<>-1"; rc[0] += localSession.executeUpdate(remove); } finally { localSession.setSilent(silent); } localSession.executeUpdate("Delete from " + dmlTableReference(ENTITY_SET_ELEMENT, localSession) + " where set_id=" + setId + ""); } } }); } }); return rc[0]; } return 0; } /** * Reads all entities which depends on given entity. * * @param table the table from which to read entities * @param association the dependency * @param resultSet current row is given entity * @param reader reads the entities * @param selectionSchema the selection schema */ public void readDependentEntities(final Table table, final Association association, final ResultSet resultSet, ResultSetMetaData resultSetMetaData, final ResultSetReader reader, final Map<String, Integer> theTypeCache, final String selectionSchema, final String originalPKAliasPrefix) throws SQLException { CellContentConverter cellContentConverter = new CellContentConverter(resultSetMetaData, localSession, localSession.dbms); String select = "Select " + upkColumnList(table, "TO_") + " from " + dmlTableReference(DEPENDENCY, localSession) + " D" + " Where " + pkEqualsEntityID(association.source, resultSet, "D", "FROM_", cellContentConverter) + " and D.to_type=" + typeName(table) + "" + " and D.from_type=" + typeName(association.source) + " and assoc=" + association.getId() + " and D.r_entitygraph=" + graphID; localSession.executeQuery(select, new RemoteInlineViewBuilder("D", upkColumnList(table, "TO_"), true) { @Override protected void process(String inlineView) throws SQLException { String select; if (originalPKAliasPrefix != null) { StringBuffer selectOPK = new StringBuffer(); List<Column> pkColumns = rowIdSupport.getPrimaryKey(table).getColumns(); for (int i = 0; i < pkColumns.size(); ++i) { if (i > 0) { selectOPK.append(", "); } selectOPK.append("T." + quoting.requote(pkColumns.get(i).name) + " AS " + originalPKAliasPrefix + i); } select = "Select " + selectionSchema + " from (" + "Select " + selectOPK + ", " + filteredSelectionClause(table) + " from " + quoting.requote(table.getName()) + " T join " + inlineView + " on " + pkEqualsEntityID(table, "T", "D", "TO_", false) + ") T"; } else { select = "Select " + selectionSchema + " from " + quoting.requote(table.getName()) + " T join " + inlineView + " on " + pkEqualsEntityID(table, "T", "D", "TO_", false) + ""; } long rc = remoteSession.executeQuery(select, reader); ProgressListenerRegistry.getProgressListener().exported(table, rc); } }); } /** * Marks all entities which depends on given entity as traversed. * * @param table the table from which to read entities * @param association the dependency * @param resultSet current row is given entity */ public void markDependentEntitiesAsTraversed(Association association, ResultSet resultSet, ResultSetMetaData resultSetMetaData, Map<String, Integer> typeCache) throws SQLException { String update; CellContentConverter cellContentConverter = new CellContentConverter(resultSetMetaData, localSession, localSession.dbms); if (DBMS.SYBASE.equals(localSession.dbms)) { update = "Update " + dmlTableReference(DEPENDENCY, localSession) + " set traversed=1" + " Where " + pkEqualsEntityID(association.source, resultSet, dmlTableReference(DEPENDENCY, localSession), "FROM_", cellContentConverter) + " and " + dmlTableReference(DEPENDENCY, localSession) + ".from_type=" + typeName(association.source) + " and assoc=" + association.getId() + " and " + dmlTableReference(DEPENDENCY, localSession) + ".r_entitygraph=" + graphID; } else { update = "Update " + dmlTableReference(DEPENDENCY, localSession) + " D set traversed=1" + " Where " + pkEqualsEntityID(association.source, resultSet, "D", "FROM_", cellContentConverter) + " and D.from_type=" + typeName(association.source) + " and assoc=" + association.getId() + " and D.r_entitygraph=" + graphID; } localSession.executeUpdate(update); } /** * Reads all non-traversed dependencies. * * @param table the source of dependencies to look for * @param reader reads the entities */ public void readNonTraversedDependencies(Table table, ResultSetReader reader) throws SQLException { String select = "Select * from " + dmlTableReference(DEPENDENCY, localSession) + " D " + " Where (traversed is null or traversed <> 1)" + " and D.from_type=" + typeName(table) + "" + " and D.r_entitygraph=" + graphID; localSession.executeQuery(select, reader); } /** * Removes all reflexive dependencies of given table. * * @param table the table */ public void removeReflexiveDependencies(Table table) throws SQLException { Map<Column, Column> match = upkMatch(table); StringBuffer sb = new StringBuffer(); for (Column column: universalPrimaryKey.getColumns()) { Column tableColumn = match.get(column); if (tableColumn != null) { if (sb.length() > 0) { sb.append(" and "); } sb.append("FROM_" + column.name + " = TO_" + column.name); } } String delete = "Delete from " + dmlTableReference(DEPENDENCY, localSession) + " Where " + sb + " and from_type=" + typeName(table) + "" + " and to_type=" + typeName(table) + "" + " and r_entitygraph=" + graphID; localSession.executeUpdate(delete); } /** * Gets a SQL comparison expression for comparing rows with given entity. * * @param table the table * @param resultSet * @return a SQL comparison expression for comparing rows of <code>table</code> with current row of resultSet */ private String pkEqualsEntityID(Table table, ResultSet resultSet, String alias, String columnPrefix, CellContentConverter cellContentConverter) throws SQLException { Map<Column, Column> match = upkMatch(table); StringBuffer sb = new StringBuffer(); for (Column column: universalPrimaryKey.getColumns()) { if (sb.length() > 0) { sb.append(" and "); } sb.append(alias + "." + columnPrefix + column.name); Column tableColumn = match.get(column); if (tableColumn != null) { int i = 0; for (Column c: rowIdSupport.getPrimaryKey(table).getColumns()) { if (c.name.equals(tableColumn.name)) { break; } ++i; } sb.append("=" + cellContentConverter.toSql(cellContentConverter.toSql(cellContentConverter.getObject(resultSet, "PK" + i)))); } else { sb.append(" is null"); } } return sb.toString(); } private final Set<String> fieldProcTables = new HashSet<String>(); /** * Gets a SQL comparison expression for comparing rows with entities. * * @param table the table * @return a SQL comparison expression for comparing rows of <code>table</code> with entities */ private String pkEqualsEntityID(Table table, String tableAlias, String entityAlias, String columnPrefix, boolean checkNull) { Map<Column, Column> match = upkMatch(table); StringBuffer sb = new StringBuffer(); for (Column column: universalPrimaryKey.getColumns()) { Column tableColumn = match.get(column); if (checkNull || tableColumn != null) { if (sb.length() > 0) { sb.append(" and "); } sb.append(entityAlias + "." + columnPrefix + column.name); if (tableColumn != null) { if (fieldProcTables.contains(table.getUnqualifiedName().toLowerCase())) { sb.append(" = " + tableColumn.type + "(" + tableAlias + "." + quoting.requote(tableColumn.name) + ")"); } else { sb.append("=" + tableAlias + "." + quoting.requote(tableColumn.name)); } } else { sb.append(" is null"); } } } return sb.toString(); } /** * Gets PK-column list for a table. * * @param table the table * @param tableAlias the alias for table * @return PK-column list for table */ private String pkList(Table table, String tableAlias) { return pkList(table, tableAlias, null); } /** * Gets PK-column list for a table. (for Select clause) * * @param table the table * @param tableAlias the alias for table * @param columnAliasPrefix optional prefix for column names */ private String pkList(Table table, String tableAlias, String columnAliasPrefix) { Map<Column, Column> match = upkMatch(table); StringBuffer sb = new StringBuffer(); for (Column column: universalPrimaryKey.getColumns()) { Column tableColumn = match.get(column); if (tableColumn != null) { if (sb.length() > 0) { sb.append(", "); } if (tableAlias != null) { sb.append(tableAlias + "."); } sb.append(quoting.requote(tableColumn.name)); sb.append(" AS " + (columnAliasPrefix == null? "" : columnAliasPrefix) + column.name); } } return sb.toString(); } /** * Gets PK-column list for a table. (for Insert clause) * * @param table the table * @param columnAliasPrefix optional prefix for column names */ private String upkColumnList(Table table, String columnAliasPrefix) { return upkColumnList(table, null, columnAliasPrefix); } /** * Gets PK-column list for a table. (for Insert clause) * * @param table the table * @param columnAliasPrefix optional prefix for column names */ private String upkColumnList(Table table, String tableAlias, String columnAliasPrefix) { Map<Column, Column> match = upkMatch(table); StringBuffer sb = new StringBuffer(); for (Column column: universalPrimaryKey.getColumns()) { Column tableColumn = match.get(column); if (tableColumn != null) { if (sb.length() > 0) { sb.append(", "); } if (tableAlias != null) { sb.append(tableAlias + "."); } if (columnAliasPrefix != null) { sb.append(columnAliasPrefix); } sb.append(column.name); } } return sb.toString(); } /** * Gets some statistical information. */ public List<String> getStatistics(final DataModel dataModel, Set<Table> tables) throws SQLException { return getStatistics(localSession, dataModel, tables); } /** * Total row-count. */ private long totalRowcount = 0; /** * Gets total row-count. * * @return total row-count */ public long getTotalRowcount() { return totalRowcount; } /** * Whether or not to store additional information in order to create a 'explain.log' */ private boolean explain = false; /** * Next unique ID for association to be used for explanation. */ private int nextExplainID = 1; /** * Whether or not to store additional information in order to create a 'explain.log'. * * @param explain <code>true</code> iff predecessors of each entity must be stored */ public void setExplain(boolean explain) { this.explain = false; // explain feature is not yet implemented for local entity graph } /** * Gets the universal primary key. * * @return the universal primary key */ public PrimaryKey getUniversalPrimaryKey() { return universalPrimaryKey; } /** * For creation of unique set-ids. */ private int nextSetId = 1; /** * Creates a unique set id. * * @return a unique set id */ private synchronized int getNextSetId() { return graphID + (nextSetId++); } /** * Shuts down statement-executor. */ public void shutDown() throws SQLException { localSession.shutDown(); } @Override public Session getSession() { return localSession; } @Override public DataModel getDatamodel() { return dataModel; } @Override public Session getTargetSession() { return remoteSession; } }