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/19 11:33:13 UTC

[3/3] cayenne git commit: CAY-2196 Support for new function expressions in expression parser - updated and refactored grammar for parser - add token id to AST* function call nodes - escape in parse exception message - tests

CAY-2196 Support for new function expressions in expression parser
 - updated and refactored grammar for parser
 - add token id to AST* function call nodes
 - escape in parse exception message
 - tests


Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/c44ccfde
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/c44ccfde
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/c44ccfde

Branch: refs/heads/master
Commit: c44ccfde3a2e0c85eb3f0853a257cb990568766a
Parents: 666c96d
Author: Nikita Timofeev <st...@gmail.com>
Authored: Thu Jan 19 14:31:41 2017 +0300
Committer: Nikita Timofeev <st...@gmail.com>
Committed: Thu Jan 19 14:31:41 2017 +0300

----------------------------------------------------------------------
 cayenne-server/pom.xml                          |   20 +-
 .../apache/cayenne/exp/ExpressionFactory.java   |   15 +-
 .../org/apache/cayenne/exp/parser/ASTAbs.java   |    2 +-
 .../exp/parser/ASTAggregateFunctionCall.java    |    4 +-
 .../org/apache/cayenne/exp/parser/ASTAvg.java   |    2 +-
 .../apache/cayenne/exp/parser/ASTConcat.java    |    2 +-
 .../org/apache/cayenne/exp/parser/ASTCount.java |    4 +-
 .../cayenne/exp/parser/ASTFunctionCall.java     |    4 +-
 .../apache/cayenne/exp/parser/ASTLength.java    |    2 +-
 .../apache/cayenne/exp/parser/ASTLocate.java    |    4 +-
 .../org/apache/cayenne/exp/parser/ASTLower.java |    2 +-
 .../org/apache/cayenne/exp/parser/ASTMax.java   |    2 +-
 .../org/apache/cayenne/exp/parser/ASTMin.java   |    2 +-
 .../org/apache/cayenne/exp/parser/ASTMod.java   |    2 +-
 .../org/apache/cayenne/exp/parser/ASTSqrt.java  |    2 +-
 .../apache/cayenne/exp/parser/ASTSubstring.java |    2 +-
 .../org/apache/cayenne/exp/parser/ASTSum.java   |    2 +-
 .../org/apache/cayenne/exp/parser/ASTTrim.java  |    2 +-
 .../org/apache/cayenne/exp/parser/ASTUpper.java |    2 +-
 .../cayenne/exp/parser/ExpressionParser.java    |  707 ++++++++--
 .../exp/parser/ExpressionParserConstants.java   |  100 +-
 .../parser/ExpressionParserTokenManager.java    | 1294 +++++++++++-------
 .../parser/ExpressionParserTreeConstants.java   |   47 +-
 .../exp/parser/JJTExpressionParserState.java    |   19 +
 .../cayenne/exp/parser/JavaCharStream.java      | 1099 ++++++++-------
 .../cayenne/exp/parser/ExpressionParser.jjt     |  246 +++-
 .../cayenne/exp/ExpressionFactoryTest.java      |   13 +
 .../exp/parser/ASTFunctionCallMathIT.java       |   27 +
 .../exp/parser/ASTFunctionCallStringIT.java     |   43 +
 29 files changed, 2435 insertions(+), 1237 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/c44ccfde/cayenne-server/pom.xml
----------------------------------------------------------------------
diff --git a/cayenne-server/pom.xml b/cayenne-server/pom.xml
index 913ceca..1e27e2f 100644
--- a/cayenne-server/pom.xml
+++ b/cayenne-server/pom.xml
@@ -199,11 +199,21 @@
 							<goal>jjtree-javacc</goal>
 						</goals>
 					</execution>
-					<!-- src/main/jjtree/org/apache/cayenne/exp/parser for now is generated 
-						outside of Maven with JavaCC command line tools (maybe we should do the same 
-						for EJBQL) : # Parser grammar out of tree grammar: $ ./javacc-5.0/bin/jjtree 
-						src/main/jjtree/org/apache/cayenne/exp/parser/ExpressionParser.jjt # docs 
-						$ ./javacc-5.0/bin/jjdoc -TEXT=true ExpressionParser.jj -->
+					<!--
+						src/main/jjtree/org/apache/cayenne/exp/parser for now is generated
+						outside of Maven with JavaCC command line tools (maybe we should do the same for EJBQL) :
+							$ cd cayenne-server/src/main/jjtree/org/apache/cayenne/exp/parser
+						# Parser grammar out of tree grammar:
+							$ ~/javacc-5.0/bin/jjtree ExpressionParser.jjt
+						# Parser java code:
+							$ ~/javacc-5.0/bin/javacc ExpressionParser.jj
+
+						# Copy generated classes to src/main/jjtree/org/apache/cayenne/exp/parser
+						# except all AST*, Node, SimpleNode, JavaCharStream
+
+						# docs
+							$ ./javacc-5.0/bin/jjdoc -TEXT=true ExpressionParser.jj
+					-->
 				</executions>
 			</plugin>
 			<plugin>

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c44ccfde/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 22dec9f..8d132a9 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
@@ -57,7 +57,6 @@ import org.apache.cayenne.exp.parser.ASTTrue;
 import org.apache.cayenne.exp.parser.ExpressionParser;
 import org.apache.cayenne.exp.parser.ExpressionParserTokenManager;
 import org.apache.cayenne.exp.parser.JavaCharStream;
-import org.apache.cayenne.exp.parser.ParseException;
 import org.apache.cayenne.exp.parser.SimpleNode;
 import org.apache.cayenne.map.Entity;
 
@@ -1310,8 +1309,8 @@ public class ExpressionFactory {
 		// optimizing parser buffers per CAY-1667...
 		// adding 1 extra char to the buffer size above the String length, as
 		// otherwise resizing still occurs at the end of the stream
-		int bufferSize = expressionString.length() > PARSE_BUFFER_MAX_SIZE ? PARSE_BUFFER_MAX_SIZE : expressionString
-				.length() + 1;
+		int bufferSize = expressionString.length() > PARSE_BUFFER_MAX_SIZE ?
+				PARSE_BUFFER_MAX_SIZE : expressionString.length() + 1;
 		Reader reader = new StringReader(expressionString);
 		JavaCharStream stream = new JavaCharStream(reader, 1, 1, bufferSize);
 		ExpressionParserTokenManager tm = new ExpressionParserTokenManager(stream);
@@ -1319,17 +1318,9 @@ public class ExpressionFactory {
 
 		try {
 			return parser.expression();
-		} catch (ParseException ex) {
-
-			// can be null
-			String message = ex.getMessage();
-			throw new ExpressionException(message != null ? message : "", ex);
 		} catch (Throwable th) {
-			// can be null
 			String message = th.getMessage();
-
-			// another common error is TokenManagerError
-			throw new ExpressionException(message != null ? message : "", th);
+			throw new ExpressionException("%s", th, message != null ? message : "");
 		}
 	}
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c44ccfde/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTAbs.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTAbs.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTAbs.java
index 47b06dc..b5220bb 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTAbs.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTAbs.java
@@ -32,7 +32,7 @@ public class ASTAbs extends ASTFunctionCall {
     }
 
     public ASTAbs(Expression expression) {
-        super("ABS", expression);
+        super(ExpressionParserTreeConstants.JJTABS, "ABS", expression);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c44ccfde/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTAggregateFunctionCall.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTAggregateFunctionCall.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTAggregateFunctionCall.java
index d4dec9e..b6e692d 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTAggregateFunctionCall.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTAggregateFunctionCall.java
@@ -30,7 +30,7 @@ public abstract class ASTAggregateFunctionCall extends ASTFunctionCall {
         super(id, functionName);
     }
 
-    ASTAggregateFunctionCall(String functionName, Object... nodes) {
-        super(functionName, nodes);
+    ASTAggregateFunctionCall(int id, String functionName, Object... nodes) {
+        super(id, functionName, nodes);
     }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c44ccfde/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTAvg.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTAvg.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTAvg.java
index 16146cf..eb95216 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTAvg.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTAvg.java
@@ -31,7 +31,7 @@ public class ASTAvg extends ASTAggregateFunctionCall {
     }
 
     public ASTAvg(Expression expression) {
-        super("AVG", expression);
+        super(0, "AVG", expression);
     }
 
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c44ccfde/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTConcat.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTConcat.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTConcat.java
index efb11ad..0e285c4 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTConcat.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTConcat.java
@@ -32,7 +32,7 @@ public class ASTConcat extends ASTFunctionCall {
     }
 
     public ASTConcat(Expression... expressions) {
-        super("CONCAT", expressions);
+        super(ExpressionParserTreeConstants.JJTCONCAT, "CONCAT", expressions);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c44ccfde/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTCount.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTCount.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTCount.java
index 72172f7..ee3a65e 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTCount.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTCount.java
@@ -31,11 +31,11 @@ public class ASTCount extends ASTAggregateFunctionCall {
     }
 
     public ASTCount(Expression expression) {
-        super("COUNT", expression);
+        super(0, "COUNT", expression);
     }
 
     public ASTCount() {
-        super("COUNT", new ASTAsterisk());
+        super(0, "COUNT", new ASTAsterisk());
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c44ccfde/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 9c3aa07..8080a52 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
@@ -33,8 +33,8 @@ public abstract class ASTFunctionCall extends SimpleNode {
         this.functionName = functionName;
     }
 
-    public ASTFunctionCall(String functionName, Object... nodes) {
-        this(0, functionName);
+    public ASTFunctionCall(int id, String functionName, Object... nodes) {
+        this(id, functionName);
         this.functionName = functionName;
         int len = nodes.length;
         for (int i = 0; i < len; i++) {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c44ccfde/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLength.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLength.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLength.java
index 5d1eea8..e12512e 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLength.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLength.java
@@ -32,7 +32,7 @@ public class ASTLength extends ASTFunctionCall {
     }
 
     public ASTLength(Expression expression) {
-        super("LENGTH", expression);
+        super(ExpressionParserTreeConstants.JJTLENGTH, "LENGTH", expression);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c44ccfde/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLocate.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLocate.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLocate.java
index 1c537e5..d26dbb5 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLocate.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLocate.java
@@ -32,11 +32,11 @@ public class ASTLocate extends ASTFunctionCall {
     }
 
     public ASTLocate(Expression substring, Expression path) {
-        super("LOCATE", substring, path);
+        super(ExpressionParserTreeConstants.JJTLOCATE, "LOCATE", substring, path);
     }
 
     public ASTLocate(Expression substring, Expression path, Expression offset) {
-        super("LOCATE", substring, path, offset);
+        super(ExpressionParserTreeConstants.JJTLOCATE, "LOCATE", substring, path, offset);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c44ccfde/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLower.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLower.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLower.java
index b63319a..78b6f80 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLower.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTLower.java
@@ -33,7 +33,7 @@ public class ASTLower extends ASTFunctionCall {
     }
 
     public ASTLower(Expression expression) {
-        super("LOWER", expression);
+        super(ExpressionParserTreeConstants.JJTLOWER, "LOWER", expression);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c44ccfde/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTMax.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTMax.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTMax.java
index b0356eb..2a59d76 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTMax.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTMax.java
@@ -31,7 +31,7 @@ public class ASTMax extends ASTAggregateFunctionCall {
     }
 
     public ASTMax(Expression expression) {
-        super("MAX", expression);
+        super(0, "MAX", expression);
     }
 
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c44ccfde/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTMin.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTMin.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTMin.java
index c55b5df..dc5142f 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTMin.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTMin.java
@@ -31,7 +31,7 @@ public class ASTMin extends ASTAggregateFunctionCall {
     }
 
     public ASTMin(Expression expression) {
-        super("MIN", expression);
+        super(0, "MIN", expression);
     }
 
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c44ccfde/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTMod.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTMod.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTMod.java
index f951cb7..c4f79a7 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTMod.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTMod.java
@@ -32,7 +32,7 @@ public class ASTMod extends ASTFunctionCall {
     }
 
     public ASTMod(Expression expression, Expression divisor) {
-        super("MOD", expression, divisor);
+        super(ExpressionParserTreeConstants.JJTMOD, "MOD", expression, divisor);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c44ccfde/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTSqrt.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTSqrt.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTSqrt.java
index d311444..71e8e32 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTSqrt.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTSqrt.java
@@ -32,7 +32,7 @@ public class ASTSqrt extends ASTFunctionCall {
     }
 
     public ASTSqrt(Expression expression) {
-        super("SQRT", expression);
+        super(ExpressionParserTreeConstants.JJTSQRT, "SQRT", expression);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c44ccfde/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTSubstring.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTSubstring.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTSubstring.java
index c6ae864..33ac4e0 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTSubstring.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTSubstring.java
@@ -33,7 +33,7 @@ public class ASTSubstring extends ASTFunctionCall {
     }
 
     public ASTSubstring(Expression path, Expression length, Expression offset) {
-        super("SUBSTRING", path, length, offset);
+        super(ExpressionParserTreeConstants.JJTSUBSTRING, "SUBSTRING", path, length, offset);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c44ccfde/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTSum.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTSum.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTSum.java
index bcc5e12..73e0b8c 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTSum.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTSum.java
@@ -31,7 +31,7 @@ public class ASTSum extends ASTAggregateFunctionCall {
     }
 
     public ASTSum(Expression expression) {
-        super("SUM", expression);
+        super(0, "SUM", expression);
     }
 
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c44ccfde/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTTrim.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTTrim.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTTrim.java
index 2a0077a..f037dd0 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTTrim.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTTrim.java
@@ -32,7 +32,7 @@ public class ASTTrim extends ASTFunctionCall {
     }
 
     public ASTTrim(Expression path) {
-        super("TRIM", path);
+        super(ExpressionParserTreeConstants.JJTTRIM, "TRIM", path);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c44ccfde/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTUpper.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTUpper.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTUpper.java
index fad6026..60716fc 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTUpper.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTUpper.java
@@ -32,7 +32,7 @@ public class ASTUpper extends ASTFunctionCall {
     }
 
     public ASTUpper(Expression expression) {
-        super("UPPER", expression);
+        super(ExpressionParserTreeConstants.JJTUPPER, "UPPER", expression);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c44ccfde/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 77fea61..20a6f42 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
@@ -163,13 +163,23 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
     case 25:
     case 26:
     case 29:
-    case 30:
-    case 31:
-    case 32:
-    case 33:
     case NULL:
     case TRUE:
     case FALSE:
+    case CONCAT:
+    case SUBSTRING:
+    case TRIM:
+    case LOWER:
+    case UPPER:
+    case LENGTH:
+    case LOCATE:
+    case ABS:
+    case SQRT:
+    case MOD:
+    case 55:
+    case 56:
+    case 57:
+    case 58:
     case PROPERTY_PATH:
     case SINGLE_QUOTED_STRING:
     case DOUBLE_QUOTED_STRING:
@@ -214,17 +224,27 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
     case 25:
     case 26:
     case 29:
-    case 30:
-    case 31:
-    case 32:
-    case 33:
     case NULL:
+    case CONCAT:
+    case SUBSTRING:
+    case TRIM:
+    case LOWER:
+    case UPPER:
+    case LENGTH:
+    case LOCATE:
+    case ABS:
+    case SQRT:
+    case MOD:
+    case 55:
+    case 56:
+    case 57:
+    case 58:
     case PROPERTY_PATH:
     case SINGLE_QUOTED_STRING:
     case DOUBLE_QUOTED_STRING:
     case INT_LITERAL:
     case FLOAT_LITERAL:
-      scalarConditionExpression();
+      conditionExpression();
       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
       case 3:
       case 4:
@@ -241,10 +261,6 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
       case 15:
       case 18:
         switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
-        case 3:
-        case 4:
-          simpleNotCondition();
-          break;
         case 5:
         case 6:
           switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
@@ -492,7 +508,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
                   jjtree.openNodeScope(jjtn011);
           try {
             switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
-            case 30:
+            case 55:
               namedParameter();
               break;
             case 16:
@@ -554,6 +570,10 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
                                                       }
           }
           break;
+        case 3:
+        case 4:
+          simpleNotCondition();
+          break;
         default:
           jj_la1[7] = jj_gen;
           jj_consume_token(-1);
@@ -647,7 +667,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
                   jjtree.openNodeScope(jjtn003);
       try {
         switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
-        case 30:
+        case 55:
           namedParameter();
           break;
         case 16:
@@ -756,21 +776,80 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
     }
   }
 
-  final public void scalarConditionExpression() throws ParseException {
+  final public void conditionExpression() throws ParseException {
     switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
     case 16:
     case 25:
     case 26:
     case 29:
-    case 30:
-    case 31:
-    case 32:
-    case 33:
+    case LENGTH:
+    case LOCATE:
+    case ABS:
+    case SQRT:
+    case MOD:
+    case 55:
+    case 56:
+    case 57:
+    case 58:
     case PROPERTY_PATH:
     case INT_LITERAL:
     case FLOAT_LITERAL:
-      scalarNumericExpression();
+      numericExpression();
+      break;
+    case CONCAT:
+    case SUBSTRING:
+    case TRIM:
+    case LOWER:
+    case UPPER:
+    case SINGLE_QUOTED_STRING:
+    case DOUBLE_QUOTED_STRING:
+      stringExpression();
+      break;
+    case NULL:
+                  ASTScalar jjtn001 = new ASTScalar(JJTSCALAR);
+                  boolean jjtc001 = true;
+                  jjtree.openNodeScope(jjtn001);
+        try {
+          jj_consume_token(NULL);
+        } finally {
+                  if (jjtc001) {
+                    jjtree.closeNodeScope(jjtn001,  0);
+                  }
+      }
       break;
+    default:
+      jj_la1[14] = jj_gen;
+      jj_consume_token(-1);
+      throw new ParseException();
+    }
+  }
+
+  final public void stringParameter() throws ParseException {
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case 56:
+    case 57:
+    case 58:
+    case PROPERTY_PATH:
+      pathExpression();
+      break;
+    case CONCAT:
+    case SUBSTRING:
+    case TRIM:
+    case LOWER:
+    case UPPER:
+    case SINGLE_QUOTED_STRING:
+    case DOUBLE_QUOTED_STRING:
+      stringExpression();
+      break;
+    default:
+      jj_la1[15] = jj_gen;
+      jj_consume_token(-1);
+      throw new ParseException();
+    }
+  }
+
+  final public void stringExpression() throws ParseException {
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
     case SINGLE_QUOTED_STRING:
       jj_consume_token(SINGLE_QUOTED_STRING);
                                          ASTScalar jjtn001 = new ASTScalar(JJTSCALAR);
@@ -801,20 +880,15 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
                                          }
       }
       break;
-    case NULL:
-                  ASTScalar jjtn003 = new ASTScalar(JJTSCALAR);
-                  boolean jjtc003 = true;
-                  jjtree.openNodeScope(jjtn003);
-      try {
-        jj_consume_token(NULL);
-      } finally {
-                  if (jjtc003) {
-                    jjtree.closeNodeScope(jjtn003,  0);
-                  }
-      }
+    case CONCAT:
+    case SUBSTRING:
+    case TRIM:
+    case LOWER:
+    case UPPER:
+      functionsReturningStrings();
       break;
     default:
-      jj_la1[14] = jj_gen;
+      jj_la1[16] = jj_gen;
       jj_consume_token(-1);
       throw new ParseException();
     }
@@ -826,17 +900,27 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
     case 25:
     case 26:
     case 29:
-    case 30:
-    case 31:
-    case 32:
-    case 33:
     case NULL:
+    case CONCAT:
+    case SUBSTRING:
+    case TRIM:
+    case LOWER:
+    case UPPER:
+    case LENGTH:
+    case LOCATE:
+    case ABS:
+    case SQRT:
+    case MOD:
+    case 55:
+    case 56:
+    case 57:
+    case 58:
     case PROPERTY_PATH:
     case SINGLE_QUOTED_STRING:
     case DOUBLE_QUOTED_STRING:
     case INT_LITERAL:
     case FLOAT_LITERAL:
-      scalarConditionExpression();
+      conditionExpression();
       break;
     case TRUE:
       jj_consume_token(TRUE);
@@ -869,7 +953,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
       }
       break;
     default:
-      jj_la1[15] = jj_gen;
+      jj_la1[17] = jj_gen;
       jj_consume_token(-1);
       throw new ParseException();
     }
@@ -907,7 +991,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
                                    }
       }
       break;
-    case 30:
+    case 55:
       namedParameter();
       break;
     case INT_LITERAL:
@@ -971,13 +1055,13 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
       }
       break;
     default:
-      jj_la1[16] = jj_gen;
+      jj_la1[18] = jj_gen;
       jj_consume_token(-1);
       throw new ParseException();
     }
   }
 
-  final public void scalarNumericExpression() throws ParseException {
+  final public void numericExpression() throws ParseException {
     bitwiseOr();
   }
 
@@ -990,7 +1074,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
         ;
         break;
       default:
-        jj_la1[17] = jj_gen;
+        jj_la1[19] = jj_gen;
         break label_4;
       }
       jj_consume_token(20);
@@ -1030,7 +1114,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
         ;
         break;
       default:
-        jj_la1[18] = jj_gen;
+        jj_la1[20] = jj_gen;
         break label_5;
       }
       jj_consume_token(21);
@@ -1070,7 +1154,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
         ;
         break;
       default:
-        jj_la1[19] = jj_gen;
+        jj_la1[21] = jj_gen;
         break label_6;
       }
       jj_consume_token(22);
@@ -1111,7 +1195,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
         ;
         break;
       default:
-        jj_la1[20] = jj_gen;
+        jj_la1[22] = jj_gen;
         break label_7;
       }
       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
@@ -1170,7 +1254,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
         }
         break;
       default:
-        jj_la1[21] = jj_gen;
+        jj_la1[23] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
@@ -1187,7 +1271,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
         ;
         break;
       default:
-        jj_la1[22] = jj_gen;
+        jj_la1[24] = jj_gen;
         break label_8;
       }
       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
@@ -1246,7 +1330,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
         }
         break;
       default:
-        jj_la1[23] = jj_gen;
+        jj_la1[25] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
@@ -1263,7 +1347,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
         ;
         break;
       default:
-        jj_la1[24] = jj_gen;
+        jj_la1[26] = jj_gen;
         break label_9;
       }
       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
@@ -1322,7 +1406,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
         }
         break;
       default:
-        jj_la1[25] = jj_gen;
+        jj_la1[27] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
@@ -1334,10 +1418,15 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
     case 16:
     case 25:
     case 26:
-    case 30:
-    case 31:
-    case 32:
-    case 33:
+    case LENGTH:
+    case LOCATE:
+    case ABS:
+    case SQRT:
+    case MOD:
+    case 55:
+    case 56:
+    case 57:
+    case 58:
     case PROPERTY_PATH:
     case INT_LITERAL:
     case FLOAT_LITERAL:
@@ -1371,7 +1460,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
       }
       break;
     default:
-      jj_la1[26] = jj_gen;
+      jj_la1[28] = jj_gen;
       jj_consume_token(-1);
       throw new ParseException();
     }
@@ -1381,10 +1470,15 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
     switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
     case 16:
     case 25:
-    case 30:
-    case 31:
-    case 32:
-    case 33:
+    case LENGTH:
+    case LOCATE:
+    case ABS:
+    case SQRT:
+    case MOD:
+    case 55:
+    case 56:
+    case 57:
+    case 58:
     case PROPERTY_PATH:
     case INT_LITERAL:
     case FLOAT_LITERAL:
@@ -1393,7 +1487,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
         jj_consume_token(25);
         break;
       default:
-        jj_la1[27] = jj_gen;
+        jj_la1[29] = jj_gen;
         ;
       }
       numericPrimary();
@@ -1426,7 +1520,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
       }
       break;
     default:
-      jj_la1[28] = jj_gen;
+      jj_la1[30] = jj_gen;
       jj_consume_token(-1);
       throw new ParseException();
     }
@@ -1439,15 +1533,6 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
       orCondition();
       jj_consume_token(17);
       break;
-    case 31:
-    case 32:
-    case 33:
-    case PROPERTY_PATH:
-      pathExpression();
-      break;
-    case 30:
-      namedParameter();
-      break;
     case INT_LITERAL:
       jj_consume_token(INT_LITERAL);
                            ASTScalar jjtn001 = new ASTScalar(JJTSCALAR);
@@ -1478,16 +1563,416 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
                             }
       }
       break;
+    case 55:
+      namedParameter();
+      break;
+    case LENGTH:
+    case LOCATE:
+    case ABS:
+    case SQRT:
+    case MOD:
+      functionsReturningNumerics();
+      break;
+    case 56:
+    case 57:
+    case 58:
+    case PROPERTY_PATH:
+      pathExpression();
+      break;
+    default:
+      jj_la1[31] = jj_gen;
+      jj_consume_token(-1);
+      throw new ParseException();
+    }
+  }
+
+  final public void functionsReturningStrings() throws ParseException {
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case CONCAT:
+      concat();
+      break;
+    case SUBSTRING:
+      substring();
+      break;
+    case TRIM:
+      trim();
+      break;
+    case LOWER:
+      lower();
+      break;
+    case UPPER:
+      upper();
+      break;
     default:
-      jj_la1[29] = jj_gen;
+      jj_la1[32] = jj_gen;
       jj_consume_token(-1);
       throw new ParseException();
     }
   }
 
+  final public void concat() throws ParseException {
+                         /*@bgen(jjtree) Concat */
+  ASTConcat jjtn000 = new ASTConcat(JJTCONCAT);
+  boolean jjtc000 = true;
+  jjtree.openNodeScope(jjtn000);
+    try {
+      jj_consume_token(CONCAT);
+      jj_consume_token(16);
+      stringParameter();
+      jj_consume_token(19);
+      stringParameter();
+      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, true);
+          }
+    }
+  }
+
+  final public void substring() throws ParseException {
+                               /*@bgen(jjtree) Substring */
+  ASTSubstring jjtn000 = new ASTSubstring(JJTSUBSTRING);
+  boolean jjtc000 = true;
+  jjtree.openNodeScope(jjtn000);
+    try {
+      jj_consume_token(SUBSTRING);
+      jj_consume_token(16);
+      stringParameter();
+      jj_consume_token(19);
+      numericExpression();
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case 19:
+        jj_consume_token(19);
+        numericExpression();
+        break;
+      default:
+        jj_la1[33] = jj_gen;
+        ;
+      }
+      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, true);
+          }
+    }
+  }
+
+  final public void trim() throws ParseException {
+                     /*@bgen(jjtree) Trim */
+  ASTTrim jjtn000 = new ASTTrim(JJTTRIM);
+  boolean jjtc000 = true;
+  jjtree.openNodeScope(jjtn000);
+    try {
+      jj_consume_token(TRIM);
+      jj_consume_token(16);
+      stringParameter();
+      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, true);
+          }
+    }
+  }
+
+  final public void lower() throws ParseException {
+                       /*@bgen(jjtree) Lower */
+  ASTLower jjtn000 = new ASTLower(JJTLOWER);
+  boolean jjtc000 = true;
+  jjtree.openNodeScope(jjtn000);
+    try {
+      jj_consume_token(LOWER);
+      jj_consume_token(16);
+      stringParameter();
+      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, true);
+          }
+    }
+  }
+
+  final public void upper() throws ParseException {
+                       /*@bgen(jjtree) Upper */
+  ASTUpper jjtn000 = new ASTUpper(JJTUPPER);
+  boolean jjtc000 = true;
+  jjtree.openNodeScope(jjtn000);
+    try {
+      jj_consume_token(UPPER);
+      jj_consume_token(16);
+      stringParameter();
+      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, true);
+          }
+    }
+  }
+
+  final public void functionsReturningNumerics() throws ParseException {
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case LENGTH:
+      length();
+      break;
+    case LOCATE:
+      locate();
+      break;
+    case ABS:
+      abs();
+      break;
+    case SQRT:
+      sqrt();
+      break;
+    case MOD:
+      mod();
+      break;
+    default:
+      jj_la1[34] = jj_gen;
+      jj_consume_token(-1);
+      throw new ParseException();
+    }
+  }
+
+  final public void length() throws ParseException {
+                         /*@bgen(jjtree) Length */
+  ASTLength jjtn000 = new ASTLength(JJTLENGTH);
+  boolean jjtc000 = true;
+  jjtree.openNodeScope(jjtn000);
+    try {
+      jj_consume_token(LENGTH);
+      jj_consume_token(16);
+      stringParameter();
+      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, true);
+          }
+    }
+  }
+
+  final public void locate() throws ParseException {
+                         /*@bgen(jjtree) Locate */
+  ASTLocate jjtn000 = new ASTLocate(JJTLOCATE);
+  boolean jjtc000 = true;
+  jjtree.openNodeScope(jjtn000);
+    try {
+      jj_consume_token(LOCATE);
+      jj_consume_token(16);
+      stringParameter();
+      jj_consume_token(19);
+      stringParameter();
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case 19:
+        jj_consume_token(19);
+        numericExpression();
+        break;
+      default:
+        jj_la1[35] = jj_gen;
+        ;
+      }
+      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, true);
+          }
+    }
+  }
+
+  final public void abs() throws ParseException {
+                   /*@bgen(jjtree) Abs */
+  ASTAbs jjtn000 = new ASTAbs(JJTABS);
+  boolean jjtc000 = true;
+  jjtree.openNodeScope(jjtn000);
+    try {
+      jj_consume_token(ABS);
+      jj_consume_token(16);
+      numericExpression();
+      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, true);
+          }
+    }
+  }
+
+  final public void sqrt() throws ParseException {
+                     /*@bgen(jjtree) Sqrt */
+  ASTSqrt jjtn000 = new ASTSqrt(JJTSQRT);
+  boolean jjtc000 = true;
+  jjtree.openNodeScope(jjtn000);
+    try {
+      jj_consume_token(SQRT);
+      jj_consume_token(16);
+      numericExpression();
+      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, true);
+          }
+    }
+  }
+
+  final public void mod() throws ParseException {
+                   /*@bgen(jjtree) Mod */
+  ASTMod jjtn000 = new ASTMod(JJTMOD);
+  boolean jjtc000 = true;
+  jjtree.openNodeScope(jjtn000);
+    try {
+      jj_consume_token(MOD);
+      jj_consume_token(16);
+      numericExpression();
+      jj_consume_token(19);
+      numericExpression();
+      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, true);
+          }
+    }
+  }
+
   final public void namedParameter() throws ParseException {
         Token t;
-    jj_consume_token(30);
+    jj_consume_token(55);
     t = jj_consume_token(PROPERTY_PATH);
                                   ASTNamedParameter jjtn001 = new ASTNamedParameter(JJTNAMEDPARAMETER);
                                   boolean jjtc001 = true;
@@ -1521,8 +2006,8 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
                            }
       }
       break;
-    case 31:
-      jj_consume_token(31);
+    case 56:
+      jj_consume_token(56);
       t = jj_consume_token(PROPERTY_PATH);
                            ASTObjPath jjtn002 = new ASTObjPath(JJTOBJPATH);
                            boolean jjtc002 = true;
@@ -1537,8 +2022,8 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
                            }
       }
       break;
-    case 32:
-      jj_consume_token(32);
+    case 57:
+      jj_consume_token(57);
       t = jj_consume_token(PROPERTY_PATH);
                            ASTDbPath jjtn003 = new ASTDbPath(JJTDBPATH);
                            boolean jjtc003 = true;
@@ -1553,8 +2038,8 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
                            }
       }
       break;
-    case 33:
-      jj_consume_token(33);
+    case 58:
+      jj_consume_token(58);
       t = jj_consume_token(PROPERTY_PATH);
                            ASTScalar jjtn004 = new ASTScalar(JJTSCALAR);
                            boolean jjtc004 = true;
@@ -1570,7 +2055,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
       }
       break;
     default:
-      jj_la1[30] = jj_gen;
+      jj_la1[36] = jj_gen;
       jj_consume_token(-1);
       throw new ParseException();
     }
@@ -1585,18 +2070,23 @@ 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[31];
+  final private int[] jj_la1 = new int[37];
   static private int[] jj_la1_0;
   static private int[] jj_la1_1;
+  static private int[] jj_la1_2;
   static {
       jj_la1_init_0();
       jj_la1_init_1();
+      jj_la1_init_2();
    }
    private static void jj_la1_init_0() {
-      jj_la1_0 = new int[] {0x2,0x4,0x18,0xe6010018,0x60,0x180,0x40010000,0x4fff8,0x4fff8,0xe6010000,0x18,0x40010000,0x4e000,0x80000,0xe6010000,0xe6010000,0x40000000,0x100000,0x200000,0x400000,0x1800000,0x1800000,0x6000000,0x6000000,0x18000000,0x18000000,0xe6010000,0x2000000,0xc6010000,0xc0010000,0x80000000,};
+      jj_la1_0 = new int[] {0x2,0x4,0x18,0x26010018,0x60,0x180,0x10000,0x4fff8,0x4fff8,0x26010000,0x18,0x10000,0x4e000,0x80000,0x26010000,0x0,0x0,0x26010000,0x0,0x100000,0x200000,0x400000,0x1800000,0x1800000,0x6000000,0x6000000,0x18000000,0x18000000,0x26010000,0x2000000,0x6010000,0x10000,0x0,0x80000,0x0,0x80000,0x0,};
    }
    private static void jj_la1_init_1() {
-      jj_la1_1 = new int[] {0x0,0x0,0x0,0x7203c3,0x0,0x0,0x0,0x0,0x0,0x7203c3,0x0,0x0,0x0,0x0,0x720243,0x7203c3,0x720180,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600203,0x0,0x600203,0x600203,0x203,};
+      jj_la1_1 = new int[] {0x0,0x0,0x0,0xf8ffc1c,0x0,0x0,0x800000,0x0,0x0,0xf8ffc1c,0x0,0x800000,0x0,0x0,0xf8ffc04,0xf007c00,0x7c00,0xf8ffc1c,0x800018,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8f8000,0x0,0xf8f8000,0xf8f8000,0x7c00,0x0,0xf8000,0x0,0xf000000,};
+   }
+   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,};
    }
 
   /** Constructor with InputStream. */
@@ -1610,7 +2100,22 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 31; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 37; i++) jj_la1[i] = -1;
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream stream) {
+     ReInit(stream, null);
+  }
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream stream, String encoding) {
+    try { jj_input_stream.ReInit(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
+    token_source.ReInit(jj_input_stream);
+    token = new Token();
+    jj_ntk = -1;
+    jjtree.reset();
+    jj_gen = 0;
+    for (int i = 0; i < 37; i++) jj_la1[i] = -1;
   }
 
   /** Constructor. */
@@ -1620,7 +2125,18 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 31; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 37; i++) jj_la1[i] = -1;
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.Reader stream) {
+    jj_input_stream.ReInit(stream, 1, 1);
+    token_source.ReInit(jj_input_stream);
+    token = new Token();
+    jj_ntk = -1;
+    jjtree.reset();
+    jj_gen = 0;
+    for (int i = 0; i < 37; i++) jj_la1[i] = -1;
   }
 
   /** Constructor with generated Token Manager. */
@@ -1629,7 +2145,17 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 31; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 37; i++) jj_la1[i] = -1;
+  }
+
+  /** Reinitialise. */
+  public void ReInit(ExpressionParserTokenManager tm) {
+    token_source = tm;
+    token = new Token();
+    jj_ntk = -1;
+    jjtree.reset();
+    jj_gen = 0;
+    for (int i = 0; i < 37; i++) jj_la1[i] = -1;
   }
 
   private Token jj_consume_token(int kind) throws ParseException {
@@ -1680,12 +2206,12 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
   /** Generate ParseException. */
   public ParseException generateParseException() {
     jj_expentries.clear();
-    boolean[] la1tokens = new boolean[59];
+    boolean[] la1tokens = new boolean[77];
     if (jj_kind >= 0) {
       la1tokens[jj_kind] = true;
       jj_kind = -1;
     }
-    for (int i = 0; i < 31; i++) {
+    for (int i = 0; i < 37; i++) {
       if (jj_la1[i] == jj_gen) {
         for (int j = 0; j < 32; j++) {
           if ((jj_la1_0[i] & (1<<j)) != 0) {
@@ -1694,10 +2220,13 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
           if ((jj_la1_1[i] & (1<<j)) != 0) {
             la1tokens[32+j] = true;
           }
+          if ((jj_la1_2[i] & (1<<j)) != 0) {
+            la1tokens[64+j] = true;
+          }
         }
       }
     }
-    for (int i = 0; i < 59; i++) {
+    for (int i = 0; i < 77; i++) {
       if (la1tokens[i]) {
         jj_expentry = new int[1];
         jj_expentry[0] = i;

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c44ccfde/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 d8bf198..7d50af0 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
@@ -31,39 +31,75 @@ public interface ExpressionParserConstants {
   /** End of File. */
   int EOF = 0;
   /** RegularExpression Id. */
-  int NULL = 38;
+  int NULL = 34;
   /** RegularExpression Id. */
-  int TRUE = 39;
+  int TRUE = 35;
   /** RegularExpression Id. */
-  int FALSE = 40;
+  int FALSE = 36;
   /** RegularExpression Id. */
-  int PROPERTY_PATH = 41;
+  int AVG = 37;
   /** RegularExpression Id. */
-  int IDENTIFIER = 42;
+  int MIN = 38;
   /** RegularExpression Id. */
-  int LETTER = 43;
+  int MAX = 39;
   /** RegularExpression Id. */
-  int DIGIT = 44;
+  int SUM = 40;
   /** RegularExpression Id. */
-  int ESC = 47;
+  int COUNT = 41;
   /** RegularExpression Id. */
-  int SINGLE_QUOTED_STRING = 49;
+  int CONCAT = 42;
   /** RegularExpression Id. */
-  int STRING_ESC = 50;
+  int SUBSTRING = 43;
   /** RegularExpression Id. */
-  int DOUBLE_QUOTED_STRING = 52;
+  int TRIM = 44;
   /** RegularExpression Id. */
-  int INT_LITERAL = 53;
+  int LOWER = 45;
   /** RegularExpression Id. */
-  int FLOAT_LITERAL = 54;
+  int UPPER = 46;
   /** RegularExpression Id. */
-  int DEC_FLT = 55;
+  int LENGTH = 47;
   /** RegularExpression Id. */
-  int DEC_DIGITS = 56;
+  int LOCATE = 48;
   /** RegularExpression Id. */
-  int EXPONENT = 57;
+  int ABS = 49;
   /** RegularExpression Id. */
-  int FLT_SUFF = 58;
+  int SQRT = 50;
+  /** RegularExpression Id. */
+  int MOD = 51;
+  /** RegularExpression Id. */
+  int CURRENT_DATE = 52;
+  /** RegularExpression Id. */
+  int CURRENT_TIME = 53;
+  /** RegularExpression Id. */
+  int CURRENT_TIMESTAMP = 54;
+  /** RegularExpression Id. */
+  int PROPERTY_PATH = 59;
+  /** RegularExpression Id. */
+  int IDENTIFIER = 60;
+  /** RegularExpression Id. */
+  int LETTER = 61;
+  /** RegularExpression Id. */
+  int DIGIT = 62;
+  /** RegularExpression Id. */
+  int ESC = 65;
+  /** RegularExpression Id. */
+  int SINGLE_QUOTED_STRING = 67;
+  /** RegularExpression Id. */
+  int STRING_ESC = 68;
+  /** RegularExpression Id. */
+  int DOUBLE_QUOTED_STRING = 70;
+  /** RegularExpression Id. */
+  int INT_LITERAL = 71;
+  /** RegularExpression Id. */
+  int FLOAT_LITERAL = 72;
+  /** RegularExpression Id. */
+  int DEC_FLT = 73;
+  /** RegularExpression Id. */
+  int DEC_DIGITS = 74;
+  /** RegularExpression Id. */
+  int EXPONENT = 75;
+  /** RegularExpression Id. */
+  int FLT_SUFF = 76;
 
   /** Lexical state. */
   int DEFAULT = 0;
@@ -104,10 +140,6 @@ public interface ExpressionParserConstants {
     "\"*\"",
     "\"/\"",
     "\"~\"",
-    "\"$\"",
-    "\"obj:\"",
-    "\"db:\"",
-    "\"enum:\"",
     "\" \"",
     "\"\\t\"",
     "\"\\n\"",
@@ -115,6 +147,28 @@ public interface ExpressionParserConstants {
     "<NULL>",
     "<TRUE>",
     "<FALSE>",
+    "\"AVG\"",
+    "\"MIN\"",
+    "\"MAX\"",
+    "\"SUM\"",
+    "\"COUNT\"",
+    "\"CONCAT\"",
+    "\"SUBSTRING\"",
+    "\"TRIM\"",
+    "\"LOWER\"",
+    "\"UPPER\"",
+    "\"LENGTH\"",
+    "\"LOCATE\"",
+    "\"ABS\"",
+    "\"SQRT\"",
+    "\"MOD\"",
+    "\"CURRENT_DATE\"",
+    "\"CURRENT_TIME\"",
+    "\"CURRENT_TIMESTAMP\"",
+    "\"$\"",
+    "\"obj:\"",
+    "\"db:\"",
+    "\"enum:\"",
     "<PROPERTY_PATH>",
     "<IDENTIFIER>",
     "<LETTER>",
@@ -122,10 +176,10 @@ public interface ExpressionParserConstants {
     "\"\\\'\"",
     "\"\\\"\"",
     "<ESC>",
-    "<token of kind 48>",
+    "<token of kind 66>",
     "\"\\\'\"",
     "<STRING_ESC>",
-    "<token of kind 51>",
+    "<token of kind 69>",
     "\"\\\"\"",
     "<INT_LITERAL>",
     "<FLOAT_LITERAL>",