You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2017/01/05 12:33:24 UTC
[3/3] cayenne git commit: CAY-2191 and CAY-2188 - expressions in
Property class - explicit type in Property class and factory methods - string
and math functions from JPA standard with support for specific DBs - factory
methods for new function call
CAY-2191 and CAY-2188
- expressions in Property class
- explicit type in Property class and factory methods
- string and math functions from JPA standard with support for specific DBs
- factory methods for new function call expressions in FunctionExpressionFactory class
Limitations:
- no parser for new expression
- usage is limited for where clause in select queries
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/803166c0
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/803166c0
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/803166c0
Branch: refs/heads/master
Commit: 803166c03395194975d9ac32f0e9c8c634dfde74
Parents: 50d4fbf
Author: Nikita Timofeev <st...@gmail.com>
Authored: Thu Jan 5 15:32:28 2017 +0300
Committer: Nikita Timofeev <st...@gmail.com>
Committed: Thu Jan 5 15:32:28 2017 +0300
----------------------------------------------------------------------
.../select/DataObjectMatchTranslator.java | 2 +-
.../translator/select/QualifierTranslator.java | 131 ++++++--
.../cayenne/dba/db2/DB2QualifierTranslator.java | 35 +++
.../dba/derby/DerbyQualifierTranslator.java | 65 +++-
.../firebird/FirebirdQualifierTranslator.java | 70 +++++
.../cayenne/dba/frontbase/FrontBaseAdapter.java | 10 +
.../frontbase/FrontBaseQualifierTranslator.java | 91 ++++++
.../cayenne/dba/ingres/IngresAdapter.java | 2 +-
.../dba/ingres/IngresQualifierTranslator.java | 59 ++++
.../dba/oracle/OracleQualifierTranslator.java | 68 ++++
.../postgres/PostgresQualifierTranslator.java | 81 +++--
.../cayenne/dba/sqlite/SQLiteAdapter.java | 10 +
.../dba/sqlite/SQLiteQualifierTranslator.java | 102 ++++++
.../SQLServerTrimmingQualifierTranslator.java | 45 +++
.../cayenne/dba/sybase/SybaseAdapter.java | 10 +
.../dba/sybase/SybaseQualifierTranslator.java | 78 +++++
.../java/org/apache/cayenne/exp/Expression.java | 7 +-
.../apache/cayenne/exp/ExpressionFactory.java | 312 +++++++++++++++++--
.../cayenne/exp/FunctionExpressionFactory.java | 288 +++++++++++++++++
.../java/org/apache/cayenne/exp/Property.java | 181 ++++++++---
.../org/apache/cayenne/exp/parser/ASTAbs.java | 48 +++
.../apache/cayenne/exp/parser/ASTBetween.java | 2 +-
.../apache/cayenne/exp/parser/ASTConcat.java | 51 +++
.../org/apache/cayenne/exp/parser/ASTEqual.java | 2 +-
.../cayenne/exp/parser/ASTFunctionCall.java | 64 ++++
.../apache/cayenne/exp/parser/ASTGreater.java | 2 +-
.../cayenne/exp/parser/ASTGreaterOrEqual.java | 2 +-
.../org/apache/cayenne/exp/parser/ASTIn.java | 2 +-
.../apache/cayenne/exp/parser/ASTLength.java | 51 +++
.../org/apache/cayenne/exp/parser/ASTLess.java | 2 +-
.../cayenne/exp/parser/ASTLessOrEqual.java | 2 +-
.../org/apache/cayenne/exp/parser/ASTLike.java | 4 +-
.../cayenne/exp/parser/ASTLikeIgnoreCase.java | 4 +-
.../apache/cayenne/exp/parser/ASTLocate.java | 64 ++++
.../org/apache/cayenne/exp/parser/ASTLower.java | 52 ++++
.../org/apache/cayenne/exp/parser/ASTMod.java | 52 ++++
.../cayenne/exp/parser/ASTNotBetween.java | 2 +-
.../apache/cayenne/exp/parser/ASTNotEqual.java | 2 +-
.../org/apache/cayenne/exp/parser/ASTNotIn.java | 2 +-
.../apache/cayenne/exp/parser/ASTNotLike.java | 4 +-
.../exp/parser/ASTNotLikeIgnoreCase.java | 4 +-
.../org/apache/cayenne/exp/parser/ASTSqrt.java | 48 +++
.../apache/cayenne/exp/parser/ASTSubstring.java | 64 ++++
.../org/apache/cayenne/exp/parser/ASTTrim.java | 62 ++++
.../org/apache/cayenne/exp/parser/ASTUpper.java | 51 +++
.../apache/cayenne/exp/parser/SimpleNode.java | 6 +-
.../org/apache/cayenne/util/ConversionUtil.java | 79 +++--
.../select/QualifierTranslatorIT.java | 20 ++
.../exp/FunctionExpressionFactoryTest.java | 195 ++++++++++++
.../org/apache/cayenne/exp/PropertyTest.java | 9 +
.../apache/cayenne/exp/parser/ASTAbsTest.java | 45 +++
.../cayenne/exp/parser/ASTConcatTest.java | 50 +++
.../exp/parser/ASTFunctionCallMathIT.java | 83 +++++
.../exp/parser/ASTFunctionCallStringIT.java | 155 +++++++++
.../cayenne/exp/parser/ASTLengthTest.java | 45 +++
.../cayenne/exp/parser/ASTLocateTest.java | 53 ++++
.../apache/cayenne/exp/parser/ASTLowerTest.java | 46 +++
.../apache/cayenne/exp/parser/ASTModTest.java | 45 +++
.../apache/cayenne/exp/parser/ASTSqrtTest.java | 44 +++
.../cayenne/exp/parser/ASTSubstringTest.java | 48 +++
.../apache/cayenne/exp/parser/ASTTrimTest.java | 45 +++
.../apache/cayenne/exp/parser/ASTUpperTest.java | 46 +++
.../cayenne/query/ObjectSelect_RunIT.java | 62 ++--
.../org/apache/cayenne/gen/StringUtils.java | 24 ++
.../templates/v1_2/client-superclass.vm | 10 +-
.../templates/v1_2/embeddable-singleclass.vm | 3 +-
.../templates/v1_2/embeddable-superclass.vm | 3 +-
.../resources/templates/v1_2/singleclass.vm | 10 +-
.../main/resources/templates/v1_2/superclass.vm | 10 +-
.../org/apache/cayenne/gen/StringUtilsTest.java | 9 +
.../cayenne/tools/CayenneGeneratorMojoTest.java | 2 +-
71 files changed, 3210 insertions(+), 227 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/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
index 653881d..07f53f1 100644
--- 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
@@ -74,7 +74,7 @@ public class DataObjectMatchTranslator {
if (rel.isToMany() || !rel.isToPK()) {
// match on target PK
- DbEntity ent = (DbEntity) rel.getTargetEntity();
+ DbEntity ent = rel.getTargetEntity();
// index by name
for (DbAttribute pkAttr : ent.getPrimaryKeys()) {
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/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 c5fd13a..1d0108d 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
@@ -26,9 +26,11 @@ import java.util.List;
import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.ObjectId;
import org.apache.cayenne.Persistent;
+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.ASTFunctionCall;
import org.apache.cayenne.exp.parser.ASTObjPath;
import org.apache.cayenne.exp.parser.PatternMatchNode;
import org.apache.cayenne.exp.parser.SimpleNode;
@@ -189,6 +191,7 @@ public class QualifierTranslator extends QueryAssemblerHelper implements Travers
objectMatchTranslator.reset();
}
+ @Override
public void finishedChild(Expression node, int childIndex, boolean hasMoreChildren) {
if (!hasMoreChildren) {
@@ -351,7 +354,18 @@ public class QualifierTranslator extends QueryAssemblerHelper implements Travers
return ">>";
}
+ @Override
public void startNode(Expression node, Expression parentNode) {
+ boolean parenthesisNeeded = parenthesisNeeded(node, parentNode);
+
+ if(node.getType() == Expression.FUNCTION_CALL) {
+ appendFunction((ASTFunctionCall)node);
+ if(parenthesisNeeded) {
+ out.append("(");
+ }
+ return;
+ }
+
int count = node.getOperandCount();
if (count == 2) {
@@ -359,7 +373,7 @@ public class QualifierTranslator extends QueryAssemblerHelper implements Travers
detectObjectMatch(node);
}
- if (parenthesisNeeded(node, parentNode)) {
+ if (parenthesisNeeded) {
out.append('(');
}
@@ -367,21 +381,17 @@ public class QualifierTranslator extends QueryAssemblerHelper implements Travers
// not all databases handle true/false
if (node.getType() == Expression.TRUE) {
out.append("1 = 1");
- }
- if (node.getType() == Expression.FALSE) {
+ } else if (node.getType() == Expression.FALSE) {
out.append("1 = 0");
}
}
if (count == 1) {
- if (node.getType() == Expression.NEGATIVE)
+ if (node.getType() == Expression.NEGATIVE) {
out.append('-');
- // ignore POSITIVE - it is a NOOP
- // else if(node.getType() == Expression.POSITIVE)
- // qualBuf.append('+');
- else if (node.getType() == Expression.NOT)
+ } else if (node.getType() == Expression.NOT) {
out.append("NOT ");
- else if (node.getType() == Expression.BITWISE_NOT) {
+ } else if (node.getType() == Expression.BITWISE_NOT) {
out.append(operandForBitwiseNot());
}
} else if ((node.getType() == Expression.LIKE_IGNORE_CASE || node.getType() == Expression.NOT_LIKE_IGNORE_CASE)
@@ -394,12 +404,11 @@ public class QualifierTranslator extends QueryAssemblerHelper implements Travers
/**
* @since 1.1
*/
+ @Override
public void endNode(Expression node, Expression parentNode) {
try {
- // check if we need to use objectMatchTranslator to finish building
- // the
- // expression
+ // check if we need to use objectMatchTranslator to finish building the expression
if (node.getOperandCount() == 2 && matchingObject) {
appendObjectMatch();
}
@@ -417,26 +426,44 @@ public class QualifierTranslator extends QueryAssemblerHelper implements Travers
appendLikeEscapeCharacter((PatternMatchNode) node);
}
+ // clean up trailing comma in function argument list
+ if(node.getType() == Expression.FUNCTION_CALL) {
+ clearLastFunctionArgDivider((ASTFunctionCall)node);
+ }
+
// closing LIKE parenthesis
if (parenthesisNeeded) {
out.append(')');
}
+
+ // if inside function call, put comma between arguments
+ if(parentNode != null && parentNode.getType() == Expression.FUNCTION_CALL) {
+ appendFunctionArgDivider((ASTFunctionCall) parentNode);
+ }
} catch (IOException ioex) {
throw new CayenneRuntimeException("Error appending content", ioex);
}
}
+ @Override
public void objectNode(Object leaf, Expression parentNode) {
try {
- if (parentNode.getType() == Expression.OBJ_PATH) {
- appendObjPath(parentNode);
- } else if (parentNode.getType() == Expression.DB_PATH) {
- appendDbPath(parentNode);
- } else if (parentNode.getType() == Expression.LIST) {
- appendList(parentNode, paramsDbType(parentNode));
- } else {
- appendLiteral(leaf, paramsDbType(parentNode), parentNode);
+ switch (parentNode.getType()) {
+ case Expression.OBJ_PATH:
+ appendObjPath(parentNode);
+ break;
+ case Expression.DB_PATH:
+ appendDbPath(parentNode);
+ break;
+ case Expression.LIST:
+ appendList(parentNode, paramsDbType(parentNode));
+ break;
+ case Expression.FUNCTION_CALL:
+ appendFunctionArg(leaf, (ASTFunctionCall)parentNode);
+ break;
+ default:
+ appendLiteral(leaf, paramsDbType(parentNode), parentNode);
}
} catch (IOException ioex) {
throw new CayenneRuntimeException("Error appending content", ioex);
@@ -444,24 +471,28 @@ public class QualifierTranslator extends QueryAssemblerHelper implements Travers
}
protected boolean parenthesisNeeded(Expression node, Expression parentNode) {
- if (parentNode == null)
+ if (parentNode == null) {
return false;
+ }
- // only unary expressions can go w/o parenthesis
- if (node.getOperandCount() > 1)
+ if (node.getType() == Expression.FUNCTION_CALL) {
return true;
+ }
- if (node.getType() == Expression.OBJ_PATH)
- return false;
+ // only unary expressions can go w/o parenthesis
+ if (node.getOperandCount() > 1) {
+ return true;
+ }
- if (node.getType() == Expression.DB_PATH)
+ if (node.getType() == Expression.OBJ_PATH || node.getType() == Expression.DB_PATH) {
return false;
+ }
return true;
}
private final void appendList(Expression listExpr, DbAttribute paramDesc) throws IOException {
- Iterator<?> it = null;
+ Iterator<?> it;
Object list = listExpr.getOperand(0);
if (list instanceof List) {
it = ((List<?>) list).iterator();
@@ -474,10 +505,11 @@ public class QualifierTranslator extends QueryAssemblerHelper implements Travers
// process first element outside the loop
// (unroll loop to avoid condition checking
- if (it.hasNext())
+ if (it.hasNext()) {
appendLiteral(it.next(), paramDesc, listExpr);
- else
+ } else {
return;
+ }
while (it.hasNext()) {
out.append(", ");
@@ -514,6 +546,47 @@ public class QualifierTranslator extends QueryAssemblerHelper implements Travers
}
/**
+ * Append function name to result SQL
+ * Override this method to rename or skip function if generic name isn't supported on target DB.
+ * @since 4.0
+ */
+ protected void appendFunction(ASTFunctionCall functionExpression) {
+ out.append(functionExpression.getFunctionName());
+ }
+
+ /**
+ * Append scalar argument of a function call
+ * Used only for values stored in ASTScalar other
+ * expressions appended in objectNode() method
+ *
+ * @since 4.0
+ */
+ protected void appendFunctionArg(Object value, ASTFunctionCall functionExpression) throws IOException {
+ // Create fake DbAttribute to pass argument info down to bind it to SQL prepared statement
+ DbAttribute dbAttrForArg = new DbAttribute();
+ dbAttrForArg.setType(TypesMapping.getSqlTypeByJava(value.getClass()));
+ super.appendLiteral(value, dbAttrForArg, functionExpression);
+ appendFunctionArgDivider(functionExpression);
+ }
+
+ /**
+ * Append divider between function arguments.
+ * In overriding methods can be replaced e.g. for " || " for CONCAT operation
+ * @since 4.0
+ */
+ protected void appendFunctionArgDivider(ASTFunctionCall functionExpression) {
+ out.append(", ");
+ }
+
+ /**
+ * Clear last divider as we currently don't now position of argument until parent element is ended.
+ * @since 4.0
+ */
+ protected void clearLastFunctionArgDivider(ASTFunctionCall functionExpression) {
+ out.delete(out.length() - 2, out.length());
+ }
+
+ /**
* Class to translate DB Entity qualifiers annotation to Obj-entity
* qualifiers annotation This is done by changing all Obj-paths to Db-paths
* and rejecting all original Db-paths
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/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 67f22ca..731c052 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.ASTFunctionCall;
import org.apache.cayenne.exp.parser.ASTNotEqual;
import org.apache.cayenne.exp.parser.SimpleNode;
import org.apache.cayenne.map.DbAttribute;
@@ -114,4 +115,38 @@ public class DB2QualifierTranslator extends TrimmingQualifierTranslator {
super.processColumnWithQuoteSqlIdentifiers(dbAttr, pathExp);
}
}
+
+ /**
+ * @since 4.0
+ */
+ @Override
+ protected void appendFunction(ASTFunctionCall functionExpression) {
+ if(!"CONCAT".equals(functionExpression.getFunctionName())) {
+ super.appendFunction(functionExpression);
+ }
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Override
+ protected void appendFunctionArgDivider(ASTFunctionCall functionExpression) {
+ if("CONCAT".equals(functionExpression.getFunctionName())) {
+ out.append(" || ");
+ } else {
+ super.appendFunctionArgDivider(functionExpression);
+ }
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Override
+ protected void clearLastFunctionArgDivider(ASTFunctionCall functionExpression) {
+ if("CONCAT".equals(functionExpression.getFunctionName())) {
+ out.delete(out.length() - " || ".length(), out.length());
+ } else {
+ super.clearLastFunctionArgDivider(functionExpression);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/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 1b59288..84c5889 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
@@ -18,12 +18,14 @@
****************************************************************/
package org.apache.cayenne.dba.derby;
+import java.io.IOException;
import java.sql.Types;
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.ASTFunctionCall;
import org.apache.cayenne.exp.parser.ASTNotEqual;
import org.apache.cayenne.exp.parser.SimpleNode;
import org.apache.cayenne.map.DbAttribute;
@@ -52,9 +54,70 @@ public class DerbyQualifierTranslator extends TrimmingQualifierTranslator {
out.append("CAST(");
super.processColumnWithQuoteSqlIdentifiers(dbAttr, pathExp);
- out.append(" AS VARCHAR(" + size + "))");
+ out.append(" AS VARCHAR(").append(size).append("))");
} else {
super.processColumnWithQuoteSqlIdentifiers(dbAttr, pathExp);
}
}
+
+ /**
+ * @since 4.0
+ */
+ @Override
+ protected void appendFunction(ASTFunctionCall functionExpression) {
+ if("SUBSTRING".equals(functionExpression.getFunctionName())) {
+ out.append("SUBSTR");
+ } else if("CONCAT".equals(functionExpression.getFunctionName())) {
+ out.append("");
+ } else {
+ super.appendFunction(functionExpression);
+ }
+ }
+
+ /**
+ * A little bit ugly code that wraps String scalars to CAST(? AS VARCHAR(length))
+ * because otherwise derby don't know what type will be at the placeholder and
+ * use LONG VARCHAR that isn't comparable what leads to statement preparation failure.
+ *
+ * @since 4.0
+ */
+ protected void appendFunctionArg(Object value, ASTFunctionCall functionExpression) throws IOException {
+ if("CONCAT".equals(functionExpression.getFunctionName())) {
+ if(value instanceof String) {
+ out.append("CAST(");
+ }
+ super.appendFunctionArg(value, functionExpression);
+ if(value instanceof String) {
+ clearLastFunctionArgDivider(functionExpression);
+ out.append(" AS VARCHAR(").append(((String)value).length()).append("))");
+ appendFunctionArgDivider(functionExpression);
+ }
+ } else {
+ super.appendFunctionArg(value, functionExpression);
+ }
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Override
+ protected void appendFunctionArgDivider(ASTFunctionCall functionExpression) {
+ if("CONCAT".equals(functionExpression.getFunctionName())) {
+ out.append(" || ");
+ } else {
+ super.appendFunctionArgDivider(functionExpression);
+ }
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Override
+ protected void clearLastFunctionArgDivider(ASTFunctionCall functionExpression) {
+ if("CONCAT".equals(functionExpression.getFunctionName())) {
+ out.delete(out.length() - 4, out.length());
+ } else {
+ super.clearLastFunctionArgDivider(functionExpression);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/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 f41bdf2..9965b12 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,8 +23,12 @@ 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.ASTFunctionCall;
public class FirebirdQualifierTranslator extends QualifierTranslator {
+
+ private int substringArg = 0;
+
public FirebirdQualifierTranslator(QueryAssembler queryAssembler) {
super(queryAssembler);
}
@@ -39,4 +43,70 @@ public class FirebirdQualifierTranslator extends QualifierTranslator {
rootNode = rootNode.transform(new OracleQualifierTranslator.INTrimmer());
rootNode.traverse(this);
}
+
+ /**
+ * @since 4.0
+ */
+ @Override
+ protected void appendFunction(ASTFunctionCall functionExpression) {
+ switch (functionExpression.getFunctionName()) {
+ case "CONCAT":
+ // noop
+ break;
+ case "LENGTH":
+ out.append("CHAR_LENGTH");
+ break;
+ case "LOCATE":
+ out.append("POSITION");
+ break;
+ case "SUBSTRING":
+ substringArg = 0;
+ default:
+ super.appendFunction(functionExpression);
+ }
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Override
+ protected void appendFunctionArgDivider(ASTFunctionCall functionExpression) {
+ switch (functionExpression.getFunctionName()) {
+ case "CONCAT":
+ out.append(" || ");
+ break;
+ case "SUBSTRING":
+ switch (substringArg++) {
+ case 0:
+ out.append(" FROM ");
+ break;
+ case 1:
+ out.append(" FOR ");
+ break;
+ }
+ break;
+ default:
+ super.appendFunctionArgDivider(functionExpression);
+ }
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Override
+ protected void clearLastFunctionArgDivider(ASTFunctionCall functionExpression) {
+ switch (functionExpression.getFunctionName()) {
+ case "CONCAT":
+ out.delete(out.length() - 4, out.length());
+ break;
+ case "SUBSTRING":
+ // no offset arg
+ if(substringArg == 2) {
+ out.delete(out.length() - " FOR ".length(), out.length());
+ }
+ break;
+ default:
+ super.clearLastFunctionArgDivider(functionExpression);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseAdapter.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseAdapter.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseAdapter.java
index f50aa7b..d6131bb 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseAdapter.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseAdapter.java
@@ -20,6 +20,8 @@
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.access.translator.select.SelectTranslator;
import org.apache.cayenne.access.types.ExtendedType;
import org.apache.cayenne.access.types.ExtendedTypeFactory;
@@ -83,6 +85,14 @@ public class FrontBaseAdapter extends JdbcAdapter {
return new FrontBaseSelectTranslator(query, this, entityResolver);
}
+ /**
+ * @since 4.0
+ */
+ @Override
+ public QualifierTranslator getQualifierTranslator(QueryAssembler queryAssembler) {
+ return new FrontBaseQualifierTranslator(queryAssembler);
+ }
+
@Override
public String tableTypeForTable() {
return "BASE TABLE";
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/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
new file mode 100644
index 0000000..0a3d99e
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseQualifierTranslator.java
@@ -0,0 +1,91 @@
+/*****************************************************************
+ * 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.dba.frontbase;
+
+import org.apache.cayenne.access.translator.select.QualifierTranslator;
+import org.apache.cayenne.access.translator.select.QueryAssembler;
+import org.apache.cayenne.exp.parser.ASTFunctionCall;
+
+/**
+ * @since 4.0
+ */
+public class FrontBaseQualifierTranslator extends QualifierTranslator {
+
+ private int substringArg = 0;
+
+ public FrontBaseQualifierTranslator(QueryAssembler queryAssembler) {
+ super(queryAssembler);
+ }
+
+ @Override
+ protected void appendFunction(ASTFunctionCall functionExpression) {
+ switch (functionExpression.getFunctionName()) {
+ case "CONCAT":
+ // noop
+ break;
+ case "LOCATE":
+ out.append("POSITION");
+ break;
+ case "SUBSTRING":
+ substringArg = 0;
+ default:
+ super.appendFunction(functionExpression);
+ }
+ }
+
+ @Override
+ protected void appendFunctionArgDivider(ASTFunctionCall functionExpression) {
+ switch (functionExpression.getFunctionName()) {
+ case "CONCAT":
+ out.append(" | ");
+ break;
+ case "SUBSTRING":
+ // SUBSTRING (str FROM offset FOR length)
+ switch (substringArg++) {
+ case 0:
+ out.append(" FROM ");
+ break;
+ case 1:
+ out.append(" FOR ");
+ break;
+ }
+ break;
+ default:
+ super.appendFunctionArgDivider(functionExpression);
+ }
+ }
+
+ @Override
+ protected void clearLastFunctionArgDivider(ASTFunctionCall functionExpression) {
+ switch (functionExpression.getFunctionName()) {
+ case "CONCAT":
+ out.delete(out.length() - 3, out.length());
+ break;
+ case "SUBSTRING":
+ // no offset arg
+ if(substringArg == 2) {
+ out.delete(out.length() - " FOR ".length(), out.length());
+ }
+ break;
+ default:
+ super.clearLastFunctionArgDivider(functionExpression);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresAdapter.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresAdapter.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresAdapter.java
index dc94bae..0ac444f 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresAdapter.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresAdapter.java
@@ -83,7 +83,7 @@ public class IngresAdapter extends JdbcAdapter {
@Override
public QualifierTranslator getQualifierTranslator(QueryAssembler queryAssembler) {
- return new TrimmingQualifierTranslator(queryAssembler, IngresAdapter.TRIM_FUNCTION);
+ return new IngresQualifierTranslator(queryAssembler);
}
@Override
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/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
new file mode 100644
index 0000000..949155b
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresQualifierTranslator.java
@@ -0,0 +1,59 @@
+/*****************************************************************
+ * 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.dba.ingres;
+
+import org.apache.cayenne.access.translator.select.QueryAssembler;
+import org.apache.cayenne.access.translator.select.TrimmingQualifierTranslator;
+import org.apache.cayenne.exp.parser.ASTFunctionCall;
+
+/**
+ * @since 4.0
+ */
+class IngresQualifierTranslator extends TrimmingQualifierTranslator {
+
+ IngresQualifierTranslator(QueryAssembler queryAssembler) {
+ super(queryAssembler, IngresAdapter.TRIM_FUNCTION);
+ }
+
+ @Override
+ protected void appendFunction(ASTFunctionCall functionExpression) {
+ if(!"CONCAT".equals(functionExpression.getFunctionName())) {
+ super.appendFunction(functionExpression);
+ }
+ }
+
+ @Override
+ protected void appendFunctionArgDivider(ASTFunctionCall functionExpression) {
+ if("CONCAT".equals(functionExpression.getFunctionName())) {
+ out.append(" + ");
+ } else {
+ super.appendFunctionArgDivider(functionExpression);
+ }
+ }
+
+ @Override
+ protected void clearLastFunctionArgDivider(ASTFunctionCall functionExpression) {
+ if("CONCAT".equals(functionExpression.getFunctionName())) {
+ out.delete(out.length() - " + ".length(), out.length());
+ } else {
+ super.clearLastFunctionArgDivider(functionExpression);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/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 3d14765..28b9f74 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,11 +21,13 @@ 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.ASTFunctionCall;
import org.apache.cayenne.exp.parser.ASTIn;
import org.apache.cayenne.exp.parser.ASTList;
import org.apache.cayenne.exp.parser.ASTNegate;
import org.apache.cayenne.exp.parser.ASTNotIn;
import org.apache.cayenne.exp.parser.ASTPath;
+import org.apache.cayenne.exp.parser.Node;
import org.apache.commons.collections.Transformer;
import java.util.ArrayList;
@@ -93,4 +95,70 @@ public class OracleQualifierTranslator extends TrimmingQualifierTranslator {
return input;
}
}
+
+ /**
+ * @since 4.0
+ */
+ @Override
+ public void endNode(Expression node, Expression parentNode) {
+ super.endNode(node, parentNode);
+ if(node.getType() == Expression.FUNCTION_CALL) {
+ if("LOCATE".equals(((ASTFunctionCall)node).getFunctionName())) {
+ // order of args in INSTR is different, so swap them back
+ swapNodeChildren((ASTFunctionCall)node, 0, 1);
+ }
+ }
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Override
+ protected void appendFunction(ASTFunctionCall functionExpression) {
+ if("CONCAT".equals(functionExpression.getFunctionName())) {
+ // CONCAT(x, y, z) -> (x || y || z)
+ } else if("SUBSTRING".equals(functionExpression.getFunctionName())) {
+ out.append("SUBSTR");
+ } else if("LOCATE".equals(functionExpression.getFunctionName())) {
+ // LOCATE(substr, str) -> INSTR(str, substr)
+ out.append("INSTR");
+ swapNodeChildren(functionExpression, 0, 1);
+ } else {
+ super.appendFunction(functionExpression);
+ }
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Override
+ protected void appendFunctionArgDivider(ASTFunctionCall functionExpression) {
+ if("CONCAT".equals(functionExpression.getFunctionName())) {
+ out.append(" || ");
+ } else {
+ super.appendFunctionArgDivider(functionExpression);
+ }
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Override
+ protected void clearLastFunctionArgDivider(ASTFunctionCall functionExpression) {
+ if("CONCAT".equals(functionExpression.getFunctionName())) {
+ out.delete(out.length() - " || ".length(), out.length());
+ } else {
+ super.clearLastFunctionArgDivider(functionExpression);
+ }
+ }
+
+ /**
+ * @since 4.0
+ */
+ private void swapNodeChildren(Node node, int i, int j) {
+ Node ni = node.jjtGetChild(i);
+ Node nj = node.jjtGetChild(j);
+ node.jjtAddChild(ni, j);
+ node.jjtAddChild(nj, i);
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/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 8b8e731..42937bb 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.ASTFunctionCall;
import org.apache.cayenne.exp.parser.PatternMatchNode;
/**
@@ -40,20 +41,15 @@ public class PostgresQualifierTranslator extends TrimmingQualifierTranslator {
@Override
public void startNode(Expression node, Expression parentNode) {
-
- if (node.getOperandCount() == 2) {
+ // super implementation has special handling of LIKE_IGNORE_CASE and NOT_LIKE_IGNORE_CASE
+ // Postgres uses ILIKE
+ boolean likeIgnoreCase = (node.getType() == Expression.LIKE_IGNORE_CASE || node.getType() == Expression.NOT_LIKE_IGNORE_CASE);
+ if (likeIgnoreCase) {
// binary nodes are the only ones that currently require this
detectObjectMatch(node);
-
if (parenthesisNeeded(node, parentNode)) {
out.append('(');
}
-
- // super implementation has special handling
- // of LIKE_IGNORE_CASE and NOT_LIKE_IGNORE_CASE
- // Postgres uses ILIKE
- // ...
-
} else {
super.startNode(node, parentNode);
}
@@ -61,12 +57,12 @@ public class PostgresQualifierTranslator extends TrimmingQualifierTranslator {
@Override
public void endNode(Expression node, Expression parentNode) {
- if (node.getOperandCount() == 2) {
+ // super implementation has special handling of LIKE_IGNORE_CASE and NOT_LIKE_IGNORE_CASE
+ // Postgres uses ILIKE
+ boolean likeIgnoreCase = (node.getType() == Expression.LIKE_IGNORE_CASE || node.getType() == Expression.NOT_LIKE_IGNORE_CASE);
+ if (likeIgnoreCase) {
try {
- // check if we need to use objectMatchTranslator to finish
- // building the
- // expression
if (matchingObject) {
appendObjectMatch();
}
@@ -78,11 +74,6 @@ public class PostgresQualifierTranslator extends TrimmingQualifierTranslator {
if (parenthesisNeeded(node, parentNode)) {
out.append(')');
}
-
- // super implementation has special handling
- // of LIKE_IGNORE_CASE and NOT_LIKE_IGNORE_CASE
- // Postgres uses ILIKE
- // ...
} catch (IOException ioex) {
throw new CayenneRuntimeException("Error appending content", ioex);
}
@@ -99,17 +90,15 @@ public class PostgresQualifierTranslator extends TrimmingQualifierTranslator {
try {
// use ILIKE
-
switch (node.getType()) {
-
- case Expression.LIKE_IGNORE_CASE:
- finishedChildNodeAppendExpression(node, " ILIKE ");
- break;
- case Expression.NOT_LIKE_IGNORE_CASE:
- finishedChildNodeAppendExpression(node, " NOT ILIKE ");
- break;
- default:
- super.finishedChild(node, childIndex, hasMoreChildren);
+ case Expression.LIKE_IGNORE_CASE:
+ finishedChildNodeAppendExpression(node, " ILIKE ");
+ break;
+ case Expression.NOT_LIKE_IGNORE_CASE:
+ finishedChildNodeAppendExpression(node, " NOT ILIKE ");
+ break;
+ default:
+ super.finishedChild(node, childIndex, hasMoreChildren);
}
} catch (IOException ioex) {
throw new CayenneRuntimeException("Error appending content", ioex);
@@ -124,4 +113,40 @@ public class PostgresQualifierTranslator extends TrimmingQualifierTranslator {
objectMatchTranslator.setExpression(node);
}
}
+
+ /**
+ * @since 4.0
+ */
+ @Override
+ protected void appendFunction(ASTFunctionCall functionExpression) {
+ if("LOCATE".equals(functionExpression.getFunctionName())) {
+ out.append("POSITION");
+ } else {
+ super.appendFunction(functionExpression);
+ }
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Override
+ protected void appendFunctionArgDivider(ASTFunctionCall functionExpression) {
+ if("LOCATE".equals(functionExpression.getFunctionName())) {
+ out.append(" in ");
+ } else {
+ super.appendFunctionArgDivider(functionExpression);
+ }
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Override
+ protected void clearLastFunctionArgDivider(ASTFunctionCall functionExpression) {
+ if("LOCATE".equals(functionExpression.getFunctionName())) {
+ out.delete(out.length() - " in ".length(), out.length());
+ } else {
+ super.clearLastFunctionArgDivider(functionExpression);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/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 839b548..b390ebc 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
@@ -19,6 +19,8 @@
package org.apache.cayenne.dba.sqlite;
import org.apache.cayenne.access.DataNode;
+import org.apache.cayenne.access.translator.select.QualifierTranslator;
+import org.apache.cayenne.access.translator.select.QueryAssembler;
import org.apache.cayenne.access.types.ExtendedType;
import org.apache.cayenne.access.types.ExtendedTypeFactory;
import org.apache.cayenne.access.types.ExtendedTypeMap;
@@ -81,6 +83,14 @@ public class SQLiteAdapter extends JdbcAdapter {
map.registerType(new SQLiteCalendarType(Calendar.class));
}
+ /**
+ * @since 4.0
+ */
+ @Override
+ public QualifierTranslator getQualifierTranslator(QueryAssembler queryAssembler) {
+ return new SQLiteQualifierTranslator(queryAssembler);
+ }
+
@Override
public String createFkConstraint(DbRelationship rel) {
return null;
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/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
new file mode 100644
index 0000000..bc2bb2e
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteQualifierTranslator.java
@@ -0,0 +1,102 @@
+/*****************************************************************
+ * 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.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.ASTFunctionCall;
+import org.apache.cayenne.exp.parser.Node;
+
+/**
+ * @since 4.0
+ */
+public class SQLiteQualifierTranslator extends QualifierTranslator {
+
+ public SQLiteQualifierTranslator(QueryAssembler queryAssembler) {
+ super(queryAssembler);
+ }
+
+ @Override
+ public void endNode(Expression node, Expression parentNode) {
+ super.endNode(node, parentNode);
+ if(node.getType() == Expression.FUNCTION_CALL) {
+ if("LOCATE".equals(((ASTFunctionCall)node).getFunctionName())) {
+ // order of args in INSTR is different, so swap them back
+ swapNodeChildren((ASTFunctionCall)node, 0, 1);
+ }
+ }
+ }
+
+ @Override
+ protected void appendFunction(ASTFunctionCall functionExpression) {
+ switch (functionExpression.getFunctionName()) {
+ case "MOD":
+ case "CONCAT":
+ // noop
+ break;
+ case "SUBSTRING":
+ out.append("SUBSTR");
+ break;
+ case "LOCATE":
+ // LOCATE(substr, str) -> INSTR(str, substr)
+ out.append("INSTR");
+ swapNodeChildren(functionExpression, 0, 1);
+ break;
+ default:
+ super.appendFunction(functionExpression);
+ }
+ }
+
+ @Override
+ protected void appendFunctionArgDivider(ASTFunctionCall functionExpression) {
+ switch (functionExpression.getFunctionName()) {
+ case "MOD":
+ out.append(" % ");
+ break;
+ case "CONCAT":
+ out.append(" || ");
+ break;
+ default:
+ super.appendFunctionArgDivider(functionExpression);
+ }
+ }
+
+ @Override
+ protected void clearLastFunctionArgDivider(ASTFunctionCall functionExpression) {
+ switch (functionExpression.getFunctionName()) {
+ case "MOD":
+ out.delete(out.length() - 3, out.length());
+ break;
+ case "CONCAT":
+ out.delete(out.length() - 4, out.length());
+ break;
+ default:
+ super.clearLastFunctionArgDivider(functionExpression);
+ }
+ }
+
+ private void swapNodeChildren(Node node, int i, int j) {
+ Node ni = node.jjtGetChild(i);
+ Node nj = node.jjtGetChild(j);
+ node.jjtAddChild(ni, j);
+ node.jjtAddChild(nj, i);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/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 fd9a191..9a4e9be 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.ASTFunctionCall;
import org.apache.cayenne.map.DbAttribute;
import java.sql.Types;
@@ -112,4 +113,48 @@ class SQLServerTrimmingQualifierTranslator extends TrimmingQualifierTranslator {
return expressionStack.get(index);
}
+
+ /**
+ * @since 4.0
+ */
+ @Override
+ protected void appendFunction(ASTFunctionCall functionExpression) {
+ switch (functionExpression.getFunctionName()) {
+ case "LENGTH":
+ out.append("LEN");
+ break;
+ case "LOCATE":
+ out.append("CHARINDEX");
+ break;
+ case "MOD":
+ // noop
+ break;
+ default:
+ super.appendFunction(functionExpression);
+ }
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Override
+ protected void appendFunctionArgDivider(ASTFunctionCall functionExpression) {
+ if("MOD".equals(functionExpression.getFunctionName())) {
+ out.append(" % ");
+ } else {
+ super.appendFunctionArgDivider(functionExpression);
+ }
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Override
+ protected void clearLastFunctionArgDivider(ASTFunctionCall functionExpression) {
+ if("MOD".equals(functionExpression.getFunctionName())) {
+ out.delete(out.length() - " % ".length(), out.length());
+ } else {
+ super.clearLastFunctionArgDivider(functionExpression);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/dba/sybase/SybaseAdapter.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/sybase/SybaseAdapter.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/sybase/SybaseAdapter.java
index d5893ac..28e948c 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/sybase/SybaseAdapter.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/sybase/SybaseAdapter.java
@@ -26,6 +26,8 @@ import java.util.List;
import org.apache.cayenne.access.translator.ParameterBinding;
import org.apache.cayenne.access.translator.ejbql.EJBQLTranslatorFactory;
+import org.apache.cayenne.access.translator.select.QualifierTranslator;
+import org.apache.cayenne.access.translator.select.QueryAssembler;
import org.apache.cayenne.access.translator.select.SelectTranslator;
import org.apache.cayenne.access.types.ByteArrayType;
import org.apache.cayenne.access.types.ByteType;
@@ -81,6 +83,14 @@ public class SybaseAdapter extends JdbcAdapter {
}
/**
+ * @since 4.0
+ */
+ @Override
+ public QualifierTranslator getQualifierTranslator(QueryAssembler queryAssembler) {
+ return new SybaseQualifierTranslator(queryAssembler);
+ }
+
+ /**
* Returns word "go".
*
* @since 1.0.4
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/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
new file mode 100644
index 0000000..1ad939e
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/sybase/SybaseQualifierTranslator.java
@@ -0,0 +1,78 @@
+/*****************************************************************
+ * 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.dba.sybase;
+
+import org.apache.cayenne.access.translator.select.QualifierTranslator;
+import org.apache.cayenne.access.translator.select.QueryAssembler;
+import org.apache.cayenne.exp.parser.ASTFunctionCall;
+
+/**
+ * @since 4.0
+ */
+public class SybaseQualifierTranslator extends QualifierTranslator {
+
+ public SybaseQualifierTranslator(QueryAssembler queryAssembler) {
+ super(queryAssembler);
+ }
+
+ @Override
+ protected void appendFunction(ASTFunctionCall functionExpression) {
+ switch (functionExpression.getFunctionName()) {
+ case "MOD":
+ case "CONCAT":
+ // noop
+ break;
+ case "LENGTH":
+ out.append("LEN");
+ break;
+ case "LOCATE":
+ out.append("CHARINDEX");
+ break;
+ default:
+ super.appendFunction(functionExpression);
+ }
+ }
+
+ @Override
+ protected void appendFunctionArgDivider(ASTFunctionCall functionExpression) {
+ switch (functionExpression.getFunctionName()) {
+ case "MOD":
+ out.append(" % ");
+ break;
+ case "CONCAT":
+ out.append(" + ");
+ break;
+ default:
+ super.appendFunctionArgDivider(functionExpression);
+ }
+ }
+
+ @Override
+ protected void clearLastFunctionArgDivider(ASTFunctionCall functionExpression) {
+ switch (functionExpression.getFunctionName()) {
+ case "MOD":
+ case "CONCAT":
+ out.delete(out.length() - 3, out.length());
+ break;
+ default:
+ super.clearLastFunctionArgDivider(functionExpression);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/Expression.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/Expression.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/Expression.java
index 11bfdb6..6d9024c 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/Expression.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/Expression.java
@@ -151,6 +151,11 @@ public abstract class Expression implements Serializable, XMLSerializable {
*/
public static final int BITWISE_RIGHT_SHIFT = 44;
+ /**
+ * @since 4.0
+ */
+ public static final int FUNCTION_CALL = 45;
+
protected int type;
/**
@@ -569,7 +574,7 @@ public abstract class Expression implements Serializable, XMLSerializable {
for (int i = 0; i < count; i++) {
Object child = getOperand(i);
- if (child instanceof Expression) {
+ if (child instanceof Expression && !(child instanceof ASTScalar)) {
Expression childExp = (Expression) child;
childExp.traverse(this, visitor);
} else {
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/cayenne-server/src/main/java/org/apache/cayenne/exp/ExpressionFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/ExpressionFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/ExpressionFactory.java
index 16380e4..22dec9f 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/ExpressionFactory.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/ExpressionFactory.java
@@ -385,21 +385,54 @@ public class ExpressionFactory {
* A convenience method to create an OBJ_PATH "equal to" expression.
*/
public static Expression matchExp(String pathSpec, Object value) {
- return new ASTEqual(new ASTObjPath(pathSpec), value);
+ return matchExp(new ASTObjPath(pathSpec), value);
+ }
+
+ /**
+ * @since 4.0
+ * @see ExpressionFactory#matchExp(String, Object)
+ */
+ static Expression matchExp(Expression exp, Object value) {
+ if(!(exp instanceof SimpleNode)) {
+ throw new IllegalArgumentException("exp should be instance of SimpleNode");
+ }
+ return new ASTEqual((SimpleNode)exp, value);
}
/**
* A convenience method to create an OBJ_PATH "not equal to" expression.
*/
public static Expression noMatchExp(String pathSpec, Object value) {
- return new ASTNotEqual(new ASTObjPath(pathSpec), value);
+ return noMatchExp(new ASTObjPath(pathSpec), value);
+ }
+
+ /**
+ * @since 4.0
+ * @see ExpressionFactory#noMatchExp(String, Object)
+ */
+ static Expression noMatchExp(Expression exp, Object value) {
+ if(!(exp instanceof SimpleNode)) {
+ throw new IllegalArgumentException("exp should be instance of SimpleNode");
+ }
+ return new ASTNotEqual((SimpleNode)exp, value);
}
/**
* A convenience method to create an OBJ_PATH "less than" expression.
*/
public static Expression lessExp(String pathSpec, Object value) {
- return new ASTLess(new ASTObjPath(pathSpec), value);
+ return lessExp(new ASTObjPath(pathSpec), value);
+ }
+
+ /**
+ * @since 4.0
+ * @see ExpressionFactory#lessExp(String, Object)
+ */
+ static Expression lessExp(Expression exp, Object value) {
+ if(!(exp instanceof SimpleNode)) {
+ throw new IllegalArgumentException("exp should be instance of SimpleNode");
+ }
+ return new ASTLess((SimpleNode)exp, value);
}
/**
@@ -416,7 +449,18 @@ public class ExpressionFactory {
* expression.
*/
public static Expression lessOrEqualExp(String pathSpec, Object value) {
- return new ASTLessOrEqual(new ASTObjPath(pathSpec), value);
+ return lessOrEqualExp(new ASTObjPath(pathSpec), value);
+ }
+
+ /**
+ * @since 4.0
+ * @see ExpressionFactory#lessOrEqualExp(String, Object)
+ */
+ static Expression lessOrEqualExp(Expression exp, Object value) {
+ if(!(exp instanceof SimpleNode)) {
+ throw new IllegalArgumentException("exp should be instance of SimpleNode");
+ }
+ return new ASTLessOrEqual((SimpleNode)exp, value);
}
/**
@@ -433,7 +477,18 @@ public class ExpressionFactory {
* A convenience method to create an OBJ_PATH "greater than" expression.
*/
public static Expression greaterExp(String pathSpec, Object value) {
- return new ASTGreater(new ASTObjPath(pathSpec), value);
+ return greaterExp(new ASTObjPath(pathSpec), value);
+ }
+
+ /**
+ * @since 4.0
+ * @see ExpressionFactory#greaterExp(String, Object)
+ */
+ static Expression greaterExp(Expression exp, Object value) {
+ if(!(exp instanceof SimpleNode)) {
+ throw new IllegalArgumentException("exp should be instance of SimpleNode");
+ }
+ return new ASTGreater((SimpleNode)exp, value);
}
/**
@@ -450,7 +505,18 @@ public class ExpressionFactory {
* expression.
*/
public static Expression greaterOrEqualExp(String pathSpec, Object value) {
- return new ASTGreaterOrEqual(new ASTObjPath(pathSpec), value);
+ return greaterOrEqualExp(new ASTObjPath(pathSpec), value);
+ }
+
+ /**
+ * @since 4.0
+ * @see ExpressionFactory#greaterOrEqualExp(String, Object)
+ */
+ static Expression greaterOrEqualExp(Expression exp, Object value) {
+ if(!(exp instanceof SimpleNode)) {
+ throw new IllegalArgumentException("exp should be instance of SimpleNode");
+ }
+ return new ASTGreaterOrEqual((SimpleNode)exp, value);
}
/**
@@ -468,10 +534,21 @@ public class ExpressionFactory {
* empty collection.
*/
public static Expression inExp(String pathSpec, Object... values) {
+ return inExp(new ASTObjPath(pathSpec), values);
+ }
+
+ /**
+ * @since 4.0
+ * @see ExpressionFactory#inExp(String, Object[])
+ */
+ static Expression inExp(Expression exp, Object... values) {
if (values.length == 0) {
return new ASTFalse();
}
- return new ASTIn(new ASTObjPath(pathSpec), new ASTList(values));
+ if(!(exp instanceof SimpleNode)) {
+ throw new IllegalArgumentException("exp should be instance of SimpleNode");
+ }
+ return new ASTIn((SimpleNode)exp, new ASTList(values));
}
/**
@@ -490,10 +567,21 @@ public class ExpressionFactory {
* empty collection.
*/
public static Expression inExp(String pathSpec, Collection<?> values) {
+ return inExp(new ASTObjPath(pathSpec), values);
+ }
+
+ /**
+ * @since 4.0
+ * @see ExpressionFactory#inExp(String, Collection)
+ */
+ static Expression inExp(Expression exp, Collection<?> values) {
if (values.isEmpty()) {
return new ASTFalse();
}
- return new ASTIn(new ASTObjPath(pathSpec), new ASTList(values));
+ if(!(exp instanceof SimpleNode)) {
+ throw new IllegalArgumentException("exp should be instance of SimpleNode");
+ }
+ return new ASTIn((SimpleNode)exp, new ASTList(values));
}
/**
@@ -512,10 +600,21 @@ public class ExpressionFactory {
* empty collection.
*/
public static Expression notInExp(String pathSpec, Collection<?> values) {
+ return notInExp(new ASTObjPath(pathSpec), values);
+ }
+
+ /**
+ * @since 4.0
+ * @see ExpressionFactory#notInExp(String, Collection)
+ */
+ static Expression notInExp(Expression exp, Collection<?> values) {
if (values.isEmpty()) {
- return new ASTTrue();
+ return new ASTFalse();
+ }
+ if(!(exp instanceof SimpleNode)) {
+ throw new IllegalArgumentException("exp should be instance of SimpleNode");
}
- return new ASTNotIn(new ASTObjPath(pathSpec), new ASTList(values));
+ return new ASTNotIn((SimpleNode)exp, new ASTList(values));
}
/**
@@ -538,10 +637,21 @@ public class ExpressionFactory {
* @since 1.0.6
*/
public static Expression notInExp(String pathSpec, Object... values) {
+ return notInExp(new ASTObjPath(pathSpec), values);
+ }
+
+ /**
+ * @since 4.0
+ * @see ExpressionFactory#notInExp(String, Object[])
+ */
+ static Expression notInExp(Expression exp, Object... values) {
if (values.length == 0) {
return new ASTTrue();
}
- return new ASTNotIn(new ASTObjPath(pathSpec), new ASTList(values));
+ if(!(exp instanceof SimpleNode)) {
+ throw new IllegalArgumentException("exp should be instance of SimpleNode");
+ }
+ return new ASTNotIn((SimpleNode)exp, new ASTList(values));
}
/**
@@ -561,7 +671,18 @@ public class ExpressionFactory {
* A convenience shortcut for building BETWEEN expressions.
*/
public static Expression betweenExp(String pathSpec, Object value1, Object value2) {
- return new ASTBetween(new ASTObjPath(pathSpec), value1, value2);
+ return betweenExp(new ASTObjPath(pathSpec), value1, value2);
+ }
+
+ /**
+ * @since 4.0
+ * @see ExpressionFactory#betweenExp(String, Object, Object)
+ */
+ static Expression betweenExp(Expression exp, Object value1, Object value2) {
+ if(!(exp instanceof SimpleNode)) {
+ throw new IllegalArgumentException("exp should be instance of SimpleNode");
+ }
+ return new ASTBetween((SimpleNode)exp, value1, value2);
}
/**
@@ -577,7 +698,18 @@ public class ExpressionFactory {
* A convenience shortcut for building NOT_BETWEEN expressions.
*/
public static Expression notBetweenExp(String pathSpec, Object value1, Object value2) {
- return new ASTNotBetween(new ASTObjPath(pathSpec), value1, value2);
+ return notBetweenExp(new ASTObjPath(pathSpec), value1, value2);
+ }
+
+ /**
+ * @since 4.0
+ * @see ExpressionFactory#notBetweenExp(String, Object, Object)
+ */
+ static Expression notBetweenExp(Expression exp, Object value1, Object value2) {
+ if(!(exp instanceof SimpleNode)) {
+ throw new IllegalArgumentException("exp should be instance of SimpleNode");
+ }
+ return new ASTNotBetween((SimpleNode)exp, value1, value2);
}
/**
@@ -597,6 +729,14 @@ public class ExpressionFactory {
}
/**
+ * @since 4.0
+ * @see ExpressionFactory#likeExp(String, Object)
+ */
+ static Expression likeExp(Expression exp, Object value) {
+ return likeExpInternal(exp, value, (char) 0);
+ }
+
+ /**
* <p>
* A convenience shortcut for building LIKE expression.
* </p>
@@ -612,8 +752,23 @@ public class ExpressionFactory {
return likeExpInternal(pathSpec, value, escapeChar);
}
+ /**
+ * @since 4.0
+ * @see ExpressionFactory#likeExp(String, Object)
+ */
+ static Expression likeExp(Expression exp, Object value, char escapeChar) {
+ return likeExpInternal(exp, value, escapeChar);
+ }
+
static ASTLike likeExpInternal(String pathSpec, Object value, char escapeChar) {
- return new ASTLike(new ASTObjPath(pathSpec), value, escapeChar);
+ return likeExpInternal(new ASTObjPath(pathSpec), value, escapeChar);
+ }
+
+ static ASTLike likeExpInternal(Expression expression, Object value, char escapeChar) {
+ if(!(expression instanceof SimpleNode)) {
+ throw new IllegalArgumentException("exp should be instance of SimpleNode");
+ }
+ return new ASTLike((SimpleNode) expression, value, escapeChar);
}
/**
@@ -645,7 +800,18 @@ public class ExpressionFactory {
* A convenience shortcut for building NOT_LIKE expression.
*/
public static Expression notLikeExp(String pathSpec, Object value) {
- return new ASTNotLike(new ASTObjPath(pathSpec), value);
+ return notLikeExp(new ASTObjPath(pathSpec), value);
+ }
+
+ /**
+ * @since 4.0
+ * @see ExpressionFactory#notLikeExp(String, Object)
+ */
+ static Expression notLikeExp(Expression exp, Object value) {
+ if(!(exp instanceof SimpleNode)) {
+ throw new IllegalArgumentException("exp should be instance of SimpleNode");
+ }
+ return new ASTNotLike((SimpleNode)exp, value);
}
/**
@@ -661,7 +827,18 @@ public class ExpressionFactory {
* @since 3.0.1
*/
public static Expression notLikeExp(String pathSpec, Object value, char escapeChar) {
- return new ASTNotLike(new ASTObjPath(pathSpec), value, escapeChar);
+ return notLikeExp(new ASTObjPath(pathSpec), value, escapeChar);
+ }
+
+ /**
+ * @since 4.0
+ * @see ExpressionFactory#notLikeExp(String, Object)
+ */
+ static Expression notLikeExp(Expression exp, Object value, char escapeChar) {
+ if(!(exp instanceof SimpleNode)) {
+ throw new IllegalArgumentException("exp should be instance of SimpleNode");
+ }
+ return new ASTNotLike((SimpleNode)exp, value, escapeChar);
}
/**
@@ -697,6 +874,14 @@ public class ExpressionFactory {
}
/**
+ * @since 4.0
+ * @see ExpressionFactory#likeIgnoreCaseExp(String, Object)
+ */
+ static Expression likeIgnoreCaseExp(Expression exp, Object value) {
+ return likeIgnoreCaseExp(exp, value, (char) 0);
+ }
+
+ /**
* <p>
* A convenience shortcut for building LIKE_IGNORE_CASE expression.
* </p>
@@ -713,7 +898,14 @@ public class ExpressionFactory {
}
static ASTLikeIgnoreCase likeIgnoreCaseExpInternal(String pathSpec, Object value, char escapeChar) {
- return new ASTLikeIgnoreCase(new ASTObjPath(pathSpec), value, escapeChar);
+ return likeIgnoreCaseExp(new ASTObjPath(pathSpec), value, escapeChar);
+ }
+
+ static ASTLikeIgnoreCase likeIgnoreCaseExp(Expression exp, Object value, char escapeChar) {
+ if(!(exp instanceof SimpleNode)) {
+ throw new IllegalArgumentException("exp should be instance of SimpleNode");
+ }
+ return new ASTLikeIgnoreCase((SimpleNode) exp, value, escapeChar);
}
/**
@@ -745,7 +937,18 @@ public class ExpressionFactory {
* A convenience shortcut for building NOT_LIKE_IGNORE_CASE expression.
*/
public static Expression notLikeIgnoreCaseExp(String pathSpec, Object value) {
- return new ASTNotLikeIgnoreCase(new ASTObjPath(pathSpec), value);
+ return notLikeIgnoreCaseExp(new ASTObjPath(pathSpec), value);
+ }
+
+ /**
+ * @since 4.0
+ * @see ExpressionFactory#notLikeIgnoreCaseExp(String, Object)
+ */
+ static Expression notLikeIgnoreCaseExp(Expression exp, Object value) {
+ if(!(exp instanceof SimpleNode)) {
+ throw new IllegalArgumentException("exp should be instance of SimpleNode");
+ }
+ return new ASTNotLikeIgnoreCase((SimpleNode)exp, value);
}
/**
@@ -761,7 +964,18 @@ public class ExpressionFactory {
* @since 3.0.1
*/
public static Expression notLikeIgnoreCaseExp(String pathSpec, Object value, char escapeChar) {
- return new ASTNotLikeIgnoreCase(new ASTObjPath(pathSpec), value, escapeChar);
+ return notLikeIgnoreCaseExp(new ASTObjPath(pathSpec), value, escapeChar);
+ }
+
+ /**
+ * @since 4.0
+ * @see ExpressionFactory#notLikeIgnoreCaseExp(String, Object, char)
+ */
+ static Expression notLikeIgnoreCaseExp(Expression exp, Object value, char escapeChar) {
+ if(!(exp instanceof SimpleNode)) {
+ throw new IllegalArgumentException("exp should be instance of SimpleNode");
+ }
+ return new ASTNotLikeIgnoreCase((SimpleNode)exp, value, escapeChar);
}
/**
@@ -801,6 +1015,16 @@ public class ExpressionFactory {
}
/**
+ * @since 4.0
+ * @see ExpressionFactory#containsExp(String, String)
+ */
+ static Expression containsExp(Expression exp, String value) {
+ ASTLike like = likeExpInternal(exp, value, (char) 0);
+ LikeExpressionHelper.toContains(like);
+ return like;
+ }
+
+ /**
* @return An expression for a database "LIKE" query with the value
* converted to a pattern matching the beginning of the String.
* @since 4.0
@@ -812,6 +1036,16 @@ public class ExpressionFactory {
}
/**
+ * @since 4.0
+ * @see ExpressionFactory#startsWithExp(String, String)
+ */
+ static Expression startsWithExp(Expression exp, String value) {
+ ASTLike like = likeExpInternal(exp, value, (char) 0);
+ LikeExpressionHelper.toStartsWith(like);
+ return like;
+ }
+
+ /**
* @return An expression for a database "LIKE" query with the value
* converted to a pattern matching the beginning of the String.
* @since 4.0
@@ -823,6 +1057,16 @@ public class ExpressionFactory {
}
/**
+ * @since 4.0
+ * @see ExpressionFactory#endsWithExp(String, String)
+ */
+ static Expression endsWithExp(Expression exp, String value) {
+ ASTLike like = likeExpInternal(exp, value, (char) 0);
+ LikeExpressionHelper.toEndsWith(like);
+ return like;
+ }
+
+ /**
* Same as {@link #containsExp(String, String)} only using case-insensitive
* comparison.
*
@@ -835,6 +1079,16 @@ public class ExpressionFactory {
}
/**
+ * @since 4.0
+ * @see ExpressionFactory#containsIgnoreCaseExp(String, String)
+ */
+ static Expression containsIgnoreCaseExp(Expression exp, String value) {
+ ASTLikeIgnoreCase like = likeIgnoreCaseExp(exp, value, (char) 0);
+ LikeExpressionHelper.toContains(like);
+ return like;
+ }
+
+ /**
* Same as {@link #startsWithExp(String, String)} only using
* case-insensitive comparison.
*
@@ -847,6 +1101,16 @@ public class ExpressionFactory {
}
/**
+ * @since 4.0
+ * @see ExpressionFactory#startsWithIgnoreCaseExp(String, String)
+ */
+ static Expression startsWithIgnoreCaseExp(Expression exp, String value) {
+ ASTLikeIgnoreCase like = likeIgnoreCaseExp(exp, value, (char) 0);
+ LikeExpressionHelper.toStartsWith(like);
+ return like;
+ }
+
+ /**
* Same as {@link #endsWithExp(String, String)} only using case-insensitive
* comparison.
*
@@ -859,6 +1123,16 @@ public class ExpressionFactory {
}
/**
+ * @since 4.0
+ * @see ExpressionFactory#endsWithIgnoreCaseExp(String, String)
+ */
+ static Expression endsWithIgnoreCaseExp(Expression exp, String value) {
+ ASTLikeIgnoreCase like = likeIgnoreCaseExp(exp, value, (char) 0);
+ LikeExpressionHelper.toEndsWith(like);
+ return like;
+ }
+
+ /**
* @param pathSpec a String "obj:" path.
* @since 4.0
* @return a new "obj:" path expression for the specified String path.
http://git-wip-us.apache.org/repos/asf/cayenne/blob/803166c0/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
new file mode 100644
index 0000000..cfecdc7
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/FunctionExpressionFactory.java
@@ -0,0 +1,288 @@
+/*****************************************************************
+ * 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;
+
+import org.apache.cayenne.exp.parser.ASTAbs;
+import org.apache.cayenne.exp.parser.ASTConcat;
+import org.apache.cayenne.exp.parser.ASTLength;
+import org.apache.cayenne.exp.parser.ASTLocate;
+import org.apache.cayenne.exp.parser.ASTLower;
+import org.apache.cayenne.exp.parser.ASTMod;
+import org.apache.cayenne.exp.parser.ASTObjPath;
+import org.apache.cayenne.exp.parser.ASTScalar;
+import org.apache.cayenne.exp.parser.ASTSqrt;
+import org.apache.cayenne.exp.parser.ASTSubstring;
+import org.apache.cayenne.exp.parser.ASTTrim;
+import org.apache.cayenne.exp.parser.ASTUpper;
+
+/**
+ * Collection of factory methods to create function call expressions.
+ *
+ * @since 4.0
+ */
+public class FunctionExpressionFactory {
+
+ /**
+ * Call SUBSTRING(string, offset, length) function
+ *
+ * @param exp expression that must evaluate to string
+ * @param offset start offset of substring
+ * @param length length of subtring
+ * @return SUBSTRING() call expression
+ */
+ public static Expression substringExp(Expression exp, int offset, int length) {
+ return substringExp(exp, new ASTScalar((Integer)offset), new ASTScalar((Integer)length));
+ }
+
+ /**
+ * Call SUBSTRING(string, offset, length) function
+ *
+ * @param path Object path value
+ * @param offset start offset of substring
+ * @param length length of subtring
+ * @return SUBSTRING() call expression
+ */
+ public static Expression substringExp(String path, int offset, int length) {
+ return substringExp(new ASTObjPath(path), new ASTScalar((Integer)offset), new ASTScalar((Integer)length));
+ }
+
+ /**
+ * Call SUBSTRING(string, offset, length) function
+ *
+ * @param exp expression that must evaluate to string
+ * @param offset start offset of substring must evaluate to int
+ * @param length length of subtring must evaluate to int
+ * @return SUBSTRING() call expression
+ */
+ public static Expression substringExp(Expression exp, Expression offset, Expression length) {
+ return new ASTSubstring(exp, offset, length);
+ }
+
+ /**
+ * @param exp string expression to trim
+ * @return TRIM() call expression
+ */
+ public static Expression trimExp(Expression exp) {
+ return new ASTTrim(exp);
+ }
+
+ /**
+ * @param path object path value
+ * @return TRIM() call expression
+ */
+ public static Expression trimExp(String path) {
+ return new ASTTrim(new ASTObjPath(path));
+ }
+
+ /**
+ * @param exp string expression
+ * @return LOWER() call expression
+ */
+ public static Expression lowerExp(Expression exp) {
+ return new ASTLower(exp);
+ }
+
+ /**
+ * @param path object path value
+ * @return LOWER() call expression
+ */
+ public static Expression lowerExp(String path) {
+ return new ASTLower(new ASTObjPath(path));
+ }
+
+ /**
+ * @param exp string expression
+ * @return UPPER() call expression
+ */
+ public static Expression upperExp(Expression exp) {
+ return new ASTUpper(exp);
+ }
+
+ /**
+ * @param path object path value
+ * @return UPPER() call expression
+ */
+ public static Expression upperExp(String path) {
+ return new ASTUpper(new ASTObjPath(path));
+ }
+
+ /**
+ * @param exp string expression
+ * @return LENGTH() call expression
+ */
+ public static Expression lengthExp(Expression exp) {
+ return new ASTLength(exp);
+ }
+
+ /**
+ * @param path object path value
+ * @return LENGTH() call expression
+ */
+ public static Expression lengthExp(String path) {
+ return new ASTLength(new ASTObjPath(path));
+ }
+
+ /**
+ * Call LOCATE(substring, string) function that return position
+ * of substring in string or 0 if it is not found.
+ *
+ * @param substring object path value
+ * @param exp string expression
+ * @return LOCATE() call expression
+ */
+ public static Expression locateExp(String substring, Expression exp) {
+ return locateExp(new ASTScalar(substring), exp);
+ }
+
+ /**
+ * Call LOCATE(substring, string) function that return position
+ * of substring in string or 0 if it is not found.
+ *
+ * @param substring object path value
+ * @param path object path
+ * @return LOCATE() call expression
+ */
+ public static Expression locateExp(String substring, String path) {
+ return locateExp(new ASTScalar(substring), new ASTObjPath(path));
+ }
+
+ /**
+ * Call LOCATE(substring, string) function that return position
+ * of substring in string or 0 if it is not found.
+ *
+ * @param substring string expression
+ * @param exp string expression
+ * @return LOCATE() call expression
+ */
+ public static Expression locateExp(Expression substring, Expression exp) {
+ return new ASTLocate(substring, exp);
+ }
+
+ /**
+ * @param exp numeric expression
+ * @return ABS() call expression
+ */
+ public static Expression absExp(Expression exp) {
+ return new ASTAbs(exp);
+ }
+
+ /**
+ * @param path object path value
+ * @return ABS() call expression
+ */
+ public static Expression absExp(String path) {
+ return new ASTAbs(new ASTObjPath(path));
+ }
+
+ /**
+ * @param exp numeric expression
+ * @return SQRT() call expression
+ */
+ public static Expression sqrtExp(Expression exp) {
+ return new ASTSqrt(exp);
+ }
+
+ /**
+ * @param path object path value
+ * @return SQRT() call expression
+ */
+ public static Expression sqrtExp(String path) {
+ return new ASTSqrt(new ASTObjPath(path));
+ }
+
+ /**
+ * @param exp numeric expression
+ * @param number divisor
+ * @return MOD() call expression
+ */
+ public static Expression modExp(Expression exp, Number number) {
+ return modExp(exp, new ASTScalar(number));
+ }
+
+ /**
+ * @param path object path value
+ * @param number divisor
+ * @return MOD() call expression
+ */
+ public static Expression modExp(String path, Number number) {
+ return modExp(new ASTObjPath(path), new ASTScalar(number));
+ }
+
+ /**
+ * @param exp object path value
+ * @param number numeric expression
+ * @return MOD() call expression
+ */
+ public static Expression modExp(Expression exp, Expression number) {
+ return new ASTMod(exp, number);
+ }
+
+ /**
+ * Factory method for expression to call CONCAT(string1, string2, ...) function
+ * Can be used like:
+ * Expression concat = concatExp(SomeClass.POPERTY_1.getPath(), SomeClass.PROPERTY_2.getPath());
+ *
+ * SQL generation note:
+ * - if DB supports CONCAT function with vararg then it will be used
+ * - if DB supports CONCAT function with two args but also supports concat operator, then operator (eg ||) will be used
+ * - if DB supports only CONCAT function with two args then it will be used what can lead to SQL exception if
+ * used with more than two arguments
+ *
+ * Currently only known DB with limited concatenation functionality is Openbase.
+ *
+ * @param expressions array of expressions
+ * @return CONCAT() call expression
+ */
+ public static Expression concatExp(Expression... expressions) {
+ if(expressions == null || expressions.length == 0) {
+ return new ASTConcat();
+ }
+
+ return new ASTConcat(expressions);
+ }
+
+ /**
+ * Factory method for expression to call CONCAT(string1, string2, ...) function
+ * Can be used like:
+ * Expression concat = concatExp("property1", "property2");
+ *
+ * SQL generation note:
+ * - if DB supports CONCAT function with vararg then it will be used
+ * - if DB supports CONCAT function with two args but also supports concat operator, then operator (eg ||) will be used
+ * - if DB supports only CONCAT function with two args then it will be used what can lead to SQL exception if
+ * used with more than two arguments
+ *
+ * Currently only known DB with limited concatenation functionality is Openbase.
+ *
+ * @param paths array of paths
+ * @return CONCAT() call expression
+ */
+ public static Expression concatExp(String... paths) {
+ if(paths == null || paths.length == 0) {
+ return new ASTConcat();
+ }
+
+ Expression[] expressions = new Expression[paths.length];
+ for(int i=0; i<paths.length; i++) {
+ expressions[i] = new ASTObjPath(paths[i]);
+ }
+ return new ASTConcat(expressions);
+ }
+}