METAMODEL-1165: Fixed - added default_table alias table Closes #165
Project: http://git-wip-us.apache.org/repos/asf/metamodel/repo Commit: http://git-wip-us.apache.org/repos/asf/metamodel/commit/fe9ab604 Tree: http://git-wip-us.apache.org/repos/asf/metamodel/tree/fe9ab604 Diff: http://git-wip-us.apache.org/repos/asf/metamodel/diff/fe9ab604 Branch: refs/heads/master Commit: fe9ab604ac568fd41b55a67c0f10fe54f39d53c8 Parents: 1d56a9f Author: Kasper Sørensen <[email protected]> Authored: Wed Nov 1 20:57:57 2017 -0700 Committer: Kasper Sørensen <[email protected]> Committed: Wed Nov 1 20:57:57 2017 -0700 ---------------------------------------------------------------------- CHANGES.md | 1 + .../cassandra/CassandraDataContext.java | 1 + .../apache/metamodel/AbstractDataContext.java | 43 ++--- .../org/apache/metamodel/MetaModelHelper.java | 150 ++++++++-------- .../metamodel/QueryPostprocessDataContext.java | 175 +++++++++---------- .../metamodel/QueryPostprocessDelegate.java | 8 +- .../create/AbstractTableCreationBuilder.java | 3 +- .../org/apache/metamodel/query/SelectItem.java | 153 ++++++++-------- .../org/apache/metamodel/schema/AliasTable.java | 82 +++++++++ .../metamodel/schema/CompositeSchema.java | 1 + .../schema/DefaultTableAliasedSchema.java | 89 ++++++++++ .../apache/metamodel/schema/MutableSchema.java | 16 +- .../apache/metamodel/schema/WrappingSchema.java | 33 ++++ .../apache/metamodel/schema/WrappingTable.java | 33 ++++ .../org/apache/metamodel/MockDataContext.java | 1 + .../metamodel/MockUpdateableDataContext.java | 16 +- .../QueryPostprocessDataContextTest.java | 119 ++++++++++--- .../intercept/InterceptableDataContextTest.java | 151 ++++++++-------- .../apache/metamodel/query/FilterItemTest.java | 18 +- .../metamodel/couchdb/CouchDbDataContext.java | 3 + .../org/apache/metamodel/csv/CsvSchema.java | 2 +- .../apache/metamodel/csv/CsvUpdateCallback.java | 3 +- .../metamodel/csv/CsvDataContextTest.java | 25 ++- .../InterceptionCsvIntegrationTest.java | 2 +- .../metamodel/dynamodb/DynamoDbDataContext.java | 1 + .../nativeclient/ElasticSearchDataContext.java | 1 + .../rest/ElasticSearchRestDataContext.java | 1 + .../metamodel/excel/ExcelDataContext.java | 2 + .../excel/ExcelTableCreationBuilder.java | 2 +- .../metamodel/excel/ExcelDataContextTest.java | 22 +-- .../excel/ExcelUpdateCallbackTest.java | 2 +- .../fixedwidth/FixedWidthDataContext.java | 2 + .../fixedwidth/FixedWidthDataContextTest.java | 10 +- .../metamodel/hbase/HBaseDataContext.java | 2 + .../apache/metamodel/json/JsonDataContext.java | 1 + .../mongodb/mongo2/MongoDbDataContext.java | 1 + .../mongodb/mongo3/MongoDbDataContext.java | 1 + .../metamodel/neo4j/Neo4jDataContext.java | 8 + .../apache/metamodel/pojo/PojoDataContext.java | 1 + .../metamodel/pojo/PojoDataContextTest.java | 4 +- .../salesforce/SalesforceDataContext.java | 4 + .../metamodel/sugarcrm/SugarCrmDataContext.java | 1 + .../apache/metamodel/xml/XmlDomDataContext.java | 3 + .../apache/metamodel/xml/XmlSaxDataContext.java | 1 + .../metamodel/xml/XmlDomDataContextTest.java | 3 +- 45 files changed, 765 insertions(+), 436 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/metamodel/blob/fe9ab604/CHANGES.md ---------------------------------------------------------------------- diff --git a/CHANGES.md b/CHANGES.md index b2ea491..ddd3c9a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,7 @@ * [METAMODEL-1139] - Employed Java 8 functional types (java.util.function) in favor of (now deprecated) Ref, Action, Func. * [METAMODEL-1140] - Allowed SalesforceDataContext without a security token. * [METAMODEL-1141] - Added RFC 4180 compliant CSV parsing. + * [METAMODEL-1165] - Added a convenient alias table "default_table" for single-table data stores. * [METAMODEL-1144] - Optimized evaluation of conditional client-side JOIN statements. * [METAMODEL-1145] - Fixed bug with modelling JDBC table relationships when there are multiple keys involved in the relationship. * [METAMODEL-1151] - Added DataContextFactory classes for instantiating DataContexts of many types based on properties. http://git-wip-us.apache.org/repos/asf/metamodel/blob/fe9ab604/cassandra/src/main/java/org/apache/metamodel/cassandra/CassandraDataContext.java ---------------------------------------------------------------------- diff --git a/cassandra/src/main/java/org/apache/metamodel/cassandra/CassandraDataContext.java b/cassandra/src/main/java/org/apache/metamodel/cassandra/CassandraDataContext.java index 08d3fd9..88c4b67 100644 --- a/cassandra/src/main/java/org/apache/metamodel/cassandra/CassandraDataContext.java +++ b/cassandra/src/main/java/org/apache/metamodel/cassandra/CassandraDataContext.java @@ -87,6 +87,7 @@ public class CassandraDataContext extends QueryPostprocessDataContext implements * and column model of the ElasticSearch index. */ public CassandraDataContext(Cluster cluster, String keySpace, SimpleTableDef... tableDefs) { + super(false); this.cassandraCluster = cluster; this.keySpaceName = keySpace; this.tableDefs = tableDefs; http://git-wip-us.apache.org/repos/asf/metamodel/blob/fe9ab604/core/src/main/java/org/apache/metamodel/AbstractDataContext.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/AbstractDataContext.java b/core/src/main/java/org/apache/metamodel/AbstractDataContext.java index 475add4..c098167 100644 --- a/core/src/main/java/org/apache/metamodel/AbstractDataContext.java +++ b/core/src/main/java/org/apache/metamodel/AbstractDataContext.java @@ -34,10 +34,11 @@ import org.apache.metamodel.query.parser.QueryParser; import org.apache.metamodel.schema.Column; import org.apache.metamodel.schema.Schema; import org.apache.metamodel.schema.Table; +import org.apache.metamodel.schema.TableType; /** - * Abstract implementation of the DataContext interface. Provides convenient - * implementations of all trivial and datastore-independent methods. + * Abstract implementation of the DataContext interface. Provides convenient implementations of all trivial and + * datastore-independent methods. */ public abstract class AbstractDataContext implements DataContext { @@ -58,9 +59,8 @@ public abstract class AbstractDataContext implements DataContext { } /** - * Method invoked when schemas have been refreshed using - * {@link #refreshSchemas()}. Can be overridden to add callback - * functionality in subclasses. + * Method invoked when schemas have been refreshed using {@link #refreshSchemas()}. Can be overridden to add + * callback functionality in subclasses. */ protected void onSchemaCacheRefreshed() { } @@ -72,7 +72,7 @@ public abstract class AbstractDataContext implements DataContext { public final List<Schema> getSchemas() throws MetaModelException { List<String> schemaNames = getSchemaNames(); List<Schema> schemas = new ArrayList<>(); - for (final String name: schemaNames) { + for (final String name : schemaNames) { final Schema schema = _schemaCache.get(getSchemaCacheKey(name)); if (schema == null) { final Schema newSchema = getSchemaByName(name); @@ -128,7 +128,7 @@ public abstract class AbstractDataContext implements DataContext { result = schemas.get(0); } else { int highestTableCount = -1; - for (Schema schema: schemas) { + for (Schema schema : schemas) { String name = schema.getName(); if (schema != null) { name = name.toLowerCase(); @@ -299,13 +299,10 @@ public abstract class AbstractDataContext implements DataContext { /** * Searches for a particular column within a schema * - * @param schemaNameSearch - * the schema name to use for search - * @param columnNameOriginal - * the original column name - * @param columnNameSearch - * the column name as it should be searched for (either the same - * as original, or lower case in case of case-insensitive search) + * @param schemaNameSearch the schema name to use for search + * @param columnNameOriginal the original column name + * @param columnNameSearch the column name as it should be searched for (either the same as original, or lower case + * in case of case-insensitive search) * @return */ private Column searchColumn(String schemaNameSearch, String columnNameOriginal, String columnNameSearch) { @@ -365,7 +362,7 @@ public abstract class AbstractDataContext implements DataContext { } } - if (table == null && tableNames.size() == 1) { + if (table == null && schema.getTables().stream().filter(t -> t.getType() != TableType.ALIAS).count() == 1) { table = schema.getTables().get(0); } @@ -536,9 +533,8 @@ public abstract class AbstractDataContext implements DataContext { } /** - * Gets schema names from the non-abstract implementation. These schema - * names will be cached except if the {@link #refreshSchemas()} method is - * called. + * Gets schema names from the non-abstract implementation. These schema names will be cached except if the + * {@link #refreshSchemas()} method is called. * * @return an array of schema names. */ @@ -552,14 +548,11 @@ public abstract class AbstractDataContext implements DataContext { protected abstract String getDefaultSchemaName(); /** - * Gets a specific schema from the non-abstract implementation. This schema - * object will be cached except if the {@link #refreshSchemas()} method is - * called. + * Gets a specific schema from the non-abstract implementation. This schema object will be cached except if the + * {@link #refreshSchemas()} method is called. * - * @param name - * the name of the schema to get - * @return a schema object representing the named schema, or null if no such - * schema exists. + * @param name the name of the schema to get + * @return a schema object representing the named schema, or null if no such schema exists. */ protected abstract Schema getSchemaByNameInternal(String name); } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/metamodel/blob/fe9ab604/core/src/main/java/org/apache/metamodel/MetaModelHelper.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/MetaModelHelper.java b/core/src/main/java/org/apache/metamodel/MetaModelHelper.java index 4c705ff..2b547da 100644 --- a/core/src/main/java/org/apache/metamodel/MetaModelHelper.java +++ b/core/src/main/java/org/apache/metamodel/MetaModelHelper.java @@ -18,12 +18,21 @@ */ package org.apache.metamodel; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; - import org.apache.metamodel.data.CachingDataSetHeader; import org.apache.metamodel.data.DataSet; import org.apache.metamodel.data.DataSetHeader; @@ -51,6 +60,8 @@ import org.apache.metamodel.schema.ColumnType; import org.apache.metamodel.schema.Schema; import org.apache.metamodel.schema.SuperColumnType; import org.apache.metamodel.schema.Table; +import org.apache.metamodel.schema.WrappingSchema; +import org.apache.metamodel.schema.WrappingTable; import org.apache.metamodel.util.AggregateBuilder; import org.apache.metamodel.util.CollectionUtils; import org.apache.metamodel.util.ObjectComparator; @@ -58,18 +69,17 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * This class contains various helper functionality to common tasks in - * MetaModel, eg.: + * This class contains various helper functionality to common tasks in MetaModel, eg.: * * <ul> * <li>Easy-access for traversing common schema items</li> - * <li>Manipulate data in memory. These methods are primarily used to enable - * queries for non-queryable data sources like CSV files and spreadsheets.</li> + * <li>Manipulate data in memory. These methods are primarily used to enable queries for non-queryable data sources like + * CSV files and spreadsheets.</li> * <li>Query rewriting, traversing and manipulation.</li> * </ul> * - * The class is mainly intended for internal use within the framework - * operations, but is kept stable, so it can also be used by framework users. + * The class is mainly intended for internal use within the framework operations, but is kept stable, so it can also be + * used by framework users. */ public final class MetaModelHelper { @@ -80,8 +90,7 @@ public final class MetaModelHelper { } /** - * Creates an array of tables where all occurences of tables in the provided - * list of tables and columns are included + * Creates an array of tables where all occurences of tables in the provided list of tables and columns are included */ public static Table[] getTables(Collection<Table> tableList, Iterable<Column> columnList) { HashSet<Table> set = new HashSet<Table>(); @@ -119,8 +128,7 @@ public final class MetaModelHelper { /** * Converts a list of columns to a corresponding array of tables * - * @param columns - * the columns that the tables will be extracted from + * @param columns the columns that the tables will be extracted from * @return an array containing the tables of the provided columns. */ public static Table[] getTables(Iterable<Column> columns) { @@ -135,8 +143,7 @@ public final class MetaModelHelper { } /** - * Creates a subset array of columns, where only columns that are contained - * within the specified table are included. + * Creates a subset array of columns, where only columns that are contained within the specified table are included. * * @param table * @param columns @@ -157,8 +164,7 @@ public final class MetaModelHelper { } /** - * Creates a subset array of columns, where only columns that are contained - * within the specified table are included. + * Creates a subset array of columns, where only columns that are contained within the specified table are included. * * @param table * @param columns @@ -185,7 +191,6 @@ public final class MetaModelHelper { // do a nested loop join, no matter what Iterator<DataSet> dsIter = Arrays.asList(fromDataSets).iterator(); - DataSet joined = dsIter.next(); while (dsIter.hasNext()) { @@ -198,8 +203,7 @@ public final class MetaModelHelper { } /** - * Executes a simple nested loop join. The innerLoopDs will be copied in an - * in-memory dataset. + * Executes a simple nested loop join. The innerLoopDs will be copied in an in-memory dataset. * */ public static InMemoryDataSet nestedLoopJoin(DataSet innerLoopDs, DataSet outerLoopDs, @@ -225,8 +229,8 @@ public final class MetaModelHelper { Object[] joinedRowObjects = new Object[outerRow.getValues().length + innerRow.getValues().length]; System.arraycopy(outerRow.getValues(), 0, joinedRowObjects, 0, outerRow.getValues().length); - System.arraycopy(innerRow.getValues(), 0, joinedRowObjects, outerRow.getValues().length, innerRow - .getValues().length); + System.arraycopy(innerRow.getValues(), 0, joinedRowObjects, outerRow.getValues().length, + innerRow.getValues().length); Row joinedRow = new DefaultRow(jointHeader, joinedRowObjects); @@ -240,8 +244,8 @@ public final class MetaModelHelper { } /** - * Filters the FilterItems such that only the FilterItems are returned, - * which contain SelectItems that are contained in selectItemList + * Filters the FilterItems such that only the FilterItems are returned, which contain SelectItems that are contained + * in selectItemList * * @param filters * @param selectItemList @@ -293,8 +297,8 @@ public final class MetaModelHelper { for (SelectItem selectItem : selectItems) { if (selectItem.getScalarFunction() != null) { - if (!dataSetSelectItems.contains(selectItem) && dataSetSelectItems.contains(selectItem.replaceFunction( - null))) { + if (!dataSetSelectItems.contains(selectItem) + && dataSetSelectItems.contains(selectItem.replaceFunction(null))) { scalarFunctionSelectItemsToEvaluate.add(selectItem); } } @@ -304,8 +308,8 @@ public final class MetaModelHelper { return new SubSelectionDataSet(selectItems, dataSet); } - final ScalarFunctionDataSet scalaFunctionDataSet = new ScalarFunctionDataSet( - scalarFunctionSelectItemsToEvaluate, dataSet); + final ScalarFunctionDataSet scalaFunctionDataSet = + new ScalarFunctionDataSet(scalarFunctionSelectItemsToEvaluate, dataSet); return new SubSelectionDataSet(selectItems, scalaFunctionDataSet); } @@ -318,9 +322,8 @@ public final class MetaModelHelper { if (groupByItems != null && groupByItems.size() > 0) { Map<Row, Map<SelectItem, List<Object>>> uniqueRows = new HashMap<Row, Map<SelectItem, List<Object>>>(); - final List<SelectItem> groupBySelects = groupByItems.stream() - .map(gbi -> gbi.getSelectItem()) - .collect(Collectors.toList()); + final List<SelectItem> groupBySelects = + groupByItems.stream().map(gbi -> gbi.getSelectItem()).collect(Collectors.toList()); final DataSetHeader groupByHeader = new CachingDataSetHeader(groupBySelects); // Creates a list of SelectItems that have aggregate functions @@ -413,13 +416,10 @@ public final class MetaModelHelper { } /** - * Applies aggregate values to a dataset. This method is to be invoked AFTER - * any filters have been applied. + * Applies aggregate values to a dataset. This method is to be invoked AFTER any filters have been applied. * - * @param workSelectItems - * all select items included in the processing of the query - * (including those originating from other clauses than the - * SELECT clause). + * @param workSelectItems all select items included in the processing of the query (including those originating from + * other clauses than the SELECT clause). * @param dataSet * @return */ @@ -595,12 +595,10 @@ public final class MetaModelHelper { } /** - * Examines a query and extracts an array of FromItem's that refer - * (directly) to tables (hence Joined FromItems and SubQuery FromItems are - * traversed but not included). + * Examines a query and extracts an array of FromItem's that refer (directly) to tables (hence Joined FromItems and + * SubQuery FromItems are traversed but not included). * - * @param q - * the query to examine + * @param q the query to examine * @return an array of FromItem's that refer directly to tables */ public static FromItem[] getTableFromItems(Query q) { @@ -633,16 +631,12 @@ public final class MetaModelHelper { } /** - * Executes a single row query, like "SELECT COUNT(*), MAX(SOME_COLUMN) FROM - * MY_TABLE" or similar. + * Executes a single row query, like "SELECT COUNT(*), MAX(SOME_COLUMN) FROM MY_TABLE" or similar. * - * @param dataContext - * the DataContext object to use for executing the query - * @param query - * the query to execute + * @param dataContext the DataContext object to use for executing the query + * @param query the query to execute * @return a row object representing the single row returned from the query - * @throws MetaModelException - * if less or more than one Row is returned from the query + * @throws MetaModelException if less or more than one Row is returned from the query */ public static Row executeSingleRowQuery(DataContext dataContext, Query query) throws MetaModelException { DataSet dataSet = dataContext.executeQuery(query); @@ -662,12 +656,9 @@ public final class MetaModelHelper { /** * Performs a left join (aka left outer join) operation on two datasets. * - * @param ds1 - * the left dataset - * @param ds2 - * the right dataset - * @param onConditions - * the conditions to join by + * @param ds1 the left dataset + * @param ds2 the right dataset + * @param onConditions the conditions to join by * @return the left joined result dataset */ public static DataSet getLeftJoin(DataSet ds1, DataSet ds2, FilterItem[] onConditions) { @@ -679,7 +670,7 @@ public final class MetaModelHelper { } List<SelectItem> si1 = ds1.getSelectItems(); List<SelectItem> si2 = ds2.getSelectItems(); - List<SelectItem> selectItems = Stream.concat(si1.stream(),si2.stream()).collect(Collectors.toList()); + List<SelectItem> selectItems = Stream.concat(si1.stream(), si2.stream()).collect(Collectors.toList()); List<Row> resultRows = new ArrayList<Row>(); List<Row> ds2data = readDataSetFull(ds2); if (ds2data.isEmpty()) { @@ -698,9 +689,9 @@ public final class MetaModelHelper { List<Row> ds1rows = new ArrayList<Row>(); ds1rows.add(ds1row); - DataSet carthesianProduct = getCarthesianProduct(new DataSet[] { new InMemoryDataSet( - new CachingDataSetHeader(si1), ds1rows), new InMemoryDataSet(new CachingDataSetHeader(si2), - ds2data) }, onConditions); + DataSet carthesianProduct = + getCarthesianProduct(new DataSet[] { new InMemoryDataSet(new CachingDataSetHeader(si1), ds1rows), + new InMemoryDataSet(new CachingDataSetHeader(si2), ds2data) }, onConditions); List<Row> carthesianRows = readDataSetFull(carthesianProduct); if (carthesianRows.size() > 0) { resultRows.addAll(carthesianRows); @@ -723,12 +714,9 @@ public final class MetaModelHelper { /** * Performs a right join (aka right outer join) operation on two datasets. * - * @param ds1 - * the left dataset - * @param ds2 - * the right dataset - * @param onConditions - * the conditions to join by + * @param ds1 the left dataset + * @param ds2 the right dataset + * @param onConditions the conditions to join by * @return the right joined result dataset */ public static DataSet getRightJoin(DataSet ds1, DataSet ds2, FilterItem[] onConditions) { @@ -756,9 +744,7 @@ public final class MetaModelHelper { public static DataSet getDistinct(DataSet dataSet) { List<SelectItem> selectItems = dataSet.getSelectItems(); - List<GroupByItem> groupByItems = selectItems.stream() - .map(GroupByItem::new) - .collect(Collectors.toList()); + List<GroupByItem> groupByItems = selectItems.stream().map(GroupByItem::new).collect(Collectors.toList()); return getGrouped(selectItems, dataSet, groupByItems); } @@ -836,11 +822,10 @@ public final class MetaModelHelper { } /** - * Determines if a query contains {@link ScalarFunction}s in any clause of - * the query EXCEPT for the SELECT clause. This is a handy thing to - * determine because decorating with {@link ScalarFunctionDataSet} only - * gives you select-item evaluation so if the rest of the query is pushed to - * an underlying datastore, then it may create issues. + * Determines if a query contains {@link ScalarFunction}s in any clause of the query EXCEPT for the SELECT clause. + * This is a handy thing to determine because decorating with {@link ScalarFunctionDataSet} only gives you + * select-item evaluation so if the rest of the query is pushed to an underlying datastore, then it may create + * issues. * * @param query * @return @@ -883,4 +868,23 @@ public final class MetaModelHelper { return false; } + + public static Table resolveTable(FromItem fromItem) { + final Table table = fromItem.getTable(); + return resolveUnderlyingTable(table); + } + + public static Table resolveUnderlyingTable(Table table) { + while (table instanceof WrappingTable) { + table = ((WrappingTable) table).getWrappedTable(); + } + return table; + } + + public static Schema resolveUnderlyingSchema(Schema schema) { + while (schema instanceof WrappingSchema) { + schema = ((WrappingSchema) schema).getWrappedSchema(); + } + return schema; + } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/metamodel/blob/fe9ab604/core/src/main/java/org/apache/metamodel/QueryPostprocessDataContext.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/QueryPostprocessDataContext.java b/core/src/main/java/org/apache/metamodel/QueryPostprocessDataContext.java index cbd97d3..c77479b 100644 --- a/core/src/main/java/org/apache/metamodel/QueryPostprocessDataContext.java +++ b/core/src/main/java/org/apache/metamodel/QueryPostprocessDataContext.java @@ -50,6 +50,7 @@ import org.apache.metamodel.query.SelectClause; import org.apache.metamodel.query.SelectItem; import org.apache.metamodel.schema.Column; import org.apache.metamodel.schema.ColumnType; +import org.apache.metamodel.schema.DefaultTableAliasedSchema; import org.apache.metamodel.schema.MutableColumn; import org.apache.metamodel.schema.MutableRelationship; import org.apache.metamodel.schema.MutableSchema; @@ -63,24 +64,37 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Abstract DataContext for data sources that do not support SQL queries - * natively. + * Abstract DataContext for data sources that do not support SQL queries natively. * - * Instead this superclass only requires that a subclass can materialize a - * single table at a time. Then the query will be executed by post processing - * the datasets client-side. + * Instead this superclass only requires that a subclass can materialize a single table at a time. Then the query will + * be executed by post processing the datasets client-side. */ public abstract class QueryPostprocessDataContext extends AbstractDataContext implements HasReadTypeConverters { private static final Logger logger = LoggerFactory.getLogger(QueryPostprocessDataContext.class); + public static final String SYSTEM_PROPERTY_CREATE_DEFAULT_TABLE_ALIAS = "metamodel.alias.default.table"; public static final String INFORMATION_SCHEMA_NAME = "information_schema"; - private final Map<Column, TypeConverter<?, ?>> _converters; + private final Map<Column, TypeConverter<?, ?>> converters; + private final boolean singleTableDatastore; public QueryPostprocessDataContext() { + this(true); + } + + /** + * + * @param singleTableDatastore a flag that, if set to true, indicates that this DataContext contains just a single + * table. This information will be used to optimize and provide convenience for the implementation. An + * additional {@link Table} of type {@link TableType#ALIAS} with the name "default_table" will be + * automatically added in addition to the single table. That again makes for convenient querying of the + * single table using a predictable name/alias. + */ + public QueryPostprocessDataContext(boolean singleTableDatastore) { super(); - _converters = new HashMap<Column, TypeConverter<?, ?>>(); + this.singleTableDatastore = singleTableDatastore; + this.converters = new HashMap<Column, TypeConverter<?, ?>>(); } @Override @@ -112,7 +126,7 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im if (singleFromItem && noGrouping) { final FromItem fromItem = query.getFromClause().getItem(0); - final Table table = fromItem.getTable(); + final Table table = MetaModelHelper.resolveTable(fromItem); if (table != null) { // check for SELECT COUNT(*) queries @@ -128,7 +142,7 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im logger.debug( "DataContext did not return any count query results. Proceeding with manual counting."); } else { - List<Row> data = new ArrayList<Row>(1); + final List<Row> data = new ArrayList<Row>(1); final DataSetHeader header = new SimpleDataSetHeader(new SelectItem[] { selectItem }); data.add(new DefaultRow(header, new Object[] { count })); return new InMemoryDataSet(header, data); @@ -151,8 +165,8 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im if (table != null) { if (isMainSchemaTable(table)) { final Object operand = whereItem.getOperand(); - final Row row = executePrimaryKeyLookupQuery(table, selectItems, column, - operand); + final Row row = + executePrimaryKeyLookupQuery(table, selectItems, column, operand); if (row == null) { logger.debug( "DataContext did not return any GET query results. Proceeding with manual lookup."); @@ -192,8 +206,8 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im // we can now exclude the select items imposed by the WHERE clause (and // should, to make the aggregation process faster) - workSelectItems = CollectionUtils.concat(true, selectItems, groupBySelectItems, havingSelectItems, - orderBySelectItems); + workSelectItems = + CollectionUtils.concat(true, selectItems, groupBySelectItems, havingSelectItems, orderBySelectItems); if (groupByItems.size() > 0) { dataSet = MetaModelHelper.getGrouped(workSelectItems, dataSet, groupByItems); @@ -216,8 +230,7 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im } /** - * Determines if all the select items are 'simple' meaning that they just - * represent scans of values in columns. + * Determines if all the select items are 'simple' meaning that they just represent scans of values in columns. * * @param clause * @return @@ -235,17 +248,13 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im } /** - * Executes a simple count query, if possible. This method is provided to - * allow subclasses to optimize count queries since they are quite common - * and often a datastore can retrieve the count using some specialized means - * which is much more performant than counting all records manually. + * Executes a simple count query, if possible. This method is provided to allow subclasses to optimize count queries + * since they are quite common and often a datastore can retrieve the count using some specialized means which is + * much more performant than counting all records manually. * - * @param table - * the table on which the count is requested. - * @param whereItems - * a (sometimes empty) list of WHERE items. - * @param functionApproximationAllowed - * whether approximation is allowed or not. + * @param table the table on which the count is requested. + * @param whereItems a (sometimes empty) list of WHERE items. + * @param functionApproximationAllowed whether approximation is allowed or not. * @return the count of the particular table, or null if not available. */ protected Number executeCountQuery(Table table, List<FilterItem> whereItems, boolean functionApproximationAllowed) { @@ -253,20 +262,14 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im } /** - * Executes a query which obtains a row by primary key (as defined by - * {@link Column#isPrimaryKey()}). This method is provided to allow - * subclasses to optimize lookup queries since they are quite common and - * often a datastore can retrieve the row using some specialized means which - * is much more performant than scanning all records manually. + * Executes a query which obtains a row by primary key (as defined by {@link Column#isPrimaryKey()}). This method is + * provided to allow subclasses to optimize lookup queries since they are quite common and often a datastore can + * retrieve the row using some specialized means which is much more performant than scanning all records manually. * - * @param table - * the table on which the lookup is requested. - * @param selectItems - * the items to select from the lookup query. - * @param primaryKeyColumn - * the column that is the primary key - * @param keyValue - * the primary key value that is specified in the lookup query. + * @param table the table on which the lookup is requested. + * @param selectItems the items to select from the lookup query. + * @param primaryKeyColumn the column that is the primary key + * @param keyValue the primary key value that is specified in the lookup query. * @return the row if the particular table, or null if not available. */ protected Row executePrimaryKeyLookupQuery(Table table, List<SelectItem> selectItems, Column primaryKeyColumn, @@ -279,7 +282,7 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im JoinType joinType = fromItem.getJoin(); if (fromItem.getTable() != null) { // We need to materialize a single table - final Table table = fromItem.getTable(); + final Table table = MetaModelHelper.resolveTable(fromItem); final List<SelectItem> selectItemsToMaterialize = new ArrayList<SelectItem>(); for (final SelectItem selectItem : selectItems) { @@ -319,13 +322,13 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im // materialize left side final List<SelectItem> leftOn = Arrays.asList(fromItem.getLeftOn()); - fromItemDataSets[0] = materializeFromItem(fromItem.getLeftSide(), - CollectionUtils.concat(true, selectItems, leftOn)); + fromItemDataSets[0] = + materializeFromItem(fromItem.getLeftSide(), CollectionUtils.concat(true, selectItems, leftOn)); // materialize right side final List<SelectItem> rightOn = Arrays.asList(fromItem.getRightOn()); - fromItemDataSets[1] = materializeFromItem(fromItem.getRightSide(), - CollectionUtils.concat(true, selectItems, rightOn)); + fromItemDataSets[1] = + materializeFromItem(fromItem.getRightSide(), CollectionUtils.concat(true, selectItems, rightOn)); final FilterItem[] onConditions = new FilterItem[leftOn.size()]; for (int i = 0; i < onConditions.length; i++) { @@ -385,8 +388,8 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im final DataSet dataSet; if (INFORMATION_SCHEMA_NAME.equals(schemaName)) { - DataSet informationDataSet = materializeInformationSchemaTable(table, - buildWorkingSelectItems(selectItems, whereItems)); + DataSet informationDataSet = + materializeInformationSchemaTable(table, buildWorkingSelectItems(selectItems, whereItems)); informationDataSet = MetaModelHelper.getFiltered(informationDataSet, whereItems); informationDataSet = MetaModelHelper.getSelection(selectItems, informationDataSet); informationDataSet = MetaModelHelper.getPaged(informationDataSet, firstRow, maxRows); @@ -396,7 +399,7 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im // conversion is done at materialization time, since it enables // the refined types to be used also in eg. where clauses. - dataSet = new ConvertedDataSetInterceptor(_converters).intercept(tableDataSet); + dataSet = new ConvertedDataSetInterceptor(converters).intercept(tableDataSet); } return dataSet; @@ -418,13 +421,11 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im } /** - * Determines if the subclass of this class can materialize - * {@link SelectItem}s with the given {@link ScalarFunction}. Usually scalar - * functions are applied by MetaModel on the client side, but when possible - * they can also be handled by e.g. - * {@link #materializeMainSchemaTable(Table, List, int, int)} and - * {@link #materializeMainSchemaTable(Table, List, List, int, int)} in which - * case MetaModel will not evaluate it client-side. + * Determines if the subclass of this class can materialize {@link SelectItem}s with the given + * {@link ScalarFunction}. Usually scalar functions are applied by MetaModel on the client side, but when possible + * they can also be handled by e.g. {@link #materializeMainSchemaTable(Table, List, int, int)} and + * {@link #materializeMainSchemaTable(Table, List, List, int, int)} in which case MetaModel will not evaluate it + * client-side. * * @param function * @return @@ -458,15 +459,18 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im @Override protected final Schema getSchemaByNameInternal(final String name) throws MetaModelException { final String mainSchemaName = getMainSchemaName(); - if (name == null) { - if (mainSchemaName == null) { - return getMainSchema(); - } + if (name == null && mainSchemaName != null) { return null; } - if (name.equalsIgnoreCase(mainSchemaName)) { - return getMainSchema(); + if (name == null || name.equalsIgnoreCase(mainSchemaName)) { + final Schema mainSchema = getMainSchema(); + final boolean createAliasTable = singleTableDatastore + && Boolean.parseBoolean(System.getProperty(SYSTEM_PROPERTY_CREATE_DEFAULT_TABLE_ALIAS, "true")); + if (createAliasTable) { + return DefaultTableAliasedSchema.wrapIfAppropriate(mainSchema); + } + return mainSchema; } else if (name.equals(INFORMATION_SCHEMA_NAME)) { return getInformationSchema(); } @@ -528,9 +532,8 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im private DataSet materializeInformationSchemaTable(final Table table, final List<SelectItem> selectItems) { final String tableName = table.getName(); - final List<SelectItem> columnSelectItems = table.getColumns().stream() - .map(SelectItem::new) - .collect(Collectors.toList()); + final List<SelectItem> columnSelectItems = + table.getColumns().stream().map(SelectItem::new).collect(Collectors.toList()); final SimpleDataSetHeader header = new SimpleDataSetHeader(columnSelectItems); final List<Table> tables = getDefaultSchema().getTables(); final List<Row> data = new ArrayList<Row>(); @@ -591,15 +594,14 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im } /** - * Adds a {@link TypeConverter} to this DataContext's query engine (Query - * Postprocessor) for read operations. Note that this method should NOT be - * invoked directly by consuming code. Rather use - * {@link Converters#addTypeConverter(DataContext, Column, TypeConverter)} - * to ensure conversion on both reads and writes. + * Adds a {@link TypeConverter} to this DataContext's query engine (Query Postprocessor) for read operations. Note + * that this method should NOT be invoked directly by consuming code. Rather use + * {@link Converters#addTypeConverter(DataContext, Column, TypeConverter)} to ensure conversion on both reads and + * writes. */ @Override public void addConverter(Column column, TypeConverter<?, ?> converter) { - _converters.put(column, converter); + converters.put(column, converter); } /** @@ -613,10 +615,9 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im protected abstract String getMainSchemaName() throws MetaModelException; /** - * Execute a simple one-table query against a table in the main schema of - * the subclasses of this class. This default implementation will delegate - * to {@link #materializeMainSchemaTable(Table, List, int, int)} and apply - * WHERE item filtering afterwards. + * Execute a simple one-table query against a table in the main schema of the subclasses of this class. This default + * implementation will delegate to {@link #materializeMainSchemaTable(Table, List, int, int)} and apply WHERE item + * filtering afterwards. * * @param table * @param selectItems @@ -644,9 +645,8 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im } /** - * Executes a simple one-table query against a table in the main schema of - * the subclasses of this class. This default implementation will delegate - * to {@link #materializeMainSchemaTable(Table, List, int, int)}. + * Executes a simple one-table query against a table in the main schema of the subclasses of this class. This + * default implementation will delegate to {@link #materializeMainSchemaTable(Table, List, int, int)}. * * @param table * @param selectItems @@ -654,10 +654,9 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im * @param maxRows * @return */ - protected DataSet materializeMainSchemaTableSelect(Table table, List<SelectItem> selectItems, int firstRow, int maxRows) { - List<Column> columns = selectItems.stream() - .map(si -> si.getColumn()) - .collect(Collectors.toList()); + protected DataSet materializeMainSchemaTableSelect(Table table, List<SelectItem> selectItems, int firstRow, + int maxRows) { + List<Column> columns = selectItems.stream().map(si -> si.getColumn()).collect(Collectors.toList()); DataSet dataSet = materializeMainSchemaTable(table, columns, firstRow, maxRows); @@ -667,9 +666,8 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im } /** - * Executes a simple one-table query against a table in the main schema of - * the subclasses of this class. This default implementation will delegate - * to {@link #materializeMainSchemaTable(Table, List, int)} and apply a + * Executes a simple one-table query against a table in the main schema of the subclasses of this class. This + * default implementation will delegate to {@link #materializeMainSchemaTable(Table, List, int)} and apply a * {@link FirstRowDataSet} if necessary. * * @param table @@ -693,16 +691,11 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im } /** - * Executes a simple one-table query against a table in the main schema of - * the subclasses of this class. + * Executes a simple one-table query against a table in the main schema of the subclasses of this class. * - * @param table - * the table to query - * @param columns - * the columns of the table to query - * @param maxRows - * the maximum amount of rows needed or -1 if all rows are - * wanted. + * @param table the table to query + * @param columns the columns of the table to query + * @param maxRows the maximum amount of rows needed or -1 if all rows are wanted. * @return a dataset with the raw table/column content. */ protected abstract DataSet materializeMainSchemaTable(Table table, List<Column> columns, int maxRows); http://git-wip-us.apache.org/repos/asf/metamodel/blob/fe9ab604/core/src/main/java/org/apache/metamodel/QueryPostprocessDelegate.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/QueryPostprocessDelegate.java b/core/src/main/java/org/apache/metamodel/QueryPostprocessDelegate.java index 9e0e4dc..1c94ff9 100644 --- a/core/src/main/java/org/apache/metamodel/QueryPostprocessDelegate.java +++ b/core/src/main/java/org/apache/metamodel/QueryPostprocessDelegate.java @@ -18,7 +18,7 @@ */ package org.apache.metamodel; -import org.apache.metamodel.schema.Schema; +import org.apache.metamodel.schema.MutableSchema; /** * A simple subclass of {@link QueryPostprocessDataContext} which provides less @@ -27,6 +27,10 @@ import org.apache.metamodel.schema.Schema; */ public abstract class QueryPostprocessDelegate extends QueryPostprocessDataContext { + + public QueryPostprocessDelegate() { + super(false); + } @Override protected String getMainSchemaName() throws MetaModelException { @@ -35,7 +39,7 @@ public abstract class QueryPostprocessDelegate extends } @Override - protected Schema getMainSchema() throws MetaModelException { + protected MutableSchema getMainSchema() throws MetaModelException { throw new UnsupportedOperationException( "QueryPostprocessDelegate cannot perform schema exploration"); } http://git-wip-us.apache.org/repos/asf/metamodel/blob/fe9ab604/core/src/main/java/org/apache/metamodel/create/AbstractTableCreationBuilder.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/create/AbstractTableCreationBuilder.java b/core/src/main/java/org/apache/metamodel/create/AbstractTableCreationBuilder.java index 1833120..7ca7216 100644 --- a/core/src/main/java/org/apache/metamodel/create/AbstractTableCreationBuilder.java +++ b/core/src/main/java/org/apache/metamodel/create/AbstractTableCreationBuilder.java @@ -18,6 +18,7 @@ */ package org.apache.metamodel.create; +import org.apache.metamodel.MetaModelHelper; import org.apache.metamodel.UpdateCallback; import org.apache.metamodel.schema.Column; import org.apache.metamodel.schema.ColumnType; @@ -50,7 +51,7 @@ public abstract class AbstractTableCreationBuilder<U extends UpdateCallback> imp + schema); } _updateCallback = updateCallback; - _schema = schema; + _schema = MetaModelHelper.resolveUnderlyingSchema(schema); _table = new MutableTable(name, TableType.TABLE, schema); } http://git-wip-us.apache.org/repos/asf/metamodel/blob/fe9ab604/core/src/main/java/org/apache/metamodel/query/SelectItem.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/query/SelectItem.java b/core/src/main/java/org/apache/metamodel/query/SelectItem.java index df4b995..fec900d 100644 --- a/core/src/main/java/org/apache/metamodel/query/SelectItem.java +++ b/core/src/main/java/org/apache/metamodel/query/SelectItem.java @@ -25,6 +25,7 @@ import org.apache.metamodel.schema.Column; import org.apache.metamodel.schema.ColumnType; import org.apache.metamodel.schema.Schema; import org.apache.metamodel.schema.Table; +import org.apache.metamodel.schema.TableType; import org.apache.metamodel.util.BaseObject; import org.apache.metamodel.util.EqualsBuilder; import org.slf4j.Logger; @@ -35,12 +36,11 @@ import org.slf4j.LoggerFactory; * <ul> * <li>column SELECTs (selects a column from a table)</li> * <li>column function SELECTs (aggregates the values of a column)</li> - * <li>expression SELECTs (retrieves data based on an expression (only supported - * for JDBC datastores)</li> - * <li>expression function SELECTs (retrieves databased on a function and an - * expression, only COUNT(*) is supported for non-JDBC datastores))</li> - * <li>SELECTs from subqueries (works just like column selects, but in stead of - * pointing to a column, it retrieves data from the select item of a subquery)</li> + * <li>expression SELECTs (retrieves data based on an expression (only supported for JDBC datastores)</li> + * <li>expression function SELECTs (retrieves databased on a function and an expression, only COUNT(*) is supported for + * non-JDBC datastores))</li> + * <li>SELECTs from subqueries (works just like column selects, but in stead of pointing to a column, it retrieves data + * from the select item of a subquery)</li> * </ul> * * @see SelectClause @@ -98,8 +98,8 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable { } public static boolean isCountAllItem(SelectItem item) { - if (item != null && item.getAggregateFunction() != null && item.getAggregateFunction().toString().equals("COUNT") - && item.getExpression() == "*") { + if (item != null && item.getAggregateFunction() != null + && item.getAggregateFunction().toString().equals("COUNT") && item.getExpression() == "*") { return true; } return false; @@ -115,8 +115,7 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable { } /** - * Creates a SelectItem that uses a function on a column, for example - * SUM(price) or MAX(age) + * Creates a SelectItem that uses a function on a column, for example SUM(price) or MAX(age) * * @param function * @param column @@ -137,8 +136,7 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable { } /** - * Creates a SelectItem that references a column from a particular - * {@link FromItem}, for example a.price or p.age + * Creates a SelectItem that references a column from a particular {@link FromItem}, for example a.price or p.age * * @param column * @param fromItem @@ -147,7 +145,7 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable { this(null, column, fromItem); if (fromItem != null) { Table fromItemTable = fromItem.getTable(); - if (fromItemTable != null) { + if (fromItemTable != null && fromItemTable.getType() != TableType.ALIAS) { Table columnTable = column.getTable(); if (columnTable != null && !columnTable.equals(fromItemTable)) { throw new IllegalArgumentException("Column's table '" + columnTable.getName() @@ -158,8 +156,8 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable { } /** - * Creates a SelectItem that uses a function on a column from a particular - * {@link FromItem}, for example SUM(a.price) or MAX(p.age) + * Creates a SelectItem that uses a function on a column from a particular {@link FromItem}, for example + * SUM(a.price) or MAX(p.age) * * @param function * @param column @@ -173,9 +171,8 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable { } /** - * Creates a SelectItem that uses a function with parameters on a column - * from a particular {@link FromItem}, for example - * MAP_VALUE('path.to.value', doc) + * Creates a SelectItem that uses a function with parameters on a column from a particular {@link FromItem}, for + * example MAP_VALUE('path.to.value', doc) * * @param function * @param functionParameters @@ -190,8 +187,7 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable { } /** - * Creates a SelectItem based on an expression. All expression-based - * SelectItems must have aliases. + * Creates a SelectItem based on an expression. All expression-based SelectItems must have aliases. * * @param expression * @param alias @@ -201,8 +197,7 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable { } /** - * Creates a SelectItem based on a function and an expression. All - * expression-based SelectItems must have aliases. + * Creates a SelectItem based on a function and an expression. All expression-based SelectItems must have aliases. * * @param function * @param expression @@ -219,8 +214,7 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable { * Creates a SelectItem that references another select item in a subquery * * @param subQuerySelectItem - * @param subQueryFromItem - * the FromItem that holds the sub-query + * @param subQueryFromItem the FromItem that holds the sub-query */ public SelectItem(SelectItem subQuerySelectItem, FromItem subQueryFromItem) { this(null, subQueryFromItem, null, null, null, subQuerySelectItem, null, false); @@ -242,7 +236,7 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable { return this; } - public boolean hasFunction(){ + public boolean hasFunction() { return _function != null; } @@ -270,11 +264,9 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable { } /** - * @return if this is a function based SelectItem where function calculation - * is allowed to be approximated (if the datastore type has an - * approximate calculation method). Approximated function results - * are as the name implies not exact, but might be valuable as an - * optimization in some cases. + * @return if this is a function based SelectItem where function calculation is allowed to be approximated (if the + * datastore type has an approximate calculation method). Approximated function results are as the name + * implies not exact, but might be valuable as an optimization in some cases. */ public boolean isFunctionApproximationAllowed() { return _functionApproximationAllowed; @@ -289,9 +281,8 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable { } /** - * Tries to infer the {@link ColumnType} of this {@link SelectItem}. For - * expression based select items, this is not possible, and the method will - * return null. + * Tries to infer the {@link ColumnType} of this {@link SelectItem}. For expression based select items, this is not + * possible, and the method will return null. * * @return */ @@ -313,9 +304,9 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable { } /** - * Returns an "expression" that this select item represents. Expressions are - * not necesarily portable across {@link DataContext} implementations, but - * may be useful for utilizing database-specific behaviour in certain cases. + * Returns an "expression" that this select item represents. Expressions are not necesarily portable across + * {@link DataContext} implementations, but may be useful for utilizing database-specific behaviour in certain + * cases. * * @return */ @@ -341,23 +332,19 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable { } /** - * @return the name that this SelectItem can be referenced with, if - * referenced from a super-query. This will usually be the alias, - * but if there is no alias, then the column name will be used. + * @return the name that this SelectItem can be referenced with, if referenced from a super-query. This will usually + * be the alias, but if there is no alias, then the column name will be used. */ public String getSuperQueryAlias() { return getSuperQueryAlias(true); } /** - * @return the name that this SelectItem can be referenced with, if - * referenced from a super-query. This will usually be the alias, - * but if there is no alias, then the column name will be used. + * @return the name that this SelectItem can be referenced with, if referenced from a super-query. This will usually + * be the alias, but if there is no alias, then the column name will be used. * - * @param includeQuotes - * indicates whether or not the output should include quotes, if - * the select item's column has quotes associated (typically - * true, but false if used for presentation) + * @param includeQuotes indicates whether or not the output should include quotes, if the select item's column has + * quotes associated (typically true, but false if used for presentation) */ public String getSuperQueryAlias(boolean includeQuotes) { if (_alias != null) { @@ -391,8 +378,7 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable { } /** - * @return an alias that can be used in WHERE, GROUP BY and ORDER BY clauses - * in the same query + * @return an alias that can be used in WHERE, GROUP BY and ORDER BY clauses in the same query */ public String getSameQueryAlias(boolean includeSchemaInColumnPath) { if (_column != null) { @@ -476,32 +462,38 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable { } private String getToStringColumnPrefix(boolean includeSchemaInColumnPath) { - final StringBuilder sb = new StringBuilder(); if (_fromItem != null && _fromItem.getAlias() != null) { - sb.append(_fromItem.getAlias()); - sb.append('.'); + return _fromItem.getAlias() + '.'; + } + + final Table table; + if (_fromItem != null && _fromItem.getTable() != null) { + table = _fromItem.getTable(); } else { - final Table table = _column.getTable(); - String tableLabel; - if (_query == null) { - tableLabel = null; - } else { - tableLabel = _query.getFromClause().getAlias(table); - } - if (table != null) { - if (tableLabel == null) { - tableLabel = table.getQuotedName(); - if (includeSchemaInColumnPath) { - Schema schema = table.getSchema(); - if (schema != null) { - tableLabel = schema.getQuotedName() + "." + tableLabel; - } - } + table = _column.getTable(); + } + if (table == null) { + return ""; + } + + final StringBuilder sb = new StringBuilder(); + String tableLabel; + if (_query == null) { + tableLabel = null; + } else { + tableLabel = _query.getFromClause().getAlias(table); + } + if (tableLabel == null) { + tableLabel = table.getQuotedName(); + if (includeSchemaInColumnPath) { + Schema schema = table.getSchema(); + if (schema != null) { + sb.append(schema.getQuotedName() + "."); } - sb.append(tableLabel); - sb.append('.'); } } + sb.append(tableLabel); + sb.append('.'); return sb.toString(); } @@ -561,13 +553,10 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable { } /** - * Creates a clone of the {@link SelectItem} for use within a cloned - * {@link Query}. + * Creates a clone of the {@link SelectItem} for use within a cloned {@link Query}. * - * @param clonedQuery - * a new {@link Query} object that represents the clone-to-be of - * a query. It is expected that {@link FromItem}s have already - * been cloned in this {@link Query}. + * @param clonedQuery a new {@link Query} object that represents the clone-to-be of a query. It is expected that + * {@link FromItem}s have already been cloned in this {@link Query}. * @return */ protected SelectItem clone(Query clonedQuery) { @@ -592,8 +581,7 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable { } /** - * Creates a copy of the {@link SelectItem}, with a different - * {@link FunctionType}. + * Creates a copy of the {@link SelectItem}, with a different {@link FunctionType}. * * @param function * @return @@ -604,8 +592,7 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable { } /** - * Creates a copy of the {@link SelectItem}, with a different - * {@link #isFunctionApproximationAllowed()} flag set. + * Creates a copy of the {@link SelectItem}, with a different {@link #isFunctionApproximationAllowed()} flag set. * * @param functionApproximationAllowed * @return @@ -616,13 +603,11 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable { } /** - * Investigates whether or not this SelectItem references a particular - * column. This will search for direct references and indirect references - * via subqueries. + * Investigates whether or not this SelectItem references a particular column. This will search for direct + * references and indirect references via subqueries. * * @param column - * @return a boolean that is true if the specified column is referenced by - * this SelectItem and false otherwise. + * @return a boolean that is true if the specified column is referenced by this SelectItem and false otherwise. */ public boolean isReferenced(Column column) { if (column != null) { http://git-wip-us.apache.org/repos/asf/metamodel/blob/fe9ab604/core/src/main/java/org/apache/metamodel/schema/AliasTable.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/schema/AliasTable.java b/core/src/main/java/org/apache/metamodel/schema/AliasTable.java new file mode 100644 index 0000000..8be55dd --- /dev/null +++ b/core/src/main/java/org/apache/metamodel/schema/AliasTable.java @@ -0,0 +1,82 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.metamodel.schema; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Represents a virtual table that acts as an alias for another table. + */ +public class AliasTable extends AbstractTable implements WrappingTable { + + private static final long serialVersionUID = 1L; + + private final String name; + private final Schema schema; + private final Table aliasedTable; + + public AliasTable(String name, Schema schema, Table aliasedTable) { + this.name = name; + this.schema = schema; + this.aliasedTable = aliasedTable; + } + + @Override + public Table getWrappedTable() { + return aliasedTable; + } + + @Override + public String getName() { + return name; + } + + @Override + public List<Column> getColumns() { + return aliasedTable.getColumns(); + } + + @Override + public Schema getSchema() { + return schema; + } + + @Override + public TableType getType() { + return TableType.ALIAS; + } + + @Override + public Collection<Relationship> getRelationships() { + return Collections.emptyList(); + } + + @Override + public String getRemarks() { + return null; + } + + @Override + public String getQuote() { + return null; + } + +} http://git-wip-us.apache.org/repos/asf/metamodel/blob/fe9ab604/core/src/main/java/org/apache/metamodel/schema/CompositeSchema.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/schema/CompositeSchema.java b/core/src/main/java/org/apache/metamodel/schema/CompositeSchema.java index b48f14b..cbaa801 100644 --- a/core/src/main/java/org/apache/metamodel/schema/CompositeSchema.java +++ b/core/src/main/java/org/apache/metamodel/schema/CompositeSchema.java @@ -71,6 +71,7 @@ public class CompositeSchema extends AbstractSchema { public List<Table> getTables() { return delegates.stream() .flatMap(delegate -> delegate.getTables().stream()) + .filter(table -> table.getType() != TableType.ALIAS) .collect(Collectors.toList()); } http://git-wip-us.apache.org/repos/asf/metamodel/blob/fe9ab604/core/src/main/java/org/apache/metamodel/schema/DefaultTableAliasedSchema.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/schema/DefaultTableAliasedSchema.java b/core/src/main/java/org/apache/metamodel/schema/DefaultTableAliasedSchema.java new file mode 100644 index 0000000..9cccc3a --- /dev/null +++ b/core/src/main/java/org/apache/metamodel/schema/DefaultTableAliasedSchema.java @@ -0,0 +1,89 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.metamodel.schema; + +import java.util.ArrayList; +import java.util.List; + +/** + * A special purpose {@link Schema} wrapper which exposes an {@link AliasTable} "default_table" for convenience when the + * table count is 1. + */ +public class DefaultTableAliasedSchema extends AbstractSchema implements WrappingSchema { + + private static final long serialVersionUID = 1L; + + public static final String DEFAULT_TABLE_NAME = "default_table"; + + public static Schema wrapIfAppropriate(Schema schema) { + if (schema.getTableCount() > 1) { + return schema; + } else { + return new DefaultTableAliasedSchema(schema); + } + } + + private static AliasTable createTable(Schema schema, Table delegateTable) { + return new AliasTable(DEFAULT_TABLE_NAME, schema, delegateTable); + } + + private final Schema wrappedSchema; + + private DefaultTableAliasedSchema(Schema wrappedSchema) { + this.wrappedSchema = wrappedSchema; + } + + @Override + public Schema getWrappedSchema() { + return wrappedSchema; + } + + @Override + public String getName() { + return wrappedSchema.getName(); + } + + @Override + public List<Table> getTables() { + List<Table> tables = wrappedSchema.getTables(); + + // ensure table size is 1 + if (tables.size() != 1) { + return tables; + } + + // ensure no name clashes + if (DEFAULT_TABLE_NAME.equals(tables.get(0).getName())) { + return tables; + } + + // ensure mutability + if (!(tables instanceof ArrayList)) { + tables = new ArrayList<>(tables); + } + + tables.add(createTable(this, tables.get(0))); + return tables; + } + + @Override + public String getQuote() { + return wrappedSchema.getQuote(); + } +} http://git-wip-us.apache.org/repos/asf/metamodel/blob/fe9ab604/core/src/main/java/org/apache/metamodel/schema/MutableSchema.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/schema/MutableSchema.java b/core/src/main/java/org/apache/metamodel/schema/MutableSchema.java index 1dbc0c0..b80f5f2 100644 --- a/core/src/main/java/org/apache/metamodel/schema/MutableSchema.java +++ b/core/src/main/java/org/apache/metamodel/schema/MutableSchema.java @@ -36,11 +36,11 @@ public class MutableSchema extends AbstractSchema implements Serializable, private static final long serialVersionUID = 4465197783868238863L; private String _name; - private final List<MutableTable> _tables; + private final List<Table> _tables; public MutableSchema() { super(); - _tables = new ArrayList<MutableTable>(); + _tables = new ArrayList<Table>(); } public MutableSchema(String name) { @@ -48,7 +48,7 @@ public class MutableSchema extends AbstractSchema implements Serializable, _name = name; } - public MutableSchema(String name, MutableTable... tables) { + public MutableSchema(String name, Table... tables) { this(name); setTables(tables); } @@ -69,17 +69,17 @@ public class MutableSchema extends AbstractSchema implements Serializable, } - public MutableSchema setTables(Collection<? extends MutableTable> tables) { + public MutableSchema setTables(Collection<? extends Table> tables) { clearTables(); - for (MutableTable table : tables) { + for (Table table : tables) { _tables.add(table); } return this; } - public MutableSchema setTables(MutableTable... tables) { + public MutableSchema setTables(Table... tables) { clearTables(); - for (MutableTable table : tables) { + for (Table table : tables) { _tables.add(table); } return this; @@ -90,7 +90,7 @@ public class MutableSchema extends AbstractSchema implements Serializable, return this; } - public MutableSchema addTable(MutableTable table) { + public MutableSchema addTable(Table table) { _tables.add(table); return this; } http://git-wip-us.apache.org/repos/asf/metamodel/blob/fe9ab604/core/src/main/java/org/apache/metamodel/schema/WrappingSchema.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/schema/WrappingSchema.java b/core/src/main/java/org/apache/metamodel/schema/WrappingSchema.java new file mode 100644 index 0000000..f1809e5 --- /dev/null +++ b/core/src/main/java/org/apache/metamodel/schema/WrappingSchema.java @@ -0,0 +1,33 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.metamodel.schema; + +/** + * Sub-interface for {@link Schema}s that wrap other {@link Schema}s, typically to apply some client-side enhancement + * logic. + */ +public interface WrappingSchema extends Schema { + + /** + * Gets the {@link Schema} that is wrapped by this {@link WrappingSchema}. + * + * @return + */ + public Schema getWrappedSchema(); +} http://git-wip-us.apache.org/repos/asf/metamodel/blob/fe9ab604/core/src/main/java/org/apache/metamodel/schema/WrappingTable.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/schema/WrappingTable.java b/core/src/main/java/org/apache/metamodel/schema/WrappingTable.java new file mode 100644 index 0000000..b147a2f --- /dev/null +++ b/core/src/main/java/org/apache/metamodel/schema/WrappingTable.java @@ -0,0 +1,33 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.metamodel.schema; + +/** + * Sub-interface for {@link Table}s that wrap other {@link Table}s, typically to apply some client-side enhancement + * logic. + */ +public interface WrappingTable extends Table { + + /** + * Gets the {@link Table} that is wrapped by this {@link WrappingTable}. + * + * @return + */ + public Table getWrappedTable(); +} http://git-wip-us.apache.org/repos/asf/metamodel/blob/fe9ab604/core/src/test/java/org/apache/metamodel/MockDataContext.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/metamodel/MockDataContext.java b/core/src/test/java/org/apache/metamodel/MockDataContext.java index 0c217f2..0a6cd72 100644 --- a/core/src/test/java/org/apache/metamodel/MockDataContext.java +++ b/core/src/test/java/org/apache/metamodel/MockDataContext.java @@ -45,6 +45,7 @@ public class MockDataContext extends QueryPostprocessDataContext { private final String _value; public MockDataContext(String schemaName, String tableName, String value) { + super(true); _schemaName = schemaName; _tableName = tableName; _value = value; http://git-wip-us.apache.org/repos/asf/metamodel/blob/fe9ab604/core/src/test/java/org/apache/metamodel/MockUpdateableDataContext.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/metamodel/MockUpdateableDataContext.java b/core/src/test/java/org/apache/metamodel/MockUpdateableDataContext.java index 4e6563a..4d06d5a 100644 --- a/core/src/test/java/org/apache/metamodel/MockUpdateableDataContext.java +++ b/core/src/test/java/org/apache/metamodel/MockUpdateableDataContext.java @@ -51,8 +51,13 @@ public class MockUpdateableDataContext extends QueryPostprocessDataContext imple private final MutableTable _table; private final MutableSchema _schema; - + public MockUpdateableDataContext() { + this(true); + } + + public MockUpdateableDataContext(boolean addDefaultTableAlias) { + super(addDefaultTableAlias); _values.add(new Object[] { "1", "hello" }); _values.add(new Object[] { "2", "there" }); _values.add(new Object[] { "3", "world" }); @@ -70,6 +75,9 @@ public class MockUpdateableDataContext extends QueryPostprocessDataContext imple @Override protected DataSet materializeMainSchemaTable(Table table, List<Column> columns, int maxRows) { + if (table != _table) { + throw new IllegalArgumentException("Unknown table: " + table); + } List<Row> rows = new ArrayList<Row>(); List<SelectItem> items = columns.stream().map(SelectItem::new).collect(Collectors.toList()); @@ -112,6 +120,9 @@ public class MockUpdateableDataContext extends QueryPostprocessDataContext imple @Override public RowDeletionBuilder deleteFrom(Table table) throws IllegalArgumentException, IllegalStateException, UnsupportedOperationException { + if (table != _table) { + throw new IllegalArgumentException("Unknown table: " + table); + } return new AbstractRowDeletionBuilder(table) { @Override public void execute() throws MetaModelException { @@ -123,6 +134,9 @@ public class MockUpdateableDataContext extends QueryPostprocessDataContext imple @Override public RowInsertionBuilder insertInto(Table table) throws IllegalArgumentException, IllegalStateException, UnsupportedOperationException { + if (table != _table) { + throw new IllegalArgumentException("Unknown table: " + table); + } return new AbstractRowInsertionBuilder<UpdateCallback>(this, table) { @Override
