Compare commits

...
Author SHA1 Message Date
rob bygrave ae68d41519 [maven-release-plugin] prepare release ebean-11.43.2 2019-08-20 22:01:04 +12:00
rob bygrave 722692d934 reduce logging for testing 2019-08-20 10:29:31 +12:00
rob bygrave 21a3f322e1 #1798 - Fix automatic query label set on nested secondary query when using profileLocation 2019-08-19 23:15:27 +12:00
rob bygrave 0d175557b0 No effective change - update test clearing data 2019-08-19 21:50:17 +12:00
rob bygrave 57ccd19fe6 #1797 - Bump ebean-migration to 11.20.1 ... which inserts a <init> row into db_migration table 2019-08-19 21:37:01 +12:00
rob bygrave 9674711516 #1796 - ENH: add query.fetchCache(path) ... for explicitly building part of graph from L2 cache 2019-08-19 21:36:19 +12:00
rob bygrave 9155716752 #1795 - Refactor bind padding for Id IN expressions (based on internal multiValueSupport and simple id) 2019-08-18 16:33:36 +12:00
rob bygrave d7d8c3e651 Refactor tidy on Load context 2019-08-18 12:17:36 +12:00
rob bygrave 16f6f2ee7f [maven-release-plugin] prepare for next development iteration 2019-08-16 00:00:42 +12:00
rob bygrave fea6dfd62b [maven-release-plugin] prepare release ebean-11.43.1 2019-08-16 00:00:31 +12:00
rob bygrave 464ed4322e #1794 - Also support "inRange" camelcase version of "inrange" in EQL 2019-08-15 23:58:58 +12:00
rob bygrave c3f556f0dc #1794 - Extend EQL to support inOrEmpty, eqOrNull, gtOrNull, ltOrNull 2019-08-15 23:40:19 +12:00
rob bygrave ba6810891e Bump to 11.43.1-SNAPSHOT 2019-08-15 22:20:09 +12:00
rob bygrave c530883e5e #1793 - ENH: Add filterMany() that takes expressions ... as a simpler option 2019-08-15 22:05:46 +12:00
rob bygrave 06542b031e Initial refactor to extract EqlWhereListener 2019-08-15 20:21:01 +12:00
rob bygrave 4b5365e708 #1792 - ENH: Support using Query beans for building FetchGroup (in a type safe manor) 2019-08-14 22:52:18 +12:00
Rob BygraveandGitHub 1d471fee92 #1768 - Initial refactor extracting CallOrigin interface (#1791) 2019-08-14 17:02:57 +12:00
rob bygrave 3825dea00a [maven-release-plugin] prepare for next development iteration 2019-08-12 21:12:45 +12:00
rob bygrave 3fd130ac4c [maven-release-plugin] prepare release ebean-11.42.3 2019-08-12 21:12:34 +12:00
rob bygrave 196645e7a2 #1789 - Add default constructor to EntityBeanIntercept for serialisation framework use only 2019-08-12 21:09:31 +12:00
rob bygrave f5459920e9 #1785 - Bean gets replaced on json deserialization with primitive 'null' id 2019-08-12 21:05:32 +12:00
rob bygrave ee6452cfb0 #1788 - ENH: Add support for NuoDB database platform 2019-08-12 20:41:45 +12:00
rob bygrave ad7e18561b #1788 - ENH: Add support for NuoDB database platform
Remove the extra truncate
2019-08-11 16:43:24 +12:00
rob bygrave c640524bfc #1788 - ENH: Add support for NuoDB database platform 2019-08-11 16:24:37 +12:00
rob bygrave fea840c859 #1782 - ENH: Add truncate(Class<?>... types) and truncate(String... tables)
Remove truncateTableBefore and truncateTableAfter for now
2019-08-10 21:44:48 +12:00
rob bygrave bb7df71391 #1782 - ENH: Add truncate(Class<?>... types) and truncate(String... tables) 2019-08-10 21:40:23 +12:00
rob bygrave cb6f0335b1 #1787 - Update CockroachDB support for DDL generation with autocommit and no history support 2019-08-08 23:22:58 +12:00
rob bygrave 762899ef10 [maven-release-plugin] prepare for next development iteration 2019-08-07 22:43:12 +12:00
rob bygrave 848d4f031e [maven-release-plugin] prepare release ebean-11.42.2 2019-08-07 22:42:59 +12:00
rob bygrave 481f828507 Bump provided scope jackson-databind version to 2.9.9.2 for security warning
This should have no effect as the dependency is scope provided.
2019-08-07 22:40:57 +12:00
rob bygrave b3d0c21a80 #1783 - Save bean with only *ToMany relations with user generated ID 2019-08-07 22:28:51 +12:00
rob bygrave a115cdf095 #1784 - Tests related to NPE with IDEA plugin and mockito 3.0.0 2019-08-07 21:38:52 +12:00
rob bygrave 410e9d475b [maven-release-plugin] prepare for next development iteration 2019-07-31 23:27:53 +12:00
rob bygrave 462385c7d0 [maven-release-plugin] prepare release ebean-11.42.1 2019-07-31 23:27:42 +12:00
rob bygrave 3655e2c6f8 #1778 - Bump jackson-core.version from 2.9.8 to 2.9.9 2019-07-31 23:25:30 +12:00
rob bygrave 318f6ae3f5 Bump to 11.42.1-SNAPSHOT 2019-07-31 23:21:01 +12:00
rob bygrave c63acad7e0 No effective change - updated review ddl 2019-07-29 23:45:20 +12:00
rob bygrave 6dba4a3d28 #1763 - Change such that db-create-all.sql and db-drop-all.sql generated files go into target (maven) or build (gradle) directories 2019-07-29 23:26:45 +12:00
rob bygrave ecf1e53616 [maven-release-plugin] prepare for next development iteration 2019-07-29 22:49:48 +12:00
rob bygrave 0ff9a00427 [maven-release-plugin] prepare release ebean-11.41.4 2019-07-29 22:49:36 +12:00
rob bygrave 725ff8dc58 git push origin masterMerge branch 'FOCONIS-bug-refresh-one-to-many' 2019-07-29 22:46:55 +12:00
rob bygrave a780dc133e #1776 Fix for - refresh does not work on oneToMany if property was not yet accessed 2019-07-29 22:46:39 +12:00
Roland Praml b111fcfecc refresh does not work on oneToMany if property was not yet accessed
(cherry picked from commit 446103e6e06d7583f8b2bb847907d7fc77a8be9f)
2019-07-26 12:14:30 +02:00
rob bygrave 114693b7cd #1774 - Change built in SQL Server I___create_procs DDL script to use GO (and not $$ delimiters)
Bump ebean-migration with handling for stored procedures delimited with GO and $$ etc
2019-07-25 21:46:35 +12:00
rob bygrave 5a39dcb39c #1775 - MySql 8 by default wants JDBC Date set and read with JVM Calendar (timezone) 2019-07-25 21:25:38 +12:00
rob bygrave b04f887bc3 Test only update generated resource - no effective change 2019-07-25 21:19:08 +12:00
rob bygrave 5c38561049 #1774 - Change built in SQL Server I___create_procs DDL script to use GO (and not $$ delimiters) 2019-07-24 23:36:36 +12:00
rob bygrave f40eb011c3 #1773 - Wrong link to timerbee.de in README.md 2019-07-24 21:57:47 +12:00
rob bygrave a08cf548e7 #1770 - Support use of Savepoints with MySql and MariaDB (that barf on savepoint.getSavepointId()) 2019-07-21 22:57:56 +12:00
rob bygrave f12f0d7f7a [maven-release-plugin] prepare for next development iteration 2019-07-21 16:50:02 +12:00
rob bygrave 0c53efcbbc [maven-release-plugin] prepare release ebean-11.41.3 2019-07-21 16:49:51 +12:00
rob bygrave 1aeed8ad12 Merge branch 'master' of github.com:ebean-orm/ebean 2019-07-19 10:44:07 +12:00
rob bygrave bcb136b692 #1765 - Wrong SQL generated on m2m and OrderBy (Regression of #1746) 2019-07-19 10:43:07 +12:00
rob bygrave 8b670fd615 Fix tests for sql server which doesn't support order by nulls last 2019-07-19 10:35:26 +12:00
rob bygrave 970bc8e973 Fix tests for sql server 2019-07-19 10:28:13 +12:00
Roland PramlandRob Bygrave 007b06e9db use local lookup instead of public lookup (#1764)
(cherry picked from commit dc0441aaea126efc23a9c2abc30a4dd67bac6752)
2019-07-19 09:12:07 +12:00
rob bygrave 4513dbf29d No effective change - whitespace change only to SqlTreeBuilder 2019-07-19 00:22:41 +12:00
rob bygrave 53bcc62a0c No effective change - whitespace change to test generated migration ddl 2019-07-19 00:21:04 +12:00
rob bygrave 858c55c978 No effective change - format only test code 2019-07-19 00:20:22 +12:00
rob bygrave cedba089e1 Merge branch 'cannot-access-referenced-prop' of https://github.com/FOCONIS/ebean into FOCONIS-cannot-access-referenced-prop 2019-07-18 23:48:47 +12:00
rob bygrave cbf7811f7c #1762 - Bump ebean-migration to 11.17.1 2019-07-18 23:37:25 +12:00
rob bygrave ea13b322ca Bump agent to 11.41.3 2019-07-18 23:34:28 +12:00
Alexander Wagner 486a8cf1c0 whitespaces 2019-07-18 12:15:44 +02:00
Alexander Wagner bfe46432da delete views from ddl 2019-07-18 12:14:38 +02:00
Alexander Wagner 272eae5f22 Do not use @View for Test 2019-07-18 11:19:43 +02:00
rob bygrave 502104eba8 [maven-release-plugin] prepare for next development iteration 2019-07-17 21:53:14 +12:00
Roland Praml 081cb20260 always add main ID property, if no foreign key (=noConstraint) is present
(cherry picked from commit ea562151a8e3043df13381abd0d076d592618340)
2019-07-12 14:23:50 +02:00
Roland Praml 53f1da32be Add testcase for @DbForeignKey(noconstraint = true) 2019-07-12 14:21:45 +02:00
193 changed files with 11926 additions and 2232 deletions
+1 -1
View File
@@ -21,7 +21,7 @@
</a>
</td>
<td align="center" valign="middle">
<a href="https://timerbe.de" target="_blank">
<a href="https://timerbee.de" target="_blank">
<img width="222px" src="https://ebean.io/images/logo-timerbee.png">
</a>
</td>
+25 -11
View File
@@ -9,20 +9,21 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>11.41.2</version>
<version>11.43.2</version>
<packaging>jar</packaging>
<name>ebean</name>
<url>http://ebean-orm.github.io/</url>
<properties>
<jackson-core.version>2.9.8</jackson-core.version>
<jackson-core.version>2.9.9</jackson-core.version>
<jackson-databind.version>2.9.9.2</jackson-databind.version>
<snakeyaml.version>1.21</snakeyaml.version>
</properties>
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-11.41.2</tag>
<tag>ebean-11.43.2</tag>
</scm>
<profiles>
@@ -117,7 +118,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-annotation</artifactId>
<version>4.11</version>
<version>4.12</version>
</dependency>
<dependency>
@@ -129,7 +130,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-datasource</artifactId>
<version>4.5.2</version>
<version>4.6.1</version>
</dependency>
<dependency>
@@ -141,7 +142,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-migration</artifactId>
<version>11.16.2</version>
<version>11.20.1</version>
</dependency>
<dependency>
@@ -161,7 +162,7 @@
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson-core.version}</version>
<version>${jackson-databind.version}</version>
<scope>provided</scope>
</dependency>
@@ -219,6 +220,13 @@
<!-- Test scope -->
<dependency>
<groupId>com.nuodb.jdbc</groupId>
<artifactId>nuodb-jdbc</artifactId>
<version>20.2.0</version>
<scope>test</scope>
</dependency>
<!--<dependency>-->
<!--<groupId>oracle</groupId>-->
<!--<artifactId>oracle-jdbc</artifactId>-->
@@ -229,7 +237,7 @@
<dependency>
<groupId>io.ebean.test</groupId>
<artifactId>ebean-test-docker</artifactId>
<version>2.5.1</version>
<version>2.10.3</version>
<scope>test</scope>
</dependency>
@@ -273,8 +281,7 @@
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!-- Do not upgrade to 6.0.6: https://bugs.mysql.com/bug.php?id=82896 -->
<version>5.1.43</version>
<version>8.0.17</version>
<scope>test</scope>
</dependency>
@@ -285,6 +292,13 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.avaje.composite</groupId>
<artifactId>avaje-composite-testing</artifactId>
@@ -316,7 +330,7 @@
<plugin>
<groupId>io.ebean</groupId>
<artifactId>ebean-maven-plugin</artifactId>
<version>11.41.2</version>
<version>11.43.2</version>
<executions>
<execution>
<id>test</id>
+14
View File
@@ -1237,4 +1237,18 @@ public class DB {
return getDefault().json();
}
/**
* Truncate the base tables for the given bean types.
*/
public static void truncate(Class<?>... types) {
getDefault().truncate(types);
}
/**
* Truncate the given tables.
*/
public static void truncate(String... tables) {
getDefault().truncate(tables);
}
}
+11
View File
@@ -1580,4 +1580,15 @@ public interface Database {
* </p>
*/
<T> Set<String> validateQuery(Query<T> query);
/**
* Truncate all the given tables.
*/
void truncate(String... tables);
/**
* Truncate the base tables for the given bean types.
*/
void truncate(Class<?>... tables);
}
@@ -536,4 +536,12 @@ public interface ExpressionFactory {
*/
<T> Junction<T> junction(Junction.Type type, Query<T> query, ExpressionList<T> parent);
/**
* Add the expressions to the given expression list.
*
* @param where The expression list to add the expressions to
* @param expressions The expressions that are parsed
* @param params Bind parameters to match ? or ?1 bind positions.
*/
<T> void where(ExpressionList<T> where, String expressions, Object[] params);
}
+33 -6
View File
@@ -489,7 +489,26 @@ public interface ExpressionList<T> {
* Add some filter predicate expressions to the many property.
*/
@Nonnull
ExpressionList<T> filterMany(String prop);
ExpressionList<T> filterMany(String manyProperty);
/**
* Add filter expressions to the many property.
*
* <pre>{@code
*
* DB.find(Customer.class)
* .where()
* .eq("name", "Rob")
* .filterMany("orders", "status = ?", Status.NEW)
* .findList();
*
* }</pre>
*
* @param manyProperty The many property
* @param expressions Filter expressions with and, or and ? or ?1 type bind parameters
* @param params Bind parameters used in the expressions
*/
Query<T> filterMany(String manyProperty, String expressions, Object... params);
/**
* Specify specific properties to fetch on the main/root bean (aka partial
@@ -663,6 +682,14 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> where();
/**
* Add the expressions to this expression list.
*
* @param expressions The expressions that are parsed and added to this expression list
* @param params Bind parameters to match ? or ?1 bind positions.
*/
ExpressionList<T> where(String expressions, Object... params);
/**
* Path exists - for the given path in a JSON document.
* <pre>{@code
@@ -831,9 +858,9 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> inRange(String propertyName, Object value1, Object value2);
/**
* Between - property between the two given values.
*/
/**
* Between - property between the two given values.
*/
ExpressionList<T> between(String propertyName, Object value1, Object value2);
/**
@@ -1327,7 +1354,7 @@ public interface ExpressionList<T> {
*
* }</pre>
* <p>
* Note that we need to cast the Postgres array for UUID types like:
* Note that we need to cast the Postgres array for UUID types like:
* </p>
* <pre>{@code
*
@@ -1335,7 +1362,7 @@ public interface ExpressionList<T> {
*
* }</pre>
*
* @param raw The raw expression that is typically a subquery
* @param raw The raw expression that is typically a subquery
* @param values The values which is typically a list or set of id values.
*/
ExpressionList<T> rawOrEmpty(String raw, Collection<?> values);
+22
View File
@@ -148,6 +148,8 @@ public class FetchConfig implements Serializable {
private boolean queryAll;
private boolean cache;
/**
* Construct the fetch configuration object.
*/
@@ -188,6 +190,17 @@ public class FetchConfig implements Serializable {
return this;
}
/**
* Eagerly fetch the beans fetching the beans from the L2 bean cache
* and using the DB for beans not in the cache.
*/
public FetchConfig cache() {
this.cache = true;
this.queryBatchSize = 0;
this.queryAll = true;
return this;
}
/**
* Eagerly fetch the beans in this path as a separate query (rather than as
* part of the main query).
@@ -247,6 +260,13 @@ public class FetchConfig implements Serializable {
return queryAll;
}
/**
* Return true if this uses L2 bean cache.
*/
public boolean isCache() {
return cache;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -255,6 +275,7 @@ public class FetchConfig implements Serializable {
FetchConfig that = (FetchConfig) o;
if (lazyBatchSize != that.lazyBatchSize) return false;
if (queryBatchSize != that.queryBatchSize) return false;
if (cache != that.cache) return false;
return queryAll == that.queryAll;
}
@@ -263,6 +284,7 @@ public class FetchConfig implements Serializable {
int result = lazyBatchSize;
result = 92821 * result + queryBatchSize;
result = 92821 * result + (queryAll ? 1 : 0);
result = 92821 * result + (cache ? 1 : 0);
return result;
}
}
+9
View File
@@ -1,5 +1,7 @@
package io.ebean;
import io.ebean.service.SpiFetchGroupQuery;
import javax.annotation.Nonnull;
/**
@@ -111,4 +113,11 @@ public interface FetchGroup<T> {
return XServiceProvider.fetchGroupOf(cls);
}
/**
* Return a query to be used by query beans for constructing FetchGroup.
*/
static <T> SpiFetchGroupQuery<T> queryFor(Class<T> beanType) {
return XServiceProvider.fetchGroupQueryFor(beanType);
}
}
@@ -67,12 +67,26 @@ public interface FetchGroupBuilder<T> {
@Nonnull
FetchGroupBuilder<T> fetchQuery(String path);
/**
* Fetch the path including all its properties using L2 cache.
* Cache misses fallback to fetchQuery().
*/
@Nonnull
FetchGroupBuilder<T> fetchCache(String path);
/**
* Fetch the path including specified properties using a query join.
*/
@Nonnull
FetchGroupBuilder<T> fetchQuery(String path, String properties);
/**
* Fetch the path including specified properties using L2 cache.
* Cache misses fallback to fetchQuery().
*/
@Nonnull
FetchGroupBuilder<T> fetchCache(String path, String properties);
/**
* Fetch the path including all its properties lazily.
*/
+1 -1
View File
@@ -412,7 +412,7 @@ public final class OrderBy<T> implements Serializable {
* Support use in select clause if no collation or nulls ordering.
*/
boolean supportsSelect() {
return nulls == null && collation == null;
return nulls == null;
}
}
+13
View File
@@ -501,6 +501,14 @@ public interface Query<T> {
*/
Query<T> fetchQuery(String path, String fetchProperties);
/**
* Fetch the path and properties using L2 bean cache.
*
* @param path The path of the beans we are fetching from L2 cache.
* @param fetchProperties The properties that should be loaded.
*/
Query<T> fetchCache(String path, String fetchProperties);
/**
* Fetch the path and properties lazily (via batch lazy loading).
* <p>
@@ -587,6 +595,11 @@ public interface Query<T> {
*/
Query<T> fetchQuery(String path);
/**
* Fetch the path eagerly using L2 cache.
*/
Query<T> fetchCache(String path);
/**
* Fetch the path lazily (via batch lazy loading).
* <p>
@@ -1,5 +1,6 @@
package io.ebean;
import io.ebean.service.SpiFetchGroupQuery;
import io.ebean.service.SpiFetchGroupService;
import io.ebean.service.SpiProfileLocationFactory;
import io.ebean.service.SpiRawSqlService;
@@ -71,4 +72,11 @@ class XServiceProvider {
static <T> FetchGroupBuilder<T> fetchGroupOf(Class<T> cls) {
return fetchGroupService.of(cls);
}
/**
* Return the FetchGroup Query for building fetch groups via query beans.
*/
static <T> SpiFetchGroupQuery<T> fetchGroupQueryFor(Class<T> cls) {
return fetchGroupService.queryFor(cls);
}
}
@@ -0,0 +1,22 @@
package io.ebean.bean;
/**
* A call origin for query execution profiling to collect graph use (for query tuning).
*/
public interface CallOrigin {
/**
* Return the top element. Typically the top stack element with class and line.
*/
String getTopElement();
/**
* Return the full description of the call origin.
*/
String getFullDescription();
/**
* Compute and return an origin key based on the query hash.
*/
String getOriginKey(int queryHash);
}
+22 -59
View File
@@ -3,6 +3,8 @@ package io.ebean.bean;
import java.io.Serializable;
import java.util.Arrays;
import static io.ebean.util.EncodeB64.enc;
/**
* Represent the call stack (stack trace elements).
* <p>
@@ -17,15 +19,15 @@ import java.util.Arrays;
* Note the call stack is trimmed to remove the common ebean internal elements.
* </p>
*/
public final class CallStack implements Serializable {
public final class CallStack implements Serializable, CallOrigin {
private static final long serialVersionUID = -8590644046907438579L;
private static final String NEWLINE = "\n";
private final String zeroHash;
private final String pathHash;
private final StackTraceElement[] callStack;
private final int hc;
public CallStack(StackTraceElement[] callStack, int zeroHash, int pathHash) {
@@ -37,12 +39,17 @@ public final class CallStack implements Serializable {
private int computeHashCode() {
int hc = 0;
for (StackTraceElement aCallStack : callStack) {
hc = 92821 * hc + aCallStack.hashCode();
for (StackTraceElement element : callStack) {
hc = 92821 * hc + element.hashCode();
}
return hc;
}
@Override
public String toString() {
return zeroHash + ":" + pathHash + ":" + callStack[0];
}
@Override
public int hashCode() {
return hc;
@@ -63,73 +70,29 @@ public final class CallStack implements Serializable {
/**
* Return the first element of the call stack.
*/
public StackTraceElement getFirstStackTraceElement() {
return callStack[0];
}
/**
* Return the call stack.
*/
public StackTraceElement[] getCallStack() {
return callStack;
}
/**
* Return the hash for the first stack element.
*/
public String getZeroHash() {
return zeroHash;
}
/**
* Return the hash for the stack elements (excluding first stack element).
*/
public String getPathHash() {
return pathHash;
}
@Override
public String toString() {
return zeroHash + ":" + pathHash + ":" + callStack[0];
public String getTopElement() {
return callStack[0].toString();
}
/**
* Return the call stack lines appended with the given newLine string.
*/
public String description(String newLine) {
@Override
public String getFullDescription() {
StringBuilder sb = new StringBuilder(400);
for (StackTraceElement aCallStack : callStack) {
sb.append(aCallStack.toString()).append(newLine);
for (int i = 0; i < callStack.length; i++) {
if (i > 0) {
sb.append(NEWLINE);
}
sb.append(callStack[i].toString());
}
return sb.toString();
}
@Override
public String getOriginKey(int queryHash) {
return enc(queryHash) + "." + zeroHash + "." + pathHash;
}
private static final int radix = 1 << 6;
private static final int mask = radix - 1;
/**
* Convert the integer to unsigned base 64.
*/
public static String enc(int i) {
char[] buf = new char[32];
int charPos = 32;
do {
buf[--charPos] = intToBase64[i & mask];
i >>>= 6;
} while (i != 0);
return new String(buf, charPos, (32 - charPos));
}
private static final char intToBase64[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
};
}
@@ -95,13 +95,10 @@ public final class EntityBeanIntercept implements Serializable {
private final byte[] flags;
private boolean fullyLoadedBean;
private boolean loadedFromCache;
private Object[] origValues;
private Exception[] loadErrors;
private int lazyLoadProperty = -1;
private Object ownerId;
private int sortOrder;
@@ -116,6 +113,14 @@ public final class EntityBeanIntercept implements Serializable {
this.flags = new byte[owner._ebean_getPropertyNames().length];
}
/**
* EXPERIMENTAL - Constructor only for use by serialization frameworks.
*/
public EntityBeanIntercept() {
this.owner = null;
this.flags = null;
}
/**
* Return the 'owning' entity bean.
*/
@@ -303,6 +308,22 @@ public final class EntityBeanIntercept implements Serializable {
}
}
/**
* Set true when the bean has been loaded from L2 bean cache.
* The effect of this is that we should skip the cache if there
* is subsequent lazy loading (bean cache partially populated).
*/
public void setLoadedFromCache(boolean loadedFromCache) {
this.loadedFromCache = loadedFromCache;
}
/**
* Return true if this bean was loaded from L2 bean cache.
*/
public boolean isLoadedFromCache() {
return loadedFromCache;
}
/**
* Return true if the bean should be treated as readOnly. If a setter method
* is called when it is readOnly an Exception is thrown.
@@ -772,7 +793,7 @@ public final class EntityBeanIntercept implements Serializable {
}
public boolean[] getLoaded() {
boolean[] ret= new boolean[flags.length];
boolean[] ret = new boolean[flags.length];
for (int i = 0; i < ret.length; i++) {
ret[i] = (flags[i] & FLAG_LOADED_PROP) != 0;
}
@@ -16,7 +16,7 @@ public final class ObjectGraphOrigin implements Serializable {
private static final long serialVersionUID = 410937765287968708L;
private final CallStack callStack;
private final CallOrigin callOrigin;
private final String beanType;
@@ -24,11 +24,11 @@ public final class ObjectGraphOrigin implements Serializable {
private final String key;
public ObjectGraphOrigin(int queryHash, CallStack callStack, String beanType) {
this.callStack = callStack;
public ObjectGraphOrigin(int queryHash, CallOrigin callOrigin, String beanType) {
this.callOrigin = callOrigin;
this.beanType = beanType;
this.queryHash = queryHash;
this.key = callStack.getOriginKey(queryHash);
this.key = callOrigin.getOriginKey(queryHash);
}
/**
@@ -49,22 +49,22 @@ public final class ObjectGraphOrigin implements Serializable {
/**
* The call stack involved.
*/
public CallStack getCallStack() {
return callStack;
public CallOrigin getCallOrigin() {
return callOrigin;
}
public String getFirstStackElement() {
return callStack.getFirstStackTraceElement().toString();
public String getTopElement() {
return callOrigin.getTopElement();
}
@Override
public String toString() {
return "key[" + key + "] type[" + beanType + "] " + callStack.getFirstStackTraceElement() + " ";
return "key[" + key + "] type[" + beanType + "] " + callOrigin.getTopElement();
}
@Override
public int hashCode() {
int hc = 92821 * callStack.hashCode();
int hc = 92821 * callOrigin.hashCode();
hc = 92821 * hc + beanType.hashCode();
hc = 92821 * hc + queryHash;
return hc;
@@ -82,6 +82,6 @@ public final class ObjectGraphOrigin implements Serializable {
ObjectGraphOrigin e = (ObjectGraphOrigin) obj;
return e.queryHash == queryHash
&& e.beanType.equals(beanType)
&& e.callStack.equals(callStack);
&& e.callOrigin.equals(callOrigin);
}
}
@@ -2929,6 +2929,7 @@ public class ServerConfig {
}
loadDocStoreSettings(p);
maxCallStack = p.getInt("maxCallStack", maxCallStack);
dumpMetricsOnShutdown = p.getBoolean("dumpMetricsOnShutdown", dumpMetricsOnShutdown);
dumpMetricsOptions = p.get("dumpMetricsOptions", dumpMetricsOptions);
queryPlanTTLSeconds = p.getInt("queryPlanTTLSeconds", queryPlanTTLSeconds);
@@ -51,6 +51,8 @@ public class DatabasePlatform {
protected boolean supportsDeleteTableAlias;
protected boolean supportsSavepointId = true;
/**
* The behaviour used when ending a read only transaction at read committed isolation level.
*/
@@ -137,6 +139,8 @@ public class DatabasePlatform {
*/
protected Platform platform = Platform.GENERIC;
protected String truncateTable = "truncate table %s";
protected String columnAliasPrefix = "c";
protected String tableAliasPlaceHolder = "${ta}";
@@ -328,6 +332,13 @@ public class DatabasePlatform {
return caseSensitiveCollation;
}
/**
* Return true if the platform supports SavepointId values.
*/
public boolean isSupportsSavepointId() {
return supportsSavepointId;
}
/**
* Return the maximum table name length.
* <p>
@@ -568,7 +579,7 @@ public class DatabasePlatform {
/**
* Normally not needed - overridden in CockroachPlatform.
*/
public boolean isDdlCommitOnCreateIndex() {
public boolean isDdlAutoCommit() {
return false;
}
@@ -699,6 +710,13 @@ public class DatabasePlatform {
return persistBatchOnCascade;
}
/**
* Return a statement to truncate a table.
*/
public String truncateStatement(String table) {
return String.format(truncateTable, table);
}
/**
* Create the DB schema if it does not exist.
*/
@@ -21,7 +21,7 @@ public class CockroachPlatform extends PostgresPlatform {
* Needs a commit after create index such that alter table add foreign key ... succeeds.
*/
@Override
public boolean isDdlCommitOnCreateIndex() {
public boolean isDdlAutoCommit() {
return true;
}
@@ -22,6 +22,7 @@ public class DB2Platform extends DatabasePlatform {
this.platform = Platform.DB2;
this.maxTableNameLength = 18;
this.maxConstraintNameLength = 18;
this.truncateTable = "truncate table %s reuse storage ignore delete triggers immediate";
this.sqlLimiter = new Db2SqlLimiter();
this.dbIdentity.setSupportsGetGeneratedKeys(true);
@@ -26,7 +26,6 @@ public class H2Platform extends DatabasePlatform {
this.supportsDeleteTableAlias = true;
this.dbDefaultValue.setNow("now()");
this.columnAliasPrefix = null;
this.exceptionTranslator =
new SqlErrorCodes()
.addAcquireLock("50200","HYT00")
@@ -18,7 +18,6 @@ public class HanaPlatform extends DatabasePlatform {
this.persistBatchOnCascade = PersistBatch.NONE;
this.supportsResultSetConcurrencyModeUpdatable = false;
this.columnAliasPrefix = null;
this.historySupport = new HanaHistorySupport();
this.basicSqlLimiter = new HanaBasicSqlLimiter();
@@ -21,7 +21,7 @@ public class HsqldbPlatform extends DatabasePlatform {
super();
this.platform = Platform.HSQLDB;
this.dbEncrypt = new H2DbEncrypt();
this.truncateTable = "delete from %s";
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsSequence(true);
@@ -27,10 +27,10 @@ public class MySqlPlatform extends DatabasePlatform {
this.platform = Platform.MYSQL;
this.useExtraTransactionOnIterateSecondaryQueries = true;
this.selectCountWithAlias = true;
this.supportsSavepointId = false;
this.dbEncrypt = new MySqlDbEncrypt();
this.historySupport = new MySqlHistorySupport();
this.columnAliasPrefix = null;
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsIdentity(true);
@@ -0,0 +1,10 @@
package io.ebean.config.dbplatform.nuodb;
import io.ebean.config.dbplatform.DbViewHistorySupport;
/**
* Runtime support for @History with NuoDB.
*/
public class NuoDbHistorySupport extends DbViewHistorySupport {
}
@@ -0,0 +1,61 @@
package io.ebean.config.dbplatform.nuodb;
import io.ebean.BackgroundExecutor;
import io.ebean.Query;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.PlatformIdGenerator;
import io.ebean.config.dbplatform.SqlErrorCodes;
import javax.sql.DataSource;
/**
* NuoDb specific platform.
*/
public class NuoDbPlatform extends DatabasePlatform {
public NuoDbPlatform() {
super();
this.platform = Platform.NUODB;
this.idInExpandedForm = true;
this.supportsResultSetConcurrencyModeUpdatable = false;
this.supportsDeleteTableAlias = true;
this.columnAliasPrefix = null;
this.likeClauseRaw = "like ?";
this.historySupport = new NuoDbHistorySupport();
//this.dbEncrypt = ...
dbIdentity.setIdType(IdType.IDENTITY);
dbIdentity.setSupportsIdentity(true);
dbIdentity.setSupportsSequence(true);
dbIdentity.setSupportsGetGeneratedKeys(true);
// No referential integrity exception to map
this.exceptionTranslator =
new SqlErrorCodes()
//.addAcquireLock("")
.addDuplicateKey("23000")
.build();
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("integer", false));
}
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new NuoDbSequence(be, ds, seqName, stepSize);
}
@Override
protected String withForUpdate(String sql, Query.ForUpdate forUpdateMode) {
switch (forUpdateMode) {
case NOWAIT:
return sql + " for update nowait";
case SKIPLOCKED:
default:
return sql + " for update";
}
}
}
@@ -0,0 +1,24 @@
package io.ebean.config.dbplatform.nuodb;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
import javax.sql.DataSource;
public class NuoDbSequence extends SequenceStepIdGenerator {
private final String nextSql;
/**
* Construct where batchSize is the sequence step size.
*/
public NuoDbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize);
this.nextSql = "select next value for " + seqName + " from dual";
}
@Override
public String getSql(int batchSize) {
return nextSql;
}
}
@@ -30,7 +30,7 @@ public class OraclePlatform extends DatabasePlatform {
this.sqlLimiter = new RownumSqlLimiter();
this.basicSqlLimiter = new BasicSqlAnsiLimiter();
this.historySupport = new OracleDbHistorySupport();
this.truncateTable = "truncate table %s cascade";
dbIdentity.setIdType(IdType.SEQUENCE);
dbIdentity.setSupportsSequence(true);
dbIdentity.setSupportsIdentity(true);
@@ -36,7 +36,7 @@ public class PostgresPlatform extends DatabasePlatform {
this.clobDbType = Types.VARCHAR;
this.nativeUuidType = true;
this.columnAliasPrefix = null;
this.truncateTable = "truncate table %s cascade";
this.dbEncrypt = new PostgresDbEncrypt();
this.historySupport = new PostgresHistorySupport();
@@ -17,7 +17,7 @@ public class SQLitePlatform extends DatabasePlatform {
this.dbIdentity.setSupportsGetGeneratedKeys(false);
this.dbIdentity.setSupportsSequence(false);
this.dbIdentity.setSelectLastInsertedIdTemplate("select last_insert_rowid()");
this.truncateTable = "delete from %s";
this.booleanDbType = Types.INTEGER;
this.likeClauseRaw = "like ?";
this.likeClauseEscaped = "like ?";
@@ -24,6 +24,11 @@ public interface BeanQueryRequest<T> {
*/
Query<T> getQuery();
/**
* Return true if an Id IN expression should have the bind parameters padded.
*/
boolean isPadInExpression();
/**
* Return true if multi-value binding using Array or Table Values is supported.
*/
@@ -0,0 +1,15 @@
package io.ebean.service;
import io.ebean.FetchGroup;
import io.ebean.Query;
/**
* Extension of Query to build FetchGroup via query beans.
*/
public interface SpiFetchGroupQuery<T> extends Query<T> {
/**
* Build the fetch group with select and fetch clauses.
*/
FetchGroup<T> buildFetchGroup();
}
@@ -23,4 +23,10 @@ public interface SpiFetchGroupService {
* @return The FetchGroupBuilder to add additional select and fetch clauses
*/
<T> FetchGroupBuilder<T> of(Class<T> beanType);
/**
* Return a new FetchGroupQuery for building FetchGroup via query beans.
*/
<T> SpiFetchGroupQuery<T> queryFor(Class<T> beanType);
}
@@ -0,0 +1,32 @@
package io.ebean.util;
/**
* Integer to base64 encoder.
*/
public final class EncodeB64 {
private static final int radix = 1 << 6;
private static final int mask = radix - 1;
/**
* Convert the integer to unsigned base 64.
*/
public static String enc(int i) {
char[] buf = new char[32];
int charPos = 32;
do {
buf[--charPos] = intToBase64[i & mask];
i >>>= 6;
} while (i != 0);
return new String(buf, charPos, (32 - charPos));
}
private static final char intToBase64[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
};
}
@@ -4,8 +4,6 @@ import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
import io.ebeaninternal.server.core.OrmQueryRequest;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashSet;
@@ -17,8 +15,6 @@ import java.util.Set;
*/
public class LoadBeanRequest extends LoadRequest {
private static final Logger logger = LoggerFactory.getLogger(LoadBeanRequest.class);
private final List<EntityBeanIntercept> batch;
private final LoadBeanBuffer loadBuffer;
@@ -27,11 +23,14 @@ public class LoadBeanRequest extends LoadRequest {
private final boolean loadCache;
private boolean loadedFromCache;
/**
* Construct for lazy load request.
*/
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, String lazyLoadProperty, boolean loadCache) {
this(LoadBuffer, null, true, lazyLoadProperty, loadCache);
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, EntityBeanIntercept ebi, boolean loadCache) {
this(LoadBuffer, null, true, ebi.getLazyLoadProperty(), loadCache);
this.loadedFromCache = ebi.isLoadedFromCache();
}
/**
@@ -56,10 +55,16 @@ public class LoadBeanRequest extends LoadRequest {
return loadBuffer.getBeanDescriptor().getBeanType();
}
public boolean isLoadCache() {
return loadCache;
/**
* Return true if the beans invoking lazy loading were previously loaded from cache.
*/
public boolean isLoadedFromCache() {
return loadedFromCache;
}
private boolean isLoadCache() {
return loadCache;
}
public String getDescription() {
return "path:" + loadBuffer.getFullPath() + " batch:" + batch.size();
@@ -75,17 +80,10 @@ public class LoadBeanRequest extends LoadRequest {
/**
* Return the load context.
*/
public LoadBeanBuffer getLoadContext() {
private LoadBeanBuffer getLoadContext() {
return loadBuffer;
}
/**
* Return the property that invoked the lazy loading.
*/
public String getLazyLoadProperty() {
return lazyLoadProperty;
}
public int getBatchSize() {
return getLoadContext().getBatchSize();
}
@@ -93,30 +91,14 @@ public class LoadBeanRequest extends LoadRequest {
/**
* Return the list of Id values for the beans in the lazy load buffer.
*/
public List<Object> getIdList(int batchSize) {
public List<Object> getIdList() {
List<Object> idList = new ArrayList<>(batchSize);
List<Object> idList = new ArrayList<>();
BeanDescriptor<?> desc = loadBuffer.getBeanDescriptor();
for (EntityBeanIntercept ebi : batch) {
EntityBean bean = ebi.getOwner();
idList.add(desc.getId(bean));
idList.add(desc.getId(ebi.getOwner()));
}
if (!desc.isMultiValueIdSupported() && !idList.isEmpty()) {
int extraIds = batchSize - batch.size();
if (extraIds > 0) {
// for performance make up the Id's to the batch size
// so we get the same query (for Ebean and the db)
Object firstId = idList.get(0);
for (int i = 0; i < extraIds; i++) {
// just add the first Id again
idList.add(firstId);
}
}
}
return idList;
}
@@ -3,6 +3,7 @@ package io.ebeaninternal.api;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.EntityBean;
import io.ebean.util.StringHelper;
import io.ebeaninternal.server.core.BindPadding;
import io.ebeaninternal.server.core.OrmQueryRequest;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
@@ -65,13 +66,6 @@ public class LoadManyRequest extends LoadRequest {
return batch;
}
/**
* Return the load context.
*/
public LoadManyBuffer getLoadContext() {
return loadContext;
}
/**
* Return true if lazy loading should only load the id values.
* <p>
@@ -80,14 +74,14 @@ public class LoadManyRequest extends LoadRequest {
* used.
* </p>
*/
public boolean isOnlyIds() {
private boolean isOnlyIds() {
return onlyIds;
}
/**
* Return true if we should load the Collection ids into the cache.
*/
public boolean isLoadCache() {
private boolean isLoadCache() {
return loadCache;
}
@@ -98,22 +92,16 @@ public class LoadManyRequest extends LoadRequest {
return loadContext.getBatchSize();
}
private List<Object> getParentIdList(int batchSize) {
private List<Object> getParentIdList() {
ArrayList<Object> idList = new ArrayList<>(batchSize);
List<Object> idList = new ArrayList<>();
BeanPropertyAssocMany<?> many = getMany();
for (BeanCollection<?> bc : batch) {
idList.add(many.getParentId(bc.getOwnerBean()));
}
if (!many.getTargetDescriptor().isMultiValueIdSupported()) {
int extraIds = batchSize - batch.size();
if (extraIds > 0) {
Object firstId = idList.get(0);
for (int i = 0; i < extraIds; i++) {
idList.add(firstId);
}
}
if (many.getTargetDescriptor().isPadInExpression()) {
BindPadding.padIds(idList);
}
return idList;
@@ -123,7 +111,7 @@ public class LoadManyRequest extends LoadRequest {
return loadContext.getBeanProperty();
}
public SpiQuery<?> createQuery(SpiEbeanServer server, int batchSize) {
public SpiQuery<?> createQuery(SpiEbeanServer server) {
BeanPropertyAssocMany<?> many = getMany();
@@ -142,10 +130,7 @@ public class LoadManyRequest extends LoadRequest {
}
query.setLazyLoadForParents(many);
List<Object> idList = getParentIdList(batchSize);
many.addWhereParentIdIn(query, idList, loadContext.isUseDocStore());
many.addWhereParentIdIn(query, getParentIdList(), loadContext.isUseDocStore());
query.setPersistenceContext(loadContext.getPersistenceContext());
String mode = isLazy() ? "+lazy" : "+query";
@@ -11,7 +11,7 @@ import io.ebean.Transaction;
import io.ebean.TxScope;
import io.ebean.bean.BeanCollectionLoader;
import io.ebean.bean.BeanLoader;
import io.ebean.bean.CallStack;
import io.ebean.bean.CallOrigin;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
@@ -81,7 +81,7 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanLoader,
* graph costing.
* </p>
*/
CallStack createCallStack();
CallOrigin createCallOrigin();
/**
* Return the PersistenceContextScope to use defined at query or server level.
@@ -7,7 +7,7 @@ import io.ebean.OrderBy;
import io.ebean.PersistenceContextScope;
import io.ebean.ProfileLocation;
import io.ebean.Query;
import io.ebean.bean.CallStack;
import io.ebean.bean.CallOrigin;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.bean.PersistenceContext;
import io.ebean.event.readaudit.ReadEvent;
@@ -289,6 +289,11 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
*/
boolean selectAllForLazyLoadProperty();
/**
* Set the on a secondary query given the label, relativePath and profile location of the parent query.
*/
void setProfilePath(String label, String relativePath, ProfileLocation profileLocation);
/**
* Set the query mode.
*/
@@ -558,7 +563,7 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
* because the queryPlanHash is used to identify the query point.
* </p>
*/
ObjectGraphNode setOrigin(CallStack callStack);
ObjectGraphNode setOrigin(CallOrigin callOrigin);
/**
* Set the profile point of the bean or collection that is lazy loading.
@@ -35,6 +35,7 @@ import java.sql.SQLException;
public class DdlGenerator {
private static final Logger log = LoggerFactory.getLogger(DdlGenerator.class);
private static final String[] BUILD_DIRS = {"target", "build"};
private final SpiEbeanServer server;
@@ -43,13 +44,14 @@ public class DdlGenerator {
private final boolean extraDdl;
private final boolean createOnly;
private final boolean jaxbPresent;
private final boolean ddlCommitOnCreateIndex;
private final boolean ddlAutoCommit;
private final String dbSchema;
private final ScriptTransform scriptTransform;
private CurrentModel currentModel;
private String dropAllContent;
private String createAllContent;
private File baseDir;
public DdlGenerator(SpiEbeanServer server, ServerConfig serverConfig) {
this.server = server;
@@ -61,12 +63,23 @@ public class DdlGenerator {
if (!serverConfig.getTenantMode().isDdlEnabled() && serverConfig.isDdlRun()) {
log.warn("DDL can't be run on startup with TenantMode " + serverConfig.getTenantMode());
this.runDdl = false;
this.ddlCommitOnCreateIndex = false;
this.ddlAutoCommit = false;
} else {
this.runDdl = serverConfig.isDdlRun();
this.ddlCommitOnCreateIndex = server.getDatabasePlatform().isDdlCommitOnCreateIndex();
this.ddlAutoCommit = server.getDatabasePlatform().isDdlAutoCommit();
}
this.scriptTransform = createScriptTransform(serverConfig.getMigrationConfig());
this.baseDir = initBaseDir();
}
private File initBaseDir() {
for (String buildDir : BUILD_DIRS) {
File dir = new File(buildDir);
if (dir.exists() && dir.isDirectory()) {
return dir;
}
}
return new File(".");
}
/**
@@ -147,13 +160,11 @@ public class DdlGenerator {
DdlRunner runner = new DdlRunner(expectErrors, scriptName);
try {
if (expectErrors) {
if (expectErrors || ddlAutoCommit) {
connection.setAutoCommit(true);
} else if (ddlCommitOnCreateIndex) {
runner.setCommitOnCreateIndex();
}
int count = runner.runAll(scriptTransform.transform(content), connection);
if (expectErrors) {
if (expectErrors || ddlAutoCommit) {
connection.setAutoCommit(false);
}
connection.commit();
@@ -212,7 +223,6 @@ public class DdlGenerator {
* extra-ddl.xml should have some partition initialisation but this helps people get going.
*/
private void checkInitialTablePartitions(Connection connection) {
DatabasePlatform databasePlatform = server.getDatabasePlatform();
try {
StringBuilder sb = new StringBuilder();
@@ -245,12 +255,11 @@ public class DdlGenerator {
}
protected void runResourceScript(Connection connection, String sqlScript) throws IOException {
if (sqlScript != null) {
try (InputStream is = getClassLoader().getResourceAsStream(sqlScript)) {
if (is != null) {
String content = readContent(new InputStreamReader(is));
runScript(connection, false, content, sqlScript);
runScript(connection, false, content, sqlScript);
}
}
}
@@ -268,7 +277,6 @@ public class DdlGenerator {
}
protected void writeDrop(String dropFile) {
try {
writeFile(dropFile, generateDropAllDdl());
} catch (IOException e) {
@@ -277,7 +285,6 @@ public class DdlGenerator {
}
protected void writeCreate(String createFile) {
try {
writeFile(createFile, generateCreateAllDdl());
} catch (IOException e) {
@@ -286,7 +293,6 @@ public class DdlGenerator {
}
protected String generateDropAllDdl() {
try {
dropAllContent = currentModel().getDropAllDdl();
return dropAllContent;
@@ -296,7 +302,6 @@ public class DdlGenerator {
}
protected String generateCreateAllDdl() {
try {
createAllContent = currentModel().getCreateDdl();
return createAllContent;
@@ -321,8 +326,7 @@ public class DdlGenerator {
}
protected void writeFile(String fileName, String fileContent) throws IOException {
File f = new File(fileName);
File f = new File(baseDir, fileName);
try (FileWriter fw = new FileWriter(f)) {
fw.write(fileContent);
fw.flush();
@@ -330,17 +334,14 @@ public class DdlGenerator {
}
protected String readFile(String fileName) throws IOException {
File f = new File(fileName);
File f = new File(baseDir, fileName);
if (!f.exists()) {
return null;
}
return readContent(new FileReader(f));
}
protected String readContent(Reader reader) throws IOException {
StringBuilder buf = new StringBuilder();
try (LineNumberReader lineReader = new LineNumberReader(reader)) {
String s;
@@ -355,7 +356,6 @@ public class DdlGenerator {
* Create the ScriptTransform for placeholder key/value replacement.
*/
private ScriptTransform createScriptTransform(DbMigrationConfig config) {
return ScriptTransform.build(config.getRunPlaceholders(), config.getRunPlaceholderMap());
}
@@ -15,6 +15,7 @@ import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebean.config.dbplatform.hana.HanaPlatform;
import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform;
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
import io.ebean.config.dbplatform.nuodb.NuoDbPlatform;
import io.ebean.config.dbplatform.oracle.OraclePlatform;
import io.ebean.config.dbplatform.postgres.PostgresPlatform;
import io.ebean.config.dbplatform.sqlanywhere.SqlAnywherePlatform;
@@ -804,6 +805,8 @@ public class DefaultDbMigration implements DbMigration {
return new SQLitePlatform();
case HANA:
return new HanaPlatform();
case NUODB:
return new NuoDbPlatform();
case COCKROACH:
return new CockroachPlatform();
case CLICKHOUSE:
@@ -10,7 +10,6 @@ public class CockroachDdl extends PlatformDdl {
public CockroachDdl(DatabasePlatform platform) {
super(platform);
this.historyDdl = new PostgresHistoryDdl();
this.dropTableCascade = " cascade";
this.columnSetType = "type ";
this.alterTableIfExists = "if exists ";
@@ -29,11 +29,11 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
protected String viewSuffix;
protected String historySuffix;
protected String currentTimestamp = "now(6)";
protected String sysPeriodType = "datetime(6)";
protected String now = "now(6)";
protected String sysPeriodEndValue = "now(6)";
public DbTriggerBasedHistoryDdl() {
DbTriggerBasedHistoryDdl() {
}
@Override
@@ -164,7 +164,7 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
protected void addSysPeriodColumns(DdlBuffer apply, String baseTableName, String whenCreatedColumn) throws IOException {
apply.append("alter table ").append(baseTableName).append(" add column ")
.append(sysPeriodStart).append(" ").append(sysPeriodType).append(" default ").append(currentTimestamp).endOfStatement();
.append(sysPeriodStart).append(" ").append(sysPeriodType).append(" default ").append(now).endOfStatement();
apply.append("alter table ").append(baseTableName).append(" add column ")
.append(sysPeriodEnd).append(" ").append(sysPeriodType).endOfStatement();
@@ -246,13 +246,12 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
buffer.append(" insert into ").append(historyTable).append(" (").append(sysPeriodStart).append(",").append(sysPeriodEnd).append(",");
appendColumnNames(buffer, columns, "");
buffer.append(") values (OLD.").append(sysPeriodStart).append(", ").append(currentTimestamp).append(",");
buffer.append(") values (OLD.").append(sysPeriodStart).append(", ").append(sysPeriodEndValue).append(",");
appendColumnNames(buffer, columns, "OLD.");
buffer.append(");").newLine();
}
protected void appendColumnNames(DdlBuffer buffer, List<String> columns, String columnPrefix) throws IOException {
void appendColumnNames(DdlBuffer buffer, List<String> columns, String columnPrefix) throws IOException {
for (int i = 0; i < columns.size(); i++) {
if (i > 0) {
buffer.append(", ");
@@ -265,8 +264,7 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
/**
* Append a single column to the buffer if it is not null.
*/
protected void appendColumnName(DdlBuffer buffer, String prefix, String columnName) throws IOException {
void appendColumnName(DdlBuffer buffer, String prefix, String columnName) throws IOException {
if (columnName != null) {
buffer.append(prefix).append(columnName);
}
@@ -280,8 +278,7 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
* the column.
* </p>
*/
protected List<String> columnNamesForApply(MTable table) {
List<String> columnNamesForApply(MTable table) {
return table.allHistoryColumns(true);
}
@@ -14,9 +14,10 @@ public class H2HistoryDdl extends DbTriggerBasedHistoryDdl {
private static final String TRIGGER_CLASS = H2HistoryTrigger.class.getName();
public H2HistoryDdl() {
H2HistoryDdl() {
this.sysPeriodType = "timestamp";
this.currentTimestamp = "now()";
this.now = "now()";
this.sysPeriodEndValue = "now()";
}
@Override
@@ -11,13 +11,11 @@ import java.io.IOException;
*/
public class MySqlHistoryDdl extends DbTriggerBasedHistoryDdl {
public MySqlHistoryDdl() {
MySqlHistoryDdl() {
}
@Override
protected void dropTriggers(DdlBuffer buffer, String baseTable) throws IOException {
buffer.append("drop trigger ").append(updateTriggerName(baseTable)).endOfStatement();
buffer.append("drop trigger ").append(deleteTriggerName(baseTable)).endOfStatement();
}
@@ -0,0 +1,41 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.annotation.ConstraintMode;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
public class NuoDbDdl extends PlatformDdl {
public NuoDbDdl(DatabasePlatform platform) {
super(platform);
this.historyDdl = new NuoDbHistoryDdl();
this.identitySuffix = " generated by default as identity";
this.dropConstraintIfExists = "drop constraint";
}
@Override
public void addTableComment(DdlBuffer apply, String tableName, String tableComment) {
// do nothing
}
@Override
public void addColumnComment(DdlBuffer apply, String table, String column, String comment) {
// do nothing
}
@Override
protected void appendForeignKeyOnUpdate(StringBuilder buffer, ConstraintMode mode) {
// do nothing
}
@Override
protected void appendForeignKeyOnDelete(StringBuilder buffer, ConstraintMode mode) {
if (mode == ConstraintMode.RESTRICT) {
// do nothing
} else if (mode == ConstraintMode.SET_NULL) {
// do nothing
} else {
super.appendForeignKeyOnDelete(buffer, mode);
}
}
}
@@ -0,0 +1,79 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.model.MTable;
import java.io.IOException;
/**
* NuoDB history support using DB triggers to maintain a history table.
*/
public class NuoDbHistoryDdl extends DbTriggerBasedHistoryDdl {
NuoDbHistoryDdl() {
this.now = "now()";
this.sysPeriodEndValue = "NEW.sys_period_start";
}
@Override
protected void dropTriggers(DdlBuffer buffer, String baseTable) throws IOException {
buffer.append("drop trigger ").append(updateTriggerName(baseTable)).endOfStatement();
buffer.append("drop trigger ").append(deleteTriggerName(baseTable)).endOfStatement();
}
@Override
protected void createTriggers(DdlWrite writer, MTable table) throws IOException {
DbTriggerUpdate update = createDbTriggerUpdate(writer, table);
addBeforeUpdate(updateTriggerName(update.getBaseTable()), update);
addBeforeDelete(deleteTriggerName(update.getBaseTable()), update);
}
@Override
protected void updateHistoryTriggers(DbTriggerUpdate update) throws IOException {
recreateHistoryView(update);
DdlBuffer buffer = update.historyTriggerBuffer();
String baseTable = update.getBaseTable();
dropTriggers(buffer, baseTable);
addBeforeUpdate(updateTriggerName(baseTable), update);
addBeforeDelete(deleteTriggerName(baseTable), update);
}
private void addBeforeUpdate(String triggerName, DbTriggerUpdate update) throws IOException {
DdlBuffer apply = update.historyTriggerBuffer();
addTriggerStart(triggerName, update, apply, " before update for each row as ");
apply.append(" NEW.sys_period_start = greatest(current_timestamp, date_add(OLD.sys_period_start, interval 1 microsecond))").endOfStatement();
appendInsertIntoHistory(apply, update.getHistoryTable(), update.getColumns());
addEndTrigger(apply);
}
private void addBeforeDelete(String triggerName, DbTriggerUpdate update) throws IOException {
DdlBuffer apply = update.historyTriggerBuffer();
addTriggerStart(triggerName, update, apply, " before delete for each row as");
appendInsertIntoHistory(apply, update.getHistoryTable(), update.getColumns());
addEndTrigger(apply);
}
private void addTriggerStart(String triggerName, DbTriggerUpdate update, DdlBuffer apply, String s) throws IOException {
apply
.append("delimiter $$").newLine()
.append("create or replace trigger ").append(triggerName).append(" for ").append(update.getBaseTable())
.append(s).newLine();
}
private void addEndTrigger(DdlBuffer apply) throws IOException {
apply.append("end_trigger")
.endOfStatement()
.append("$$").newLine()
.newLine();
}
}
@@ -12,9 +12,9 @@ import java.util.List;
*/
public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl {
public PostgresHistoryDdl() {
this.currentTimestamp = "current_timestamp";
PostgresHistoryDdl() {
this.now = "current_timestamp";
this.sysPeriodEndValue = "current_timestamp";
}
/**
@@ -36,7 +36,7 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl {
protected void addSysPeriodColumns(DdlBuffer apply, String baseTableName, String whenCreatedColumn) throws IOException {
apply
.append("alter table ").append(baseTableName)
.append(" add column ").append(sysPeriod).append(" tstzrange not null default tstzrange(").append(currentTimestamp).append(", null)")
.append(" add column ").append(sysPeriod).append(" tstzrange not null default tstzrange(").append(now).append(", null)")
.endOfStatement();
if (whenCreatedColumn != null) {
@@ -149,7 +149,7 @@ public class AutoTuneDiffCollection {
origin.setKey(point.getKey());
origin.setBeanType(point.getBeanType());
origin.setDetail(entry.getDetail().toString());
origin.setCallStack(point.getCallStack().description("\n"));
origin.setCallStack(point.getCallOrigin().getFullDescription());
origin.setOriginal(query);
if (updateTuning) {
@@ -1,6 +1,6 @@
package io.ebeaninternal.server.autotune.service;
import io.ebean.bean.CallStack;
import io.ebean.bean.CallOrigin;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.config.AutoTuneConfig;
import io.ebean.config.AutoTuneMode;
@@ -97,7 +97,7 @@ public class BaseQueryTuner {
if (!useTuning(query)) {
if (profiling) {
profiling(query, server.createCallStack());
profiling(query, server.createCallOrigin());
}
return false;
}
@@ -110,8 +110,8 @@ public class BaseQueryTuner {
}
// create a query point to identify the query
CallStack stack = server.createCallStack();
ObjectGraphNode origin = query.setOrigin(stack);
CallOrigin callOrigin = server.createCallOrigin();
ObjectGraphNode origin = query.setOrigin(callOrigin);
if (profiling) {
if (profilingListener.isProfileRequest(origin, query)) {
@@ -154,10 +154,10 @@ public class BaseQueryTuner {
}
}
private void profiling(SpiQuery<?> query, CallStack stack) {
private void profiling(SpiQuery<?> query, CallOrigin call) {
// create a query point to identify the query
ObjectGraphNode origin = query.setOrigin(stack);
ObjectGraphNode origin = query.setOrigin(call);
if (profilingListener.isProfileRequest(origin, query)) {
// collect more profiling based on profiling rate etc
query.setProfilingListener(profilingListener);
@@ -13,6 +13,8 @@ public class CachedBeanDataToBean {
public static void load(BeanDescriptor<?> desc, EntityBean bean, CachedBeanData cacheBeanData, PersistenceContext context) {
EntityBeanIntercept ebi = bean._ebean_getIntercept();
// any future lazy loading skips L2 bean cache
ebi.setLoadedFromCache(true);
BeanProperty idProperty = desc.getIdProperty();
if (desc.getInheritInfo() != null) {
@@ -0,0 +1,57 @@
package io.ebeaninternal.server.core;
import java.util.List;
/**
* Supports padding bind parameters for IN expressions.
* <p>
* We do this in order to get better hit ratio on DB query plans.
* </p>
*/
public final class BindPadding {
/**
* Pad out the Ids into common bucket sizes.
*
* @param idCollection The collection of Ids values being bound.
*/
public static void padIds(List<Object> idCollection) {
int extraIds = padding(idCollection.size());
if (extraIds > 0) {
// for performance make up the Id's to the batch size
// so we get the same query (for Ebean and the db)
Object firstId = idCollection.get(0);
for (int i = 0; i < extraIds; i++) {
// just add the first Id again
idCollection.add(firstId);
}
}
}
/**
* Extra padding on binding id's in order to get better hit ratio on DB prepared statements / query plans.
*/
static int padding(int size) {
if (size == 1) {
return 0;
}
if (size <= 5) {
return 5 - size;
}
if (size <= 10) {
return 10 - size;
}
if (size <= 20) {
return 20 - size;
}
if (size <= 40) {
return 40 - size;
}
if (size <= 50) {
return 50 - size;
}
return size <= 100 ? 100 - size : 0;
}
}
@@ -0,0 +1,14 @@
package io.ebeaninternal.server.core;
import io.ebean.bean.CallOrigin;
/**
* Creates CallOrigin based on the stack trace.
*/
public interface CallOriginFactory {
/**
* Create and return the CallStack given the stack trace elements.
*/
CallOrigin createCallOrigin();
}
@@ -1,14 +0,0 @@
package io.ebeaninternal.server.core;
import io.ebean.bean.CallStack;
/**
* Creates CallStack based on the stack trace.
*/
public interface CallStackFactory {
/**
* Create and return the CallStack given the stack trace elements.
*/
CallStack createCallStack();
}
@@ -9,6 +9,7 @@ import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebean.config.dbplatform.hana.HanaPlatform;
import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform;
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
import io.ebean.config.dbplatform.nuodb.NuoDbPlatform;
import io.ebean.config.dbplatform.oracle.OraclePlatform;
import io.ebean.config.dbplatform.postgres.Postgres8Platform;
import io.ebean.config.dbplatform.postgres.PostgresPlatform;
@@ -108,6 +109,9 @@ public class DatabasePlatformFactory {
if (dbName.equals("clickhouse")) {
return new ClickHousePlatform();
}
if (dbName.equals("nuodb")) {
return new NuoDbPlatform();
}
if (dbName.equals("sqlite")) {
return new SQLitePlatform();
}
@@ -158,6 +162,8 @@ public class DatabasePlatformFactory {
return new HsqldbPlatform();
} else if (dbProductName.contains("postgres")) {
return readPostgres(connection);
} else if (dbProductName.contains("nuo")) {
return new NuoDbPlatform();
} else if (dbProductName.contains("sqlite")) {
return new SQLitePlatform();
} else if (dbProductName.contains("db2")) {
@@ -177,7 +183,7 @@ public class DatabasePlatformFactory {
/**
* Use a select version() query as it could be Postgres or CockroachDB.
*/
private static PostgresPlatform readPostgres(Connection connection) {
private static DatabasePlatform readPostgres(Connection connection) {
// Postgres driver uses a hardcoded product name so use version() query
PreparedStatement statement = null;
ResultSet resultSet = null;
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.core;
import io.ebean.CacheMode;
import io.ebean.ExpressionList;
import io.ebean.Transaction;
import io.ebean.bean.BeanCollection;
@@ -25,7 +26,7 @@ import java.util.List;
/**
* Helper to handle lazy loading and refreshing of beans.
*/
public class DefaultBeanLoader {
class DefaultBeanLoader {
private static final Logger logger = LoggerFactory.getLogger(DefaultBeanLoader.class);
@@ -33,63 +34,19 @@ public class DefaultBeanLoader {
private final boolean onIterateUseExtraTxn;
protected DefaultBeanLoader(DefaultServer server) {
DefaultBeanLoader(DefaultServer server) {
this.server = server;
this.onIterateUseExtraTxn = server.getDatabasePlatform().useExtraTransactionOnIterateSecondaryQueries();
}
/**
* Return a batch size that might be less than the requestedBatchSize.
* <p>
* This means we can have large and variable requestedBatchSizes.
* </p>
* <p>
* We want to restrict the number of different batch sizes as we want to
* re-use the query plan cache and get DB statement re-use.
* </p>
*/
private int getBatchSize(int batchSize) {
void loadMany(LoadManyRequest loadRequest) {
if (batchSize == 1) {
// there is only one bean/collection to load
return 1;
}
if (batchSize <= 5) {
// anything less than 5 becomes 5
return 5;
}
if (batchSize <= 10) {
return 10;
}
if (batchSize <= 20) {
return 20;
}
if (batchSize <= 50) {
return 50;
}
if (batchSize <= 100) {
return 100;
}
return batchSize;
}
public void refreshMany(EntityBean parentBean, String propertyName) {
refreshMany(parentBean, propertyName, null);
}
public void loadMany(LoadManyRequest loadRequest) {
List<BeanCollection<?>> batch = loadRequest.getBatch();
int batchSize = getBatchSize(batch.size());
SpiQuery<?> query = loadRequest.createQuery(server, batchSize);
SpiQuery<?> query = loadRequest.createQuery(server);
executeQuery(loadRequest, query);
loadRequest.postLoad();
}
public void loadMany(BeanCollection<?> bc, boolean onlyIds) {
void loadMany(BeanCollection<?> bc, boolean onlyIds) {
EntityBean parentBean = bc.getOwnerBean();
String propertyName = bc.getPropertyName();
@@ -97,8 +54,8 @@ public class DefaultBeanLoader {
loadManyInternal(parentBean, propertyName, null, false, onlyIds);
}
public void refreshMany(EntityBean parentBean, String propertyName, Transaction t) {
loadManyInternal(parentBean, propertyName, t, true, false);
void refreshMany(EntityBean parentBean, String propertyName) {
loadManyInternal(parentBean, propertyName, null, true, false);
}
private void loadManyInternal(EntityBean parentBean, String propertyName, Transaction t, boolean refresh, boolean onlyIds) {
@@ -147,8 +104,7 @@ public class DefaultBeanLoader {
query.setLoadDescription("+lazy", null);
}
String idProperty = parentDesc.getIdBinder().getIdProperty();
query.select(idProperty);
query.select(parentDesc.getIdBinder().getIdProperty());
if (onlyIds) {
query.fetch(many.getName(), many.getTargetIdProperty());
@@ -185,16 +141,14 @@ public class DefaultBeanLoader {
/**
* Load a batch of beans for +query or +lazy loading.
*/
public void loadBean(LoadBeanRequest loadRequest) {
void loadBean(LoadBeanRequest loadRequest) {
List<EntityBeanIntercept> batch = loadRequest.getBatch();
if (batch.isEmpty()) {
throw new RuntimeException("Nothing in batch?");
}
int batchSize = getBatchSize(batch.size());
List<Object> idList = loadRequest.getIdList(batchSize);
List<Object> idList = loadRequest.getIdList();
if (idList.isEmpty()) {
// everything was loaded from cache
return;
@@ -202,6 +156,9 @@ public class DefaultBeanLoader {
SpiQuery<?> query = server.createQuery(loadRequest.getBeanType());
loadRequest.configureQuery(query, idList);
if (loadRequest.isLoadedFromCache()) {
query.setBeanCacheMode(CacheMode.PUT);
}
List<?> list = executeQuery(loadRequest, query);
loadRequest.postLoad(list);
@@ -228,7 +185,7 @@ public class DefaultBeanLoader {
refreshBeanInternal(bean, SpiQuery.Mode.REFRESH_BEAN, -1);
}
public void loadBean(EntityBeanIntercept ebi) {
void loadBean(EntityBeanIntercept ebi) {
refreshBeanInternal(ebi.getOwner(), SpiQuery.Mode.LAZYLOAD_BEAN, -1);
}
@@ -315,6 +272,5 @@ public class DefaultBeanLoader {
}
desc.resetManyProperties(dbBean);
}
}
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.core;
import io.ebean.bean.CallOrigin;
import io.ebean.bean.CallStack;
import java.util.Arrays;
@@ -7,7 +8,7 @@ import java.util.Arrays;
/**
* Default CallStackFactory where the Hash function for StackTraceElement includes the line number.
*/
public class DefaultCallStackFactory implements CallStackFactory {
public class DefaultCallOriginFactory implements CallOriginFactory {
private static final int IGNORE_LEADING_ELEMENTS = 5;
@@ -15,12 +16,12 @@ public class DefaultCallStackFactory implements CallStackFactory {
private final int maxCallStack;
DefaultCallStackFactory(int maxCallStack) {
DefaultCallOriginFactory(int maxCallStack) {
this.maxCallStack = maxCallStack;
}
@Override
public CallStack createCallStack() {
public CallOrigin createCallOrigin() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
// ignore the first 6 as they are always avaje stack elements
@@ -58,7 +59,7 @@ public class DefaultCallStackFactory implements CallStackFactory {
return element.getMethodName().startsWith("_ebean_");
}
private CallStack createCallStack(StackTraceElement[] finalTrace) {
private CallOrigin createCallStack(StackTraceElement[] finalTrace) {
return new CallStack(finalTrace, finalTrace[0].hashCode(), pathHash(finalTrace));
}
@@ -35,7 +35,7 @@ import io.ebean.ValuePair;
import io.ebean.Version;
import io.ebean.annotation.TxIsolation;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.CallStack;
import io.ebean.bean.CallOrigin;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
import io.ebean.bean.ObjectGraphNode;
@@ -124,8 +124,10 @@ import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.time.Clock;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
@@ -162,7 +164,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
*/
private final ClockService clockService;
private final CallStackFactory callStackFactory;
private final CallOriginFactory callStackFactory;
/**
* Handles the save, delete, updateSql CallableSql.
@@ -310,12 +312,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
/**
* Create the CallStackFactory depending if AutoTune is being used.
*/
private CallStackFactory initCallStackFactory(ServerConfig serverConfig) {
private CallOriginFactory initCallStackFactory(ServerConfig serverConfig) {
if (!serverConfig.getAutoTuneConfig().isActive()) {
// use a common CallStack for performance as we don't care with no AutoTune
return new NoopCallStackFactory();
return new NoopCallOriginFactory();
}
return new DefaultCallStackFactory(serverConfig.getMaxCallStack());
return new DefaultCallOriginFactory(serverConfig.getMaxCallStack());
}
private void configureServerPlugins() {
@@ -568,37 +570,31 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@Override
public void refreshMany(Object parentBean, String propertyName) {
beanLoader.refreshMany(checkEntityBean(parentBean), propertyName);
}
@Override
public void loadMany(LoadManyRequest loadRequest) {
beanLoader.loadMany(loadRequest);
}
@Override
public void loadMany(BeanCollection<?> bc, boolean onlyIds) {
beanLoader.loadMany(bc, onlyIds);
}
@Override
public void refresh(Object bean) {
beanLoader.refresh(checkEntityBean(bean));
}
@Override
public void loadBean(LoadBeanRequest loadRequest) {
beanLoader.loadBean(loadRequest);
}
@Override
public void loadBean(EntityBeanIntercept ebi) {
beanLoader.loadBean(ebi);
}
@@ -639,6 +635,36 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
externalModification(evt);
}
@Override
public void truncate(Class<?>... types) {
List<String> tableNames = new ArrayList<>();
for (Class<?> type : types) {
tableNames.add(getBeanDescriptor(type).getBaseTable());
}
truncate(tableNames.toArray(new String[0]));
}
@Override
public void truncate(String... tables) {
try (Connection connection = getDataSource().getConnection()) {
for (String table : tables) {
executeSql(connection, databasePlatform.truncateStatement(table));
}
connection.commit();
} catch(SQLException e) {
throw new PersistenceException("Error executing truncate", e);
}
}
private void executeSql(Connection connection, String sql) throws SQLException {
if (sql != null) {
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
transactionManager.log().sql().debug(sql);
stmt.execute();
}
}
}
/**
* Clear the query execution statistics.
*/
@@ -1103,7 +1129,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
// if determine cost and no origin for AutoTune
if (query.getParentNode() == null) {
query.setOrigin(createCallStack());
query.setOrigin(createCallOrigin());
}
return new OrmQueryRequest<>(this, queryEngine, query, (SpiTransaction) t);
@@ -2277,8 +2303,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
* </p>
*/
@Override
public CallStack createCallStack() {
return callStackFactory.createCallStack();
public CallOrigin createCallOrigin() {
return callStackFactory.createCallOrigin();
}
@Override
@@ -19,7 +19,7 @@ class DefaultSlowQueryListener implements SlowQueryListener {
String firstStack = "";
ObjectGraphNode node = event.getOriginNode();
if (node != null) {
firstStack = node.getOriginQueryPoint().getFirstStackElement();
firstStack = node.getOriginQueryPoint().getTopElement();
}
log.warn("Slow query warning - millis:{} rows:{} caller[{}] sql[{}]", event.getTimeMillis(), event.getRowCount(), firstStack, event.getSql());
}
@@ -45,6 +45,7 @@ import io.ebeaninternal.server.cluster.ClusterManager;
import io.ebeaninternal.server.core.bootup.BootupClasses;
import io.ebeaninternal.server.core.timezone.CloneDataTimeZone;
import io.ebeaninternal.server.core.timezone.DataTimeZone;
import io.ebeaninternal.server.core.timezone.LocalDataTimeZone;
import io.ebeaninternal.server.core.timezone.NoDataTimeZone;
import io.ebeaninternal.server.core.timezone.SimpleDataTimeZone;
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
@@ -366,6 +367,10 @@ public class InternalConfiguration {
return bootupClasses;
}
private Platform getPlatform() {
return getDatabasePlatform().getPlatform();
}
public DatabasePlatform getDatabasePlatform() {
return serverConfig.getDatabasePlatform();
}
@@ -509,9 +514,12 @@ public class InternalConfiguration {
String tz = serverConfig.getDataTimeZone();
if (tz == null) {
if (getPlatform() == Platform.MYSQL) {
return new LocalDataTimeZone();
}
return new NoDataTimeZone();
}
if (getDatabasePlatform().getPlatform() == Platform.ORACLE) {
if (getPlatform() == Platform.ORACLE) {
return new CloneDataTimeZone(tz);
} else {
return new SimpleDataTimeZone(tz);
@@ -0,0 +1,17 @@
package io.ebeaninternal.server.core;
import io.ebean.bean.CallOrigin;
import io.ebean.bean.CallStack;
/**
* A CallOriginFactory we can use when we don't use AutoTune.
*/
class NoopCallOriginFactory implements CallOriginFactory {
private final CallOrigin COMMON = new CallStack(Thread.currentThread().getStackTrace(), 0, 0);
@Override
public CallOrigin createCallOrigin() {
return COMMON;
}
}
@@ -1,16 +0,0 @@
package io.ebeaninternal.server.core;
import io.ebean.bean.CallStack;
/**
* A CallStackFactory we can use when we don't use AutoTune.
*/
class NoopCallStackFactory implements CallStackFactory {
private final CallStack COMMON = new CallStack(Thread.currentThread().getStackTrace(), 0, 0);
@Override
public CallStack createCallStack() {
return COMMON;
}
}
@@ -97,6 +97,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
this.queryEngine = queryEngine;
this.query = query;
this.readOnly = query.isReadOnly();
this.persistenceContext = query.getPersistenceContext();
}
public PersistenceException translate(String bindLog, String sql, SQLException e) {
@@ -115,6 +116,11 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
}
}
@Override
public boolean isPadInExpression() {
return beanDescriptor.isPadInExpression();
}
@Override
public boolean isMultiValueIdSupported() {
return beanDescriptor.isMultiValueIdSupported();
@@ -8,6 +8,7 @@ import io.ebeaninternal.dbmigration.ddlgeneration.platform.H2Ddl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.HanaColumnStoreDdl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.HsqldbDdl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.MySqlDdl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.NuoDbDdl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.Oracle10Ddl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.PlatformDdl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.PostgresDdl;
@@ -33,6 +34,8 @@ public class PlatformDdlBuilder {
return new MySqlDdl(platform);
case HSQLDB:
return new HsqldbDdl(platform);
case NUODB:
return new NuoDbDdl(platform);
case ORACLE:
return new Oracle10Ddl(platform);
case SQLITE:
@@ -0,0 +1,20 @@
package io.ebeaninternal.server.core.timezone;
import java.util.Calendar;
/**
* Implementation of DataTimeZone when single Calendar instance is used with local timezone.
*/
public class LocalDataTimeZone implements DataTimeZone {
protected final Calendar zone;
public LocalDataTimeZone() {
this.zone = Calendar.getInstance();
}
@Override
public Calendar getTimeZone() {
return zone;
}
}
@@ -112,6 +112,8 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import static io.ebeaninternal.server.persist.DmlUtil.isNullOrZero;
/**
* Describes Beans including their deployment information.
*/
@@ -1833,6 +1835,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return multiValueSupported && isSimpleId();
}
/**
* Return true if Id IN expression should have bind parameters padded.
*/
public boolean isPadInExpression() {
return !multiValueSupported && isSimpleId();
}
/**
* Return the sql for binding an id. This is the columns with table alias that
* make up the id.
@@ -2307,6 +2316,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return idBinder.convertSetId(idValue, bean);
}
/**
* Set the Id value to the bean (without type conversion).
*/
public void setId(Object idValue, EntityBean bean) {
idProperty.setValueIntercept(bean, idValue);
}
@Override
public Property getProperty(String propName) {
return findProperty(propName);
@@ -3202,7 +3218,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
}
boolean hasIdPropertyOnly(EntityBeanIntercept ebi) {
return ebi.hasIdOnly(idPropertyIndex);
return propertiesBaseScalar.length > 0 && ebi.hasIdOnly(idPropertyIndex);
}
public boolean isIdLoaded(EntityBeanIntercept ebi) {
@@ -3210,7 +3226,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
}
boolean hasIdValue(EntityBean bean) {
return (idProperty != null && !DmlUtil.isNullOrZero(idProperty.getValue(bean)));
return (idProperty != null && !isNullOrZero(idProperty.getValue(bean)));
}
boolean hasVersionProperty(EntityBeanIntercept ebi) {
@@ -675,25 +675,30 @@ final class BeanDescriptorCacheHelp<T> {
*/
EntityBean loadBeanDirect(Object id, Boolean readOnly, CachedBeanData data, PersistenceContext context) {
id = desc.convertId(id);
EntityBean bean = null;
if (context == null) {
context = new DefaultPersistenceContext();
} else {
bean = (EntityBean)desc.contextGet(context, id);
}
if (bean == null) {
bean = desc.createEntityBean();
desc.setId(id, bean);
desc.contextPut(context, id, bean);
EntityBeanIntercept ebi = bean._ebean_getIntercept();
// Not using loadContext here so no batch lazy loading for these beans
ebi.setBeanLoader(desc.getEbeanServer());
if (Boolean.TRUE.equals(readOnly)) {
ebi.setReadOnly(true);
}
ebi.setPersistenceContext(context);
}
EntityBean bean = desc.createEntityBean();
id = desc.convertSetId(id, bean);
CachedBeanDataToBean.load(desc, bean, data, context);
EntityBeanIntercept ebi = bean._ebean_getIntercept();
// Not using a loadContext for beans coming out of L2 cache
// so that means no batch lazy loading for these beans
ebi.setBeanLoader(desc.getEbeanServer());
if (Boolean.TRUE.equals(readOnly)) {
ebi.setReadOnly(true);
}
ebi.setPersistenceContext(context);
desc.contextPut(context, id, bean);
if (desc.isReadAuditing()) {
desc.readAuditBean("l2", "", bean);
}
@@ -14,6 +14,8 @@ import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import static io.ebeaninternal.server.persist.DmlUtil.isNullOrZero;
class BeanDescriptorJsonHelp<T> {
private final BeanDescriptor<T> desc;
@@ -158,7 +160,7 @@ class BeanDescriptorJsonHelp<T> {
}
Object contextBean = null;
Object id = desc.beanId(bean);
if (id != null) {
if (!isNullOrZero(id)) {
// check if the bean has already been loaded
contextBean = readJson.persistenceContextPutIfAbsent(id, bean, desc);
}
@@ -613,8 +613,12 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
}
@Override
public BeanCollection<?> createReferenceIfNull(EntityBean parentBean) {
public BeanCollection<?> createReference(EntityBean localBean, boolean forceNewReference) {
return forceNewReference ? createReference(localBean) : createReferenceIfNull(localBean);
}
@Override
public BeanCollection<?> createReferenceIfNull(EntityBean parentBean) {
Object v = getValue(parentBean);
if (v instanceof BeanCollection<?>) {
BeanCollection<?> bc = (BeanCollection<?>) v;
@@ -627,7 +631,6 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
}
public BeanCollection<?> createReference(EntityBean parentBean) {
BeanCollection<?> ref = help.createReference(parentBean);
setValue(parentBean, ref);
return ref;
@@ -24,11 +24,9 @@ public class IdBinderFactory {
* Create the IdConvertSet for the given type of Id properties.
*/
public IdBinder createIdBinder(BeanProperty id) {
if (id == null) {
// for report type beans that don't need an id
return EMPTY;
}
if (id.isEmbedded()) {
return new IdBinderEmbedded(idInExpandedForm, (BeanPropertyAssocOne<?>) id);
@@ -12,11 +12,13 @@ import java.sql.SQLException;
class DtoMetaConstructor {
private final Class<?>[] types;
private final MethodHandle handle;
private final Class<?>[] types;
private final MethodHandle handle;
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
private final ScalarType<?>[] scalarTypes;
DtoMetaConstructor(TypeManager typeManager, Constructor<?> constructor, Class<?> someClass) throws NoSuchMethodException, IllegalAccessException {
DtoMetaConstructor(TypeManager typeManager, Constructor<?> constructor, Class<?> someClass) throws NoSuchMethodException, IllegalAccessException {
this.types = constructor.getParameterTypes();
this.scalarTypes = new ScalarType[types.length];
@@ -24,13 +26,12 @@ class DtoMetaConstructor {
scalarTypes[i] = typeManager.getScalarType(types[i]);
}
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
this.handle = lookup.findConstructor(someClass, typeFor(types));
}
this.handle = LOOKUP.findConstructor(someClass, typeFor(types));
}
private MethodType typeFor(Class<?>[] types) {
return MethodType.methodType(void.class, types);
}
private MethodType typeFor(Class<?>[] types) {
return MethodType.methodType(void.class, types);
}
Class<?>[] getTypes() {
return types;
@@ -48,7 +49,7 @@ class DtoMetaConstructor {
}
}
public Object process(DataReader dataReader) throws SQLException {
public Object process(DataReader dataReader) throws SQLException {
Object[] values = new Object[scalarTypes.length];
for (int i = 0; i < scalarTypes.length; i++) {
values[i] = scalarTypes[i].read(dataReader);
@@ -56,12 +57,12 @@ class DtoMetaConstructor {
return invoke(values);
}
private Object invoke(Object... args) {
try {
return handle.invokeWithArguments(args);
private Object invoke(Object... args) {
try {
return handle.invokeWithArguments(args);
} catch (Throwable e) {
throw new RuntimeException("Unexpected error invoking constructor", e);
throw new RuntimeException("Unexpected error invoking constructor", e);
}
}
}
}
@@ -13,6 +13,8 @@ import java.sql.SQLException;
class DtoMetaProperty implements DtoReadSet {
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
private final Class<?> dtoType;
private final String name;
private final MethodHandle setter;
@@ -28,8 +30,7 @@ class DtoMetaProperty implements DtoReadSet {
Class<?> propertyType = descriptor.getPropertyType();
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
this.setter = lookup.findVirtual(dtoType, writeMethod.getName(), MethodType.methodType(void.class, propertyType));
this.setter = LOOKUP.findVirtual(dtoType, writeMethod.getName(), MethodType.methodType(void.class, propertyType));
this.scalarType = typeManager.getScalarType(propertyType);
} else {
@@ -16,6 +16,7 @@ import io.ebean.search.TextQueryString;
import io.ebean.search.TextSimple;
import io.ebeaninternal.api.SpiExpressionFactory;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.server.grammer.EqlParser;
import java.util.Arrays;
import java.util.Collection;
@@ -151,6 +152,11 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
return new BitwiseExpression(propertyName, BitwiseOp.AND, flags, "=", match);
}
@Override
public <T> void where(ExpressionList<T> list, String expressions, Object[] params) {
EqlParser.parseWhere(expressions, list, this, params);
}
/**
* Equal To - property equal to the given value.
*/
@@ -470,8 +470,14 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
}
@Override
public ExpressionList<T> filterMany(String prop) {
return query.filterMany(prop);
public ExpressionList<T> filterMany(String manyProperty) {
return query.filterMany(manyProperty);
}
@Override
public Query<T> filterMany(String manyProperty, String expressions, Object... params) {
query.filterMany(manyProperty).where(expressions, params);
return query;
}
@Override
@@ -745,6 +751,12 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return this;
}
@Override
public ExpressionList<T> where(String expressions, Object... params) {
expr.where(this, expressions, params);
return this;
}
@Override
public ExpressionList<T> bitwiseAny(String propertyName, long flags) {
add(expr.bitwiseAny(propertyName, flags));
@@ -5,6 +5,7 @@ import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.api.SpiExpressionValidation;
import io.ebeaninternal.server.core.BindPadding;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.id.IdBinder;
@@ -46,6 +47,10 @@ public class IdInExpression extends NonPrepareExpression {
@Override
public void prepareExpression(BeanQueryRequest<?> request) {
multiValueIdSupported = request.isMultiValueIdSupported();
if (!multiValueIdSupported && !idCollection.isEmpty() && request.isPadInExpression()) {
// pad out the ids for better hit ratio on DB query plans
BindPadding.padIds(idCollection);
}
}
@Override
@@ -326,11 +326,21 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
return exprList.exampleLike(example);
}
@Override
public ExpressionList<T> where(String expressions, Object... params) {
throw new IllegalStateException("where not allowed on Junction expression list");
}
@Override
public ExpressionList<T> filterMany(String prop) {
throw new IllegalStateException("filterMany not allowed on Junction expression list");
}
@Override
public Query<T> filterMany(String manyProperty, String expressions, Object... params) {
throw new IllegalStateException("filterMany not allowed on Junction expression list");
}
@Override
public Query<T> usingTransaction(Transaction transaction) {
return exprList.usingTransaction(transaction);
@@ -1,25 +1,15 @@
package io.ebeaninternal.server.grammer;
import io.ebean.Expression;
import io.ebean.ExpressionFactory;
import io.ebean.ExpressionList;
import io.ebean.FetchConfig;
import io.ebean.LikeType;
import io.ebean.OrderBy;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.server.grammer.antlr.EQLBaseListener;
import io.ebeaninternal.server.grammer.antlr.EQLLexer;
import io.ebeaninternal.server.grammer.antlr.EQLParser;
import io.ebeaninternal.server.util.ArrayStack;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
import java.util.ArrayList;
import java.util.List;
class EqlAdapter<T> extends EQLBaseListener {
private static final OperatorMapping operatorMapping = new OperatorMapping();
class EqlAdapter<T> extends EqlWhereListener<T> {
private static final String DISTINCT = "distinct";
@@ -29,27 +19,33 @@ class EqlAdapter<T> extends EQLBaseListener {
private final SpiQuery<T> query;
private final EqlAdapterHelper helper;
private final ExpressionFactory expressionFactory;
private ArrayStack<ExpressionList<T>> textStack;
private ArrayStack<ExpressionList<T>> whereStack;
private boolean textMode;
private List<Object> inValues;
private String inPropertyName;
public EqlAdapter(SpiQuery<T> query) {
EqlAdapter(SpiQuery<T> query) {
this.query = query;
this.helper = new EqlAdapterHelper(this);
this.expressionFactory = query.getExpressionFactory();
}
@Override
ExpressionFactory expressionFactory() {
return expressionFactory;
}
@Override
Object namedParam(String parameterName) {
return query.createNamedParameter(parameterName);
}
@Override
Object positionParam(String paramPosition) {
return query.createNamedParameter(paramPosition);
}
/**
* Return the current expression list that expressions should be added to.
*/
protected ExpressionList<T> peekExprList() {
@Override
ExpressionList<T> peekExprList() {
if (textMode) {
// return the current text expression list
@@ -74,28 +70,6 @@ class EqlAdapter<T> extends EQLBaseListener {
return textStack.peek();
}
/**
* Push the expression list onto the appropriate stack.
*/
private void pushExprList(ExpressionList<T> list) {
if (textMode) {
textStack.push(list);
} else {
whereStack.push(list);
}
}
/**
* End a list of expressions added by 'OR'.
*/
private void popJunction() {
if (textMode) {
textStack.pop();
} else {
whereStack.pop();
}
}
@Override
public void enterSelect_clause(EQLParser.Select_clauseContext ctx) {
@@ -199,233 +173,4 @@ class EqlAdapter<T> extends EQLBaseListener {
}
}
private String getLeftHandSidePath(ParserRuleContext ctx) {
TerminalNode pathToken = ctx.getToken(EQLLexer.PATH_VARIABLE, 0);
return pathToken.getText();
}
@Override
public void enterInrange_expression(EQLParser.Inrange_expressionContext ctx) {
checkChildren(ctx, 5);
String path = getLeftHandSidePath(ctx);
EqlOperator op = getOperator(ctx);
if (op != EqlOperator.INRANGE) {
throw new IllegalStateException("Expecting INRANGE operator but got " + op);
}
helper.addInRange(path, child(ctx, 2), child(ctx, 4));
}
@Override
public void enterBetween_expression(EQLParser.Between_expressionContext ctx) {
checkChildren(ctx, 5);
String path = getLeftHandSidePath(ctx);
EqlOperator op = getOperator(ctx);
if (op != EqlOperator.BETWEEN) {
throw new IllegalStateException("Expecting BETWEEN operator but got " + op);
}
helper.addBetween(path, child(ctx, 2), child(ctx, 4));
}
@Override
public void enterPropertyBetween_expression(EQLParser.PropertyBetween_expressionContext ctx) {
checkChildren(ctx, 5);
String rawValue = child(ctx, 0);
EqlOperator op = getOperator(ctx);
if (op != EqlOperator.BETWEEN) {
throw new IllegalStateException("Expecting BETWEEN operator but got " + op);
}
helper.addBetweenProperty(rawValue, child(ctx, 2), child(ctx, 4));
}
@Override
public void enterIn_expression(EQLParser.In_expressionContext ctx) {
this.inValues = new ArrayList<>();
this.inPropertyName = getLeftHandSidePath(ctx);
}
@Override
public void enterIn_value(EQLParser.In_valueContext ctx) {
int childCount = ctx.getChildCount();
for (int i = 0; i < childCount; i++) {
String text = child(ctx, i);
if (isValue(text)) {
inValues.add(helper.bind(text));
}
}
}
private String child(ParserRuleContext ctx, int position) {
ParseTree child = ctx.getChild(position);
return child.getText();
}
private boolean isValue(String text) {
return text.length() != 1 || (!text.equals("(") && !text.equals(")") && !text.equals(","));
}
@Override
public void exitIn_expression(EQLParser.In_expressionContext ctx) {
helper.addIn(inPropertyName, inValues);
}
@Override
public void enterIsNull_expression(EQLParser.IsNull_expressionContext ctx) {
String path = getLeftHandSidePath(ctx);
peekExprList().isNull(path);
}
@Override
public void enterIsNotNull_expression(EQLParser.IsNotNull_expressionContext ctx) {
String path = getLeftHandSidePath(ctx);
peekExprList().isNotNull(path);
}
@Override
public void enterIsEmpty_expression(EQLParser.IsEmpty_expressionContext ctx) {
String path = getLeftHandSidePath(ctx);
peekExprList().isEmpty(path);
}
@Override
public void enterIsNotEmpty_expression(EQLParser.IsNotEmpty_expressionContext ctx) {
String path = getLeftHandSidePath(ctx);
peekExprList().isNotEmpty(path);
}
@Override
public void enterLike_expression(EQLParser.Like_expressionContext ctx) {
addExpression(ctx);
}
@Override
public void enterComparison_expression(EQLParser.Comparison_expressionContext ctx) {
addExpression(ctx);
}
private void addExpression(ParserRuleContext ctx) {
int childCount = ctx.getChildCount();
if (childCount < 3) {
throw new IllegalStateException("expecting 3 children for comparison? " + ctx);
}
String operator = child(ctx, 1);
EqlOperator op = operatorMapping.get(operator);
if (op == null) {
throw new IllegalStateException("No operator found for " + operator);
}
String path = getLeftHandSidePath(ctx);
String rhs = child(ctx, 2);
if (path.equals(rhs)) {
// the 'value operator path' form
// invert the operator and use LHS as RHS
op = invert(op);
rhs = child(ctx, 0);
}
// RHS is Path, Literal or Named input parameter
helper.addExpression(path, op, rhs);
}
private EqlOperator invert(EqlOperator op) {
switch (op) {
// no change
case EQ:
return EqlOperator.EQ;
case IEQ:
return EqlOperator.IEQ;
case INE:
return EqlOperator.INE;
case NE:
return EqlOperator.NE;
// invert
case LT:
return EqlOperator.GT;
case LTE:
return EqlOperator.GTE;
case GT:
return EqlOperator.LT;
case GTE:
return EqlOperator.LTE;
default:
throw new IllegalStateException("Can not invert operator " + op);
}
}
@Override
public void enterConditional_term(EQLParser.Conditional_termContext ctx) {
int childCount = ctx.getChildCount();
if (childCount > 1) {
pushExprList(peekExprList().and());
}
}
@Override
public void exitConditional_term(EQLParser.Conditional_termContext ctx) {
if (ctx.getChildCount() > 1) {
popJunction();
}
}
@Override
public void enterConditional_expression(EQLParser.Conditional_expressionContext ctx) {
if (ctx.getChildCount() > 1) {
pushExprList(peekExprList().or());
}
}
@Override
public void exitConditional_expression(EQLParser.Conditional_expressionContext ctx) {
if (ctx.getChildCount() > 1) {
popJunction();
}
}
@Override
public void enterConditional_factor(EQLParser.Conditional_factorContext ctx) {
if (ctx.getChildCount() > 1) {
pushExprList(peekExprList().not());
}
}
@Override
public void exitConditional_factor(EQLParser.Conditional_factorContext ctx) {
if (ctx.getChildCount() > 1) {
popJunction();
}
}
private EqlOperator getOperator(ParserRuleContext ctx) {
String operator = child(ctx, 1);
EqlOperator op = operatorMapping.get(operator);
if (op == null) {
throw new IllegalStateException("No operator found for " + operator);
}
return op;
}
/**
* Check for the minimum number of children.
*/
private void checkChildren(ParserRuleContext ctx, int min) {
if (ctx.getChildCount() < min) {
throw new IllegalStateException("expecting " + min + " children for comparison but got " + ctx.getChildCount());
}
}
public Object namedParam(String parameterName) {
return query.createNamedParameter(parameterName);
}
public Expression like(boolean caseInsensitive, LikeType likeType, String property, Object bindValue) {
return query.getExpressionFactory().like(property, bindValue, caseInsensitive, likeType);
}
public Expression ieq(String property, Object bindValue) {
return query.getExpressionFactory().ieqObject(property, bindValue);
}
public Expression ine(String property, Object bindValue) {
return query.getExpressionFactory().ineObject(property, bindValue);
}
}
@@ -1,148 +0,0 @@
package io.ebeaninternal.server.grammer;
import io.ebean.ExpressionList;
import io.ebean.LikeType;
import java.math.BigDecimal;
import java.util.List;
class EqlAdapterHelper {
private final EqlAdapter<?> owner;
public EqlAdapterHelper(EqlAdapter<?> owner) {
this.owner = owner;
}
enum ValueType {
NAMED_PARAM,
STRING,
BOOL,
NUMBER
}
private ValueType getValueType(String valueAsText) {
char firstChar = Character.toLowerCase(valueAsText.charAt(0));
switch (firstChar) {
case ':':
return ValueType.NAMED_PARAM;
case 't':
return ValueType.BOOL;
case 'f':
return ValueType.BOOL;
case '\'':
return ValueType.STRING;
default:
if (Character.isDigit(firstChar)) {
return ValueType.NUMBER;
}
throw new IllegalArgumentException("Unexpected first character in value [" + valueAsText + "]");
}
}
protected void addBetweenProperty(String rawValue, String lowProperty, String highProperty) {
peekExprList().betweenProperties(lowProperty, highProperty, bind(rawValue));
}
protected void addBetween(String path, String value1, String value2) {
peekExprList().between(path, bind(value1), bind(value2));
}
protected void addInRange(String path, String value1, String value2) {
peekExprList().inRange(path, bind(value1), bind(value2));
}
protected void addIn(String path, List<Object> inValues) {
peekExprList().in(path, inValues);
}
protected void addExpression(String path, EqlOperator op, String value) {
switch (op) {
case EQ:
peekExprList().eq(path, bind(value));
break;
case IEQ:
peekExprList().add(owner.ieq(path, bind(value)));
break;
case NE:
peekExprList().ne(path, bind(value));
break;
case INE:
peekExprList().add(owner.ine(path, bind(value)));
break;
case GT:
peekExprList().gt(path, bind(value));
break;
case LT:
peekExprList().lt(path, bind(value));
break;
case GTE:
peekExprList().ge(path, bind(value));
break;
case LTE:
peekExprList().le(path, bind(value));
break;
case LIKE:
addLike(false, LikeType.RAW, path, bind(value));
break;
case CONTAINS:
addLike(false, LikeType.CONTAINS, path, bind(value));
break;
case STARTS_WITH:
addLike(false, LikeType.STARTS_WITH, path, bind(value));
break;
case ENDS_WITH:
addLike(false, LikeType.ENDS_WITH, path, bind(value));
break;
case ILIKE:
addLike(true, LikeType.RAW, path, bind(value));
break;
case ICONTAINS:
addLike(true, LikeType.CONTAINS, path, bind(value));
break;
case ISTARTS_WITH:
addLike(true, LikeType.STARTS_WITH, path, bind(value));
break;
case IENDS_WITH:
addLike(true, LikeType.ENDS_WITH, path, bind(value));
break;
default:
throw new IllegalStateException("Unhandled operator " + op);
}
}
private void addLike(boolean caseInsensitive, LikeType likeType, String path, Object bindValue) {
peekExprList().add(owner.like(caseInsensitive, likeType, path, bindValue));
}
protected Object bind(String value) {
ValueType valueType = getValueType(value);
return getBindValue(valueType, value);
}
private ExpressionList<?> peekExprList() {
return owner.peekExprList();
}
private Object getBindValue(ValueType valueType, String value) {
switch (valueType) {
case BOOL:
return Boolean.parseBoolean(value);
case NUMBER:
return new BigDecimal(value);
case STRING:
return unquote(value);
case NAMED_PARAM:
return owner.namedParam(value.substring(1));
default:
throw new IllegalArgumentException("Unhandled valueType " + valueType);
}
}
private String unquote(String value) {
return value.substring(1, value.length() - 1);
}
}
@@ -10,6 +10,9 @@ enum EqlOperator {
LTE,
GT,
GTE,
EQORNULL,
LTORNULL,
GTORNULL,
CONTAINS,
STARTS_WITH,
ENDS_WITH,
@@ -1,5 +1,7 @@
package io.ebeaninternal.server.grammer;
import io.ebean.ExpressionFactory;
import io.ebean.ExpressionList;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.server.grammer.antlr.EQLLexer;
import io.ebeaninternal.server.grammer.antlr.EQLParser;
@@ -22,20 +24,21 @@ public class EqlParser {
*/
public static <T> void parse(String raw, SpiQuery<T> query) {
EQLLexer lexer = new EQLLexer(CharStreams.fromString(raw));
CommonTokenStream tokens = new CommonTokenStream(lexer);
EQLParser parser = new EQLParser(tokens);
EQLParser parser = new EQLParser(new CommonTokenStream(new EQLLexer(CharStreams.fromString(raw))));
parser.addErrorListener(errorListener);
EQLParser.Select_statementContext context = parser.select_statement();
EqlAdapter<T> adapter = new EqlAdapter<>(query);
ParseTreeWalker walker = new ParseTreeWalker();
walker.walk(adapter, context);
new ParseTreeWalker().walk(new EqlAdapter<>(query), parser.select_statement());
query.simplifyExpressions();
}
public static <T> void parseWhere(String raw, ExpressionList<T> where, ExpressionFactory expr, Object[] params) {
EQLParser parser = new EQLParser(new CommonTokenStream(new EQLLexer(CharStreams.fromString(raw))));
parser.addErrorListener(errorListener);
new ParseTreeWalker().walk(new EqlWhereAdapter<>(where, expr, params), parser.conditional_expression());
}
static class ErrorListener extends BaseErrorListener {
@Override
@@ -0,0 +1,9 @@
package io.ebeaninternal.server.grammer;
enum EqlValueType {
POS_PARAM,
NAMED_PARAM,
STRING,
BOOL,
NUMBER
}
@@ -0,0 +1,48 @@
package io.ebeaninternal.server.grammer;
import io.ebean.ExpressionFactory;
import io.ebean.ExpressionList;
import io.ebeaninternal.server.util.ArrayStack;
class EqlWhereAdapter<T> extends EqlWhereListener<T> {
private final ExpressionList<T> where;
private final ExpressionFactory expr;
private final Object[] params;
private int paramIndex;
EqlWhereAdapter(ExpressionList<T> where, ExpressionFactory expr, Object[] params) {
this.where = where;
this.expr = expr;
this.params = params;
}
@Override
ExpressionList<T> peekExprList() {
if (whereStack == null) {
whereStack = new ArrayStack<>();
whereStack.push(where);
}
return whereStack.peek();
}
@Override
ExpressionFactory expressionFactory() {
return expr;
}
@Override
Object positionParam(String paramPosition) {
if ("?".equals(paramPosition)) {
return params[paramIndex++];
}
final int pos = Integer.parseInt(paramPosition.substring(1));
return params[pos -1];
}
@Override
Object namedParam(String substring) {
throw new RuntimeException("Not supported");
}
}
@@ -0,0 +1,459 @@
package io.ebeaninternal.server.grammer;
import io.ebean.Expression;
import io.ebean.ExpressionFactory;
import io.ebean.ExpressionList;
import io.ebean.LikeType;
import io.ebeaninternal.server.grammer.antlr.EQLBaseListener;
import io.ebeaninternal.server.grammer.antlr.EQLLexer;
import io.ebeaninternal.server.grammer.antlr.EQLParser;
import io.ebeaninternal.server.util.ArrayStack;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
abstract class EqlWhereListener<T> extends EQLBaseListener {
private static final OperatorMapping operatorMapping = new OperatorMapping();
ArrayStack<ExpressionList<T>> textStack;
ArrayStack<ExpressionList<T>> whereStack;
boolean textMode;
private boolean inWithEmpty;
private List<Object> inValues;
private String inPropertyName;
/**
* Return the current expression list that expressions should be added to.
*/
abstract ExpressionList<T> peekExprList();
abstract ExpressionFactory expressionFactory();
abstract Object namedParam(String paramName);
abstract Object positionParam(String paramPosition);
/**
* Push the expression list onto the appropriate stack.
*/
private void pushExprList(ExpressionList<T> list) {
if (textMode) {
textStack.push(list);
} else {
whereStack.push(list);
}
}
/**
* End a list of expressions added by 'OR'.
*/
private void popJunction() {
if (textMode) {
textStack.pop();
} else {
whereStack.pop();
}
}
private String getLeftHandSidePath(ParserRuleContext ctx) {
TerminalNode pathToken = ctx.getToken(EQLLexer.PATH_VARIABLE, 0);
return pathToken.getText();
}
@Override
public void enterInrange_expression(EQLParser.Inrange_expressionContext ctx) {
checkChildren(ctx, 5);
String path = getLeftHandSidePath(ctx);
EqlOperator op = getOperator(ctx);
if (op != EqlOperator.INRANGE) {
throw new IllegalStateException("Expecting INRANGE operator but got " + op);
}
addInRange(path, child(ctx, 2), child(ctx, 4));
}
@Override
public void enterBetween_expression(EQLParser.Between_expressionContext ctx) {
checkChildren(ctx, 5);
String path = getLeftHandSidePath(ctx);
EqlOperator op = getOperator(ctx);
if (op != EqlOperator.BETWEEN) {
throw new IllegalStateException("Expecting BETWEEN operator but got " + op);
}
addBetween(path, child(ctx, 2), child(ctx, 4));
}
@Override
public void enterPropertyBetween_expression(EQLParser.PropertyBetween_expressionContext ctx) {
checkChildren(ctx, 5);
String rawValue = child(ctx, 0);
EqlOperator op = getOperator(ctx);
if (op != EqlOperator.BETWEEN) {
throw new IllegalStateException("Expecting BETWEEN operator but got " + op);
}
addBetweenProperty(rawValue, child(ctx, 2), child(ctx, 4));
}
@Override
public void enterInOrEmpty_expression(EQLParser.InOrEmpty_expressionContext ctx) {
this.inWithEmpty = true;
this.inValues = new ArrayList<>();
this.inPropertyName = getLeftHandSidePath(ctx);
}
@Override
public void enterIn_expression(EQLParser.In_expressionContext ctx) {
this.inValues = new ArrayList<>();
this.inPropertyName = getLeftHandSidePath(ctx);
}
@Override
public void enterIn_value(EQLParser.In_valueContext ctx) {
int childCount = ctx.getChildCount();
for (int i = 0; i < childCount; i++) {
String text = child(ctx, i);
if (text.startsWith("?")) {
inValues = toList(getBindValue(EqlValueType.POS_PARAM, text));
} else {
if (inWithEmpty) {
throw new IllegalArgumentException("Sorry, can only use inOrEmpty with positioned parameters");
}
if (isValue(text)) {
inValues.add(bind(text));
}
}
}
}
@SuppressWarnings("unchecked")
private List<Object> toList(Object value) {
if (value == null) return null;
if (value instanceof List) {
return (List<Object>)value;
}
if (value instanceof Set) {
return new ArrayList<>((Set)value);
}
throw new IllegalArgumentException("Expected List of Set but got " + value);
}
@Override
public void exitIn_expression(EQLParser.In_expressionContext ctx) {
peekExprList().in(inPropertyName, inValues);
}
@Override
public void exitInOrEmpty_expression(EQLParser.InOrEmpty_expressionContext ctx) {
inWithEmpty = false;
peekExprList().inOrEmpty(inPropertyName, inValues);
}
String child(ParserRuleContext ctx, int position) {
ParseTree child = ctx.getChild(position);
return child.getText();
}
private boolean isValue(String text) {
return text.length() != 1 || (!text.equals("(") && !text.equals(")") && !text.equals(","));
}
@Override
public void enterIsNull_expression(EQLParser.IsNull_expressionContext ctx) {
String path = getLeftHandSidePath(ctx);
peekExprList().isNull(path);
}
@Override
public void enterIsNotNull_expression(EQLParser.IsNotNull_expressionContext ctx) {
String path = getLeftHandSidePath(ctx);
peekExprList().isNotNull(path);
}
@Override
public void enterIsEmpty_expression(EQLParser.IsEmpty_expressionContext ctx) {
String path = getLeftHandSidePath(ctx);
peekExprList().isEmpty(path);
}
@Override
public void enterIsNotEmpty_expression(EQLParser.IsNotEmpty_expressionContext ctx) {
String path = getLeftHandSidePath(ctx);
peekExprList().isNotEmpty(path);
}
@Override
public void enterLike_expression(EQLParser.Like_expressionContext ctx) {
addExpression(ctx);
}
@Override
public void enterComparison_expression(EQLParser.Comparison_expressionContext ctx) {
addExpression(ctx);
}
private void addExpression(ParserRuleContext ctx) {
int childCount = ctx.getChildCount();
if (childCount < 3) {
throw new IllegalStateException("expecting 3 children for comparison? " + ctx);
}
String operator = child(ctx, 1);
EqlOperator op = operatorMapping.get(operator);
if (op == null) {
throw new IllegalStateException("No operator found for " + operator);
}
String path = getLeftHandSidePath(ctx);
String rhs = child(ctx, 2);
if (path.equals(rhs)) {
// the 'value operator path' form
// invert the operator and use LHS as RHS
op = invert(op);
rhs = child(ctx, 0);
}
// RHS is Path, Literal or Named input parameter
addExpression(path, op, rhs);
}
private EqlOperator invert(EqlOperator op) {
switch (op) {
// no change
case EQ:
return EqlOperator.EQ;
case IEQ:
return EqlOperator.IEQ;
case INE:
return EqlOperator.INE;
case NE:
return EqlOperator.NE;
// invert
case LT:
return EqlOperator.GT;
case LTE:
return EqlOperator.GTE;
case GT:
return EqlOperator.LT;
case GTE:
return EqlOperator.LTE;
case EQORNULL:
return EqlOperator.EQORNULL;
case GTORNULL:
return EqlOperator.LTORNULL;
case LTORNULL:
return EqlOperator.GTORNULL;
default:
throw new IllegalStateException("Can not invert operator " + op);
}
}
@Override
public void enterConditional_term(EQLParser.Conditional_termContext ctx) {
int childCount = ctx.getChildCount();
if (childCount > 1) {
pushExprList(peekExprList().and());
}
}
@Override
public void exitConditional_term(EQLParser.Conditional_termContext ctx) {
if (ctx.getChildCount() > 1) {
popJunction();
}
}
@Override
public void enterConditional_expression(EQLParser.Conditional_expressionContext ctx) {
if (ctx.getChildCount() > 1) {
pushExprList(peekExprList().or());
}
}
@Override
public void exitConditional_expression(EQLParser.Conditional_expressionContext ctx) {
if (ctx.getChildCount() > 1) {
popJunction();
}
}
@Override
public void enterConditional_factor(EQLParser.Conditional_factorContext ctx) {
if (ctx.getChildCount() > 1) {
pushExprList(peekExprList().not());
}
}
@Override
public void exitConditional_factor(EQLParser.Conditional_factorContext ctx) {
if (ctx.getChildCount() > 1) {
popJunction();
}
}
private EqlOperator getOperator(ParserRuleContext ctx) {
String operator = child(ctx, 1);
EqlOperator op = operatorMapping.get(operator);
if (op == null) {
throw new IllegalStateException("No operator found for " + operator);
}
return op;
}
/**
* Check for the minimum number of children.
*/
void checkChildren(ParserRuleContext ctx, int min) {
if (ctx.getChildCount() < min) {
throw new IllegalStateException("expecting " + min + " children for comparison but got " + ctx.getChildCount());
}
}
private Expression like(boolean caseInsensitive, LikeType likeType, String property, Object bindValue) {
return expressionFactory().like(property, bindValue, caseInsensitive, likeType);
}
private Expression ieq(String property, Object bindValue) {
return expressionFactory().ieqObject(property, bindValue);
}
private Expression ine(String property, Object bindValue) {
return expressionFactory().ineObject(property, bindValue);
}
private EqlValueType getValueType(String valueAsText) {
char firstChar = Character.toLowerCase(valueAsText.charAt(0));
switch (firstChar) {
case '?':
return EqlValueType.POS_PARAM;
case ':':
return EqlValueType.NAMED_PARAM;
case 't':
return EqlValueType.BOOL;
case 'f':
return EqlValueType.BOOL;
case '\'':
return EqlValueType.STRING;
default:
if (Character.isDigit(firstChar)) {
return EqlValueType.NUMBER;
}
throw new IllegalArgumentException("Unexpected first character in value [" + valueAsText + "]");
}
}
private void addBetweenProperty(String rawValue, String lowProperty, String highProperty) {
peekExprList().betweenProperties(lowProperty, highProperty, bind(rawValue));
}
private void addBetween(String path, String value1, String value2) {
peekExprList().between(path, bind(value1), bind(value2));
}
private void addInRange(String path, String value1, String value2) {
peekExprList().inRange(path, bind(value1), bind(value2));
}
private void addExpression(String path, EqlOperator op, String value) {
switch (op) {
case EQ:
peekExprList().eq(path, bind(value));
break;
case IEQ:
peekExprList().add(ieq(path, bind(value)));
break;
case NE:
peekExprList().ne(path, bind(value));
break;
case INE:
peekExprList().add(ine(path, bind(value)));
break;
case GT:
peekExprList().gt(path, bind(value));
break;
case LT:
peekExprList().lt(path, bind(value));
break;
case GTE:
peekExprList().ge(path, bind(value));
break;
case LTE:
peekExprList().le(path, bind(value));
break;
case LIKE:
addLike(false, LikeType.RAW, path, bind(value));
break;
case CONTAINS:
addLike(false, LikeType.CONTAINS, path, bind(value));
break;
case STARTS_WITH:
addLike(false, LikeType.STARTS_WITH, path, bind(value));
break;
case ENDS_WITH:
addLike(false, LikeType.ENDS_WITH, path, bind(value));
break;
case ILIKE:
addLike(true, LikeType.RAW, path, bind(value));
break;
case ICONTAINS:
addLike(true, LikeType.CONTAINS, path, bind(value));
break;
case ISTARTS_WITH:
addLike(true, LikeType.STARTS_WITH, path, bind(value));
break;
case IENDS_WITH:
addLike(true, LikeType.ENDS_WITH, path, bind(value));
break;
case EQORNULL:
peekExprList().eqOrNull(path, bind(value));
break;
case GTORNULL:
peekExprList().gtOrNull(path, bind(value));
break;
case LTORNULL:
peekExprList().ltOrNull(path, bind(value));
break;
default:
throw new IllegalStateException("Unhandled operator " + op);
}
}
private void addLike(boolean caseInsensitive, LikeType likeType, String path, Object bindValue) {
peekExprList().add(like(caseInsensitive, likeType, path, bindValue));
}
private Object bind(String value) {
return getBindValue(getValueType(value), value);
}
private Object getBindValue(EqlValueType valueType, String value) {
switch (valueType) {
case BOOL:
return Boolean.parseBoolean(value);
case NUMBER:
return new BigDecimal(value);
case STRING:
return unquote(value);
case POS_PARAM:
return positionParam(value);
case NAMED_PARAM:
return namedParam(value.substring(1));
default:
throw new IllegalArgumentException("Unhandled valueType " + valueType);
}
}
private String unquote(String value) {
return value.substring(1, value.length() - 1);
}
}
@@ -5,9 +5,9 @@ import java.util.Map;
class OperatorMapping {
final Map<String, EqlOperator> map = new HashMap<>();
private final Map<String, EqlOperator> map = new HashMap<>();
public OperatorMapping() {
OperatorMapping() {
map.put("eq", EqlOperator.EQ);
map.put("=", EqlOperator.EQ);
map.put("ieq", EqlOperator.IEQ);
@@ -42,7 +42,11 @@ class OperatorMapping {
map.put("ilike", EqlOperator.ILIKE);
map.put("inrange", EqlOperator.INRANGE);
map.put("inRange", EqlOperator.INRANGE);
map.put("between", EqlOperator.BETWEEN);
map.put("eqOrNull", EqlOperator.EQORNULL);
map.put("gtOrNull", EqlOperator.GTORNULL);
map.put("ltOrNull", EqlOperator.LTORNULL);
}
public EqlOperator get(String key) {
File diff suppressed because one or more lines are too long
@@ -58,17 +58,22 @@ T__56=57
T__57=58
T__58=59
T__59=60
INPUT_VARIABLE=61
PATH_VARIABLE=62
QUOTED_PATH_VARIABLE=63
PROP_FORMULA=64
BOOLEAN_LITERAL=65
NUMBER_LITERAL=66
DOUBLE=67
INT=68
ZERO=69
STRING_LITERAL=70
WS=71
T__60=61
T__61=62
T__62=63
T__63=64
T__64=65
INPUT_VARIABLE=66
PATH_VARIABLE=67
QUOTED_PATH_VARIABLE=68
PROP_FORMULA=69
BOOLEAN_LITERAL=70
NUMBER_LITERAL=71
DOUBLE=72
INT=73
ZERO=74
STRING_LITERAL=75
WS=76
'('=1
')'=2
'select'=3
@@ -91,42 +96,47 @@ WS=71
'or'=20
'and'=21
'not'=22
'in'=23
'between'=24
'inrange'=25
'inOrEmpty'=23
'in'=24
'between'=25
'to'=26
'is'=27
'null'=28
'isNull'=29
'isNotNull'=30
'notNull'=31
'empty'=32
'isEmpty'=33
'isNotEmpty'=34
'notEmpty'=35
'like'=36
'ilike'=37
'contains'=38
'icontains'=39
'startsWith'=40
'istartsWith'=41
'endsWith'=42
'iendsWith'=43
'='=44
'eq'=45
'>'=46
'gt'=47
'>='=48
'ge'=49
'gte'=50
'<'=51
'lt'=52
'<='=53
'le'=54
'lte'=55
'<>'=56
'!='=57
'ne'=58
'ieq'=59
'ine'=60
'0'=69
'inrange'=27
'inRange'=28
'is'=29
'null'=30
'isNull'=31
'isNotNull'=32
'notNull'=33
'empty'=34
'isEmpty'=35
'isNotEmpty'=36
'notEmpty'=37
'like'=38
'ilike'=39
'contains'=40
'icontains'=41
'startsWith'=42
'istartsWith'=43
'endsWith'=44
'iendsWith'=45
'='=46
'eq'=47
'>'=48
'gt'=49
'>='=50
'ge'=51
'gte'=52
'<'=53
'lt'=54
'<='=55
'le'=56
'lte'=57
'<>'=58
'!='=59
'ne'=60
'ieq'=61
'ine'=62
'eqOrNull'=63
'gtOrNull'=64
'ltOrNull'=65
'0'=74
@@ -347,6 +347,18 @@ public class EQLBaseListener implements EQLListener {
* <p>The default implementation does nothing.</p>
*/
@Override public void exitAny_expression(EQLParser.Any_expressionContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterInOrEmpty_expression(EQLParser.InOrEmpty_expressionContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitInOrEmpty_expression(EQLParser.InOrEmpty_expressionContext ctx) { }
/**
* {@inheritDoc}
*
@@ -395,6 +407,18 @@ public class EQLBaseListener implements EQLListener {
* <p>The default implementation does nothing.</p>
*/
@Override public void exitInrange_expression(EQLParser.Inrange_expressionContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterInrange_op(EQLParser.Inrange_opContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitInrange_op(EQLParser.Inrange_opContext ctx) { }
/**
* {@inheritDoc}
*
File diff suppressed because one or more lines are too long
@@ -28,9 +28,9 @@ public class EQLLexer extends Lexer {
T__38=39, T__39=40, T__40=41, T__41=42, T__42=43, T__43=44, T__44=45,
T__45=46, T__46=47, T__47=48, T__48=49, T__49=50, T__50=51, T__51=52,
T__52=53, T__53=54, T__54=55, T__55=56, T__56=57, T__57=58, T__58=59,
T__59=60, INPUT_VARIABLE=61, PATH_VARIABLE=62, QUOTED_PATH_VARIABLE=63,
PROP_FORMULA=64, BOOLEAN_LITERAL=65, NUMBER_LITERAL=66, DOUBLE=67, INT=68,
ZERO=69, STRING_LITERAL=70, WS=71;
T__59=60, T__60=61, T__61=62, T__62=63, T__63=64, T__64=65, INPUT_VARIABLE=66,
PATH_VARIABLE=67, QUOTED_PATH_VARIABLE=68, PROP_FORMULA=69, BOOLEAN_LITERAL=70,
NUMBER_LITERAL=71, DOUBLE=72, INT=73, ZERO=74, STRING_LITERAL=75, WS=76;
public static String[] channelNames = {
"DEFAULT_TOKEN_CHANNEL", "HIDDEN"
};
@@ -48,9 +48,10 @@ public class EQLLexer extends Lexer {
"T__33", "T__34", "T__35", "T__36", "T__37", "T__38", "T__39", "T__40",
"T__41", "T__42", "T__43", "T__44", "T__45", "T__46", "T__47", "T__48",
"T__49", "T__50", "T__51", "T__52", "T__53", "T__54", "T__55", "T__56",
"T__57", "T__58", "T__59", "INPUT_VARIABLE", "PATH_VARIABLE", "QUOTED_PATH_VARIABLE",
"PROP_FORMULA", "BOOLEAN_LITERAL", "NUMBER_LITERAL", "DOUBLE", "INT",
"ZERO", "STRING_LITERAL", "WS"
"T__57", "T__58", "T__59", "T__60", "T__61", "T__62", "T__63", "T__64",
"INPUT_VARIABLE", "PATH_VARIABLE", "QUOTED_PATH_VARIABLE", "PROP_FORMULA",
"BOOLEAN_LITERAL", "NUMBER_LITERAL", "DOUBLE", "INT", "ZERO", "STRING_LITERAL",
"WS"
};
}
public static final String[] ruleNames = makeRuleNames();
@@ -60,12 +61,14 @@ public class EQLLexer extends Lexer {
null, "'('", "')'", "'select'", "'distinct'", "'where'", "'order'", "'by'",
"','", "'nulls'", "'first'", "'last'", "'asc'", "'desc'", "'limit'",
"'offset'", "'fetch'", "'+'", "'query'", "'lazy'", "'or'", "'and'", "'not'",
"'in'", "'between'", "'inrange'", "'to'", "'is'", "'null'", "'isNull'",
"'isNotNull'", "'notNull'", "'empty'", "'isEmpty'", "'isNotEmpty'", "'notEmpty'",
"'like'", "'ilike'", "'contains'", "'icontains'", "'startsWith'", "'istartsWith'",
"'endsWith'", "'iendsWith'", "'='", "'eq'", "'>'", "'gt'", "'>='", "'ge'",
"'gte'", "'<'", "'lt'", "'<='", "'le'", "'lte'", "'<>'", "'!='", "'ne'",
"'ieq'", "'ine'", null, null, null, null, null, null, null, null, "'0'"
"'inOrEmpty'", "'in'", "'between'", "'to'", "'inrange'", "'inRange'",
"'is'", "'null'", "'isNull'", "'isNotNull'", "'notNull'", "'empty'",
"'isEmpty'", "'isNotEmpty'", "'notEmpty'", "'like'", "'ilike'", "'contains'",
"'icontains'", "'startsWith'", "'istartsWith'", "'endsWith'", "'iendsWith'",
"'='", "'eq'", "'>'", "'gt'", "'>='", "'ge'", "'gte'", "'<'", "'lt'",
"'<='", "'le'", "'lte'", "'<>'", "'!='", "'ne'", "'ieq'", "'ine'", "'eqOrNull'",
"'gtOrNull'", "'ltOrNull'", null, null, null, null, null, null, null,
null, "'0'"
};
}
private static final String[] _LITERAL_NAMES = makeLiteralNames();
@@ -76,9 +79,9 @@ public class EQLLexer extends Lexer {
null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null,
null, "INPUT_VARIABLE", "PATH_VARIABLE", "QUOTED_PATH_VARIABLE", "PROP_FORMULA",
"BOOLEAN_LITERAL", "NUMBER_LITERAL", "DOUBLE", "INT", "ZERO", "STRING_LITERAL",
"WS"
null, null, null, null, null, null, "INPUT_VARIABLE", "PATH_VARIABLE",
"QUOTED_PATH_VARIABLE", "PROP_FORMULA", "BOOLEAN_LITERAL", "NUMBER_LITERAL",
"DOUBLE", "INT", "ZERO", "STRING_LITERAL", "WS"
};
}
private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames();
@@ -140,7 +143,7 @@ public class EQLLexer extends Lexer {
public ATN getATN() { return _ATN; }
public static final String _serializedATN =
"\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2I\u0249\b\1\4\2\t"+
"\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2N\u0289\b\1\4\2\t"+
"\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13"+
"\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+
"\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31"+
@@ -148,188 +151,211 @@ public class EQLLexer extends Lexer {
"\t!\4\"\t\"\4#\t#\4$\t$\4%\t%\4&\t&\4\'\t\'\4(\t(\4)\t)\4*\t*\4+\t+\4"+
",\t,\4-\t-\4.\t.\4/\t/\4\60\t\60\4\61\t\61\4\62\t\62\4\63\t\63\4\64\t"+
"\64\4\65\t\65\4\66\t\66\4\67\t\67\48\t8\49\t9\4:\t:\4;\t;\4<\t<\4=\t="+
"\4>\t>\4?\t?\4@\t@\4A\tA\4B\tB\4C\tC\4D\tD\4E\tE\4F\tF\4G\tG\4H\tH\3\2"+
"\3\2\3\3\3\3\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3"+
"\5\3\5\3\6\3\6\3\6\3\6\3\6\3\6\3\7\3\7\3\7\3\7\3\7\3\7\3\b\3\b\3\b\3\t"+
"\3\t\3\n\3\n\3\n\3\n\3\n\3\n\3\13\3\13\3\13\3\13\3\13\3\13\3\f\3\f\3\f"+
"\3\f\3\f\3\r\3\r\3\r\3\r\3\16\3\16\3\16\3\16\3\16\3\17\3\17\3\17\3\17"+
"\3\17\3\17\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\21\3\21\3\21\3\21\3\21"+
"\3\21\3\22\3\22\3\23\3\23\3\23\3\23\3\23\3\23\3\24\3\24\3\24\3\24\3\24"+
"\3\25\3\25\3\25\3\26\3\26\3\26\3\26\3\27\3\27\3\27\3\27\3\30\3\30\3\30"+
"\3\31\3\31\3\31\3\31\3\31\3\31\3\31\3\31\3\32\3\32\3\32\3\32\3\32\3\32"+
"\3\32\3\32\3\33\3\33\3\33\3\34\3\34\3\34\3\35\3\35\3\35\3\35\3\35\3\36"+
"\3\36\3\36\3\36\3\36\3\36\3\36\3\37\3\37\3\37\3\37\3\37\3\37\3\37\3\37"+
"\3\37\3\37\3 \3 \3 \3 \3 \3 \3 \3 \3!\3!\3!\3!\3!\3!\3\"\3\"\3\"\3\"\3"+
"\"\3\"\3\"\3\"\3#\3#\3#\3#\3#\3#\3#\3#\3#\3#\3#\3$\3$\3$\3$\3$\3$\3$\3"+
"$\3$\3%\3%\3%\3%\3%\3&\3&\3&\3&\3&\3&\3\'\3\'\3\'\3\'\3\'\3\'\3\'\3\'"+
"\3\'\3(\3(\3(\3(\3(\3(\3(\3(\3(\3(\3)\3)\3)\3)\3)\3)\3)\3)\3)\3)\3)\3"+
"*\3*\3*\3*\3*\3*\3*\3*\3*\3*\3*\3*\3+\3+\3+\3+\3+\3+\3+\3+\3+\3,\3,\3"+
",\3,\3,\3,\3,\3,\3,\3,\3-\3-\3.\3.\3.\3/\3/\3\60\3\60\3\60\3\61\3\61\3"+
"\61\3\62\3\62\3\62\3\63\3\63\3\63\3\63\3\64\3\64\3\65\3\65\3\65\3\66\3"+
"\66\3\66\3\67\3\67\3\67\38\38\38\38\39\39\39\3:\3:\3:\3;\3;\3;\3<\3<\3"+
"<\3<\3=\3=\3=\3=\3>\3>\3>\7>\u01d4\n>\f>\16>\u01d7\13>\3?\3?\7?\u01db"+
"\n?\f?\16?\u01de\13?\3@\3@\3@\3@\3A\3A\3A\3A\3A\3A\3A\3A\3A\3A\3A\3A\3"+
"A\3A\3A\3A\3A\3A\3A\3A\3A\3A\3A\3A\3A\3A\3A\3A\3A\3A\3A\3A\3A\3A\3A\3"+
"A\3A\3A\3A\3A\3A\3A\5A\u020e\nA\3B\3B\3B\3B\3B\3B\3B\3B\3B\5B\u0219\n"+
"B\3C\5C\u021c\nC\3C\3C\5C\u0220\nC\3C\3C\5C\u0224\nC\3D\6D\u0227\nD\r"+
"D\16D\u0228\3D\3D\7D\u022d\nD\fD\16D\u0230\13D\3E\3E\7E\u0234\nE\fE\16"+
"E\u0237\13E\3F\3F\3G\3G\3G\3G\7G\u023f\nG\fG\16G\u0242\13G\3G\3G\3H\3"+
"H\3H\3H\2\2I\3\3\5\4\7\5\t\6\13\7\r\b\17\t\21\n\23\13\25\f\27\r\31\16"+
"\4>\t>\4?\t?\4@\t@\4A\tA\4B\tB\4C\tC\4D\tD\4E\tE\4F\tF\4G\tG\4H\tH\4I"+
"\tI\4J\tJ\4K\tK\4L\tL\4M\tM\3\2\3\2\3\3\3\3\3\4\3\4\3\4\3\4\3\4\3\4\3"+
"\4\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\6\3\6\3\6\3\6\3\6\3\6\3\7\3\7"+
"\3\7\3\7\3\7\3\7\3\b\3\b\3\b\3\t\3\t\3\n\3\n\3\n\3\n\3\n\3\n\3\13\3\13"+
"\3\13\3\13\3\13\3\13\3\f\3\f\3\f\3\f\3\f\3\r\3\r\3\r\3\r\3\16\3\16\3\16"+
"\3\16\3\16\3\17\3\17\3\17\3\17\3\17\3\17\3\20\3\20\3\20\3\20\3\20\3\20"+
"\3\20\3\21\3\21\3\21\3\21\3\21\3\21\3\22\3\22\3\23\3\23\3\23\3\23\3\23"+
"\3\23\3\24\3\24\3\24\3\24\3\24\3\25\3\25\3\25\3\26\3\26\3\26\3\26\3\27"+
"\3\27\3\27\3\27\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\31"+
"\3\31\3\31\3\32\3\32\3\32\3\32\3\32\3\32\3\32\3\32\3\33\3\33\3\33\3\34"+
"\3\34\3\34\3\34\3\34\3\34\3\34\3\34\3\35\3\35\3\35\3\35\3\35\3\35\3\35"+
"\3\35\3\36\3\36\3\36\3\37\3\37\3\37\3\37\3\37\3 \3 \3 \3 \3 \3 \3 \3!"+
"\3!\3!\3!\3!\3!\3!\3!\3!\3!\3\"\3\"\3\"\3\"\3\"\3\"\3\"\3\"\3#\3#\3#\3"+
"#\3#\3#\3$\3$\3$\3$\3$\3$\3$\3$\3%\3%\3%\3%\3%\3%\3%\3%\3%\3%\3%\3&\3"+
"&\3&\3&\3&\3&\3&\3&\3&\3\'\3\'\3\'\3\'\3\'\3(\3(\3(\3(\3(\3(\3)\3)\3)"+
"\3)\3)\3)\3)\3)\3)\3*\3*\3*\3*\3*\3*\3*\3*\3*\3*\3+\3+\3+\3+\3+\3+\3+"+
"\3+\3+\3+\3+\3,\3,\3,\3,\3,\3,\3,\3,\3,\3,\3,\3,\3-\3-\3-\3-\3-\3-\3-"+
"\3-\3-\3.\3.\3.\3.\3.\3.\3.\3.\3.\3.\3/\3/\3\60\3\60\3\60\3\61\3\61\3"+
"\62\3\62\3\62\3\63\3\63\3\63\3\64\3\64\3\64\3\65\3\65\3\65\3\65\3\66\3"+
"\66\3\67\3\67\3\67\38\38\38\39\39\39\3:\3:\3:\3:\3;\3;\3;\3<\3<\3<\3="+
"\3=\3=\3>\3>\3>\3>\3?\3?\3?\3?\3@\3@\3@\3@\3@\3@\3@\3@\3@\3A\3A\3A\3A"+
"\3A\3A\3A\3A\3A\3B\3B\3B\3B\3B\3B\3B\3B\3B\3C\3C\3C\7C\u020b\nC\fC\16"+
"C\u020e\13C\3C\3C\7C\u0212\nC\fC\16C\u0215\13C\5C\u0217\nC\3D\3D\7D\u021b"+
"\nD\fD\16D\u021e\13D\3E\3E\3E\3E\3F\3F\3F\3F\3F\3F\3F\3F\3F\3F\3F\3F\3"+
"F\3F\3F\3F\3F\3F\3F\3F\3F\3F\3F\3F\3F\3F\3F\3F\3F\3F\3F\3F\3F\3F\3F\3"+
"F\3F\3F\3F\3F\3F\3F\5F\u024e\nF\3G\3G\3G\3G\3G\3G\3G\3G\3G\5G\u0259\n"+
"G\3H\5H\u025c\nH\3H\3H\5H\u0260\nH\3H\3H\5H\u0264\nH\3I\6I\u0267\nI\r"+
"I\16I\u0268\3I\3I\7I\u026d\nI\fI\16I\u0270\13I\3J\3J\7J\u0274\nJ\fJ\16"+
"J\u0277\13J\3K\3K\3L\3L\3L\3L\7L\u027f\nL\fL\16L\u0282\13L\3L\3L\3M\3"+
"M\3M\3M\2\2N\3\3\5\4\7\5\t\6\13\7\r\b\17\t\21\n\23\13\25\f\27\r\31\16"+
"\33\17\35\20\37\21!\22#\23%\24\'\25)\26+\27-\30/\31\61\32\63\33\65\34"+
"\67\359\36;\37= ?!A\"C#E$G%I&K\'M(O)Q*S+U,W-Y.[/]\60_\61a\62c\63e\64g"+
"\65i\66k\67m8o9q:s;u<w=y>{?}@\177A\u0081B\u0083C\u0085D\u0087E\u0089F"+
"\u008bG\u008dH\u008fI\3\2\t\5\2C\\aac|\6\2\62;C\\aac|\7\2\60\60\62;C\\"+
"aac|\3\2\62;\3\2\63;\3\2))\5\2\13\f\17\17\"\"\2\u0258\2\3\3\2\2\2\2\5"+
"\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2\2\2\2\17\3\2\2\2"+
"\2\21\3\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27\3\2\2\2\2\31\3\2\2\2\2\33"+
"\3\2\2\2\2\35\3\2\2\2\2\37\3\2\2\2\2!\3\2\2\2\2#\3\2\2\2\2%\3\2\2\2\2"+
"\'\3\2\2\2\2)\3\2\2\2\2+\3\2\2\2\2-\3\2\2\2\2/\3\2\2\2\2\61\3\2\2\2\2"+
"\63\3\2\2\2\2\65\3\2\2\2\2\67\3\2\2\2\29\3\2\2\2\2;\3\2\2\2\2=\3\2\2\2"+
"\2?\3\2\2\2\2A\3\2\2\2\2C\3\2\2\2\2E\3\2\2\2\2G\3\2\2\2\2I\3\2\2\2\2K"+
"\3\2\2\2\2M\3\2\2\2\2O\3\2\2\2\2Q\3\2\2\2\2S\3\2\2\2\2U\3\2\2\2\2W\3\2"+
"\2\2\2Y\3\2\2\2\2[\3\2\2\2\2]\3\2\2\2\2_\3\2\2\2\2a\3\2\2\2\2c\3\2\2\2"+
"\2e\3\2\2\2\2g\3\2\2\2\2i\3\2\2\2\2k\3\2\2\2\2m\3\2\2\2\2o\3\2\2\2\2q"+
"\3\2\2\2\2s\3\2\2\2\2u\3\2\2\2\2w\3\2\2\2\2y\3\2\2\2\2{\3\2\2\2\2}\3\2"+
"\2\2\2\177\3\2\2\2\2\u0081\3\2\2\2\2\u0083\3\2\2\2\2\u0085\3\2\2\2\2\u0087"+
"\3\2\2\2\2\u0089\3\2\2\2\2\u008b\3\2\2\2\2\u008d\3\2\2\2\2\u008f\3\2\2"+
"\2\3\u0091\3\2\2\2\5\u0093\3\2\2\2\7\u0095\3\2\2\2\t\u009c\3\2\2\2\13"+
"\u00a5\3\2\2\2\r\u00ab\3\2\2\2\17\u00b1\3\2\2\2\21\u00b4\3\2\2\2\23\u00b6"+
"\3\2\2\2\25\u00bc\3\2\2\2\27\u00c2\3\2\2\2\31\u00c7\3\2\2\2\33\u00cb\3"+
"\2\2\2\35\u00d0\3\2\2\2\37\u00d6\3\2\2\2!\u00dd\3\2\2\2#\u00e3\3\2\2\2"+
"%\u00e5\3\2\2\2\'\u00eb\3\2\2\2)\u00f0\3\2\2\2+\u00f3\3\2\2\2-\u00f7\3"+
"\2\2\2/\u00fb\3\2\2\2\61\u00fe\3\2\2\2\63\u0106\3\2\2\2\65\u010e\3\2\2"+
"\2\67\u0111\3\2\2\29\u0114\3\2\2\2;\u0119\3\2\2\2=\u0120\3\2\2\2?\u012a"+
"\3\2\2\2A\u0132\3\2\2\2C\u0138\3\2\2\2E\u0140\3\2\2\2G\u014b\3\2\2\2I"+
"\u0154\3\2\2\2K\u0159\3\2\2\2M\u015f\3\2\2\2O\u0168\3\2\2\2Q\u0172\3\2"+
"\2\2S\u017d\3\2\2\2U\u0189\3\2\2\2W\u0192\3\2\2\2Y\u019c\3\2\2\2[\u019e"+
"\3\2\2\2]\u01a1\3\2\2\2_\u01a3\3\2\2\2a\u01a6\3\2\2\2c\u01a9\3\2\2\2e"+
"\u01ac\3\2\2\2g\u01b0\3\2\2\2i\u01b2\3\2\2\2k\u01b5\3\2\2\2m\u01b8\3\2"+
"\2\2o\u01bb\3\2\2\2q\u01bf\3\2\2\2s\u01c2\3\2\2\2u\u01c5\3\2\2\2w\u01c8"+
"\3\2\2\2y\u01cc\3\2\2\2{\u01d0\3\2\2\2}\u01d8\3\2\2\2\177\u01df\3\2\2"+
"\2\u0081\u020d\3\2\2\2\u0083\u0218\3\2\2\2\u0085\u0223\3\2\2\2\u0087\u0226"+
"\3\2\2\2\u0089\u0231\3\2\2\2\u008b\u0238\3\2\2\2\u008d\u023a\3\2\2\2\u008f"+
"\u0245\3\2\2\2\u0091\u0092\7*\2\2\u0092\4\3\2\2\2\u0093\u0094\7+\2\2\u0094"+
"\6\3\2\2\2\u0095\u0096\7u\2\2\u0096\u0097\7g\2\2\u0097\u0098\7n\2\2\u0098"+
"\u0099\7g\2\2\u0099\u009a\7e\2\2\u009a\u009b\7v\2\2\u009b\b\3\2\2\2\u009c"+
"\u009d\7f\2\2\u009d\u009e\7k\2\2\u009e\u009f\7u\2\2\u009f\u00a0\7v\2\2"+
"\u00a0\u00a1\7k\2\2\u00a1\u00a2\7p\2\2\u00a2\u00a3\7e\2\2\u00a3\u00a4"+
"\7v\2\2\u00a4\n\3\2\2\2\u00a5\u00a6\7y\2\2\u00a6\u00a7\7j\2\2\u00a7\u00a8"+
"\7g\2\2\u00a8\u00a9\7t\2\2\u00a9\u00aa\7g\2\2\u00aa\f\3\2\2\2\u00ab\u00ac"+
"\7q\2\2\u00ac\u00ad\7t\2\2\u00ad\u00ae\7f\2\2\u00ae\u00af\7g\2\2\u00af"+
"\u00b0\7t\2\2\u00b0\16\3\2\2\2\u00b1\u00b2\7d\2\2\u00b2\u00b3\7{\2\2\u00b3"+
"\20\3\2\2\2\u00b4\u00b5\7.\2\2\u00b5\22\3\2\2\2\u00b6\u00b7\7p\2\2\u00b7"+
"\u00b8\7w\2\2\u00b8\u00b9\7n\2\2\u00b9\u00ba\7n\2\2\u00ba\u00bb\7u\2\2"+
"\u00bb\24\3\2\2\2\u00bc\u00bd\7h\2\2\u00bd\u00be\7k\2\2\u00be\u00bf\7"+
"t\2\2\u00bf\u00c0\7u\2\2\u00c0\u00c1\7v\2\2\u00c1\26\3\2\2\2\u00c2\u00c3"+
"\7n\2\2\u00c3\u00c4\7c\2\2\u00c4\u00c5\7u\2\2\u00c5\u00c6\7v\2\2\u00c6"+
"\30\3\2\2\2\u00c7\u00c8\7c\2\2\u00c8\u00c9\7u\2\2\u00c9\u00ca\7e\2\2\u00ca"+
"\32\3\2\2\2\u00cb\u00cc\7f\2\2\u00cc\u00cd\7g\2\2\u00cd\u00ce\7u\2\2\u00ce"+
"\u00cf\7e\2\2\u00cf\34\3\2\2\2\u00d0\u00d1\7n\2\2\u00d1\u00d2\7k\2\2\u00d2"+
"\u00d3\7o\2\2\u00d3\u00d4\7k\2\2\u00d4\u00d5\7v\2\2\u00d5\36\3\2\2\2\u00d6"+
"\u00d7\7q\2\2\u00d7\u00d8\7h\2\2\u00d8\u00d9\7h\2\2\u00d9\u00da\7u\2\2"+
"\u00da\u00db\7g\2\2\u00db\u00dc\7v\2\2\u00dc \3\2\2\2\u00dd\u00de\7h\2"+
"\2\u00de\u00df\7g\2\2\u00df\u00e0\7v\2\2\u00e0\u00e1\7e\2\2\u00e1\u00e2"+
"\7j\2\2\u00e2\"\3\2\2\2\u00e3\u00e4\7-\2\2\u00e4$\3\2\2\2\u00e5\u00e6"+
"\7s\2\2\u00e6\u00e7\7w\2\2\u00e7\u00e8\7g\2\2\u00e8\u00e9\7t\2\2\u00e9"+
"\u00ea\7{\2\2\u00ea&\3\2\2\2\u00eb\u00ec\7n\2\2\u00ec\u00ed\7c\2\2\u00ed"+
"\u00ee\7|\2\2\u00ee\u00ef\7{\2\2\u00ef(\3\2\2\2\u00f0\u00f1\7q\2\2\u00f1"+
"\u00f2\7t\2\2\u00f2*\3\2\2\2\u00f3\u00f4\7c\2\2\u00f4\u00f5\7p\2\2\u00f5"+
"\u00f6\7f\2\2\u00f6,\3\2\2\2\u00f7\u00f8\7p\2\2\u00f8\u00f9\7q\2\2\u00f9"+
"\u00fa\7v\2\2\u00fa.\3\2\2\2\u00fb\u00fc\7k\2\2\u00fc\u00fd\7p\2\2\u00fd"+
"\60\3\2\2\2\u00fe\u00ff\7d\2\2\u00ff\u0100\7g\2\2\u0100\u0101\7v\2\2\u0101"+
"\u0102\7y\2\2\u0102\u0103\7g\2\2\u0103\u0104\7g\2\2\u0104\u0105\7p\2\2"+
"\u0105\62\3\2\2\2\u0106\u0107\7k\2\2\u0107\u0108\7p\2\2\u0108\u0109\7"+
"t\2\2\u0109\u010a\7c\2\2\u010a\u010b\7p\2\2\u010b\u010c\7i\2\2\u010c\u010d"+
"\7g\2\2\u010d\64\3\2\2\2\u010e\u010f\7v\2\2\u010f\u0110\7q\2\2\u0110\66"+
"\3\2\2\2\u0111\u0112\7k\2\2\u0112\u0113\7u\2\2\u01138\3\2\2\2\u0114\u0115"+
"\7p\2\2\u0115\u0116\7w\2\2\u0116\u0117\7n\2\2\u0117\u0118\7n\2\2\u0118"+
":\3\2\2\2\u0119\u011a\7k\2\2\u011a\u011b\7u\2\2\u011b\u011c\7P\2\2\u011c"+
"\u011d\7w\2\2\u011d\u011e\7n\2\2\u011e\u011f\7n\2\2\u011f<\3\2\2\2\u0120"+
"\u0121\7k\2\2\u0121\u0122\7u\2\2\u0122\u0123\7P\2\2\u0123\u0124\7q\2\2"+
"\u0124\u0125\7v\2\2\u0125\u0126\7P\2\2\u0126\u0127\7w\2\2\u0127\u0128"+
"\7n\2\2\u0128\u0129\7n\2\2\u0129>\3\2\2\2\u012a\u012b\7p\2\2\u012b\u012c"+
"\7q\2\2\u012c\u012d\7v\2\2\u012d\u012e\7P\2\2\u012e\u012f\7w\2\2\u012f"+
"\u0130\7n\2\2\u0130\u0131\7n\2\2\u0131@\3\2\2\2\u0132\u0133\7g\2\2\u0133"+
"\u0134\7o\2\2\u0134\u0135\7r\2\2\u0135\u0136\7v\2\2\u0136\u0137\7{\2\2"+
"\u0137B\3\2\2\2\u0138\u0139\7k\2\2\u0139\u013a\7u\2\2\u013a\u013b\7G\2"+
"\2\u013b\u013c\7o\2\2\u013c\u013d\7r\2\2\u013d\u013e\7v\2\2\u013e\u013f"+
"\7{\2\2\u013fD\3\2\2\2\u0140\u0141\7k\2\2\u0141\u0142\7u\2\2\u0142\u0143"+
"\7P\2\2\u0143\u0144\7q\2\2\u0144\u0145\7v\2\2\u0145\u0146\7G\2\2\u0146"+
"\u0147\7o\2\2\u0147\u0148\7r\2\2\u0148\u0149\7v\2\2\u0149\u014a\7{\2\2"+
"\u014aF\3\2\2\2\u014b\u014c\7p\2\2\u014c\u014d\7q\2\2\u014d\u014e\7v\2"+
"\2\u014e\u014f\7G\2\2\u014f\u0150\7o\2\2\u0150\u0151\7r\2\2\u0151\u0152"+
"\7v\2\2\u0152\u0153\7{\2\2\u0153H\3\2\2\2\u0154\u0155\7n\2\2\u0155\u0156"+
"\7k\2\2\u0156\u0157\7m\2\2\u0157\u0158\7g\2\2\u0158J\3\2\2\2\u0159\u015a"+
"\7k\2\2\u015a\u015b\7n\2\2\u015b\u015c\7k\2\2\u015c\u015d\7m\2\2\u015d"+
"\u015e\7g\2\2\u015eL\3\2\2\2\u015f\u0160\7e\2\2\u0160\u0161\7q\2\2\u0161"+
"\u0162\7p\2\2\u0162\u0163\7v\2\2\u0163\u0164\7c\2\2\u0164\u0165\7k\2\2"+
"\u0165\u0166\7p\2\2\u0166\u0167\7u\2\2\u0167N\3\2\2\2\u0168\u0169\7k\2"+
"\2\u0169\u016a\7e\2\2\u016a\u016b\7q\2\2\u016b\u016c\7p\2\2\u016c\u016d"+
"\7v\2\2\u016d\u016e\7c\2\2\u016e\u016f\7k\2\2\u016f\u0170\7p\2\2\u0170"+
"\u0171\7u\2\2\u0171P\3\2\2\2\u0172\u0173\7u\2\2\u0173\u0174\7v\2\2\u0174"+
"\u0175\7c\2\2\u0175\u0176\7t\2\2\u0176\u0177\7v\2\2\u0177\u0178\7u\2\2"+
"\u0178\u0179\7Y\2\2\u0179\u017a\7k\2\2\u017a\u017b\7v\2\2\u017b\u017c"+
"\7j\2\2\u017cR\3\2\2\2\u017d\u017e\7k\2\2\u017e\u017f\7u\2\2\u017f\u0180"+
"\7v\2\2\u0180\u0181\7c\2\2\u0181\u0182\7t\2\2\u0182\u0183\7v\2\2\u0183"+
"\u0184\7u\2\2\u0184\u0185\7Y\2\2\u0185\u0186\7k\2\2\u0186\u0187\7v\2\2"+
"\u0187\u0188\7j\2\2\u0188T\3\2\2\2\u0189\u018a\7g\2\2\u018a\u018b\7p\2"+
"\2\u018b\u018c\7f\2\2\u018c\u018d\7u\2\2\u018d\u018e\7Y\2\2\u018e\u018f"+
"\7k\2\2\u018f\u0190\7v\2\2\u0190\u0191\7j\2\2\u0191V\3\2\2\2\u0192\u0193"+
"\7k\2\2\u0193\u0194\7g\2\2\u0194\u0195\7p\2\2\u0195\u0196\7f\2\2\u0196"+
"\u0197\7u\2\2\u0197\u0198\7Y\2\2\u0198\u0199\7k\2\2\u0199\u019a\7v\2\2"+
"\u019a\u019b\7j\2\2\u019bX\3\2\2\2\u019c\u019d\7?\2\2\u019dZ\3\2\2\2\u019e"+
"\u019f\7g\2\2\u019f\u01a0\7s\2\2\u01a0\\\3\2\2\2\u01a1\u01a2\7@\2\2\u01a2"+
"^\3\2\2\2\u01a3\u01a4\7i\2\2\u01a4\u01a5\7v\2\2\u01a5`\3\2\2\2\u01a6\u01a7"+
"\7@\2\2\u01a7\u01a8\7?\2\2\u01a8b\3\2\2\2\u01a9\u01aa\7i\2\2\u01aa\u01ab"+
"\7g\2\2\u01abd\3\2\2\2\u01ac\u01ad\7i\2\2\u01ad\u01ae\7v\2\2\u01ae\u01af"+
"\7g\2\2\u01aff\3\2\2\2\u01b0\u01b1\7>\2\2\u01b1h\3\2\2\2\u01b2\u01b3\7"+
"n\2\2\u01b3\u01b4\7v\2\2\u01b4j\3\2\2\2\u01b5\u01b6\7>\2\2\u01b6\u01b7"+
"\7?\2\2\u01b7l\3\2\2\2\u01b8\u01b9\7n\2\2\u01b9\u01ba\7g\2\2\u01ban\3"+
"\2\2\2\u01bb\u01bc\7n\2\2\u01bc\u01bd\7v\2\2\u01bd\u01be\7g\2\2\u01be"+
"p\3\2\2\2\u01bf\u01c0\7>\2\2\u01c0\u01c1\7@\2\2\u01c1r\3\2\2\2\u01c2\u01c3"+
"\7#\2\2\u01c3\u01c4\7?\2\2\u01c4t\3\2\2\2\u01c5\u01c6\7p\2\2\u01c6\u01c7"+
"\7g\2\2\u01c7v\3\2\2\2\u01c8\u01c9\7k\2\2\u01c9\u01ca\7g\2\2\u01ca\u01cb"+
"\7s\2\2\u01cbx\3\2\2\2\u01cc\u01cd\7k\2\2\u01cd\u01ce\7p\2\2\u01ce\u01cf"+
"\7g\2\2\u01cfz\3\2\2\2\u01d0\u01d1\7<\2\2\u01d1\u01d5\t\2\2\2\u01d2\u01d4"+
"\t\3\2\2\u01d3\u01d2\3\2\2\2\u01d4\u01d7\3\2\2\2\u01d5\u01d3\3\2\2\2\u01d5"+
"\u01d6\3\2\2\2\u01d6|\3\2\2\2\u01d7\u01d5\3\2\2\2\u01d8\u01dc\t\2\2\2"+
"\u01d9\u01db\t\4\2\2\u01da\u01d9\3\2\2\2\u01db\u01de\3\2\2\2\u01dc\u01da"+
"\3\2\2\2\u01dc\u01dd\3\2\2\2\u01dd~\3\2\2\2\u01de\u01dc\3\2\2\2\u01df"+
"\u01e0\7b\2\2\u01e0\u01e1\5}?\2\u01e1\u01e2\7b\2\2\u01e2\u0080\3\2\2\2"+
"\u01e3\u01e4\7u\2\2\u01e4\u01e5\7w\2\2\u01e5\u01e6\7o\2\2\u01e6\u01e7"+
"\7*\2\2\u01e7\u01e8\3\2\2\2\u01e8\u01e9\5}?\2\u01e9\u01ea\7+\2\2\u01ea"+
"\u020e\3\2\2\2\u01eb\u01ec\7o\2\2\u01ec\u01ed\7c\2\2\u01ed\u01ee\7z\2"+
"\2\u01ee\u01ef\7*\2\2\u01ef\u01f0\3\2\2\2\u01f0\u01f1\5}?\2\u01f1\u01f2"+
"\7+\2\2\u01f2\u020e\3\2\2\2\u01f3\u01f4\7o\2\2\u01f4\u01f5\7k\2\2\u01f5"+
"\u01f6\7p\2\2\u01f6\u01f7\7*\2\2\u01f7\u01f8\3\2\2\2\u01f8\u01f9\5}?\2"+
"\u01f9\u01fa\7+\2\2\u01fa\u020e\3\2\2\2\u01fb\u01fc\7c\2\2\u01fc\u01fd"+
"\7x\2\2\u01fd\u01fe\7i\2\2\u01fe\u01ff\7*\2\2\u01ff\u0200\3\2\2\2\u0200"+
"\u0201\5}?\2\u0201\u0202\7+\2\2\u0202\u020e\3\2\2\2\u0203\u0204\7e\2\2"+
"\u0204\u0205\7q\2\2\u0205\u0206\7w\2\2\u0206\u0207\7p\2\2\u0207\u0208"+
"\7v\2\2\u0208\u0209\7*\2\2\u0209\u020a\3\2\2\2\u020a\u020b\5}?\2\u020b"+
"\u020c\7+\2\2\u020c\u020e\3\2\2\2\u020d\u01e3\3\2\2\2\u020d\u01eb\3\2"+
"\2\2\u020d\u01f3\3\2\2\2\u020d\u01fb\3\2\2\2\u020d\u0203\3\2\2\2\u020e"+
"\u0082\3\2\2\2\u020f\u0210\7v\2\2\u0210\u0211\7t\2\2\u0211\u0212\7w\2"+
"\2\u0212\u0219\7g\2\2\u0213\u0214\7h\2\2\u0214\u0215\7c\2\2\u0215\u0216"+
"\7n\2\2\u0216\u0217\7u\2\2\u0217\u0219\7g\2\2\u0218\u020f\3\2\2\2\u0218"+
"\u0213\3\2\2\2\u0219\u0084\3\2\2\2\u021a\u021c\7/\2\2\u021b\u021a\3\2"+
"\2\2\u021b\u021c\3\2\2\2\u021c\u021d\3\2\2\2\u021d\u0224\5\u0087D\2\u021e"+
"\u0220\7/\2\2\u021f\u021e\3\2\2\2\u021f\u0220\3\2\2\2\u0220\u0221\3\2"+
"\2\2\u0221\u0224\5\u0089E\2\u0222\u0224\5\u008bF\2\u0223\u021b\3\2\2\2"+
"\u0223\u021f\3\2\2\2\u0223\u0222\3\2\2\2\u0224\u0086\3\2\2\2\u0225\u0227"+
"\t\5\2\2\u0226\u0225\3\2\2\2\u0227\u0228\3\2\2\2\u0228\u0226\3\2\2\2\u0228"+
"\u0229\3\2\2\2\u0229\u022a\3\2\2\2\u022a\u022e\7\60\2\2\u022b\u022d\t"+
"\5\2\2\u022c\u022b\3\2\2\2\u022d\u0230\3\2\2\2\u022e\u022c\3\2\2\2\u022e"+
"\u022f\3\2\2\2\u022f\u0088\3\2\2\2\u0230\u022e\3\2\2\2\u0231\u0235\t\6"+
"\2\2\u0232\u0234\t\5\2\2\u0233\u0232\3\2\2\2\u0234\u0237\3\2\2\2\u0235"+
"\u0233\3\2\2\2\u0235\u0236\3\2\2\2\u0236\u008a\3\2\2\2\u0237\u0235\3\2"+
"\2\2\u0238\u0239\7\62\2\2\u0239\u008c\3\2\2\2\u023a\u0240\7)\2\2\u023b"+
"\u023f\n\7\2\2\u023c\u023d\7)\2\2\u023d\u023f\7)\2\2\u023e\u023b\3\2\2"+
"\2\u023e\u023c\3\2\2\2\u023f\u0242\3\2\2\2\u0240\u023e\3\2\2\2\u0240\u0241"+
"\3\2\2\2\u0241\u0243\3\2\2\2\u0242\u0240\3\2\2\2\u0243\u0244\7)\2\2\u0244"+
"\u008e\3\2\2\2\u0245\u0246\t\b\2\2\u0246\u0247\3\2\2\2\u0247\u0248\bH"+
"\2\2\u0248\u0090\3\2\2\2\17\2\u01d5\u01dc\u020d\u0218\u021b\u021f\u0223"+
"\u0228\u022e\u0235\u023e\u0240\3\b\2\2";
"\u008bG\u008dH\u008fI\u0091J\u0093K\u0095L\u0097M\u0099N\3\2\t\5\2C\\"+
"aac|\6\2\62;C\\aac|\7\2\60\60\62;C\\aac|\3\2\62;\3\2\63;\3\2))\5\2\13"+
"\f\17\17\"\"\2\u029a\2\3\3\2\2\2\2\5\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2\2\2"+
"\13\3\2\2\2\2\r\3\2\2\2\2\17\3\2\2\2\2\21\3\2\2\2\2\23\3\2\2\2\2\25\3"+
"\2\2\2\2\27\3\2\2\2\2\31\3\2\2\2\2\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2\2"+
"\2\2!\3\2\2\2\2#\3\2\2\2\2%\3\2\2\2\2\'\3\2\2\2\2)\3\2\2\2\2+\3\2\2\2"+
"\2-\3\2\2\2\2/\3\2\2\2\2\61\3\2\2\2\2\63\3\2\2\2\2\65\3\2\2\2\2\67\3\2"+
"\2\2\29\3\2\2\2\2;\3\2\2\2\2=\3\2\2\2\2?\3\2\2\2\2A\3\2\2\2\2C\3\2\2\2"+
"\2E\3\2\2\2\2G\3\2\2\2\2I\3\2\2\2\2K\3\2\2\2\2M\3\2\2\2\2O\3\2\2\2\2Q"+
"\3\2\2\2\2S\3\2\2\2\2U\3\2\2\2\2W\3\2\2\2\2Y\3\2\2\2\2[\3\2\2\2\2]\3\2"+
"\2\2\2_\3\2\2\2\2a\3\2\2\2\2c\3\2\2\2\2e\3\2\2\2\2g\3\2\2\2\2i\3\2\2\2"+
"\2k\3\2\2\2\2m\3\2\2\2\2o\3\2\2\2\2q\3\2\2\2\2s\3\2\2\2\2u\3\2\2\2\2w"+
"\3\2\2\2\2y\3\2\2\2\2{\3\2\2\2\2}\3\2\2\2\2\177\3\2\2\2\2\u0081\3\2\2"+
"\2\2\u0083\3\2\2\2\2\u0085\3\2\2\2\2\u0087\3\2\2\2\2\u0089\3\2\2\2\2\u008b"+
"\3\2\2\2\2\u008d\3\2\2\2\2\u008f\3\2\2\2\2\u0091\3\2\2\2\2\u0093\3\2\2"+
"\2\2\u0095\3\2\2\2\2\u0097\3\2\2\2\2\u0099\3\2\2\2\3\u009b\3\2\2\2\5\u009d"+
"\3\2\2\2\7\u009f\3\2\2\2\t\u00a6\3\2\2\2\13\u00af\3\2\2\2\r\u00b5\3\2"+
"\2\2\17\u00bb\3\2\2\2\21\u00be\3\2\2\2\23\u00c0\3\2\2\2\25\u00c6\3\2\2"+
"\2\27\u00cc\3\2\2\2\31\u00d1\3\2\2\2\33\u00d5\3\2\2\2\35\u00da\3\2\2\2"+
"\37\u00e0\3\2\2\2!\u00e7\3\2\2\2#\u00ed\3\2\2\2%\u00ef\3\2\2\2\'\u00f5"+
"\3\2\2\2)\u00fa\3\2\2\2+\u00fd\3\2\2\2-\u0101\3\2\2\2/\u0105\3\2\2\2\61"+
"\u010f\3\2\2\2\63\u0112\3\2\2\2\65\u011a\3\2\2\2\67\u011d\3\2\2\29\u0125"+
"\3\2\2\2;\u012d\3\2\2\2=\u0130\3\2\2\2?\u0135\3\2\2\2A\u013c\3\2\2\2C"+
"\u0146\3\2\2\2E\u014e\3\2\2\2G\u0154\3\2\2\2I\u015c\3\2\2\2K\u0167\3\2"+
"\2\2M\u0170\3\2\2\2O\u0175\3\2\2\2Q\u017b\3\2\2\2S\u0184\3\2\2\2U\u018e"+
"\3\2\2\2W\u0199\3\2\2\2Y\u01a5\3\2\2\2[\u01ae\3\2\2\2]\u01b8\3\2\2\2_"+
"\u01ba\3\2\2\2a\u01bd\3\2\2\2c\u01bf\3\2\2\2e\u01c2\3\2\2\2g\u01c5\3\2"+
"\2\2i\u01c8\3\2\2\2k\u01cc\3\2\2\2m\u01ce\3\2\2\2o\u01d1\3\2\2\2q\u01d4"+
"\3\2\2\2s\u01d7\3\2\2\2u\u01db\3\2\2\2w\u01de\3\2\2\2y\u01e1\3\2\2\2{"+
"\u01e4\3\2\2\2}\u01e8\3\2\2\2\177\u01ec\3\2\2\2\u0081\u01f5\3\2\2\2\u0083"+
"\u01fe\3\2\2\2\u0085\u0216\3\2\2\2\u0087\u0218\3\2\2\2\u0089\u021f\3\2"+
"\2\2\u008b\u024d\3\2\2\2\u008d\u0258\3\2\2\2\u008f\u0263\3\2\2\2\u0091"+
"\u0266\3\2\2\2\u0093\u0271\3\2\2\2\u0095\u0278\3\2\2\2\u0097\u027a\3\2"+
"\2\2\u0099\u0285\3\2\2\2\u009b\u009c\7*\2\2\u009c\4\3\2\2\2\u009d\u009e"+
"\7+\2\2\u009e\6\3\2\2\2\u009f\u00a0\7u\2\2\u00a0\u00a1\7g\2\2\u00a1\u00a2"+
"\7n\2\2\u00a2\u00a3\7g\2\2\u00a3\u00a4\7e\2\2\u00a4\u00a5\7v\2\2\u00a5"+
"\b\3\2\2\2\u00a6\u00a7\7f\2\2\u00a7\u00a8\7k\2\2\u00a8\u00a9\7u\2\2\u00a9"+
"\u00aa\7v\2\2\u00aa\u00ab\7k\2\2\u00ab\u00ac\7p\2\2\u00ac\u00ad\7e\2\2"+
"\u00ad\u00ae\7v\2\2\u00ae\n\3\2\2\2\u00af\u00b0\7y\2\2\u00b0\u00b1\7j"+
"\2\2\u00b1\u00b2\7g\2\2\u00b2\u00b3\7t\2\2\u00b3\u00b4\7g\2\2\u00b4\f"+
"\3\2\2\2\u00b5\u00b6\7q\2\2\u00b6\u00b7\7t\2\2\u00b7\u00b8\7f\2\2\u00b8"+
"\u00b9\7g\2\2\u00b9\u00ba\7t\2\2\u00ba\16\3\2\2\2\u00bb\u00bc\7d\2\2\u00bc"+
"\u00bd\7{\2\2\u00bd\20\3\2\2\2\u00be\u00bf\7.\2\2\u00bf\22\3\2\2\2\u00c0"+
"\u00c1\7p\2\2\u00c1\u00c2\7w\2\2\u00c2\u00c3\7n\2\2\u00c3\u00c4\7n\2\2"+
"\u00c4\u00c5\7u\2\2\u00c5\24\3\2\2\2\u00c6\u00c7\7h\2\2\u00c7\u00c8\7"+
"k\2\2\u00c8\u00c9\7t\2\2\u00c9\u00ca\7u\2\2\u00ca\u00cb\7v\2\2\u00cb\26"+
"\3\2\2\2\u00cc\u00cd\7n\2\2\u00cd\u00ce\7c\2\2\u00ce\u00cf\7u\2\2\u00cf"+
"\u00d0\7v\2\2\u00d0\30\3\2\2\2\u00d1\u00d2\7c\2\2\u00d2\u00d3\7u\2\2\u00d3"+
"\u00d4\7e\2\2\u00d4\32\3\2\2\2\u00d5\u00d6\7f\2\2\u00d6\u00d7\7g\2\2\u00d7"+
"\u00d8\7u\2\2\u00d8\u00d9\7e\2\2\u00d9\34\3\2\2\2\u00da\u00db\7n\2\2\u00db"+
"\u00dc\7k\2\2\u00dc\u00dd\7o\2\2\u00dd\u00de\7k\2\2\u00de\u00df\7v\2\2"+
"\u00df\36\3\2\2\2\u00e0\u00e1\7q\2\2\u00e1\u00e2\7h\2\2\u00e2\u00e3\7"+
"h\2\2\u00e3\u00e4\7u\2\2\u00e4\u00e5\7g\2\2\u00e5\u00e6\7v\2\2\u00e6 "+
"\3\2\2\2\u00e7\u00e8\7h\2\2\u00e8\u00e9\7g\2\2\u00e9\u00ea\7v\2\2\u00ea"+
"\u00eb\7e\2\2\u00eb\u00ec\7j\2\2\u00ec\"\3\2\2\2\u00ed\u00ee\7-\2\2\u00ee"+
"$\3\2\2\2\u00ef\u00f0\7s\2\2\u00f0\u00f1\7w\2\2\u00f1\u00f2\7g\2\2\u00f2"+
"\u00f3\7t\2\2\u00f3\u00f4\7{\2\2\u00f4&\3\2\2\2\u00f5\u00f6\7n\2\2\u00f6"+
"\u00f7\7c\2\2\u00f7\u00f8\7|\2\2\u00f8\u00f9\7{\2\2\u00f9(\3\2\2\2\u00fa"+
"\u00fb\7q\2\2\u00fb\u00fc\7t\2\2\u00fc*\3\2\2\2\u00fd\u00fe\7c\2\2\u00fe"+
"\u00ff\7p\2\2\u00ff\u0100\7f\2\2\u0100,\3\2\2\2\u0101\u0102\7p\2\2\u0102"+
"\u0103\7q\2\2\u0103\u0104\7v\2\2\u0104.\3\2\2\2\u0105\u0106\7k\2\2\u0106"+
"\u0107\7p\2\2\u0107\u0108\7Q\2\2\u0108\u0109\7t\2\2\u0109\u010a\7G\2\2"+
"\u010a\u010b\7o\2\2\u010b\u010c\7r\2\2\u010c\u010d\7v\2\2\u010d\u010e"+
"\7{\2\2\u010e\60\3\2\2\2\u010f\u0110\7k\2\2\u0110\u0111\7p\2\2\u0111\62"+
"\3\2\2\2\u0112\u0113\7d\2\2\u0113\u0114\7g\2\2\u0114\u0115\7v\2\2\u0115"+
"\u0116\7y\2\2\u0116\u0117\7g\2\2\u0117\u0118\7g\2\2\u0118\u0119\7p\2\2"+
"\u0119\64\3\2\2\2\u011a\u011b\7v\2\2\u011b\u011c\7q\2\2\u011c\66\3\2\2"+
"\2\u011d\u011e\7k\2\2\u011e\u011f\7p\2\2\u011f\u0120\7t\2\2\u0120\u0121"+
"\7c\2\2\u0121\u0122\7p\2\2\u0122\u0123\7i\2\2\u0123\u0124\7g\2\2\u0124"+
"8\3\2\2\2\u0125\u0126\7k\2\2\u0126\u0127\7p\2\2\u0127\u0128\7T\2\2\u0128"+
"\u0129\7c\2\2\u0129\u012a\7p\2\2\u012a\u012b\7i\2\2\u012b\u012c\7g\2\2"+
"\u012c:\3\2\2\2\u012d\u012e\7k\2\2\u012e\u012f\7u\2\2\u012f<\3\2\2\2\u0130"+
"\u0131\7p\2\2\u0131\u0132\7w\2\2\u0132\u0133\7n\2\2\u0133\u0134\7n\2\2"+
"\u0134>\3\2\2\2\u0135\u0136\7k\2\2\u0136\u0137\7u\2\2\u0137\u0138\7P\2"+
"\2\u0138\u0139\7w\2\2\u0139\u013a\7n\2\2\u013a\u013b\7n\2\2\u013b@\3\2"+
"\2\2\u013c\u013d\7k\2\2\u013d\u013e\7u\2\2\u013e\u013f\7P\2\2\u013f\u0140"+
"\7q\2\2\u0140\u0141\7v\2\2\u0141\u0142\7P\2\2\u0142\u0143\7w\2\2\u0143"+
"\u0144\7n\2\2\u0144\u0145\7n\2\2\u0145B\3\2\2\2\u0146\u0147\7p\2\2\u0147"+
"\u0148\7q\2\2\u0148\u0149\7v\2\2\u0149\u014a\7P\2\2\u014a\u014b\7w\2\2"+
"\u014b\u014c\7n\2\2\u014c\u014d\7n\2\2\u014dD\3\2\2\2\u014e\u014f\7g\2"+
"\2\u014f\u0150\7o\2\2\u0150\u0151\7r\2\2\u0151\u0152\7v\2\2\u0152\u0153"+
"\7{\2\2\u0153F\3\2\2\2\u0154\u0155\7k\2\2\u0155\u0156\7u\2\2\u0156\u0157"+
"\7G\2\2\u0157\u0158\7o\2\2\u0158\u0159\7r\2\2\u0159\u015a\7v\2\2\u015a"+
"\u015b\7{\2\2\u015bH\3\2\2\2\u015c\u015d\7k\2\2\u015d\u015e\7u\2\2\u015e"+
"\u015f\7P\2\2\u015f\u0160\7q\2\2\u0160\u0161\7v\2\2\u0161\u0162\7G\2\2"+
"\u0162\u0163\7o\2\2\u0163\u0164\7r\2\2\u0164\u0165\7v\2\2\u0165\u0166"+
"\7{\2\2\u0166J\3\2\2\2\u0167\u0168\7p\2\2\u0168\u0169\7q\2\2\u0169\u016a"+
"\7v\2\2\u016a\u016b\7G\2\2\u016b\u016c\7o\2\2\u016c\u016d\7r\2\2\u016d"+
"\u016e\7v\2\2\u016e\u016f\7{\2\2\u016fL\3\2\2\2\u0170\u0171\7n\2\2\u0171"+
"\u0172\7k\2\2\u0172\u0173\7m\2\2\u0173\u0174\7g\2\2\u0174N\3\2\2\2\u0175"+
"\u0176\7k\2\2\u0176\u0177\7n\2\2\u0177\u0178\7k\2\2\u0178\u0179\7m\2\2"+
"\u0179\u017a\7g\2\2\u017aP\3\2\2\2\u017b\u017c\7e\2\2\u017c\u017d\7q\2"+
"\2\u017d\u017e\7p\2\2\u017e\u017f\7v\2\2\u017f\u0180\7c\2\2\u0180\u0181"+
"\7k\2\2\u0181\u0182\7p\2\2\u0182\u0183\7u\2\2\u0183R\3\2\2\2\u0184\u0185"+
"\7k\2\2\u0185\u0186\7e\2\2\u0186\u0187\7q\2\2\u0187\u0188\7p\2\2\u0188"+
"\u0189\7v\2\2\u0189\u018a\7c\2\2\u018a\u018b\7k\2\2\u018b\u018c\7p\2\2"+
"\u018c\u018d\7u\2\2\u018dT\3\2\2\2\u018e\u018f\7u\2\2\u018f\u0190\7v\2"+
"\2\u0190\u0191\7c\2\2\u0191\u0192\7t\2\2\u0192\u0193\7v\2\2\u0193\u0194"+
"\7u\2\2\u0194\u0195\7Y\2\2\u0195\u0196\7k\2\2\u0196\u0197\7v\2\2\u0197"+
"\u0198\7j\2\2\u0198V\3\2\2\2\u0199\u019a\7k\2\2\u019a\u019b\7u\2\2\u019b"+
"\u019c\7v\2\2\u019c\u019d\7c\2\2\u019d\u019e\7t\2\2\u019e\u019f\7v\2\2"+
"\u019f\u01a0\7u\2\2\u01a0\u01a1\7Y\2\2\u01a1\u01a2\7k\2\2\u01a2\u01a3"+
"\7v\2\2\u01a3\u01a4\7j\2\2\u01a4X\3\2\2\2\u01a5\u01a6\7g\2\2\u01a6\u01a7"+
"\7p\2\2\u01a7\u01a8\7f\2\2\u01a8\u01a9\7u\2\2\u01a9\u01aa\7Y\2\2\u01aa"+
"\u01ab\7k\2\2\u01ab\u01ac\7v\2\2\u01ac\u01ad\7j\2\2\u01adZ\3\2\2\2\u01ae"+
"\u01af\7k\2\2\u01af\u01b0\7g\2\2\u01b0\u01b1\7p\2\2\u01b1\u01b2\7f\2\2"+
"\u01b2\u01b3\7u\2\2\u01b3\u01b4\7Y\2\2\u01b4\u01b5\7k\2\2\u01b5\u01b6"+
"\7v\2\2\u01b6\u01b7\7j\2\2\u01b7\\\3\2\2\2\u01b8\u01b9\7?\2\2\u01b9^\3"+
"\2\2\2\u01ba\u01bb\7g\2\2\u01bb\u01bc\7s\2\2\u01bc`\3\2\2\2\u01bd\u01be"+
"\7@\2\2\u01beb\3\2\2\2\u01bf\u01c0\7i\2\2\u01c0\u01c1\7v\2\2\u01c1d\3"+
"\2\2\2\u01c2\u01c3\7@\2\2\u01c3\u01c4\7?\2\2\u01c4f\3\2\2\2\u01c5\u01c6"+
"\7i\2\2\u01c6\u01c7\7g\2\2\u01c7h\3\2\2\2\u01c8\u01c9\7i\2\2\u01c9\u01ca"+
"\7v\2\2\u01ca\u01cb\7g\2\2\u01cbj\3\2\2\2\u01cc\u01cd\7>\2\2\u01cdl\3"+
"\2\2\2\u01ce\u01cf\7n\2\2\u01cf\u01d0\7v\2\2\u01d0n\3\2\2\2\u01d1\u01d2"+
"\7>\2\2\u01d2\u01d3\7?\2\2\u01d3p\3\2\2\2\u01d4\u01d5\7n\2\2\u01d5\u01d6"+
"\7g\2\2\u01d6r\3\2\2\2\u01d7\u01d8\7n\2\2\u01d8\u01d9\7v\2\2\u01d9\u01da"+
"\7g\2\2\u01dat\3\2\2\2\u01db\u01dc\7>\2\2\u01dc\u01dd\7@\2\2\u01ddv\3"+
"\2\2\2\u01de\u01df\7#\2\2\u01df\u01e0\7?\2\2\u01e0x\3\2\2\2\u01e1\u01e2"+
"\7p\2\2\u01e2\u01e3\7g\2\2\u01e3z\3\2\2\2\u01e4\u01e5\7k\2\2\u01e5\u01e6"+
"\7g\2\2\u01e6\u01e7\7s\2\2\u01e7|\3\2\2\2\u01e8\u01e9\7k\2\2\u01e9\u01ea"+
"\7p\2\2\u01ea\u01eb\7g\2\2\u01eb~\3\2\2\2\u01ec\u01ed\7g\2\2\u01ed\u01ee"+
"\7s\2\2\u01ee\u01ef\7Q\2\2\u01ef\u01f0\7t\2\2\u01f0\u01f1\7P\2\2\u01f1"+
"\u01f2\7w\2\2\u01f2\u01f3\7n\2\2\u01f3\u01f4\7n\2\2\u01f4\u0080\3\2\2"+
"\2\u01f5\u01f6\7i\2\2\u01f6\u01f7\7v\2\2\u01f7\u01f8\7Q\2\2\u01f8\u01f9"+
"\7t\2\2\u01f9\u01fa\7P\2\2\u01fa\u01fb\7w\2\2\u01fb\u01fc\7n\2\2\u01fc"+
"\u01fd\7n\2\2\u01fd\u0082\3\2\2\2\u01fe\u01ff\7n\2\2\u01ff\u0200\7v\2"+
"\2\u0200\u0201\7Q\2\2\u0201\u0202\7t\2\2\u0202\u0203\7P\2\2\u0203\u0204"+
"\7w\2\2\u0204\u0205\7n\2\2\u0205\u0206\7n\2\2\u0206\u0084\3\2\2\2\u0207"+
"\u0208\7<\2\2\u0208\u020c\t\2\2\2\u0209\u020b\t\3\2\2\u020a\u0209\3\2"+
"\2\2\u020b\u020e\3\2\2\2\u020c\u020a\3\2\2\2\u020c\u020d\3\2\2\2\u020d"+
"\u0217\3\2\2\2\u020e\u020c\3\2\2\2\u020f\u0213\7A\2\2\u0210\u0212\4\62"+
";\2\u0211\u0210\3\2\2\2\u0212\u0215\3\2\2\2\u0213\u0211\3\2\2\2\u0213"+
"\u0214\3\2\2\2\u0214\u0217\3\2\2\2\u0215\u0213\3\2\2\2\u0216\u0207\3\2"+
"\2\2\u0216\u020f\3\2\2\2\u0217\u0086\3\2\2\2\u0218\u021c\t\2\2\2\u0219"+
"\u021b\t\4\2\2\u021a\u0219\3\2\2\2\u021b\u021e\3\2\2\2\u021c\u021a\3\2"+
"\2\2\u021c\u021d\3\2\2\2\u021d\u0088\3\2\2\2\u021e\u021c\3\2\2\2\u021f"+
"\u0220\7b\2\2\u0220\u0221\5\u0087D\2\u0221\u0222\7b\2\2\u0222\u008a\3"+
"\2\2\2\u0223\u0224\7u\2\2\u0224\u0225\7w\2\2\u0225\u0226\7o\2\2\u0226"+
"\u0227\7*\2\2\u0227\u0228\3\2\2\2\u0228\u0229\5\u0087D\2\u0229\u022a\7"+
"+\2\2\u022a\u024e\3\2\2\2\u022b\u022c\7o\2\2\u022c\u022d\7c\2\2\u022d"+
"\u022e\7z\2\2\u022e\u022f\7*\2\2\u022f\u0230\3\2\2\2\u0230\u0231\5\u0087"+
"D\2\u0231\u0232\7+\2\2\u0232\u024e\3\2\2\2\u0233\u0234\7o\2\2\u0234\u0235"+
"\7k\2\2\u0235\u0236\7p\2\2\u0236\u0237\7*\2\2\u0237\u0238\3\2\2\2\u0238"+
"\u0239\5\u0087D\2\u0239\u023a\7+\2\2\u023a\u024e\3\2\2\2\u023b\u023c\7"+
"c\2\2\u023c\u023d\7x\2\2\u023d\u023e\7i\2\2\u023e\u023f\7*\2\2\u023f\u0240"+
"\3\2\2\2\u0240\u0241\5\u0087D\2\u0241\u0242\7+\2\2\u0242\u024e\3\2\2\2"+
"\u0243\u0244\7e\2\2\u0244\u0245\7q\2\2\u0245\u0246\7w\2\2\u0246\u0247"+
"\7p\2\2\u0247\u0248\7v\2\2\u0248\u0249\7*\2\2\u0249\u024a\3\2\2\2\u024a"+
"\u024b\5\u0087D\2\u024b\u024c\7+\2\2\u024c\u024e\3\2\2\2\u024d\u0223\3"+
"\2\2\2\u024d\u022b\3\2\2\2\u024d\u0233\3\2\2\2\u024d\u023b\3\2\2\2\u024d"+
"\u0243\3\2\2\2\u024e\u008c\3\2\2\2\u024f\u0250\7v\2\2\u0250\u0251\7t\2"+
"\2\u0251\u0252\7w\2\2\u0252\u0259\7g\2\2\u0253\u0254\7h\2\2\u0254\u0255"+
"\7c\2\2\u0255\u0256\7n\2\2\u0256\u0257\7u\2\2\u0257\u0259\7g\2\2\u0258"+
"\u024f\3\2\2\2\u0258\u0253\3\2\2\2\u0259\u008e\3\2\2\2\u025a\u025c\7/"+
"\2\2\u025b\u025a\3\2\2\2\u025b\u025c\3\2\2\2\u025c\u025d\3\2\2\2\u025d"+
"\u0264\5\u0091I\2\u025e\u0260\7/\2\2\u025f\u025e\3\2\2\2\u025f\u0260\3"+
"\2\2\2\u0260\u0261\3\2\2\2\u0261\u0264\5\u0093J\2\u0262\u0264\5\u0095"+
"K\2\u0263\u025b\3\2\2\2\u0263\u025f\3\2\2\2\u0263\u0262\3\2\2\2\u0264"+
"\u0090\3\2\2\2\u0265\u0267\t\5\2\2\u0266\u0265\3\2\2\2\u0267\u0268\3\2"+
"\2\2\u0268\u0266\3\2\2\2\u0268\u0269\3\2\2\2\u0269\u026a\3\2\2\2\u026a"+
"\u026e\7\60\2\2\u026b\u026d\t\5\2\2\u026c\u026b\3\2\2\2\u026d\u0270\3"+
"\2\2\2\u026e\u026c\3\2\2\2\u026e\u026f\3\2\2\2\u026f\u0092\3\2\2\2\u0270"+
"\u026e\3\2\2\2\u0271\u0275\t\6\2\2\u0272\u0274\t\5\2\2\u0273\u0272\3\2"+
"\2\2\u0274\u0277\3\2\2\2\u0275\u0273\3\2\2\2\u0275\u0276\3\2\2\2\u0276"+
"\u0094\3\2\2\2\u0277\u0275\3\2\2\2\u0278\u0279\7\62\2\2\u0279\u0096\3"+
"\2\2\2\u027a\u0280\7)\2\2\u027b\u027f\n\7\2\2\u027c\u027d\7)\2\2\u027d"+
"\u027f\7)\2\2\u027e\u027b\3\2\2\2\u027e\u027c\3\2\2\2\u027f\u0282\3\2"+
"\2\2\u0280\u027e\3\2\2\2\u0280\u0281\3\2\2\2\u0281\u0283\3\2\2\2\u0282"+
"\u0280\3\2\2\2\u0283\u0284\7)\2\2\u0284\u0098\3\2\2\2\u0285\u0286\t\b"+
"\2\2\u0286\u0287\3\2\2\2\u0287\u0288\bM\2\2\u0288\u009a\3\2\2\2\21\2\u020c"+
"\u0213\u0216\u021c\u024d\u0258\u025b\u025f\u0263\u0268\u026e\u0275\u027e"+
"\u0280\3\b\2\2";
public static final ATN _ATN =
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
static {
@@ -58,17 +58,22 @@ T__56=57
T__57=58
T__58=59
T__59=60
INPUT_VARIABLE=61
PATH_VARIABLE=62
QUOTED_PATH_VARIABLE=63
PROP_FORMULA=64
BOOLEAN_LITERAL=65
NUMBER_LITERAL=66
DOUBLE=67
INT=68
ZERO=69
STRING_LITERAL=70
WS=71
T__60=61
T__61=62
T__62=63
T__63=64
T__64=65
INPUT_VARIABLE=66
PATH_VARIABLE=67
QUOTED_PATH_VARIABLE=68
PROP_FORMULA=69
BOOLEAN_LITERAL=70
NUMBER_LITERAL=71
DOUBLE=72
INT=73
ZERO=74
STRING_LITERAL=75
WS=76
'('=1
')'=2
'select'=3
@@ -91,42 +96,47 @@ WS=71
'or'=20
'and'=21
'not'=22
'in'=23
'between'=24
'inrange'=25
'inOrEmpty'=23
'in'=24
'between'=25
'to'=26
'is'=27
'null'=28
'isNull'=29
'isNotNull'=30
'notNull'=31
'empty'=32
'isEmpty'=33
'isNotEmpty'=34
'notEmpty'=35
'like'=36
'ilike'=37
'contains'=38
'icontains'=39
'startsWith'=40
'istartsWith'=41
'endsWith'=42
'iendsWith'=43
'='=44
'eq'=45
'>'=46
'gt'=47
'>='=48
'ge'=49
'gte'=50
'<'=51
'lt'=52
'<='=53
'le'=54
'lte'=55
'<>'=56
'!='=57
'ne'=58
'ieq'=59
'ine'=60
'0'=69
'inrange'=27
'inRange'=28
'is'=29
'null'=30
'isNull'=31
'isNotNull'=32
'notNull'=33
'empty'=34
'isEmpty'=35
'isNotEmpty'=36
'notEmpty'=37
'like'=38
'ilike'=39
'contains'=40
'icontains'=41
'startsWith'=42
'istartsWith'=43
'endsWith'=44
'iendsWith'=45
'='=46
'eq'=47
'>'=48
'gt'=49
'>='=50
'ge'=51
'gte'=52
'<'=53
'lt'=54
'<='=55
'le'=56
'lte'=57
'<>'=58
'!='=59
'ne'=60
'ieq'=61
'ine'=62
'eqOrNull'=63
'gtOrNull'=64
'ltOrNull'=65
'0'=74
@@ -287,6 +287,16 @@ public interface EQLListener extends ParseTreeListener {
* @param ctx the parse tree
*/
void exitAny_expression(EQLParser.Any_expressionContext ctx);
/**
* Enter a parse tree produced by {@link EQLParser#inOrEmpty_expression}.
* @param ctx the parse tree
*/
void enterInOrEmpty_expression(EQLParser.InOrEmpty_expressionContext ctx);
/**
* Exit a parse tree produced by {@link EQLParser#inOrEmpty_expression}.
* @param ctx the parse tree
*/
void exitInOrEmpty_expression(EQLParser.InOrEmpty_expressionContext ctx);
/**
* Enter a parse tree produced by {@link EQLParser#in_expression}.
* @param ctx the parse tree
@@ -327,6 +337,16 @@ public interface EQLListener extends ParseTreeListener {
* @param ctx the parse tree
*/
void exitInrange_expression(EQLParser.Inrange_expressionContext ctx);
/**
* Enter a parse tree produced by {@link EQLParser#inrange_op}.
* @param ctx the parse tree
*/
void enterInrange_op(EQLParser.Inrange_opContext ctx);
/**
* Exit a parse tree produced by {@link EQLParser#inrange_op}.
* @param ctx the parse tree
*/
void exitInrange_op(EQLParser.Inrange_opContext ctx);
/**
* Enter a parse tree produced by {@link EQLParser#propertyBetween_expression}.
* @param ctx the parse tree
File diff suppressed because it is too large Load Diff
@@ -10,7 +10,7 @@ import io.ebeaninternal.server.querydefn.OrmQueryProperties;
/**
* Base class for Bean and BeanCollection loading (lazy loading and query join loading).
*/
public abstract class DLoadBaseContext {
abstract class DLoadBaseContext {
protected final DLoadContext parent;
@@ -18,22 +18,21 @@ public abstract class DLoadBaseContext {
protected final String fullPath;
protected final OrmQueryProperties queryProps;
protected final boolean hitCache;
protected final String serverName;
protected final int firstBatchSize;
final OrmQueryProperties queryProps;
protected final int secondaryBatchSize;
final boolean hitCache;
protected final ObjectGraphNode objectGraphNode;
final int firstBatchSize;
protected final boolean queryFetch;
final int secondaryBatchSize;
final ObjectGraphNode objectGraphNode;
public DLoadBaseContext(DLoadContext parent, BeanDescriptor<?> desc, String path, int defaultBatchSize, OrmQueryProperties queryProps) {
final boolean queryFetch;
DLoadBaseContext(DLoadContext parent, BeanDescriptor<?> desc, String path, int defaultBatchSize, OrmQueryProperties queryProps) {
this.parent = parent;
this.serverName = parent.getEbeanServer().getName();
@@ -86,13 +85,11 @@ public abstract class DLoadBaseContext {
String label = parent.getPlanLabel();
if (label != null) {
label += "_" + fullPath;
query.setLabel(label);
query.setProfileLocation(parent.getProfileLocation());
query.setProfilePath(label, fullPath, parent.getProfileLocation());
}
}
protected PersistenceContext getPersistenceContext() {
PersistenceContext getPersistenceContext() {
return parent.getPersistenceContext();
}
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.loadcontext;
import io.ebean.CacheMode;
import io.ebean.bean.BeanLoader;
import io.ebean.bean.EntityBeanIntercept;
import io.ebean.bean.PersistenceContext;
@@ -16,19 +17,22 @@ import java.util.List;
import java.util.Set;
/**
* Default implementation of LoadBeanContext.
* ToOne bean load context.
*/
public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
private final boolean cache;
private List<LoadBuffer> bufferList;
private LoadBuffer currentBuffer;
public DLoadBeanContext(DLoadContext parent, BeanDescriptor<?> desc, String path, int defaultBatchSize, OrmQueryProperties queryProps) {
DLoadBeanContext(DLoadContext parent, BeanDescriptor<?> desc, String path, int defaultBatchSize, OrmQueryProperties queryProps) {
super(parent, desc, path, defaultBatchSize, queryProps);
// bufferList only required when using query joins (queryFetch)
this.bufferList = (!queryFetch) ? null : new ArrayList<>();
this.currentBuffer = createBuffer(firstBatchSize);
this.cache = (queryProps == null) ? false : queryProps.isCache();
}
/**
@@ -41,8 +45,11 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
currentBuffer = createBuffer(secondaryBatchSize);
}
protected void configureQuery(SpiQuery<?> query, String lazyLoadProperty) {
private void configureQuery(SpiQuery<?> query, String lazyLoadProperty) {
if (cache) {
query.setBeanCacheMode(CacheMode.ON);
}
setLabel(query);
parent.propagateQueryState(query, desc.isDocStoreMapped());
query.setParentNode(objectGraphNode);
@@ -79,8 +86,7 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
if (bufferList != null) {
for (LoadBuffer loadBuffer : bufferList) {
if (!loadBuffer.list.isEmpty()) {
LoadBeanRequest req = new LoadBeanRequest(loadBuffer, parentRequest);
parent.getEbeanServer().loadBean(req);
parent.getEbeanServer().loadBean(new LoadBeanRequest(loadBuffer, parentRequest));
if (!queryProps.isQueryFetchAll()) {
// Stop - only fetch the first batch ... the rest will be lazy loaded
break;
@@ -97,18 +103,17 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
}
}
/**
* A buffer for batch loading beans on a given path.
*/
public static class LoadBuffer implements BeanLoader, LoadBeanBuffer {
static class LoadBuffer implements BeanLoader, LoadBeanBuffer {
private final DLoadBeanContext context;
private final int batchSize;
private final List<EntityBeanIntercept> list;
private PersistenceContext persistenceContext;
public LoadBuffer(DLoadBeanContext context, int batchSize) {
LoadBuffer(DLoadBeanContext context, int batchSize) {
this.context = context;
this.batchSize = batchSize;
this.list = new ArrayList<>(batchSize);
@@ -186,7 +191,7 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
}
}
LoadBeanRequest req = new LoadBeanRequest(this, ebi.getLazyLoadProperty(), context.hitCache);
LoadBeanRequest req = new LoadBeanRequest(this, ebi, context.hitCache);
context.desc.getEbeanServer().loadBean(req);
}
}
@@ -3,7 +3,7 @@ package io.ebeaninternal.server.loadcontext;
import io.ebean.CacheMode;
import io.ebean.ProfileLocation;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.CallStack;
import io.ebean.bean.CallOrigin;
import io.ebean.bean.EntityBeanIntercept;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.bean.ObjectGraphOrigin;
@@ -94,8 +94,8 @@ public class DLoadContext implements LoadContext {
}
private ObjectGraphOrigin initOrigin() {
CallStack callStack = ebeanServer.createCallStack();
return new ObjectGraphOrigin(0, callStack, rootDescriptor.getFullName());
CallOrigin callOrigin = ebeanServer.createCallOrigin();
return new ObjectGraphOrigin(0, callOrigin, rootDescriptor.getFullName());
}
public DLoadContext(OrmQueryRequest<?> request, SpiQuerySecondary secondaryQueries) {
@@ -224,10 +224,7 @@ public class DLoadContext implements LoadContext {
*/
private LoadSecondaryQuery getLoadSecondaryQuery(String path) {
LoadSecondaryQuery beanLoad = beanMap.get(path);
if (beanLoad == null) {
beanLoad = manyMap.get(path);
}
return beanLoad;
return beanLoad == null ? manyMap.get(path) : beanLoad;
}
@Override
@@ -17,7 +17,10 @@ import io.ebeaninternal.server.querydefn.OrmQueryProperties;
import java.util.ArrayList;
import java.util.List;
public class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
/**
* ToMany bean load context.
*/
class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
protected final BeanPropertyAssocMany<?> property;
@@ -27,7 +30,7 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
private LoadBuffer currentBuffer;
public DLoadManyContext(DLoadContext parent, BeanPropertyAssocMany<?> property,
DLoadManyContext(DLoadContext parent, BeanPropertyAssocMany<?> property,
String path, int defaultBatchSize, OrmQueryProperties queryProps) {
super(parent, property.getBeanDescriptor(), path, defaultBatchSize, queryProps);
@@ -57,7 +60,7 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
currentBuffer = createBuffer(secondaryBatchSize);
}
public void configureQuery(SpiQuery<?> query) {
private void configureQuery(SpiQuery<?> query) {
setLabel(query);
parent.propagateQueryState(query, docStoreMapped);
@@ -122,14 +125,14 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
* A buffer for batch loading bean collections on a given path.
* Supports batch lazy loading and secondary query loading.
*/
public static class LoadBuffer implements BeanCollectionLoader, LoadManyBuffer {
static class LoadBuffer implements BeanCollectionLoader, LoadManyBuffer {
private final PersistenceContext persistenceContext;
private final DLoadManyContext context;
private final int batchSize;
private final List<BeanCollection<?>> list;
public LoadBuffer(DLoadManyContext context, int batchSize) {
LoadBuffer(DLoadManyContext context, int batchSize) {
this.context = context;
// set the persistence context as at this moment in
// case it changes as part of a findIterate etc
@@ -43,6 +43,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import static io.ebeaninternal.server.persist.DmlUtil.isNullOrZero;
/**
* Persister implementation using DML.
* <p>
@@ -1227,11 +1229,9 @@ public final class DefaultPersister implements Persister {
EntityBean bean = request.getEntityBean();
Object uid = idProp.getValue(bean);
if (DmlUtil.isNullOrZero(uid)) {
if (isNullOrZero(uid)) {
// generate the nextId and set it to the property
Object nextId = desc.nextId(request.getTransaction());
// cast the data type if required and set it
desc.convertSetId(nextId, bean);
}
@@ -20,6 +20,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import static io.ebeaninternal.server.persist.DmlUtil.isNullOrZero;
/**
* Saves the details for a OneToMany or ManyToMany relationship (entity beans).
*/
@@ -222,7 +224,7 @@ public class SaveManyBeans extends SaveManyBase {
}
if (detailBean instanceof EntityBean) {
Object id = targetDescriptor.getId((EntityBean) detailBean);
if (!DmlUtil.isNullOrZero(id)) {
if (!isNullOrZero(id)) {
// remember the Id (other details not in the collection) will be removed
detailIds.add(id);
}
@@ -6,7 +6,6 @@ import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.server.core.Message;
import io.ebeaninternal.server.core.PersistRequestBean;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.persist.DmlUtil;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
@@ -15,6 +14,8 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import static io.ebeaninternal.server.persist.DmlUtil.isNullOrZero;
/**
* Insert bean handler.
*/
@@ -66,7 +67,7 @@ public class InsertHandler extends DmlHandler {
Object idValue = desc.getId(bean);
boolean withId = !DmlUtil.isNullOrZero(idValue);
boolean withId = !isNullOrZero(idValue);
// check to see if we are going to use generated keys
if (!withId) {
@@ -11,6 +11,8 @@ import io.ebeaninternal.server.querydefn.SpiFetchGroup;
*/
class DFetchGroupBuilder<T> implements FetchGroupBuilder<T> {
private static final FetchConfig FETCH_CACHE = new FetchConfig().cache();
private static final FetchConfig FETCH_QUERY = new FetchConfig().query();
private static final FetchConfig FETCH_LAZY = new FetchConfig().lazy();
@@ -61,6 +63,12 @@ class DFetchGroupBuilder<T> implements FetchGroupBuilder<T> {
return this;
}
@Override
public FetchGroupBuilder<T> fetchCache(String path) {
detail.fetch(path, null, FETCH_CACHE);
return this;
}
@Override
public FetchGroupBuilder<T> fetchLazy(String path) {
detail.fetch(path, null, FETCH_LAZY);
@@ -79,6 +87,12 @@ class DFetchGroupBuilder<T> implements FetchGroupBuilder<T> {
return this;
}
@Override
public FetchGroupBuilder<T> fetchCache(String path, String properties) {
detail.fetch(path, properties, FETCH_CACHE);
return this;
}
@Override
public FetchGroupBuilder<T> fetchLazy(String path, String properties) {
detail.fetch(path, properties, FETCH_LAZY);
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.query;
import io.ebean.FetchGroup;
import io.ebean.FetchGroupBuilder;
import io.ebean.service.SpiFetchGroupQuery;
import io.ebean.service.SpiFetchGroupService;
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
@@ -20,6 +21,11 @@ public final class DFetchGroupService implements SpiFetchGroupService {
return new DFetchGroupBuilder<>();
}
@Override
public <T> SpiFetchGroupQuery<T> queryFor(Class<T> beanType) {
return new DefaultFetchGroupQuery<>();
}
private OrmQueryDetail detail(String select) {
OrmQueryDetail detail = new OrmQueryDetail();
detail.select(select);

Some files were not shown because too many files have changed in this diff Show More