CAY-2269 Add support for date/time components extraction in expression functions
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/05a77250 Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/05a77250 Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/05a77250 Branch: refs/heads/master Commit: 05a77250221c23aede7267835fb55d997560c5b0 Parents: e56a93f Author: Nikita Timofeev <[email protected]> Authored: Mon Mar 20 13:00:23 2017 +0300 Committer: Nikita Timofeev <[email protected]> Committed: Mon Mar 20 13:00:23 2017 +0300 ---------------------------------------------------------------------- .../translator/select/QualifierTranslator.java | 15 +- .../cayenne/dba/db2/DB2QualifierTranslator.java | 20 + .../dba/derby/DerbyQualifierTranslator.java | 17 + .../firebird/FirebirdQualifierTranslator.java | 36 + .../frontbase/FrontBaseQualifierTranslator.java | 34 + .../dba/hsqldb/HSQLQualifierTranslator.java | 15 + .../dba/ingres/IngresQualifierTranslator.java | 15 + .../dba/mysql/MySQLQualifierTranslator.java | 15 + .../openbase/OpenBaseQualifierTranslator.java | 15 + .../dba/oracle/OracleQualifierTranslator.java | 47 + .../postgres/PostgresQualifierTranslator.java | 37 + .../cayenne/dba/sqlite/SQLiteAdapter.java | 4 +- .../cayenne/dba/sqlite/SQLiteDateType.java | 70 +- .../dba/sqlite/SQLiteQualifierTranslator.java | 60 ++ .../SQLServerTrimmingQualifierTranslator.java | 35 + .../dba/sybase/SybaseQualifierTranslator.java | 36 + .../cayenne/exp/FunctionExpressionFactory.java | 155 +++ .../cayenne/exp/parser/ASTCurrentDate.java | 5 - .../cayenne/exp/parser/ASTCurrentTime.java | 5 - .../cayenne/exp/parser/ASTCurrentTimestamp.java | 5 - .../apache/cayenne/exp/parser/ASTExtract.java | 125 +++ .../cayenne/exp/parser/ASTFunctionCall.java | 24 +- .../cayenne/exp/parser/ExpressionParser.java | 302 ++++-- .../exp/parser/ExpressionParserConstants.java | 64 +- .../parser/ExpressionParserTokenManager.java | 979 ++++++++++--------- .../parser/ExpressionParserTreeConstants.java | 10 +- .../cayenne/exp/parser/ExpressionParser.jjt | 49 +- .../cayenne/exp/parser/ASTCurrentDateTest.java | 51 + .../cayenne/exp/parser/ASTCurrentTimeTest.java | 50 + .../exp/parser/ASTCurrentTimestampTest.java | 54 + .../apache/cayenne/exp/parser/ASTExtractIT.java | 240 +++++ .../cayenne/exp/parser/ASTExtractTest.java | 162 +++ .../apache/cayenne/unit/DerbyUnitDbAdapter.java | 12 + .../cayenne/unit/FrontBaseUnitDbAdapter.java | 11 + .../org/apache/cayenne/unit/UnitDbAdapter.java | 5 + .../ServerCaseDataSourceInfoProvider.java | 2 +- docs/doc/src/main/resources/RELEASE-NOTES.txt | 1 + 37 files changed, 2161 insertions(+), 621 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java index aad7eca..deb7d52 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java @@ -30,6 +30,7 @@ import org.apache.cayenne.dba.TypesMapping; import org.apache.cayenne.exp.Expression; import org.apache.cayenne.exp.TraversalHandler; import org.apache.cayenne.exp.parser.ASTDbPath; +import org.apache.cayenne.exp.parser.ASTExtract; import org.apache.cayenne.exp.parser.ASTFunctionCall; import org.apache.cayenne.exp.parser.ASTObjPath; import org.apache.cayenne.exp.parser.PatternMatchNode; @@ -413,7 +414,11 @@ public class QualifierTranslator extends QueryAssemblerHelper implements Travers boolean parenthesisNeeded = parenthesisNeeded(node, parentNode); if(node.getType() == Expression.FUNCTION_CALL) { - appendFunction((ASTFunctionCall)node); + if(node instanceof ASTExtract) { + appendExtractFunction((ASTExtract) node); + } else { + appendFunction((ASTFunctionCall) node); + } if(parenthesisNeeded) { out.append("("); } @@ -623,6 +628,14 @@ public class QualifierTranslator extends QueryAssemblerHelper implements Travers } /** + * Special case for extract date/time parts functions as they have many variants + * @since 4.0 + */ + protected void appendExtractFunction(ASTExtract functionExpression) { + appendFunction(functionExpression); + } + + /** * Append scalar argument of a function call * Used only for values stored in ASTScalar other * expressions appended in objectNode() method http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2QualifierTranslator.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2QualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2QualifierTranslator.java index 09b369a..6a60233 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2QualifierTranslator.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2QualifierTranslator.java @@ -28,6 +28,7 @@ import org.apache.cayenne.access.translator.select.TrimmingQualifierTranslator; import org.apache.cayenne.dba.TypesMapping; import org.apache.cayenne.exp.Expression; import org.apache.cayenne.exp.parser.ASTEqual; +import org.apache.cayenne.exp.parser.ASTExtract; import org.apache.cayenne.exp.parser.ASTFunctionCall; import org.apache.cayenne.exp.parser.ASTNotEqual; import org.apache.cayenne.exp.parser.SimpleNode; @@ -151,4 +152,23 @@ public class DB2QualifierTranslator extends TrimmingQualifierTranslator { super.clearLastFunctionArgDivider(functionExpression); } } + + /** + * @since 4.0 + */ + @Override + protected void appendExtractFunction(ASTExtract functionExpression) { + switch (functionExpression.getPart()) { + case DAY_OF_MONTH: + out.append("DAY"); + break; + case DAY_OF_WEEK: + case DAY_OF_YEAR: + // db2 variants are without '_' + out.append(functionExpression.getPart().name().replace("_", "")); + break; + default: + appendFunction(functionExpression); + } + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbyQualifierTranslator.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbyQualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbyQualifierTranslator.java index 84c5889..2767417 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbyQualifierTranslator.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbyQualifierTranslator.java @@ -21,10 +21,12 @@ package org.apache.cayenne.dba.derby; import java.io.IOException; import java.sql.Types; +import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.access.translator.select.QueryAssembler; import org.apache.cayenne.access.translator.select.TrimmingQualifierTranslator; import org.apache.cayenne.exp.Expression; import org.apache.cayenne.exp.parser.ASTEqual; +import org.apache.cayenne.exp.parser.ASTExtract; import org.apache.cayenne.exp.parser.ASTFunctionCall; import org.apache.cayenne.exp.parser.ASTNotEqual; import org.apache.cayenne.exp.parser.SimpleNode; @@ -120,4 +122,19 @@ public class DerbyQualifierTranslator extends TrimmingQualifierTranslator { super.clearLastFunctionArgDivider(functionExpression); } } + + @Override + protected void appendExtractFunction(ASTExtract functionExpression) { + switch (functionExpression.getPart()) { + case DAY_OF_MONTH: + out.append("DAY"); + break; + case DAY_OF_WEEK: + case DAY_OF_YEAR: + case WEEK: + throw new CayenneRuntimeException("Function " + functionExpression.getPartCamelCaseName() + "() is unsupported in Derby."); + default: + super.appendExtractFunction(functionExpression); + } + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/dba/firebird/FirebirdQualifierTranslator.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/firebird/FirebirdQualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/firebird/FirebirdQualifierTranslator.java index 503358f..eae42f7 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/firebird/FirebirdQualifierTranslator.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/firebird/FirebirdQualifierTranslator.java @@ -23,6 +23,7 @@ import org.apache.cayenne.access.translator.select.QualifierTranslator; import org.apache.cayenne.access.translator.select.QueryAssembler; import org.apache.cayenne.dba.oracle.OracleQualifierTranslator; import org.apache.cayenne.exp.Expression; +import org.apache.cayenne.exp.parser.ASTExtract; import org.apache.cayenne.exp.parser.ASTFunctionCall; import java.io.IOException; @@ -141,5 +142,40 @@ public class FirebirdQualifierTranslator extends QualifierTranslator { default: super.clearLastFunctionArgDivider(functionExpression); } + if(functionExpression instanceof ASTExtract) { + out.append(")"); + } + } + + @Override + protected boolean parenthesisNeeded(Expression node, Expression parentNode) { + if (node.getType() == Expression.FUNCTION_CALL) { + if (node instanceof ASTExtract) { + return false; + } + } + + return super.parenthesisNeeded(node, parentNode); + } + + + @Override + protected void appendExtractFunction(ASTExtract functionExpression) { + out.append("EXTRACT("); + switch (functionExpression.getPart()) { + case DAY_OF_MONTH: + out.append("DAY"); + break; + case DAY_OF_WEEK: + out.append("WEEKDAY"); + break; + case DAY_OF_YEAR: + out.append("YEARDAY"); + break; + default: + out.append(functionExpression.getPartCamelCaseName()); + } + + out.append(" FROM "); } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseQualifierTranslator.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseQualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseQualifierTranslator.java index 615bf68..8315a4c 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseQualifierTranslator.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseQualifierTranslator.java @@ -19,8 +19,11 @@ package org.apache.cayenne.dba.frontbase; +import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.access.translator.select.QualifierTranslator; import org.apache.cayenne.access.translator.select.QueryAssembler; +import org.apache.cayenne.exp.Expression; +import org.apache.cayenne.exp.parser.ASTExtract; import org.apache.cayenne.exp.parser.ASTFunctionCall; /** @@ -96,5 +99,36 @@ public class FrontBaseQualifierTranslator extends QualifierTranslator { default: super.clearLastFunctionArgDivider(functionExpression); } + if(functionExpression instanceof ASTExtract) { + out.append(")"); + } + } + + @Override + protected boolean parenthesisNeeded(Expression node, Expression parentNode) { + if (node.getType() == Expression.FUNCTION_CALL) { + if (node instanceof ASTExtract) { + return false; + } + } + + return super.parenthesisNeeded(node, parentNode); + } + + @Override + protected void appendExtractFunction(ASTExtract functionExpression) { + out.append("EXTRACT("); + switch (functionExpression.getPart()) { + case DAY_OF_WEEK: + case DAY_OF_YEAR: + case WEEK: + throw new CayenneRuntimeException("Function " + functionExpression.getPartCamelCaseName() + "() is unsupported in FrontBase."); + case DAY_OF_MONTH: + out.append("DAY"); + break; + default: + out.append(functionExpression.getPart().name()); + } + out.append(" FROM "); } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLQualifierTranslator.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLQualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLQualifierTranslator.java index bc3990d..513a707 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLQualifierTranslator.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLQualifierTranslator.java @@ -24,6 +24,7 @@ import java.io.IOException; import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.access.translator.select.QueryAssembler; import org.apache.cayenne.access.translator.select.TrimmingQualifierTranslator; +import org.apache.cayenne.exp.parser.ASTExtract; import org.apache.cayenne.exp.parser.ASTFunctionCall; import org.apache.cayenne.exp.parser.PatternMatchNode; @@ -69,4 +70,18 @@ public class HSQLQualifierTranslator extends TrimmingQualifierTranslator { super.appendFunction(functionExpression); } } + + @Override + protected void appendExtractFunction(ASTExtract functionExpression) { + switch (functionExpression.getPart()) { + case DAY_OF_WEEK: + case DAY_OF_MONTH: + case DAY_OF_YEAR: + // hsqldb variants are without '_' + out.append(functionExpression.getPart().name().replace("_", "")); + break; + default: + appendFunction(functionExpression); + } + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresQualifierTranslator.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresQualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresQualifierTranslator.java index 222fe96..8334ff4 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresQualifierTranslator.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresQualifierTranslator.java @@ -24,6 +24,7 @@ import java.io.IOException; import org.apache.cayenne.access.translator.select.QueryAssembler; import org.apache.cayenne.access.translator.select.TrimmingQualifierTranslator; import org.apache.cayenne.exp.Expression; +import org.apache.cayenne.exp.parser.ASTExtract; import org.apache.cayenne.exp.parser.ASTFunctionCall; import org.apache.cayenne.exp.parser.Node; @@ -98,6 +99,20 @@ class IngresQualifierTranslator extends TrimmingQualifierTranslator { } } + @Override + protected void appendExtractFunction(ASTExtract functionExpression) { + switch (functionExpression.getPart()) { + case DAY_OF_WEEK: + case DAY_OF_MONTH: + case DAY_OF_YEAR: + // ingres variants are without '_' + out.append(functionExpression.getPart().name().replace("_", "")); + break; + default: + appendFunction(functionExpression); + } + } + private void swapNodeChildren(Node node, int i, int j) { Node ni = node.jjtGetChild(i); Node nj = node.jjtGetChild(j); http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLQualifierTranslator.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLQualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLQualifierTranslator.java index 77f4dde..ac520e2 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLQualifierTranslator.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLQualifierTranslator.java @@ -24,6 +24,7 @@ import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.access.translator.select.QualifierTranslator; import org.apache.cayenne.access.translator.select.QueryAssembler; import org.apache.cayenne.exp.Expression; +import org.apache.cayenne.exp.parser.ASTExtract; import org.apache.cayenne.exp.parser.PatternMatchNode; class MySQLQualifierTranslator extends QualifierTranslator { @@ -88,4 +89,18 @@ class MySQLQualifierTranslator extends QualifierTranslator { } } } + + @Override + protected void appendExtractFunction(ASTExtract functionExpression) { + switch (functionExpression.getPart()) { + case DAY_OF_WEEK: + case DAY_OF_MONTH: + case DAY_OF_YEAR: + // mysql variants are without '_' + out.append(functionExpression.getPart().name().replace("_", "")); + break; + default: + appendFunction(functionExpression); + } + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseQualifierTranslator.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseQualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseQualifierTranslator.java index 42383b9..dbedc60 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseQualifierTranslator.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseQualifierTranslator.java @@ -25,6 +25,7 @@ import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.access.translator.select.QualifierTranslator; import org.apache.cayenne.access.translator.select.QueryAssembler; import org.apache.cayenne.exp.Expression; +import org.apache.cayenne.exp.parser.ASTExtract; import org.apache.cayenne.exp.parser.PatternMatchNode; import org.apache.cayenne.map.DbAttribute; @@ -159,4 +160,18 @@ public class OpenBaseQualifierTranslator extends QualifierTranslator { objectMatchTranslator.setExpression(node); } } + + @Override + protected void appendExtractFunction(ASTExtract functionExpression) { + switch (functionExpression.getPart()) { + case DAY_OF_WEEK: + case DAY_OF_MONTH: + case DAY_OF_YEAR: + // openbase variants are without '_' + out.append(functionExpression.getPart().name().replace("_", "")); + break; + default: + appendFunction(functionExpression); + } + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleQualifierTranslator.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleQualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleQualifierTranslator.java index 011a33b..fadd468 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleQualifierTranslator.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleQualifierTranslator.java @@ -21,6 +21,7 @@ package org.apache.cayenne.dba.oracle; import org.apache.cayenne.access.translator.select.QueryAssembler; import org.apache.cayenne.access.translator.select.TrimmingQualifierTranslator; import org.apache.cayenne.exp.Expression; +import org.apache.cayenne.exp.parser.ASTExtract; import org.apache.cayenne.exp.parser.ASTFunctionCall; import org.apache.cayenne.exp.parser.ASTIn; import org.apache.cayenne.exp.parser.ASTList; @@ -152,6 +153,52 @@ public class OracleQualifierTranslator extends TrimmingQualifierTranslator { } else { super.clearLastFunctionArgDivider(functionExpression); } + + if(functionExpression instanceof ASTExtract) { + switch (((ASTExtract)functionExpression).getPart()) { + case DAY_OF_YEAR: + out.append(", 'DDD'"); + break; + case DAY_OF_WEEK: + out.append(", 'D'"); + break; + case WEEK: + out.append(", 'IW'"); + break; + } + out.append(")"); + } + } + + @Override + protected boolean parenthesisNeeded(Expression node, Expression parentNode) { + if (node.getType() == Expression.FUNCTION_CALL) { + if (node instanceof ASTExtract) { + return false; + } + } + + return super.parenthesisNeeded(node, parentNode); + } + + @Override + protected void appendExtractFunction(ASTExtract functionExpression) { + switch (functionExpression.getPart()) { + // use TO_CHAR(date, format) function for parts that is unsupported by EXTRACT() + case DAY_OF_YEAR: + case DAY_OF_WEEK: + case WEEK: + out.append("TO_CHAR("); + break; + case DAY_OF_MONTH: + out.append("EXTRACT(DAY FROM "); + break; + default: + out.append("EXTRACT("); + out.append(functionExpression.getPart().name()); + out.append(" FROM "); + } + } /** http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresQualifierTranslator.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresQualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresQualifierTranslator.java index 42937bb..3fdc22c 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresQualifierTranslator.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresQualifierTranslator.java @@ -25,6 +25,7 @@ import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.access.translator.select.QueryAssembler; import org.apache.cayenne.access.translator.select.TrimmingQualifierTranslator; import org.apache.cayenne.exp.Expression; +import org.apache.cayenne.exp.parser.ASTExtract; import org.apache.cayenne.exp.parser.ASTFunctionCall; import org.apache.cayenne.exp.parser.PatternMatchNode; @@ -148,5 +149,41 @@ public class PostgresQualifierTranslator extends TrimmingQualifierTranslator { } else { super.clearLastFunctionArgDivider(functionExpression); } + + if(functionExpression instanceof ASTExtract) { + out.append(")"); + } + } + + @Override + protected boolean parenthesisNeeded(Expression node, Expression parentNode) { + if (node.getType() == Expression.FUNCTION_CALL) { + if (node instanceof ASTExtract) { + return false; + } + } + + return super.parenthesisNeeded(node, parentNode); + } + + + @Override + protected void appendExtractFunction(ASTExtract functionExpression) { + out.append("EXTRACT("); + switch (functionExpression.getPart()) { + case DAY_OF_MONTH: + out.append("day"); + break; + case DAY_OF_WEEK: + out.append("dow"); + break; + case DAY_OF_YEAR: + out.append("doy"); + break; + default: + out.append(functionExpression.getPartCamelCaseName()); + } + + out.append(" FROM "); } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteAdapter.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteAdapter.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteAdapter.java index b390ebc..1796700 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteAdapter.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteAdapter.java @@ -79,8 +79,8 @@ public class SQLiteAdapter extends JdbcAdapter { map.registerType(new SQLiteBigDecimalType()); map.registerType(new SQLiteFloatType()); map.registerType(new SQLiteByteArrayType()); - map.registerType(new SQLiteCalendarType(GregorianCalendar.class)); - map.registerType(new SQLiteCalendarType(Calendar.class)); + map.registerType(new SQLiteCalendarType<>(GregorianCalendar.class)); + map.registerType(new SQLiteCalendarType<>(Calendar.class)); } /** http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteDateType.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteDateType.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteDateType.java index 0dba203..497cc81 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteDateType.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteDateType.java @@ -39,48 +39,27 @@ import org.apache.cayenne.access.types.UtilDateType; // handling fun. class SQLiteDateType extends UtilDateType { - private DateFormat timestampFormat; - private DateFormat dateFormat; - private DateFormat timeFormat; + private final DateFormat timestampFormat; + private final DateFormat dateFormat; + private final DateFormat timeFormat; public SQLiteDateType() { - timestampFormat = new SimpleDateFormat("yyyy-MM-dd kk:mm:ss"); + timestampFormat = new SimpleDateFormat("yyyy-MM-dd kk:mm:ss.SSS"); dateFormat = new SimpleDateFormat("yyyy-MM-dd"); timeFormat = new SimpleDateFormat("kk:mm:ss"); } @Override public Date materializeObject(ResultSet rs, int index, int type) throws Exception { - - String string = rs.getString(index); - - if (string == null) { - return null; - } - - long ts = getLongTimestamp(string); - if (ts >= 0) { - return new Date(ts); - } - - switch (type) { - case Types.TIMESTAMP: - return getTimestamp(string); - case Types.DATE: - return getDate(string); - case Types.TIME: - return rs.getTime(index); - default: - return getTimestamp(string); - } + return parseDate(rs.getString(index), type); } @Override - public Date materializeObject(CallableStatement rs, int index, int type) - throws Exception { - - String string = rs.getString(index); + public Date materializeObject(CallableStatement rs, int index, int type) throws Exception { + return parseDate(rs.getString(index), type); + } + protected Date parseDate(String string, int type) throws SQLException { if (string == null) { return null; } @@ -91,12 +70,10 @@ class SQLiteDateType extends UtilDateType { } switch (type) { - case Types.TIMESTAMP: - return getTimestamp(string); - case Types.DATE: - return getDate(string); case Types.TIME: return getTime(string); + case Types.DATE: + case Types.TIMESTAMP: default: return getTimestamp(string); } @@ -107,17 +84,9 @@ class SQLiteDateType extends UtilDateType { synchronized (timestampFormat) { return timestampFormat.parse(string); } - } - catch (ParseException e) { + } catch (ParseException e) { // also try date format... - try { - synchronized (dateFormat) { - return dateFormat.parse(string); - } - } - catch (ParseException e1) { - throw new SQLException("Unparsable timestamp string: " + string); - } + return getDate(string); } } @@ -126,9 +95,8 @@ class SQLiteDateType extends UtilDateType { synchronized (dateFormat) { return dateFormat.parse(string); } - } - catch (ParseException e) { - throw new SQLException("Unparsable date string: " + string); + } catch (ParseException e) { + throw new SQLException("Unparsable date/time string: " + string); } } @@ -137,17 +105,15 @@ class SQLiteDateType extends UtilDateType { synchronized (timeFormat) { return timeFormat.parse(string); } - } - catch (ParseException e) { - throw new SQLException("Unparsable time string: " + string); + } catch (ParseException e) { + return getTimestamp(string); } } protected long getLongTimestamp(String string) { try { return Long.parseLong(string); - } - catch (NumberFormatException e) { + } catch (NumberFormatException e) { return -1; } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteQualifierTranslator.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteQualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteQualifierTranslator.java index bc2bb2e..d31019b 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteQualifierTranslator.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteQualifierTranslator.java @@ -22,6 +22,7 @@ package org.apache.cayenne.dba.sqlite; import org.apache.cayenne.access.translator.select.QualifierTranslator; import org.apache.cayenne.access.translator.select.QueryAssembler; import org.apache.cayenne.exp.Expression; +import org.apache.cayenne.exp.parser.ASTExtract; import org.apache.cayenne.exp.parser.ASTFunctionCall; import org.apache.cayenne.exp.parser.Node; @@ -91,6 +92,65 @@ public class SQLiteQualifierTranslator extends QualifierTranslator { default: super.clearLastFunctionArgDivider(functionExpression); } + if(functionExpression instanceof ASTExtract) { + out.append(") as integer)"); + } + } + + @Override + protected boolean parenthesisNeeded(Expression node, Expression parentNode) { + if (node.getType() == Expression.FUNCTION_CALL) { + if (node instanceof ASTExtract) { + return false; + } + } + + return super.parenthesisNeeded(node, parentNode); + } + + + /** + * Translates to cast(strftime('format', column) as integer). + * Depends on connection property "date_class", can be set in connection URL (date_class=text). + * + * https://www.sqlite.org/lang_datefunc.html + */ + @Override + protected void appendExtractFunction(ASTExtract functionExpression) { + out.append("cast(strftime("); + + switch (functionExpression.getPart()) { + case YEAR: + out.append("'%Y'"); + break; + case MONTH: + out.append("'%m'"); + break; + case WEEK: + out.append("'%W'"); + break; + case DAY: + case DAY_OF_MONTH: + out.append("'%d'"); + break; + case DAY_OF_WEEK: + out.append("'%w'"); + break; + case DAY_OF_YEAR: + out.append("'%j'"); + break; + case HOUR: + out.append("'%H'"); + break; + case MINUTE: + out.append("'%M'"); + break; + case SECOND: + out.append("'%S'"); + break; + } + + out.append(", "); } private void swapNodeChildren(Node node, int i, int j) { http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerTrimmingQualifierTranslator.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerTrimmingQualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerTrimmingQualifierTranslator.java index 9def833..9847742 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerTrimmingQualifierTranslator.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerTrimmingQualifierTranslator.java @@ -21,6 +21,7 @@ package org.apache.cayenne.dba.sqlserver; import org.apache.cayenne.access.translator.select.QueryAssembler; import org.apache.cayenne.access.translator.select.TrimmingQualifierTranslator; import org.apache.cayenne.exp.Expression; +import org.apache.cayenne.exp.parser.ASTExtract; import org.apache.cayenne.exp.parser.ASTFunctionCall; import org.apache.cayenne.map.DbAttribute; @@ -168,5 +169,39 @@ class SQLServerTrimmingQualifierTranslator extends TrimmingQualifierTranslator { out.append(")"); } } + + if(functionExpression instanceof ASTExtract) { + out.append(")"); + } + } + + @Override + protected boolean parenthesisNeeded(Expression node, Expression parentNode) { + if (node.getType() == Expression.FUNCTION_CALL) { + if (node instanceof ASTExtract) { + return false; + } + } + + return super.parenthesisNeeded(node, parentNode); + } + + @Override + protected void appendExtractFunction(ASTExtract functionExpression) { + out.append("DATEPART("); + switch (functionExpression.getPart()) { + case DAY_OF_MONTH: + out.append("DAY"); + break; + case DAY_OF_WEEK: + out.append("WEEKDAY"); + break; + case DAY_OF_YEAR: + out.append("DAYOFYEAR"); + break; + default: + out.append(functionExpression.getPart().name()); + } + out.append(" , "); } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/dba/sybase/SybaseQualifierTranslator.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/sybase/SybaseQualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/sybase/SybaseQualifierTranslator.java index 1ad939e..0f6e239 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/sybase/SybaseQualifierTranslator.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/sybase/SybaseQualifierTranslator.java @@ -21,6 +21,8 @@ package org.apache.cayenne.dba.sybase; import org.apache.cayenne.access.translator.select.QualifierTranslator; import org.apache.cayenne.access.translator.select.QueryAssembler; +import org.apache.cayenne.exp.Expression; +import org.apache.cayenne.exp.parser.ASTExtract; import org.apache.cayenne.exp.parser.ASTFunctionCall; /** @@ -74,5 +76,39 @@ public class SybaseQualifierTranslator extends QualifierTranslator { default: super.clearLastFunctionArgDivider(functionExpression); } + + if(functionExpression instanceof ASTExtract) { + out.append(")"); + } + } + + @Override + protected boolean parenthesisNeeded(Expression node, Expression parentNode) { + if (node.getType() == Expression.FUNCTION_CALL) { + if (node instanceof ASTExtract) { + return false; + } + } + + return super.parenthesisNeeded(node, parentNode); + } + + @Override + protected void appendExtractFunction(ASTExtract functionExpression) { + out.append("datepart("); + switch (functionExpression.getPart()) { + case DAY_OF_MONTH: + out.append("day"); + break; + case DAY_OF_WEEK: + out.append("weekday"); + break; + case DAY_OF_YEAR: + out.append("dayofyear"); + break; + default: + out.append(functionExpression.getPart().name().toLowerCase()); + } + out.append(" , "); } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/exp/FunctionExpressionFactory.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/FunctionExpressionFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/FunctionExpressionFactory.java index e4cbf07..114fb1d 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/FunctionExpressionFactory.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/FunctionExpressionFactory.java @@ -26,6 +26,7 @@ import org.apache.cayenne.exp.parser.ASTCount; import org.apache.cayenne.exp.parser.ASTCurrentDate; import org.apache.cayenne.exp.parser.ASTCurrentTime; import org.apache.cayenne.exp.parser.ASTCurrentTimestamp; +import org.apache.cayenne.exp.parser.ASTExtract; import org.apache.cayenne.exp.parser.ASTLength; import org.apache.cayenne.exp.parser.ASTLocate; import org.apache.cayenne.exp.parser.ASTLower; @@ -370,4 +371,158 @@ public class FunctionExpressionFactory { public static Expression currentTimestamp() { return new ASTCurrentTimestamp(); } + + /** + * @param exp date/timestamp expression + * @return year(exp) function expression + */ + public static Expression yearExp(Expression exp) { + return extractExp(exp, ASTExtract.DateTimePart.YEAR); + } + + /** + * @param path String path + * @return year(path) function expression + */ + public static Expression yearExp(String path) { + return extractExp(path, ASTExtract.DateTimePart.YEAR); + } + + /** + * @param exp date/timestamp expression + * @return month(exp) function expression + */ + public static Expression monthExp(Expression exp) { + return extractExp(exp, ASTExtract.DateTimePart.MONTH); + } + + /** + * @param path String path + * @return month(path) function expression + */ + public static Expression monthExp(String path) { + return extractExp(path, ASTExtract.DateTimePart.MONTH); + } + + /** + * @param exp date/timestamp expression + * @return week(exp) function expression + */ + public static Expression weekExp(Expression exp) { + return extractExp(exp, ASTExtract.DateTimePart.WEEK); + } + + /** + * @param path String path + * @return week(path) function expression + */ + public static Expression weekExp(String path) { + return extractExp(path, ASTExtract.DateTimePart.WEEK); + } + + /** + * @param exp date/timestamp expression + * @return dayOfYear(exp) function expression + */ + public static Expression dayOfYearExp(Expression exp) { + return extractExp(exp, ASTExtract.DateTimePart.DAY_OF_YEAR); + } + + /** + * @param path String path + * @return dayOfYear(path) function expression + */ + public static Expression dayOfYearExp(String path) { + return extractExp(path, ASTExtract.DateTimePart.DAY_OF_YEAR); + } + + /** + * @param exp date/timestamp expression + * @return dayOfMonth(exp) function expression, synonym for day() + */ + public static Expression dayOfMonthExp(Expression exp) { + return extractExp(exp, ASTExtract.DateTimePart.DAY_OF_MONTH); + } + + /** + * @param path String path + * @return dayOfMonth(path) function expression, synonym for day() + */ + public static Expression dayOfMonthExp(String path) { + return extractExp(path, ASTExtract.DateTimePart.DAY_OF_MONTH); + } + + /** + * @param exp date/timestamp expression + * @return dayOfWeek(exp) function expression + */ + public static Expression dayOfWeekExp(Expression exp) { + return extractExp(exp, ASTExtract.DateTimePart.DAY_OF_WEEK); + } + + /** + * @param path String path + * @return dayOfWeek(path) function expression + */ + public static Expression dayOfWeekExp(String path) { + return extractExp(path, ASTExtract.DateTimePart.DAY_OF_WEEK); + } + + /** + * @param exp date/timestamp expression + * @return hour(exp) function expression + */ + public static Expression hourExp(Expression exp) { + return extractExp(exp, ASTExtract.DateTimePart.HOUR); + } + + /** + * @param path String path + * @return hour(path) function expression + */ + public static Expression hourExp(String path) { + return extractExp(path, ASTExtract.DateTimePart.HOUR); + } + + /** + * @param exp date/timestamp expression + * @return minute(exp) function expression + */ + public static Expression minuteExp(Expression exp) { + return extractExp(exp, ASTExtract.DateTimePart.MINUTE); + } + + /** + * @param path String path + * @return minute(path) function expression + */ + public static Expression minuteExp(String path) { + return extractExp(path, ASTExtract.DateTimePart.MINUTE); + } + + /** + * @param exp date/timestamp expression + * @return second(exp) function expression + */ + public static Expression secondExp(Expression exp) { + return extractExp(exp, ASTExtract.DateTimePart.SECOND); + } + + /** + * @param path String path + * @return second(path) function expression + */ + public static Expression secondExp(String path) { + return extractExp(path, ASTExtract.DateTimePart.SECOND); + } + + static Expression extractExp(String path, ASTExtract.DateTimePart part) { + return extractExp(ExpressionFactory.pathExp(path), part); + } + + static Expression extractExp(Expression exp, ASTExtract.DateTimePart part) { + ASTExtract extract = new ASTExtract(exp); + extract.setPart(part); + return extract; + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTCurrentDate.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTCurrentDate.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTCurrentDate.java index e453e8f..73743df 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTCurrentDate.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTCurrentDate.java @@ -53,11 +53,6 @@ public class ASTCurrentDate extends ASTFunctionCall { } @Override - protected void appendFunctionNameAsString(Appendable out) throws IOException { - out.append("currentDate"); - } - - @Override public Expression shallowCopy() { return new ASTCurrentDate(id); } http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTCurrentTime.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTCurrentTime.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTCurrentTime.java index feb9dfa..6cee32c 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTCurrentTime.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTCurrentTime.java @@ -53,11 +53,6 @@ public class ASTCurrentTime extends ASTFunctionCall { } @Override - protected void appendFunctionNameAsString(Appendable out) throws IOException { - out.append("currentTime"); - } - - @Override public Expression shallowCopy() { return new ASTCurrentTime(id); } http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTCurrentTimestamp.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTCurrentTimestamp.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTCurrentTimestamp.java index df55492..9edf263 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTCurrentTimestamp.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTCurrentTimestamp.java @@ -53,11 +53,6 @@ public class ASTCurrentTimestamp extends ASTFunctionCall { } @Override - protected void appendFunctionNameAsString(Appendable out) throws IOException { - out.append("currentTimestamp"); - } - - @Override public Expression shallowCopy() { return new ASTCurrentTimestamp(id); } http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTExtract.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTExtract.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTExtract.java new file mode 100644 index 0000000..184d298 --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTExtract.java @@ -0,0 +1,125 @@ +/***************************************************************** + * 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.exp.parser; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.cayenne.CayenneRuntimeException; +import org.apache.cayenne.exp.Expression; + +public class ASTExtract extends ASTFunctionCall { + + /** + * Available components of date/time. + * Names must be in sync with tokens used in dateTimeExtractingFunction() rule in ExpressionParser.jjt + */ + public enum DateTimePart { + YEAR, MONTH, WEEK, + // day options, day is synonym for dayOfMonth + DAY_OF_YEAR, DAY, DAY_OF_MONTH, DAY_OF_WEEK, + HOUR, MINUTE, SECOND + } + + /** + * Map from camelCase name to enum elements. + * @see ASTFunctionCall#nameToCamelCase(String) + */ + private static final Map<String, DateTimePart> NAME_TO_PART = new HashMap<>(); + static { + for(DateTimePart part : DateTimePart.values()) { + NAME_TO_PART.put(nameToCamelCase(part.name()), part); + } + } + + /** + * camelCase name, found in ExpressionParser.jjt tokens + */ + private String partName; + + private DateTimePart part; + + ASTExtract(int id) { + super(id, "EXTRACT"); + } + + public ASTExtract(Expression expression) { + super(ExpressionParserTreeConstants.JJTEXTRACT, "EXTRACT", expression); + } + + @Override + public String getFunctionName() { + return part.name(); + } + + @Override + protected void appendFunctionNameAsString(Appendable out) throws IOException { + out.append(partName); + } + + /** + * This method is used by {@link ExpressionParser} + * @param partToken {@link Token#image} from {@link ExpressionParser} + */ + void setPartToken(String partToken) { + part = NAME_TO_PART.get(partToken); + if(part == null) { + throw new CayenneRuntimeException("Unknown timestamp part: " + partToken); + } + this.partName = partToken; + } + + /** + * This method is used by FunctionExpressionFactory + * @param part date/time part to extract + */ + public void setPart(DateTimePart part) { + this.part = part; + this.partName = nameToCamelCase(part.name()); + } + + public DateTimePart getPart() { + return part; + } + + public String getPartCamelCaseName() { + return partName; + } + + @Override + public Expression shallowCopy() { + ASTExtract copy = new ASTExtract(id); + copy.partName = partName; + copy.part = part; + return copy; + } + + @Override + protected int getRequiredChildrenCount() { + return 1; + } + + @Override + protected Object evaluateSubNode(Object o, Object[] evaluatedChildren) throws Exception { + return null; + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTFunctionCall.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTFunctionCall.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTFunctionCall.java index d31c148..3766793 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTFunctionCall.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTFunctionCall.java @@ -80,7 +80,7 @@ public abstract class ASTFunctionCall extends EvaluatedNode { } protected void appendFunctionNameAsString(Appendable out) throws IOException { - out.append(getFunctionName().toLowerCase()); + out.append(nameToCamelCase(getFunctionName())); } @Override @@ -103,4 +103,26 @@ public abstract class ASTFunctionCall extends EvaluatedNode { super.appendChildrenAsEJBQL(parameterAccumulator, out, rootId); out.append(")"); } + + /** + * + * @param functionName in UPPER_UNDERSCORE convention + * @return functionName in camelCase convention + */ + protected static String nameToCamelCase(String functionName) { + String[] parts = functionName.split("_"); + StringBuilder sb = new StringBuilder(); + boolean first = true; + for(String part : parts) { + if(first) { + sb.append(part.toLowerCase()); + first = false; + } else { + char[] chars = part.toLowerCase().toCharArray(); + chars[0] = Character.toTitleCase(chars[0]); + sb.append(chars); + } + } + return sb.toString(); + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParser.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParser.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParser.java index 732110d..ff0fde8 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParser.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParser.java @@ -184,10 +184,20 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon case CURRENT_DATE: case CURRENT_TIME: case CURRENT_TIMESTAMP: - case 54: - case 55: - case 56: - case 57: + case YEAR: + case MONTH: + case WEEK: + case DAY_OF_YEAR: + case DAY: + case DAY_OF_MONTH: + case DAY_OF_WEEK: + case HOUR: + case MINUTE: + case SECOND: + case 64: + case 65: + case 66: + case 67: case PROPERTY_PATH: case SINGLE_QUOTED_STRING: case DOUBLE_QUOTED_STRING: @@ -251,10 +261,20 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon case CURRENT_DATE: case CURRENT_TIME: case CURRENT_TIMESTAMP: - case 54: - case 55: - case 56: - case 57: + case YEAR: + case MONTH: + case WEEK: + case DAY_OF_YEAR: + case DAY: + case DAY_OF_MONTH: + case DAY_OF_WEEK: + case HOUR: + case MINUTE: + case SECOND: + case 64: + case 65: + case 66: + case 67: case PROPERTY_PATH: case SINGLE_QUOTED_STRING: case DOUBLE_QUOTED_STRING: @@ -524,7 +544,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon jjtree.openNodeScope(jjtn011); try { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { - case 54: + case 64: namedParameter(); break; case 16: @@ -683,7 +703,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon jjtree.openNodeScope(jjtn003); try { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { - case 54: + case 64: namedParameter(); break; case 16: @@ -803,10 +823,20 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon case ABS: case SQRT: case MOD: - case 54: - case 55: - case 56: - case 57: + case YEAR: + case MONTH: + case WEEK: + case DAY_OF_YEAR: + case DAY: + case DAY_OF_MONTH: + case DAY_OF_WEEK: + case HOUR: + case MINUTE: + case SECOND: + case 64: + case 65: + case 66: + case 67: case PROPERTY_PATH: case INT_LITERAL: case FLOAT_LITERAL: @@ -854,9 +884,9 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon final public void stringParameter() throws ParseException { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { - case 55: - case 56: - case 57: + case 65: + case 66: + case 67: case PROPERTY_PATH: pathExpression(); break; @@ -947,10 +977,20 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon case CURRENT_DATE: case CURRENT_TIME: case CURRENT_TIMESTAMP: - case 54: - case 55: - case 56: - case 57: + case YEAR: + case MONTH: + case WEEK: + case DAY_OF_YEAR: + case DAY: + case DAY_OF_MONTH: + case DAY_OF_WEEK: + case HOUR: + case MINUTE: + case SECOND: + case 64: + case 65: + case 66: + case 67: case PROPERTY_PATH: case SINGLE_QUOTED_STRING: case DOUBLE_QUOTED_STRING: @@ -1027,22 +1067,22 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon } } break; - case 54: + case 64: namedParameter(); break; case INT_LITERAL: jj_consume_token(INT_LITERAL); - ASTScalar jjtn003 = new ASTScalar(JJTSCALAR); - boolean jjtc003 = true; - jjtree.openNodeScope(jjtn003); + ASTScalar jjtn003 = new ASTScalar(JJTSCALAR); + boolean jjtc003 = true; + jjtree.openNodeScope(jjtn003); try { - jjtree.closeNodeScope(jjtn003, 0); - jjtc003 = false; - jjtn003.setValue(token_source.literalValue); - } finally { - if (jjtc003) { jjtree.closeNodeScope(jjtn003, 0); - } + jjtc003 = false; + jjtn003.setValue(token_source.literalValue); + } finally { + if (jjtc003) { + jjtree.closeNodeScope(jjtn003, 0); + } } break; case FLOAT_LITERAL: @@ -1062,17 +1102,17 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon break; case TRUE: jj_consume_token(TRUE); - ASTScalar jjtn005 = new ASTScalar(JJTSCALAR); - boolean jjtc005 = true; - jjtree.openNodeScope(jjtn005); + ASTScalar jjtn005 = new ASTScalar(JJTSCALAR); + boolean jjtc005 = true; + jjtree.openNodeScope(jjtn005); try { - jjtree.closeNodeScope(jjtn005, 0); - jjtc005 = false; - jjtn005.setValue(true); + jjtree.closeNodeScope(jjtn005, 0); + jjtc005 = false; + jjtn005.setValue(true); } finally { - if (jjtc005) { - jjtree.closeNodeScope(jjtn005, 0); - } + if (jjtc005) { + jjtree.closeNodeScope(jjtn005, 0); + } } break; case FALSE: @@ -1459,10 +1499,20 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon case ABS: case SQRT: case MOD: - case 54: - case 55: - case 56: - case 57: + case YEAR: + case MONTH: + case WEEK: + case DAY_OF_YEAR: + case DAY: + case DAY_OF_MONTH: + case DAY_OF_WEEK: + case HOUR: + case MINUTE: + case SECOND: + case 64: + case 65: + case 66: + case 67: case PROPERTY_PATH: case INT_LITERAL: case FLOAT_LITERAL: @@ -1511,10 +1561,20 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon case ABS: case SQRT: case MOD: - case 54: - case 55: - case 56: - case 57: + case YEAR: + case MONTH: + case WEEK: + case DAY_OF_YEAR: + case DAY: + case DAY_OF_MONTH: + case DAY_OF_WEEK: + case HOUR: + case MINUTE: + case SECOND: + case 64: + case 65: + case 66: + case 67: case PROPERTY_PATH: case INT_LITERAL: case FLOAT_LITERAL: @@ -1599,7 +1659,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon } } break; - case 54: + case 64: namedParameter(); break; case LENGTH: @@ -1607,11 +1667,21 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon case ABS: case SQRT: case MOD: + case YEAR: + case MONTH: + case WEEK: + case DAY_OF_YEAR: + case DAY: + case DAY_OF_MONTH: + case DAY_OF_WEEK: + case HOUR: + case MINUTE: + case SECOND: functionsReturningNumerics(); break; - case 55: - case 56: - case 57: + case 65: + case 66: + case 67: case PROPERTY_PATH: pathExpression(); break; @@ -1842,6 +1912,18 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon case MOD: mod(); break; + case YEAR: + case MONTH: + case WEEK: + case DAY_OF_YEAR: + case DAY: + case DAY_OF_MONTH: + case DAY_OF_WEEK: + case HOUR: + case MINUTE: + case SECOND: + dateTimeExtractingFunction(); + break; default: jj_la1[35] = jj_gen; jj_consume_token(-1); @@ -2067,9 +2149,9 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon case ASTERISK: asterisk(); break; - case 55: - case 56: - case 57: + case 65: + case 66: + case 67: case PROPERTY_PATH: pathExpression(); break; @@ -2290,9 +2372,77 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon } } +/* Date/time parts extracting function */ + final public void dateTimeExtractingFunction() throws ParseException { + /*@bgen(jjtree) #Extract( 1) */ + ASTExtract jjtn000 = new ASTExtract(JJTEXTRACT); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000);Token t; + try { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case YEAR: + t = jj_consume_token(YEAR); + break; + case MONTH: + t = jj_consume_token(MONTH); + break; + case WEEK: + t = jj_consume_token(WEEK); + break; + case DAY_OF_YEAR: + t = jj_consume_token(DAY_OF_YEAR); + break; + case DAY: + t = jj_consume_token(DAY); + break; + case DAY_OF_MONTH: + t = jj_consume_token(DAY_OF_MONTH); + break; + case DAY_OF_WEEK: + t = jj_consume_token(DAY_OF_WEEK); + break; + case HOUR: + t = jj_consume_token(HOUR); + break; + case MINUTE: + t = jj_consume_token(MINUTE); + break; + case SECOND: + t = jj_consume_token(SECOND); + break; + default: + jj_la1[40] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + jjtn000.setPartToken(t.image); + jj_consume_token(16); + pathExpression(); + jj_consume_token(17); + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte000;} + } + if (jjte000 instanceof ParseException) { + {if (true) throw (ParseException)jjte000;} + } + {if (true) throw (Error)jjte000;} + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, 1); + } + } + } + final public void namedParameter() throws ParseException { Token t; - jj_consume_token(54); + jj_consume_token(64); t = jj_consume_token(PROPERTY_PATH); ASTNamedParameter jjtn001 = new ASTNamedParameter(JJTNAMEDPARAMETER); boolean jjtc001 = true; @@ -2326,8 +2476,8 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon } } break; - case 55: - jj_consume_token(55); + case 65: + jj_consume_token(65); t = jj_consume_token(PROPERTY_PATH); ASTObjPath jjtn002 = new ASTObjPath(JJTOBJPATH); boolean jjtc002 = true; @@ -2342,8 +2492,8 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon } } break; - case 56: - jj_consume_token(56); + case 66: + jj_consume_token(66); t = jj_consume_token(PROPERTY_PATH); ASTDbPath jjtn003 = new ASTDbPath(JJTDBPATH); boolean jjtc003 = true; @@ -2358,8 +2508,8 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon } } break; - case 57: - jj_consume_token(57); + case 67: + jj_consume_token(67); t = jj_consume_token(PROPERTY_PATH); ASTScalar jjtn004 = new ASTScalar(JJTSCALAR); boolean jjtc004 = true; @@ -2375,7 +2525,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon } break; default: - jj_la1[40] = jj_gen; + jj_la1[41] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -2390,7 +2540,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon public Token jj_nt; private int jj_ntk; private int jj_gen; - final private int[] jj_la1 = new int[41]; + final private int[] jj_la1 = new int[42]; static private int[] jj_la1_0; static private int[] jj_la1_1; static private int[] jj_la1_2; @@ -2400,13 +2550,13 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon jj_la1_init_2(); } private static void jj_la1_init_0() { - jj_la1_0 = new int[] {0x2,0x4,0x18,0x16010018,0x60,0x180,0x10000,0x4fff8,0x4fff8,0x16010000,0x18,0x10000,0x4e000,0x80000,0x16010000,0x0,0x0,0x16010000,0x0,0x100000,0x200000,0x400000,0x1800000,0x1800000,0x6000000,0x6000000,0x8000000,0x8000000,0x16010000,0x2000000,0x6010000,0x10000,0x0,0x80000,0x80000,0x0,0x80000,0x0,0x0,0x0,0x0,}; + jj_la1_0 = new int[] {0x2,0x4,0x18,0x16010018,0x60,0x180,0x10000,0x4fff8,0x4fff8,0x16010000,0x18,0x10000,0x4e000,0x80000,0x16010000,0x0,0x0,0x16010000,0x0,0x100000,0x200000,0x400000,0x1800000,0x1800000,0x6000000,0x6000000,0x8000000,0x8000000,0x16010000,0x2000000,0x6010000,0x10000,0x0,0x80000,0x80000,0x0,0x80000,0x0,0x0,0x0,0x0,0x0,}; } private static void jj_la1_init_1() { - jj_la1_1 = new int[] {0x0,0x0,0x0,0xbfffffe,0x0,0x0,0x400000,0x0,0x0,0xbfffffe,0x0,0x400000,0x0,0x0,0xbfffff2,0xb803e00,0x3e00,0xbfffffe,0x40000c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4000000,0x4000000,0xbc7c000,0x0,0xbc7c000,0xbc7c000,0x3e00,0x0,0x0,0x7c000,0x0,0x1f0,0xf800000,0x380000,0xb800000,}; + jj_la1_1 = new int[] {0x0,0x0,0x0,0xfffffffe,0x0,0x0,0x0,0x0,0x0,0xfffffffe,0x0,0x0,0x0,0x0,0xfffffff2,0x3e00,0x3e00,0xfffffffe,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xffc7c000,0x0,0xffc7c000,0xffc7c000,0x3e00,0x0,0x0,0xffc7c000,0x0,0x1f0,0x0,0x380000,0xffc00000,0x0,}; } private static void jj_la1_init_2() { - jj_la1_2 = new int[] {0x0,0x0,0x0,0x1c8,0x0,0x0,0x0,0x0,0x0,0x1c8,0x0,0x0,0x0,0x0,0x1c8,0x48,0x48,0x1c8,0x1c8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180,0x0,0x180,0x180,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,}; + jj_la1_2 = new int[] {0x0,0x0,0x0,0x7202f,0x0,0x0,0x1,0x0,0x0,0x7202f,0x0,0x1,0x0,0x0,0x7202f,0x1202e,0x12000,0x7202f,0x72001,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x10,0x6002f,0x0,0x6002f,0x6002f,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x0,0x0,0x2e,}; } /** Constructor with InputStream. */ @@ -2420,7 +2570,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 41; i++) jj_la1[i] = -1; + for (int i = 0; i < 42; i++) jj_la1[i] = -1; } /** Reinitialise. */ @@ -2435,7 +2585,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon jj_ntk = -1; jjtree.reset(); jj_gen = 0; - for (int i = 0; i < 41; i++) jj_la1[i] = -1; + for (int i = 0; i < 42; i++) jj_la1[i] = -1; } /** Constructor. */ @@ -2445,7 +2595,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 41; i++) jj_la1[i] = -1; + for (int i = 0; i < 42; i++) jj_la1[i] = -1; } /** Reinitialise. */ @@ -2456,7 +2606,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon jj_ntk = -1; jjtree.reset(); jj_gen = 0; - for (int i = 0; i < 41; i++) jj_la1[i] = -1; + for (int i = 0; i < 42; i++) jj_la1[i] = -1; } /** Constructor with generated Token Manager. */ @@ -2465,7 +2615,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 41; i++) jj_la1[i] = -1; + for (int i = 0; i < 42; i++) jj_la1[i] = -1; } /** Reinitialise. */ @@ -2475,7 +2625,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon jj_ntk = -1; jjtree.reset(); jj_gen = 0; - for (int i = 0; i < 41; i++) jj_la1[i] = -1; + for (int i = 0; i < 42; i++) jj_la1[i] = -1; } private Token jj_consume_token(int kind) throws ParseException { @@ -2526,12 +2676,12 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon /** Generate ParseException. */ public ParseException generateParseException() { jj_expentries.clear(); - boolean[] la1tokens = new boolean[77]; + boolean[] la1tokens = new boolean[87]; if (jj_kind >= 0) { la1tokens[jj_kind] = true; jj_kind = -1; } - for (int i = 0; i < 41; i++) { + for (int i = 0; i < 42; i++) { if (jj_la1[i] == jj_gen) { for (int j = 0; j < 32; j++) { if ((jj_la1_0[i] & (1<<j)) != 0) { @@ -2546,7 +2696,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon } } } - for (int i = 0; i < 77; i++) { + for (int i = 0; i < 87; i++) { if (la1tokens[i]) { jj_expentry = new int[1]; jj_expentry[0] = i; http://git-wip-us.apache.org/repos/asf/cayenne/blob/05a77250/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParserConstants.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParserConstants.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParserConstants.java index a4c1a77..18c00e2 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParserConstants.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParserConstants.java @@ -73,35 +73,55 @@ public interface ExpressionParserConstants { /** RegularExpression Id. */ int CURRENT_TIMESTAMP = 53; /** RegularExpression Id. */ - int ASTERISK = 58; + int YEAR = 54; /** RegularExpression Id. */ - int PROPERTY_PATH = 59; + int MONTH = 55; /** RegularExpression Id. */ - int IDENTIFIER = 60; + int WEEK = 56; /** RegularExpression Id. */ - int LETTER = 61; + int DAY_OF_YEAR = 57; /** RegularExpression Id. */ - int DIGIT = 62; + int DAY = 58; /** RegularExpression Id. */ - int ESC = 65; + int DAY_OF_MONTH = 59; /** RegularExpression Id. */ - int SINGLE_QUOTED_STRING = 67; + int DAY_OF_WEEK = 60; /** RegularExpression Id. */ - int STRING_ESC = 68; + int HOUR = 61; /** RegularExpression Id. */ - int DOUBLE_QUOTED_STRING = 70; + int MINUTE = 62; /** RegularExpression Id. */ - int INT_LITERAL = 71; + int SECOND = 63; /** RegularExpression Id. */ - int FLOAT_LITERAL = 72; + int ASTERISK = 68; /** RegularExpression Id. */ - int DEC_FLT = 73; + int PROPERTY_PATH = 69; /** RegularExpression Id. */ - int DEC_DIGITS = 74; + int IDENTIFIER = 70; /** RegularExpression Id. */ - int EXPONENT = 75; + int LETTER = 71; /** RegularExpression Id. */ - int FLT_SUFF = 76; + int DIGIT = 72; + /** RegularExpression Id. */ + int ESC = 75; + /** RegularExpression Id. */ + int SINGLE_QUOTED_STRING = 77; + /** RegularExpression Id. */ + int STRING_ESC = 78; + /** RegularExpression Id. */ + int DOUBLE_QUOTED_STRING = 80; + /** RegularExpression Id. */ + int INT_LITERAL = 81; + /** RegularExpression Id. */ + int FLOAT_LITERAL = 82; + /** RegularExpression Id. */ + int DEC_FLT = 83; + /** RegularExpression Id. */ + int DEC_DIGITS = 84; + /** RegularExpression Id. */ + int EXPONENT = 85; + /** RegularExpression Id. */ + int FLT_SUFF = 86; /** Lexical state. */ int DEFAULT = 0; @@ -166,6 +186,16 @@ public interface ExpressionParserConstants { "\"currentDate\"", "\"currentTime\"", "<CURRENT_TIMESTAMP>", + "\"year\"", + "\"month\"", + "\"week\"", + "\"dayOfYear\"", + "\"day\"", + "\"dayOfMonth\"", + "\"dayOfWeek\"", + "\"hour\"", + "\"minute\"", + "\"second\"", "\"$\"", "\"obj:\"", "\"db:\"", @@ -178,10 +208,10 @@ public interface ExpressionParserConstants { "\"\\\'\"", "\"\\\"\"", "<ESC>", - "<token of kind 66>", + "<token of kind 76>", "\"\\\'\"", "<STRING_ESC>", - "<token of kind 69>", + "<token of kind 79>", "\"\\\"\"", "<INT_LITERAL>", "<FLOAT_LITERAL>",
