CAY-2465 New SelectTranslator implementation
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/f6b2dac9 Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/f6b2dac9 Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/f6b2dac9 Branch: refs/heads/master Commit: f6b2dac9667343928324cd7ce364ee6e5df17275 Parents: eb0373e Author: Nikita Timofeev <stari...@gmail.com> Authored: Wed Jan 9 11:41:46 2019 +0300 Committer: Nikita Timofeev <stari...@gmail.com> Committed: Wed Jan 9 11:41:46 2019 +0300 ---------------------------------------------------------------------- .../access/MixedResultIncrementalFaultList.java | 12 +- .../cayenne/access/jdbc/ColumnDescriptor.java | 7 + .../access/sqlbuilder/AliasedNodeBuilder.java | 6 +- .../access/sqlbuilder/ColumnNodeBuilder.java | 7 +- .../cayenne/access/sqlbuilder/SQLBuilder.java | 15 + .../access/sqlbuilder/ValueNodeBuilder.java | 9 +- .../access/sqlbuilder/sqltree/AliasedNode.java | 55 + .../access/sqlbuilder/sqltree/BetweenNode.java | 4 + .../access/sqlbuilder/sqltree/ColumnNode.java | 6 +- .../sqlbuilder/sqltree/ExpressionNode.java | 4 +- .../access/sqlbuilder/sqltree/FunctionNode.java | 6 +- .../sqlbuilder/sqltree/OpExpressionNode.java | 4 + .../access/sqlbuilder/sqltree/TextNode.java | 4 + .../access/sqlbuilder/sqltree/ValueNode.java | 27 +- .../translator/select/BaseColumnExtractor.java | 44 + .../translator/select/BaseSQLTreeProcessor.java | 128 +++ .../select/ColumnDescriptorStage.java | 56 + .../translator/select/ColumnExtractor.java | 33 + .../translator/select/ColumnExtractorStage.java | 54 + .../translator/select/ColumnSelectWrapper.java | 84 ++ .../select/CustomColumnSetExtractor.java | 133 +++ .../select/DataObjectMatchTranslator.java | 152 --- .../select/DbEntityColumnExtractor.java | 45 + .../translator/select/DbPathProcessor.java | 108 ++ .../select/DefaultQuotingAppendable.java | 47 + .../select/DefaultSelectTranslator.java | 1076 ++---------------- .../select/DescriptorColumnExtractor.java | 140 +++ .../access/translator/select/DistinctStage.java | 71 ++ .../access/translator/select/GroupByStage.java | 54 + .../select/HavingTranslationStage.java | 40 + .../translator/select/IdColumnExtractor.java | 49 + .../access/translator/select/JoinStack.java | 219 ---- .../access/translator/select/JoinTreeNode.java | 145 --- .../translator/select/LimitOffsetStage.java | 37 + .../translator/select/ObjPathProcessor.java | 132 +++ .../translator/select/ObjectSelectWrapper.java | 84 ++ .../access/translator/select/OrderingStage.java | 81 ++ .../translator/select/OrderingTranslator.java | 127 --- .../translator/select/PathComponents.java | 95 ++ .../access/translator/select/PathProcessor.java | 129 +++ .../select/PathTranslationResult.java | 51 + .../translator/select/PathTranslator.java | 60 + .../translator/select/PrefetchNodeStage.java | 128 +++ .../select/QualifierTranslationStage.java | 74 ++ .../translator/select/QualifierTranslator.java | 1026 ++++++----------- .../translator/select/QueryAssembler.java | 197 ---- .../translator/select/QueryAssemblerHelper.java | 488 -------- .../translator/select/ResultNodeDescriptor.java | 138 +++ .../translator/select/SQLGenerationStage.java | 44 + .../translator/select/SelectQueryWrapper.java | 84 ++ .../access/translator/select/TableTree.java | 109 ++ .../access/translator/select/TableTreeNode.java | 75 ++ .../translator/select/TableTreeStage.java | 104 ++ .../select/TranslatableQueryWrapper.java | 57 + .../translator/select/TranslationStage.java | 27 + .../translator/select/TranslatorContext.java | 245 ++++ .../select/TrimmingQualifierTranslator.java | 92 -- .../org/apache/cayenne/dba/AutoAdapter.java | 8 +- .../java/org/apache/cayenne/dba/DbAdapter.java | 10 +- .../cayenne/dba/DefaultQuotingStrategy.java | 48 +- .../org/apache/cayenne/dba/JdbcAdapter.java | 25 +- .../org/apache/cayenne/dba/QuotingStrategy.java | 20 +- .../cayenne/dba/db2/DB2ActionBuilder.java | 6 + .../org/apache/cayenne/dba/db2/DB2Adapter.java | 12 +- .../cayenne/dba/db2/DB2QualifierTranslator.java | 174 --- .../cayenne/dba/db2/DB2SQLTreeProcessor.java | 85 ++ .../apache/cayenne/dba/db2/DB2SelectAction.java | 39 + .../cayenne/dba/derby/DerbyActionBuilder.java | 40 + .../apache/cayenne/dba/derby/DerbyAdapter.java | 23 +- .../dba/derby/DerbyQualifierTranslator.java | 141 --- .../dba/derby/DerbySQLTreeProcessor.java | 81 ++ .../cayenne/dba/derby/DerbySelectAction.java | 39 + .../dba/derby/sqltree/DerbyValueNode.java | 51 + .../dba/firebird/FirebirdActionBuilder.java | 41 + .../cayenne/dba/firebird/FirebirdAdapter.java | 21 +- .../firebird/FirebirdQualifierTranslator.java | 181 --- .../dba/firebird/FirebirdSQLTreeProcessor.java | 189 +++ .../dba/firebird/FirebirdSelectAction.java | 40 + .../dba/firebird/sqltree/FirebirdLimitNode.java | 50 + .../sqltree/FirebirdSubstringFunctionNode.java | 65 ++ .../cayenne/dba/frontbase/FrontBaseAdapter.java | 25 +- .../frontbase/FrontBaseQualifierTranslator.java | 134 --- .../frontbase/FrontBaseSQLTreeProcessor.java | 104 ++ .../frontbase/FrontBaseSelectTranslator.java | 52 - .../apache/cayenne/dba/h2/H2ActionBuilder.java | 39 + .../org/apache/cayenne/dba/h2/H2Adapter.java | 18 + .../cayenne/dba/h2/H2SQLTreeProcessor.java | 37 + .../apache/cayenne/dba/h2/H2SelectAction.java | 39 + .../cayenne/dba/hsqldb/HSQLDBAdapter.java | 27 +- .../dba/hsqldb/HSQLQualifierTranslator.java | 86 -- .../dba/hsqldb/HSQLSelectTranslator.java | 57 - .../cayenne/dba/hsqldb/HSQLTreeProcessor.java | 70 ++ .../cayenne/dba/ingres/IngresAdapter.java | 23 +- .../dba/ingres/IngresQualifierTranslator.java | 122 -- .../dba/ingres/IngresSelectTranslator.java | 49 - .../dba/ingres/IngressSQLTreeProcessor.java | 108 ++ .../apache/cayenne/dba/mysql/MySQLAdapter.java | 21 +- .../dba/mysql/MySQLQualifierTranslator.java | 105 -- .../dba/mysql/MySQLSelectTranslator.java | 57 - .../cayenne/dba/mysql/MySQLTreeProcessor.java | 68 ++ .../dba/mysql/sqltree/MysqlLikeNode.java | 46 + .../dba/mysql/sqltree/MysqlLimitOffsetNode.java | 40 + .../cayenne/dba/openbase/OpenBaseAdapter.java | 21 +- .../cayenne/dba/openbase/OpenBaseJoinStack.java | 115 -- .../openbase/OpenBaseQualifierTranslator.java | 177 --- .../dba/openbase/OpenBaseSQLTreeProcessor.java | 109 ++ .../dba/openbase/OpenBaseSelectTranslator.java | 53 - .../cayenne/dba/oracle/Oracle8Adapter.java | 19 - .../cayenne/dba/oracle/Oracle8JoinStack.java | 108 -- .../dba/oracle/Oracle8QualifierTranslator.java | 49 - .../dba/oracle/Oracle8SelectTranslator.java | 47 - .../cayenne/dba/oracle/OracleAdapter.java | 23 +- .../dba/oracle/OracleQualifierTranslator.java | 212 ---- .../dba/oracle/OracleSQLTreeProcessor.java | 241 ++++ .../dba/oracle/OracleSelectTranslator.java | 78 -- .../dba/postgres/PostgreSQLTreeProcessor.java | 83 ++ .../cayenne/dba/postgres/PostgresAdapter.java | 27 +- .../postgres/PostgresQualifierTranslator.java | 189 --- .../dba/postgres/PostgresSelectTranslator.java | 54 - .../postgres/sqltree/PositionFunctionNode.java | 43 + .../sqltree/PostgresExtractFunctionNode.java | 59 + .../dba/postgres/sqltree/PostgresLikeNode.java | 46 + .../sqltree/PostgresLimitOffsetNode.java | 60 + .../cayenne/dba/sqlite/SQLiteActionBuilder.java | 9 + .../cayenne/dba/sqlite/SQLiteAdapter.java | 23 +- .../dba/sqlite/SQLiteQualifierTranslator.java | 162 --- .../cayenne/dba/sqlite/SQLiteSelectAction.java | 43 + .../cayenne/dba/sqlite/SQLiteTreeProcessor.java | 100 ++ .../cayenne/dba/sqlserver/SQLServerAdapter.java | 23 +- .../sqlserver/SQLServerSelectTranslator.java | 54 - .../dba/sqlserver/SQLServerTreeProcessor.java | 128 +++ .../SQLServerTrimmingQualifierTranslator.java | 207 ---- .../sqlserver/sqltree/SQLServerColumnNode.java | 46 + .../cayenne/dba/sybase/SybaseAdapter.java | 25 +- .../dba/sybase/SybaseQualifierTranslator.java | 114 -- .../dba/sybase/SybaseSelectTranslator.java | 52 - .../org/apache/cayenne/query/ColumnSelect.java | 7 + .../cayenne/query/SelectQueryMetadata.java | 13 +- .../java/org/apache/cayenne/util/ArrayUtil.java | 260 +++++ .../cayenne/FlattenedRelationshipsIT.java | 3 +- .../select/BaseColumnExtractorTest.java | 43 + .../select/ColumnDescriptorStageTest.java | 55 + .../select/CustomColumnSetExtractorTest.java | 94 ++ .../select/DbEntityColumnExtractorTest.java | 121 ++ .../select/DefaultObjectSelectTranslatorIT.java | 136 +++ .../select/DefaultSelectTranslatorIT.java | 249 ++-- .../select/DescriptorColumnExtractorTest.java | 97 ++ .../translator/select/DistinctStageTest.java | 103 ++ .../translator/select/GroupByStageTest.java | 86 ++ .../select/HavingTranslationStageTest.java | 90 ++ .../select/IdColumnExtractorTest.java | 113 ++ .../translator/select/LimitOffsetStageTest.java | 65 ++ .../select/MockQueryMetadataBuilder.java | 100 ++ .../select/MockQueryWrapperBuilder.java | 137 +++ .../select/MockTranslatorContext.java | 35 + .../translator/select/ObjPathProcessorIT.java | 92 ++ .../translator/select/ObjPathProcessorIT2.java | 91 ++ .../translator/select/ObjPathProcessorIT3.java | 82 ++ .../translator/select/ObjPathProcessorIT4.java | 72 ++ .../translator/select/OrderingStageTest.java | 110 ++ .../translator/select/OrderingTranslatorIT.java | 136 --- .../translator/select/PathComponentsTest.java | 77 ++ .../select/QualifierTranslationStageTest.java | 89 ++ .../select/QualifierTranslatorIT.java | 164 --- .../select/QualifierTranslatorTest.java | 524 +++++++++ .../translator/select/QueryAssemblerIT.java | 61 - .../translator/select/TstQueryAssembler.java | 70 -- .../cayenne/exp/property/PathAliasesIT.java | 4 +- .../apache/cayenne/query/ColumnSelectIT.java | 56 +- .../cayenne/unit/IngresUnitDbAdapter.java | 8 + .../org/apache/cayenne/util/ArrayUtilTest.java | 354 ++++++ 171 files changed, 9096 insertions(+), 6744 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/MixedResultIncrementalFaultList.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/MixedResultIncrementalFaultList.java b/cayenne-server/src/main/java/org/apache/cayenne/access/MixedResultIncrementalFaultList.java index 9ca2136..f8c02b8 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/MixedResultIncrementalFaultList.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/MixedResultIncrementalFaultList.java @@ -46,7 +46,6 @@ import org.apache.cayenne.util.Util; * if there is no Persistent objects in the result Collection it will be iterated as is, without faulting anything. * * @see QueryMetadata#getPageSize() - * @see org.apache.cayenne.access.translator.select.DefaultSelectTranslator * @see org.apache.cayenne.query.SelectQueryMetadata * * @since 4.0 @@ -142,7 +141,10 @@ class MixedResultIncrementalFaultList<E> extends IncrementalFaultList<E> { for (int i = fromIndex; i < toIndex; i++) { Object[] object = (Object[])elements.get(i); if (helper.unresolvedSuspect(object[dataIdx])) { - quals.add(buildIdQualifier(dataIdx, object)); + Expression nextQualifier = buildIdQualifier(dataIdx, object); + if(nextQualifier != null) { + quals.add(nextQualifier); + } } } @@ -188,7 +190,9 @@ class MixedResultIncrementalFaultList<E> extends IncrementalFaultList<E> { Expression buildIdQualifier(int index, Object[] data) { Map<String, Object> map; if(data[index] instanceof Map) { - map = (Map<String, Object>)data[index]; + map = (Map<String, Object>) data[index]; + } else if(data[index] == null) { + return null; } else { map = new HashMap<>(); int i = 0; @@ -235,6 +239,8 @@ class MixedResultIncrementalFaultList<E> extends IncrementalFaultList<E> { return false; } } + } else if(dataInTheList[dataIdx] == null) { + return false; } else { for(Object id : map.values()) { if (!dataInTheList[dataIdx++].equals(id)) { http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java index 632f5e3..90cd61b 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java @@ -161,6 +161,13 @@ public class ColumnDescriptor { } /** + * @since 4.2 + */ + public void setAttribute(DbAttribute attribute) { + this.attribute = attribute; + } + + /** * Returns true if another object is a ColumnDescriptor with the same name, * name prefix, table and procedure names. Other fields are ignored in the * equality test. http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/AliasedNodeBuilder.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/AliasedNodeBuilder.java b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/AliasedNodeBuilder.java index 50f07bd..aae7835 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/AliasedNodeBuilder.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/AliasedNodeBuilder.java @@ -19,9 +19,8 @@ package org.apache.cayenne.access.sqlbuilder; -import org.apache.cayenne.access.sqlbuilder.sqltree.EmptyNode; +import org.apache.cayenne.access.sqlbuilder.sqltree.AliasedNode; import org.apache.cayenne.access.sqlbuilder.sqltree.Node; -import org.apache.cayenne.access.sqlbuilder.sqltree.TextNode; /** * @since 4.2 @@ -38,9 +37,8 @@ class AliasedNodeBuilder implements NodeBuilder { @Override public Node build() { - Node root = new EmptyNode(); + Node root = new AliasedNode(alias); root.addChild(nodeBuilder.build()); - root.addChild(new TextNode(" " + alias)); return root; } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/ColumnNodeBuilder.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/ColumnNodeBuilder.java b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/ColumnNodeBuilder.java index 8236744..55c1032 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/ColumnNodeBuilder.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/ColumnNodeBuilder.java @@ -74,13 +74,10 @@ public class ColumnNodeBuilder implements ExpressionTrait { @Override public Node build() { - ColumnNode columnNode; if(unescaped) { - columnNode = new UnescapedColumnNode(table, column, alias, attribute); - } else { - columnNode = new ColumnNode(table, column, alias, attribute); + return new UnescapedColumnNode(table, column, alias, attribute); } - return columnNode; + return new ColumnNode(table, column, alias, attribute); } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/SQLBuilder.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/SQLBuilder.java b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/SQLBuilder.java index 0dcdfc1..dfa3d2e 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/SQLBuilder.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/SQLBuilder.java @@ -19,6 +19,7 @@ package org.apache.cayenne.access.sqlbuilder; +import org.apache.cayenne.access.sqlbuilder.sqltree.AliasedNode; import org.apache.cayenne.access.sqlbuilder.sqltree.ColumnNode; import org.apache.cayenne.access.sqlbuilder.sqltree.FunctionNode; import org.apache.cayenne.access.sqlbuilder.sqltree.Node; @@ -87,6 +88,17 @@ public final class SQLBuilder { if(suppressAlias(node)) { return node(node); } + + if(node instanceof FunctionNode) { + ((FunctionNode) node).setAlias(alias); + return node(node); + } + + if(node instanceof ColumnNode) { + ((ColumnNode) node).setAlias(alias); + return node(node); + } + return new AliasedNodeBuilder(node(node), alias); } @@ -154,6 +166,9 @@ public final class SQLBuilder { } else if(node.getType() == NodeType.FUNCTION && ((FunctionNode) node).getAlias() != null) { suppressAlias = true; return false; + } else if(node instanceof AliasedNode) { + suppressAlias = true; + return false; } return true; } http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/ValueNodeBuilder.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/ValueNodeBuilder.java b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/ValueNodeBuilder.java index 5e027c6..4e3e659 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/ValueNodeBuilder.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/ValueNodeBuilder.java @@ -32,6 +32,8 @@ public class ValueNodeBuilder implements NodeBuilder, ExpressionTrait { private DbAttribute attribute; + private boolean isArray; + ValueNodeBuilder(Object value) { this.value = value; } @@ -41,8 +43,13 @@ public class ValueNodeBuilder implements NodeBuilder, ExpressionTrait { return this; } + public ValueNodeBuilder array(boolean isArray) { + this.isArray = isArray; + return this; + } + @Override public Node build() { - return new ValueNode(value, attribute); + return new ValueNode(value, isArray, attribute); } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/AliasedNode.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/AliasedNode.java b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/AliasedNode.java new file mode 100644 index 0000000..524fc14 --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/AliasedNode.java @@ -0,0 +1,55 @@ +/***************************************************************** + * 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.cayenne.access.sqlbuilder.sqltree; + +import org.apache.cayenne.access.sqlbuilder.QuotingAppendable; + +/** + * @since 4.2 + */ +public class AliasedNode extends Node { + + protected final String alias; + + public AliasedNode(String alias) { + this.alias = alias; + } + + @Override + public Node copy() { + return new AliasedNode(alias); + } + + @Override + public QuotingAppendable append(QuotingAppendable buffer) { + return buffer; + } + + @Override + public void appendChildrenEnd(QuotingAppendable buffer) { + super.appendChildrenEnd(buffer); + buffer.append(' ').append(alias); + } + + public String getAlias() { + return alias; + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/BetweenNode.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/BetweenNode.java b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/BetweenNode.java index 1102a8a..6826931 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/BetweenNode.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/BetweenNode.java @@ -49,4 +49,8 @@ public class BetweenNode extends ExpressionNode { public Node copy() { return new BetweenNode(not); } + + public boolean isNot() { + return not; + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/ColumnNode.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/ColumnNode.java b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/ColumnNode.java index b610104..9c49a4b 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/ColumnNode.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/ColumnNode.java @@ -29,8 +29,8 @@ public class ColumnNode extends Node { protected final String table; protected final String column; - protected final String alias; protected final DbAttribute attribute; + protected String alias; public ColumnNode(String table, String column, String alias, DbAttribute attribute) { super(NodeType.COLUMN); @@ -65,6 +65,10 @@ public class ColumnNode extends Node { return alias; } + public void setAlias(String alias) { + this.alias = alias; + } + public DbAttribute getAttribute() { return attribute; } http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/ExpressionNode.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/ExpressionNode.java b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/ExpressionNode.java index 8344380..3e9795e 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/ExpressionNode.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/ExpressionNode.java @@ -41,14 +41,14 @@ public class ExpressionNode extends Node { @Override public void appendChildrenStart(QuotingAppendable buffer) { - if(parent.type != NodeType.WHERE && parent.type != NodeType.JOIN) { + if(parent != null && parent.type != NodeType.WHERE && parent.type != NodeType.JOIN) { buffer.append(" ("); } } @Override public void appendChildrenEnd(QuotingAppendable buffer) { - if(parent.type != NodeType.WHERE && parent.type != NodeType.JOIN) { + if(parent != null && parent.type != NodeType.WHERE && parent.type != NodeType.JOIN) { buffer.append(" )"); } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/FunctionNode.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/FunctionNode.java b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/FunctionNode.java index 23b0fa7..3673ed0 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/FunctionNode.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/FunctionNode.java @@ -28,8 +28,8 @@ import org.apache.cayenne.access.sqlbuilder.QuotingAppendable; public class FunctionNode extends Node { private final String functionName; - private final String alias; private final boolean needParentheses; + private String alias; public FunctionNode(String functionName, String alias) { this(functionName, alias, true); @@ -103,6 +103,10 @@ public class FunctionNode extends Node { return alias; } + public void setAlias(String alias) { + this.alias = alias; + } + @Override public Node copy() { return new FunctionNode(functionName, alias, needParentheses); http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/OpExpressionNode.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/OpExpressionNode.java b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/OpExpressionNode.java index 7099000..6b1b55d 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/OpExpressionNode.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/OpExpressionNode.java @@ -41,4 +41,8 @@ public class OpExpressionNode extends ExpressionNode { public Node copy() { return new OpExpressionNode(op); } + + public String getOp() { + return op; + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/TextNode.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/TextNode.java b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/TextNode.java index 45ee5cf..afd0f47 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/TextNode.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/TextNode.java @@ -41,4 +41,8 @@ public class TextNode extends Node { public Node copy() { return new TextNode(text); } + + public CharSequence getText() { + return text; + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/ValueNode.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/ValueNode.java b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/ValueNode.java index 2ba5fbc..2911397 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/ValueNode.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/ValueNode.java @@ -34,12 +34,14 @@ import org.apache.cayenne.map.DbAttribute; public class ValueNode extends Node { private final Object value; + private final boolean isArray; // Used as hint for type of this value private final DbAttribute attribute; - public ValueNode(Object value, DbAttribute attribute) { + public ValueNode(Object value, boolean isArray, DbAttribute attribute) { super(NodeType.VALUE); this.value = value; + this.isArray = isArray; this.attribute = attribute; } @@ -51,6 +53,10 @@ public class ValueNode extends Node { return attribute; } + public boolean isArray() { + return isArray; + } + @Override public QuotingAppendable append(QuotingAppendable buffer) { appendValue(value, buffer); @@ -62,8 +68,7 @@ public class ValueNode extends Node { return; } - boolean isArray = val.getClass().isArray(); - if(isArray) { + if(isArray && val.getClass().isArray()) { if(val instanceof short[]) { appendValue((short[])val, buffer); } else if(val instanceof char[]) { @@ -82,7 +87,7 @@ public class ValueNode extends Node { appendValue((Object[]) val, buffer); } else if(val instanceof byte[]) { // append byte[] array as single object - appendObjectValue(buffer, val); + appendValue((byte[])val, buffer); } else { throw new CayenneRuntimeException("Unsupported array type %s", val.getClass().getName()); } @@ -227,6 +232,18 @@ public class ValueNode extends Node { } } + private void appendValue(byte[] val, QuotingAppendable buffer) { + boolean first = true; + for(byte i : val) { + if(first) { + first = false; + } else { + buffer.append(','); + } + appendValue(i, buffer); + } + } + private void appendValue(Object[] val, QuotingAppendable buffer) { boolean first = true; for(Object i : val) { @@ -241,6 +258,6 @@ public class ValueNode extends Node { @Override public Node copy() { - return new ValueNode(value, attribute); + return new ValueNode(value, isArray, attribute); } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/BaseColumnExtractor.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/BaseColumnExtractor.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/BaseColumnExtractor.java new file mode 100644 index 0000000..20cc5f7 --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/BaseColumnExtractor.java @@ -0,0 +1,44 @@ +/***************************************************************** + * 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.cayenne.access.translator.select; + +import org.apache.cayenne.access.sqlbuilder.sqltree.Node; +import org.apache.cayenne.map.DbAttribute; + +import static org.apache.cayenne.access.sqlbuilder.SQLBuilder.table; + +/** + * @since 4.2 + */ +abstract class BaseColumnExtractor implements ColumnExtractor { + + protected final TranslatorContext context; + + BaseColumnExtractor(TranslatorContext context) { + this.context = context; + } + + protected void addDbAttribute(String prefix, String labelPrefix, DbAttribute dba) { + String alias = context.getTableTree().aliasForPath(prefix); + String dataRowKey = labelPrefix != null ? labelPrefix + '.' + dba.getName() : dba.getName(); + Node columnNode = table(alias).column(dba).build(); + context.addResultNode(columnNode, dataRowKey).setDbAttribute(dba); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/BaseSQLTreeProcessor.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/BaseSQLTreeProcessor.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/BaseSQLTreeProcessor.java new file mode 100644 index 0000000..2119e26 --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/BaseSQLTreeProcessor.java @@ -0,0 +1,128 @@ +/***************************************************************** + * 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.cayenne.access.translator.select; + +import java.util.function.Function; + +import org.apache.cayenne.access.sqlbuilder.sqltree.ColumnNode; +import org.apache.cayenne.access.sqlbuilder.sqltree.DistinctNode; +import org.apache.cayenne.access.sqlbuilder.sqltree.FunctionNode; +import org.apache.cayenne.access.sqlbuilder.sqltree.InNode; +import org.apache.cayenne.access.sqlbuilder.sqltree.LikeNode; +import org.apache.cayenne.access.sqlbuilder.sqltree.LimitOffsetNode; +import org.apache.cayenne.access.sqlbuilder.sqltree.Node; +import org.apache.cayenne.access.sqlbuilder.sqltree.SimpleNodeTreeVisitor; +import org.apache.cayenne.access.sqlbuilder.sqltree.ValueNode; + + +/** + * @since 4.2 + */ +public class BaseSQLTreeProcessor extends SimpleNodeTreeVisitor implements Function<Node, Node> { + + @Override + public Node apply(Node node) { + node.visit(this); + return node; + } + + protected void onValueNode(Node parent, ValueNode child, int index) { + } + + protected void onFunctionNode(Node parent, FunctionNode child, int index) { + } + + protected void onLimitOffsetNode(Node parent, LimitOffsetNode child, int index) { + } + + protected void onColumnNode(Node parent, ColumnNode child, int index) { + } + + protected void onInNode(Node parent, InNode child, int index) { + } + + protected void onLikeNode(Node parent, LikeNode child, int index) { + } + + protected void onResultNode(Node parent, Node child, int index) { + } + + protected void onDistinctNode(Node parent, DistinctNode child, int index) { + } + + protected void onUndefinedNode(Node parent, Node child, int index) { + } + + protected void replaceChild(Node parent, int index, Node newChild) { + replaceChild(parent, index, newChild, true); + } + + protected void replaceChild(Node parent, int index, Node newChild, boolean transferChildren) { + if (transferChildren) { + Node oldChild = parent.getChild(index); + for (int i = 0; i < oldChild.getChildrenCount(); i++) { + newChild.addChild(oldChild.getChild(i)); + } + } + parent.replaceChild(index, newChild); + } + + @Override + public boolean onChildNodeStart(Node parent, Node child, int index, boolean hasMore) { + switch (child.getType()) { + case VALUE: + onValueNode(parent, (ValueNode) child, index); + break; + + case FUNCTION: + onFunctionNode(parent, (FunctionNode) child, index); + break; + + case LIMIT_OFFSET: + onLimitOffsetNode(parent, (LimitOffsetNode) child, index); + break; + + case COLUMN: + onColumnNode(parent, (ColumnNode) child, index); + break; + + case IN: + onInNode(parent, (InNode) child, index); + break; + + case LIKE: + onLikeNode(parent, (LikeNode) child, index); + break; + + case RESULT: + onResultNode(parent, child, index); + break; + + case DISTINCT: + onDistinctNode(parent, (DistinctNode) child, index); + break; + + default: + onUndefinedNode(parent, child, index); + break; + } + return true; + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/ColumnDescriptorStage.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/ColumnDescriptorStage.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/ColumnDescriptorStage.java new file mode 100644 index 0000000..d15803a --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/ColumnDescriptorStage.java @@ -0,0 +1,56 @@ +/***************************************************************** + * 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.cayenne.access.translator.select; + +import org.apache.cayenne.access.jdbc.ColumnDescriptor; +import org.apache.cayenne.map.DbAttribute; + +/** + * @since 4.2 + */ +class ColumnDescriptorStage implements TranslationStage { + + @Override + public void perform(TranslatorContext context) { + int i = 0; + for(ResultNodeDescriptor resultNode : context.getResultNodeList()) { + context.getSelectBuilder().result(resultNode::getNode); + + if(!resultNode.isInDataRow()) { + continue; + } + + String name; + DbAttribute attribute = resultNode.getDbAttribute(); + if(attribute != null) { + name = attribute.getName(); + } else { + // generated name + name = "c" + i++; + } + + ColumnDescriptor descriptor = new ColumnDescriptor(name, resultNode.getJdbcType(), resultNode.getJavaType()); + descriptor.setAttribute(attribute); + descriptor.setDataRowKey(resultNode.getDataRowKey()); + + context.getColumnDescriptors().add(descriptor); + } + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/ColumnExtractor.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/ColumnExtractor.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/ColumnExtractor.java new file mode 100644 index 0000000..e1551a7 --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/ColumnExtractor.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.cayenne.access.translator.select; + +/** + * @since 4.2 + */ +interface ColumnExtractor { + + void extract(String prefix); + + default void extract() { + extract(null); + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/ColumnExtractorStage.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/ColumnExtractorStage.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/ColumnExtractorStage.java new file mode 100644 index 0000000..cd51d77 --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/ColumnExtractorStage.java @@ -0,0 +1,54 @@ +/***************************************************************** + * 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.cayenne.access.translator.select; + +/** + * Get result columns based on query, options are: <ol> + * <li> for column queries - defined set of columns + * <li> for paginated or nested queries - root pk columns + * <li> for queries with defined class descriptor - full column set including flattened + * <li> for everything else - all columns of the root db entity + * </ol> + * + * @since 4.2 + */ +class ColumnExtractorStage implements TranslationStage { + + @Override + public void perform(TranslatorContext context) { + ColumnExtractor extractor; + + if(context.getQuery().getColumns() != null && !context.getQuery().getColumns().isEmpty()) { + extractor = new CustomColumnSetExtractor(context, context.getQuery().getColumns()); + } else if (context.getParentContext() != null || context.getMetadata().getPageSize() > 0) { + if(context.getMetadata().getObjEntity() != null) { + extractor = new IdColumnExtractor(context, context.getMetadata().getObjEntity()); + } else { + extractor = new IdColumnExtractor(context, context.getMetadata().getDbEntity()); + } + } else if (context.getMetadata().getClassDescriptor() != null) { + extractor = new DescriptorColumnExtractor(context, context.getMetadata().getClassDescriptor()); + } else { + extractor = new DbEntityColumnExtractor(context); + } + + extractor.extract(); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/ColumnSelectWrapper.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/ColumnSelectWrapper.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/ColumnSelectWrapper.java new file mode 100644 index 0000000..21a0069 --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/ColumnSelectWrapper.java @@ -0,0 +1,84 @@ +/***************************************************************** + * 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.cayenne.access.translator.select; + +import java.util.Collection; +import java.util.Objects; + +import org.apache.cayenne.exp.Expression; +import org.apache.cayenne.exp.property.BaseProperty; +import org.apache.cayenne.map.EntityResolver; +import org.apache.cayenne.query.ColumnSelect; +import org.apache.cayenne.query.Ordering; +import org.apache.cayenne.query.PrefetchTreeNode; +import org.apache.cayenne.query.QueryMetadata; +import org.apache.cayenne.query.Select; + +/** + * @since 4.2 + */ +public class ColumnSelectWrapper implements TranslatableQueryWrapper { + + private final ColumnSelect<?> columnSelect; + + public ColumnSelectWrapper(ColumnSelect<?> columnSelect) { + this.columnSelect = Objects.requireNonNull(columnSelect); + } + + @Override + public boolean isDistinct() { + return columnSelect.isDistinct(); + } + + @Override + public QueryMetadata getMetaData(EntityResolver resolver) { + return columnSelect.getMetaData(resolver); + } + + @Override + public PrefetchTreeNode getPrefetchTree() { + return columnSelect.getPrefetches(); + } + + @Override + public Expression getQualifier() { + return columnSelect.getWhere(); + } + + @Override + public Collection<Ordering> getOrderings() { + return columnSelect.getOrderings(); + } + + @Override + public Collection<BaseProperty<?>> getColumns() { + return columnSelect.getColumns(); + } + + @Override + public Expression getHavingQualifier() { + return columnSelect.getHaving(); + } + + @Override + public Select<?> unwrap() { + return columnSelect; + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/CustomColumnSetExtractor.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/CustomColumnSetExtractor.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/CustomColumnSetExtractor.java new file mode 100644 index 0000000..534fd3a --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/CustomColumnSetExtractor.java @@ -0,0 +1,133 @@ +/***************************************************************** + * 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.cayenne.access.translator.select; + +import java.util.Collection; +import java.util.Map; + +import org.apache.cayenne.CayenneRuntimeException; +import org.apache.cayenne.Persistent; +import org.apache.cayenne.access.sqlbuilder.sqltree.Node; +import org.apache.cayenne.exp.Expression; +import org.apache.cayenne.exp.parser.ASTPath; +import org.apache.cayenne.exp.property.BaseProperty; +import org.apache.cayenne.map.JoinType; +import org.apache.cayenne.map.ObjEntity; +import org.apache.cayenne.reflect.ClassDescriptor; +import org.apache.cayenne.util.Util; + +/** + * @since 4.2 + */ +class CustomColumnSetExtractor implements ColumnExtractor { + + private final TranslatorContext context; + private final Collection<BaseProperty<?>> columns; + + CustomColumnSetExtractor(TranslatorContext context, Collection<BaseProperty<?>> columns) { + this.context = context; + this.columns = columns; + } + + @Override + public void extract(String prefix) { + for (BaseProperty<?> property : columns) { + if (isFullObjectProp(property)) { + extractFullObject(prefix, property); + } else { + extractSimpleProperty(property); + } + } + } + + private void extractSimpleProperty(BaseProperty<?> property) { + Node sqlNode = context.getQualifierTranslator().translate(property); + context.addResultNode(sqlNode, true, property, property.getAlias()); + } + + private boolean isFullObjectProp(BaseProperty<?> property) { + int expressionType = property.getExpression().getType(); + + // forbid direct selection of toMany relationships columns + if(property.getType() != null && (expressionType == Expression.OBJ_PATH || expressionType == Expression.DB_PATH) + && (Collection.class.isAssignableFrom(property.getType()) + || Map.class.isAssignableFrom(property.getType()))) { + throw new CayenneRuntimeException("Can't directly select toMany relationship columns. " + + "Either select it with aggregate functions like count() or with flat() function to select full related objects."); + } + + // evaluate ObjPath with Persistent type as toOne relations and use it as full object + return expressionType == Expression.FULL_OBJECT + || (property.getType() != null + && expressionType == Expression.OBJ_PATH + && Persistent.class.isAssignableFrom(property.getType())); + } + + private void extractFullObject(String prefix, BaseProperty<?> property) { + prefix = calculatePrefix(prefix, property); + ensureJoin(prefix); + + ObjEntity entity = context.getResolver().getObjEntity(property.getType()); + + ColumnExtractor extractor; + if(context.getMetadata().getPageSize() > 0) { + extractor = new IdColumnExtractor(context, entity); + } else { + ClassDescriptor descriptor = context.getResolver().getClassDescriptor(entity.getName()); + extractor = new DescriptorColumnExtractor(context, descriptor); + } + + int index = context.getResultNodeList().size(); + + // extract required columns of entity + extractor.extract(prefix); + + // Reset data row key as ObjectResolver expects it to match attribute name. + // Maybe we should change resolver, as it seems cleaner to have path from root as prefix in data row key. + for(int i=index; i<context.getResultNodeList().size(); i++) { + context.getResultNodeList().get(i).setDataRowKey(null); + } + } + + private String calculatePrefix(String prefix, BaseProperty<?> property) { + Expression propertyExpression = property.getExpression(); + int expressionType = property.getExpression().getType(); + + if(expressionType == Expression.FULL_OBJECT && propertyExpression.getOperandCount() > 0) { + Object op = propertyExpression.getOperand(0); + if(op instanceof ASTPath) { + prefix = ((ASTPath) op).getPath(); + } + } else if(propertyExpression instanceof ASTPath) { + prefix = ((ASTPath) propertyExpression).getPath(); + } + + return prefix; + } + + private void ensureJoin(String prefix) { + // ensure all joins for given property + if(!Util.isEmptyString(prefix)) { + PathTranslationResult result = context.getPathTranslator().translatePath(context.getMetadata().getObjEntity(), prefix); + result.getDbRelationship().ifPresent(relationship + -> context.getTableTree().addJoinTable(result.getFinalPath(), relationship, JoinType.LEFT_OUTER)); + } + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DataObjectMatchTranslator.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DataObjectMatchTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DataObjectMatchTranslator.java deleted file mode 100644 index 07f53f1..0000000 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DataObjectMatchTranslator.java +++ /dev/null @@ -1,152 +0,0 @@ -/***************************************************************** - * 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.cayenne.access.translator.select; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import org.apache.cayenne.CayenneRuntimeException; -import org.apache.cayenne.ObjectId; -import org.apache.cayenne.Persistent; -import org.apache.cayenne.exp.Expression; -import org.apache.cayenne.map.DbAttribute; -import org.apache.cayenne.map.DbEntity; -import org.apache.cayenne.map.DbJoin; -import org.apache.cayenne.map.DbRelationship; - -/** - */ -public class DataObjectMatchTranslator { - - protected Map<String, DbAttribute> attributes; - protected Map<String, Object> values; - protected String operation; - protected Expression expression; - protected DbRelationship relationship; - protected String joinSplitAlias; - - public Expression getExpression() { - return expression; - } - - public void setExpression(Expression expression) { - this.expression = expression; - } - - public void reset() { - attributes = null; - values = null; - operation = null; - expression = null; - relationship = null; - } - - /** - * Initializes itself to do translation of the match ending with a - * DbRelationship. - * - * @since 3.0 - */ - public void setRelationship(DbRelationship rel, String joinSplitAlias) { - this.relationship = rel; - this.joinSplitAlias = joinSplitAlias; - attributes = new HashMap<>(rel.getJoins().size() * 2); - - if (rel.isToMany() || !rel.isToPK()) { - - // match on target PK - DbEntity ent = rel.getTargetEntity(); - - // index by name - for (DbAttribute pkAttr : ent.getPrimaryKeys()) { - attributes.put(pkAttr.getName(), pkAttr); - } - } else { - - // match on this FK - for (DbJoin join : rel.getJoins()) { - // index by target name - attributes.put(join.getTargetName(), join.getSource()); - } - } - } - - public void setDataObject(Persistent obj) { - if (obj == null) { - values = Collections.emptyMap(); - return; - } - - setObjectId(obj.getObjectId()); - } - - /** - * @since 1.2 - */ - public void setObjectId(ObjectId id) { - if (id == null) { - throw new CayenneRuntimeException( - "Null ObjectId, probably an attempt to use TRANSIENT object as a query parameter."); - } else if (id.isTemporary()) { - throw new CayenneRuntimeException( - "Temporary id, probably an attempt to use NEW object as a query parameter."); - } else { - values = id.getIdSnapshot(); - } - } - - public Iterator<String> keys() { - if (attributes == null) { - throw new IllegalStateException("An attempt to use uninitialized DataObjectMatchTranslator: " - + "[attributes: null, values: " + values + "]"); - } - - return attributes.keySet().iterator(); - } - - /** - * @since 3.0 - */ - public String getJoinSplitAlias() { - return joinSplitAlias; - } - - public DbRelationship getRelationship() { - return relationship; - } - - public DbAttribute getAttribute(String key) { - return attributes.get(key); - } - - public Object getValue(String key) { - return values.get(key); - } - - public void setOperation(String operation) { - this.operation = operation; - } - - public String getOperation() { - return operation; - } -} http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DbEntityColumnExtractor.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DbEntityColumnExtractor.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DbEntityColumnExtractor.java new file mode 100644 index 0000000..3354fac --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DbEntityColumnExtractor.java @@ -0,0 +1,45 @@ +/***************************************************************** + * 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.cayenne.access.translator.select; + +import java.util.Objects; + +import org.apache.cayenne.map.DbAttribute; +import org.apache.cayenne.map.DbEntity; + +/** + * @since 4.2 + */ +class DbEntityColumnExtractor extends BaseColumnExtractor { + + private final DbEntity dbEntity; + + DbEntityColumnExtractor(TranslatorContext context) { + super(context); + this.dbEntity = Objects.requireNonNull(context.getMetadata().getDbEntity(), "No root entity"); + } + + @Override + public void extract(String prefix) { + for(DbAttribute attribute : dbEntity.getAttributes()) { + addDbAttribute(prefix, prefix, attribute); + } + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DbPathProcessor.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DbPathProcessor.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DbPathProcessor.java new file mode 100644 index 0000000..cccea12 --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DbPathProcessor.java @@ -0,0 +1,108 @@ +/***************************************************************** + * 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.cayenne.access.translator.select; + +import org.apache.cayenne.map.DbAttribute; +import org.apache.cayenne.map.DbEntity; +import org.apache.cayenne.map.DbJoin; +import org.apache.cayenne.map.DbRelationship; +import org.apache.cayenne.map.JoinType; + +/** + * @since 4.2 + */ +class DbPathProcessor extends PathProcessor<DbEntity> { + + DbPathProcessor(TranslatorContext context, DbEntity entity, String parentPath) { + super(context, entity); + if(parentPath != null) { + currentDbPath.append(parentPath); + } + } + + @Override + protected void processNormalAttribute(String next) { + DbAttribute dbAttribute = entity.getAttribute(next); + if(dbAttribute != null) { + processAttribute(dbAttribute); + return; + } + + DbRelationship relationship = entity.getRelationship(next); + if(relationship != null) { + entity = relationship.getTargetEntity(); + processRelationship(relationship); + return; + } + + throw new IllegalStateException("Unable to resolve path: " + currentDbPath.toString() + "." + next); + } + + @Override + protected void processAliasedAttribute(String next, String alias) { + DbRelationship relationship = entity.getRelationship(alias); + if(relationship == null) { + throw new IllegalStateException("Non-relationship aliased path part: " + alias); + } + + processRelationship(relationship); + } + + private void processAttribute(DbAttribute attribute) { + addAttribute(currentDbPath.toString(), attribute); + appendCurrentPath(attribute.getName()); + } + + private void processRelationship(DbRelationship relationship) { + if (lastComponent) { + // if this is a last relationship in the path, it needs special handling + processRelTermination(relationship); + } else { + appendCurrentPath(relationship.getName()); + context.getTableTree().addJoinTable(currentDbPath.toString(), relationship, JoinType.LEFT_OUTER); + if(!relationship.isToMany()) { + String path = currentDbPath.toString(); + for (DbAttribute attribute : relationship.getTargetEntity().getPrimaryKeys()) { + addAttribute(path, attribute); + } + } + } + } + + protected void processRelTermination(DbRelationship rel) { + this.relationship = rel; + String path = currentDbPath.toString(); + appendCurrentPath(rel.getName()); + + if (rel.isToMany() || !rel.isToPK()) { + // match on target PK + context.getTableTree().addJoinTable(currentDbPath.toString(), rel, JoinType.LEFT_OUTER); + path = currentDbPath.toString(); + for(DbAttribute attribute : rel.getTargetEntity().getPrimaryKeys()) { + addAttribute(path, attribute); + } + } else { + for(DbJoin join : rel.getJoins()) { + addAttribute(path, join.getSource()); + } + } + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/f6b2dac9/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultQuotingAppendable.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultQuotingAppendable.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultQuotingAppendable.java new file mode 100644 index 0000000..712cc5f --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultQuotingAppendable.java @@ -0,0 +1,47 @@ +/***************************************************************** + * 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.cayenne.access.translator.select; + +import org.apache.cayenne.access.sqlbuilder.QuotingAppendable; +import org.apache.cayenne.access.sqlbuilder.StringBuilderAppendable; + +/** + * @since 4.2 + */ +public class DefaultQuotingAppendable extends StringBuilderAppendable { + + private final TranslatorContext context; + + public DefaultQuotingAppendable(TranslatorContext context) { + super(); + this.context = context; + } + + @Override + public QuotingAppendable appendQuoted(CharSequence content) { + context.getQuotingStrategy().quotedIdentifier(context.getRootDbEntity(), content, builder); + return this; + } + + @Override + public TranslatorContext getContext() { + return context; + } +}