/* * Copyright 2009-2011 Collaborative Research Centre SFB 632 * * 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 annis.sqlgen; import annis.model.QueryNode; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.collections.Bag; import org.apache.commons.collections.bag.HashBag; public class TableAccessStrategy { // default table names public final static String NODE_TABLE = "node"; public final static String RANK_TABLE = "rank"; public final static String COMPONENT_TABLE = "component"; public final static String NODE_ANNOTATION_TABLE = "node_annotation"; public final static String EDGE_ANNOTATION_TABLE = "edge_annotation"; public final static String ANNOTATION_POOL_TABLE = "annotation_pool"; public final static String FACTS_TABLE = "facts"; public final static String CORPUS_TABLE = "corpus"; public final static String CORPUS_ANNOTATION_TABLE = "corpus_annotation"; public final static String TEXT_TABLE = "text"; // the wrapped node private QueryNode node; // table aliases private Map<String, String> tableAliases; // aliased column names private Map<String, Map<String, String>> columnAliases; private Map<String, Boolean> tablePartitioned; public TableAccessStrategy() { this.tableAliases = new HashMap<>(); this.columnAliases = new HashMap<>(); } public TableAccessStrategy(QueryNode node) { this(); this.node = node; } /** Copy constructor */ public TableAccessStrategy(TableAccessStrategy tas) { this.tableAliases = new HashMap<>(tas.getTableAliases()); this.columnAliases = new HashMap<>(tas.getColumnAliases()); this.tablePartitioned = new HashMap<>(); this.node = tas.getNode(); } ///// table and column aliases public String tableName(String table) { return tableName(tableAliases, table); } public String partitionTableName(String table, List<Long> corpusList) { return partitionTableName(tableAliases, tablePartitioned, table, corpusList); } public static String tableName(Map<String, String> tableAliases, String table) { return tableAliases.containsKey(table) ? tableAliases.get(table) : table; } public static String partitionTableName(Map<String, String> tableAliases, Map<String, Boolean> tablePartitioned, String table, List<Long> corpusList) { String result = tableName(tableAliases, table); /* when we only have one corpus and the table partitions are used, optimize the query by directly querying the partition table */ if(corpusList != null && corpusList.size() == 1 && isPartitioned(tablePartitioned, table)) { result = result + "_" + corpusList.get(0); } return result; } public String columnName(String table, String column) { return columnName(columnAliases, table, column); } public static String columnName(Map<String, Map<String,String>> columnAliases, String table, String column) { if (columnAliases.containsKey(table)) { Map<String, String> columns = columnAliases.get(table); if (columns.containsKey(column)) { return columns.get(column); } } return column; } public String aliasedTable(String table, int count) { if (node != null) { // sanity checks // if (table.equals(NODE_ANNOTATION_TABLE) && count > node.getNodeAnnotations().size()) // throw new IllegalArgumentException("access to node annotation table out of range: " + count); if (table.equals(EDGE_ANNOTATION_TABLE) && count > node.getEdgeAnnotations().size()) throw new IllegalArgumentException("access to edge annotation table out of range: " + count); if (table.equals(NODE_TABLE) && count > 1) throw new IllegalArgumentException("access to struct table out of range: " + count); if (table.equals(RANK_TABLE) && count > 1) throw new IllegalArgumentException("access to rank table out of range: " + count); // offset table count for edge annotations if node and edge annotations are the same table if (table.equals(EDGE_ANNOTATION_TABLE) && isMaterialized(EDGE_ANNOTATION_TABLE, NODE_ANNOTATION_TABLE)) count = count + node.getNodeAnnotations().size() - 1; } if (count == 0) { count = 1; } // compute table counts Bag tables = computeSourceTables(node, tableAliases); String aliasedName = tableName(table); String aliasCount = node != null ? String.valueOf(node.getId()) : ""; String countSuffix = tables.getCount(aliasedName) > 1 ? "_" + count : ""; return aliasedName + aliasCount + countSuffix; } public static String aliasedTable(QueryNode node, Map<String, String> tableAliases, String table, int count) { if (node != null) { // sanity checks // if (table.equals(NODE_ANNOTATION_TABLE) && count > node.getNodeAnnotations().size()) // throw new IllegalArgumentException("access to node annotation table out of range: " + count); if (table.equals(EDGE_ANNOTATION_TABLE) && count > node.getEdgeAnnotations().size()) { throw new IllegalArgumentException("access to edge annotation table out of range: " + count); } if (table.equals(NODE_TABLE) && count > 1) { throw new IllegalArgumentException("access to struct table out of range: " + count); } if (table.equals(RANK_TABLE) && count > 1) { throw new IllegalArgumentException("access to rank table out of range: " + count); } // offset table count for edge annotations if node and edge annotations are the same table if (table.equals(EDGE_ANNOTATION_TABLE) && isMaterialized(tableAliases, EDGE_ANNOTATION_TABLE, NODE_ANNOTATION_TABLE)) { count = count + node.getNodeAnnotations().size() - 1; } } if (count == 0) { count = 1; } // compute table counts Bag tables = computeSourceTables(node, tableAliases); String aliasedName = tableName(tableAliases, table); String aliasCount = node != null ? String.valueOf(node.getId()) : ""; String countSuffix = tables.getCount(aliasedName) > 1 ? "_" + count : ""; return aliasedName + aliasCount + countSuffix; } public String aliasedColumn(String table, String column) { return aliasedColumn(tableAliases, columnAliases, node, table, column); } public String aliasedColumn(String table, String column, int count) { return aliasedColumn(tableAliases, columnAliases, node, table, column, count); } public static String aliasedColumn(Map<String, String> tableAliases, Map<String, Map<String,String>> columnAliases, QueryNode node, String table, String column) { return aliasedColumn(tableAliases, columnAliases, node, table, column, 1); } public static String aliasedColumn(Map<String, String> tableAliases, Map<String, Map<String,String>> columnAliases, QueryNode node, String table, String column, int count) { return column(aliasedTable(node, tableAliases, table, count), columnName(columnAliases, table, column)); } protected static String column(String table, String column) { return table + "." + column; } ///// table usage protected Bag computeSourceTables() { return computeSourceTables(node, tableAliases); } protected static Bag computeSourceTables(QueryNode node, Map<String, String> tableAliases) { Bag tables = new HashBag(); // hack to support table selections for ANNOTATE query if (node == null) { String[] tableNames = {NODE_TABLE, RANK_TABLE, COMPONENT_TABLE, NODE_ANNOTATION_TABLE, EDGE_ANNOTATION_TABLE}; for (String table : tableNames) tables.add(table); return tables; } tables.add(tableName(tableAliases, NODE_ANNOTATION_TABLE), node.getNodeAnnotations().size()); if (node.getNodeAnnotations().isEmpty() && node.getNodeAnnotations().size() > 0) tables.add(tableName(tableAliases, NODE_ANNOTATION_TABLE)); tables.add(tableName(tableAliases, EDGE_ANNOTATION_TABLE), node.getEdgeAnnotations().size()); if ( tables.getCount(tableName(tableAliases, RANK_TABLE)) == 0 && usesRankTable(node) ) tables.add(tableName(tableAliases, RANK_TABLE)); if ( tables.getCount(tableName(tableAliases, COMPONENT_TABLE)) == 0 && usesRankTable(node) ) tables.add(tableName(tableAliases, COMPONENT_TABLE)); if (tables.getCount(tableName(tableAliases, NODE_TABLE)) == 0) tables.add(tableName(tableAliases, NODE_TABLE)); return tables; } public boolean usesNodeAnnotationTable() { return usesNodeAnnotationTable(node); } public static boolean usesNodeAnnotationTable(QueryNode node) { return node == null || ! node.getNodeAnnotations().isEmpty(); } public boolean usesRankTable() { return usesRankTable(node); } public static boolean usesRankTable(QueryNode node) { return node == null || usesComponentTable(node) || node.isRoot() || node.getArity() != null; } public boolean usesComponentTable() { return node == null || node.isPartOfEdge() || usesEdgeAnnotationTable(); } public static boolean usesComponentTable(QueryNode node) { return node == null || node.isPartOfEdge() || useEdgeAnnotationTable(node); } public boolean usesEdgeAnnotationTable() { return node == null || ! node.getEdgeAnnotations().isEmpty(); } public static boolean useEdgeAnnotationTable(QueryNode node) { return node == null || ! node.getEdgeAnnotations().isEmpty(); } public boolean isMaterialized(String table, String otherTable) { return isMaterialized(tableAliases, table, otherTable); } public static boolean isMaterialized(Map<String, String> tableAliases, String table, String otherTable) { return tableName(tableAliases, table).equals(tableName(tableAliases, otherTable)); } public boolean isPartitioned(String table) { return isPartitioned(tablePartitioned, table); } public static boolean isPartitioned(Map<String, Boolean> tablePartitioned, String table) { if(tablePartitioned != null) { Boolean result = tablePartitioned.get(table); if(result != null) { return result; } } return false; } ///// delegates public void addTableAlias(String table, String alias) { tableAliases.put(table, alias); } public void addColumnAlias(String table, String column, String alias) { if ( ! columnAliases.containsKey(table) ) columnAliases.put(table, new HashMap<String, String>()); Map<String, String> aliases = columnAliases.get(table); aliases.put(column, alias); } ///// Getter / Setter public QueryNode getNode() { return node; } public void setNode(QueryNode node) { this.node = node; } public Map<String, String> getTableAliases() { return tableAliases; } public void setTableAliases(Map<String, String> tableAliases) { this.tableAliases = tableAliases; } public Map<String, Map<String, String>> getColumnAliases() { return columnAliases; } public void setColumnAliases(Map<String, Map<String, String>> columnAliases) { this.columnAliases = columnAliases; } public Map<String, Boolean> getTablePartitioned() { return tablePartitioned; } public void setTablePartitioned(Map<String, Boolean> tablePartitioned) { this.tablePartitioned = tablePartitioned; } }