From edbad9d9cb88416ca1cc587291b221374a908bba Mon Sep 17 00:00:00 2001 From: Evgenij Ryazanov Date: Sun, 5 Jun 2022 14:37:33 +0800 Subject: [PATCH 01/13] Fix synthetic access --- h2/src/main/org/h2/mvstore/db/LobStorageMap.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h2/src/main/org/h2/mvstore/db/LobStorageMap.java b/h2/src/main/org/h2/mvstore/db/LobStorageMap.java index 02ab3005fa..ac4624e871 100644 --- a/h2/src/main/org/h2/mvstore/db/LobStorageMap.java +++ b/h2/src/main/org/h2/mvstore/db/LobStorageMap.java @@ -58,7 +58,7 @@ public final class LobStorageMap implements LobStorageInterface private static final boolean TRACE = false; private final Database database; - private final MVStore mvStore; + final MVStore mvStore; private final AtomicLong nextLobId = new AtomicLong(0); private final ThreadPoolExecutor cleanupExecutor; From 2f863ebc4cf4995ebe5c172142fd7d2bda857dec Mon Sep 17 00:00:00 2001 From: Evgenij Ryazanov Date: Sun, 5 Jun 2022 14:38:33 +0800 Subject: [PATCH 02/13] Remove unused imports --- h2/src/main/org/h2/engine/Database.java | 1 - h2/src/main/org/h2/mvstore/db/MVTempResult.java | 1 - h2/src/main/org/h2/server/web/WebThread.java | 1 - 3 files changed, 3 deletions(-) diff --git a/h2/src/main/org/h2/engine/Database.java b/h2/src/main/org/h2/engine/Database.java index 3edb33450b..28aa24d2ab 100644 --- a/h2/src/main/org/h2/engine/Database.java +++ b/h2/src/main/org/h2/engine/Database.java @@ -54,7 +54,6 @@ import org.h2.store.FileLockMethod; import org.h2.store.FileStore; import org.h2.store.InDoubtTransaction; -import org.h2.store.LobStorageFrontend; import org.h2.store.LobStorageInterface; import org.h2.store.fs.FileUtils; import org.h2.store.fs.encrypt.FileEncrypt; diff --git a/h2/src/main/org/h2/mvstore/db/MVTempResult.java b/h2/src/main/org/h2/mvstore/db/MVTempResult.java index 5618d7e79b..53ec8e0a2a 100644 --- a/h2/src/main/org/h2/mvstore/db/MVTempResult.java +++ b/h2/src/main/org/h2/mvstore/db/MVTempResult.java @@ -15,7 +15,6 @@ import org.h2.message.DbException; import org.h2.mvstore.FileStore; import org.h2.mvstore.MVStore; -import org.h2.mvstore.MVStore.Builder; import org.h2.result.ResultExternal; import org.h2.result.SortOrder; import org.h2.store.fs.FileUtils; diff --git a/h2/src/main/org/h2/server/web/WebThread.java b/h2/src/main/org/h2/server/web/WebThread.java index 2c6a7fd6b5..41f55206f4 100644 --- a/h2/src/main/org/h2/server/web/WebThread.java +++ b/h2/src/main/org/h2/server/web/WebThread.java @@ -9,7 +9,6 @@ import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.InterruptedIOException; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; From 3d67916a645a312d9baa5ca13ece576241d42b2c Mon Sep 17 00:00:00 2001 From: Evgenij Ryazanov Date: Sun, 5 Jun 2022 15:10:49 +0800 Subject: [PATCH 03/13] Change spatial parameters from Object to Spatial --- .../main/org/h2/mvstore/rtree/MVRTreeMap.java | 22 +-- .../org/h2/mvstore/rtree/SpatialDataType.java | 128 ++++++++---------- 2 files changed, 66 insertions(+), 84 deletions(-) diff --git a/h2/src/main/org/h2/mvstore/rtree/MVRTreeMap.java b/h2/src/main/org/h2/mvstore/rtree/MVRTreeMap.java index b856e720b5..6464cfa843 100644 --- a/h2/src/main/org/h2/mvstore/rtree/MVRTreeMap.java +++ b/h2/src/main/org/h2/mvstore/rtree/MVRTreeMap.java @@ -69,7 +69,7 @@ public RTreeCursor findContainedKeys(Spatial x) { return new ContainsRTreeCursor<>(getRootPage(), x, keyType); } - private boolean contains(Page p, int index, Object key) { + private boolean contains(Page p, int index, Spatial key) { return keyType.contains(p.getKey(index), key); } @@ -236,7 +236,7 @@ private V operate(Page p, Spatial key, V value, DecisionMaker split(Page p) { private Page splitLinear(Page p) { int keyCount = p.getKeyCount(); - ArrayList keys = new ArrayList<>(keyCount); + ArrayList keys = new ArrayList<>(keyCount); for (int i = 0; i < keyCount; i++) { keys.add(p.getKey(i)); } @@ -323,10 +323,10 @@ private Page splitLinear(Page p) { extremes[1]--; } move(p, splitB, extremes[1]); - Object boundsA = keyType.createBoundingBox(splitA.getKey(0)); - Object boundsB = keyType.createBoundingBox(splitB.getKey(0)); + Spatial boundsA = keyType.createBoundingBox(splitA.getKey(0)); + Spatial boundsB = keyType.createBoundingBox(splitB.getKey(0)); while (p.getKeyCount() > 0) { - Object o = p.getKey(0); + Spatial o = p.getKey(0); float a = keyType.getAreaIncrease(boundsA, o); float b = keyType.getAreaIncrease(boundsB, o); if (a < b) { @@ -350,12 +350,12 @@ private Page splitQuadratic(Page p) { int ia = 0, ib = 0; int keyCount = p.getKeyCount(); for (int a = 0; a < keyCount; a++) { - Object objA = p.getKey(a); + Spatial objA = p.getKey(a); for (int b = 0; b < keyCount; b++) { if (a == b) { continue; } - Object objB = p.getKey(b); + Spatial objB = p.getKey(b); float area = keyType.getCombinedArea(objA, objB); if (area > largest) { largest = area; @@ -369,14 +369,14 @@ private Page splitQuadratic(Page p) { ib--; } move(p, splitB, ib); - Object boundsA = keyType.createBoundingBox(splitA.getKey(0)); - Object boundsB = keyType.createBoundingBox(splitB.getKey(0)); + Spatial boundsA = keyType.createBoundingBox(splitA.getKey(0)); + Spatial boundsB = keyType.createBoundingBox(splitB.getKey(0)); while (p.getKeyCount() > 0) { float diff = 0, bestA = 0, bestB = 0; int best = 0; keyCount = p.getKeyCount(); for (int i = 0; i < keyCount; i++) { - Object o = p.getKey(i); + Spatial o = p.getKey(i); float incA = keyType.getAreaIncrease(boundsA, o); float incB = keyType.getAreaIncrease(boundsB, o); float d = Math.abs(incA - incB); diff --git a/h2/src/main/org/h2/mvstore/rtree/SpatialDataType.java b/h2/src/main/org/h2/mvstore/rtree/SpatialDataType.java index 6af8a5887e..2f1be678d3 100644 --- a/h2/src/main/org/h2/mvstore/rtree/SpatialDataType.java +++ b/h2/src/main/org/h2/mvstore/rtree/SpatialDataType.java @@ -68,15 +68,13 @@ public int compare(Spatial a, Spatial b) { * @param b the second value * @return true if they are equal */ - public boolean equals(Object a, Object b) { + public boolean equals(Spatial a, Spatial b) { if (a == b) { return true; } else if (a == null || b == null) { return false; } - long la = ((Spatial) a).getId(); - long lb = ((Spatial) b).getId(); - return la == lb; + return a.getId() == b.getId(); } @Override @@ -155,20 +153,18 @@ public boolean isOverlap(Spatial a, Spatial b) { * @param bounds the bounds (may be modified) * @param add the value */ - public void increaseBounds(Object bounds, Object add) { - Spatial a = (Spatial) add; - Spatial b = (Spatial) bounds; - if (a.isNull() || b.isNull()) { + public void increaseBounds(Spatial bounds, Spatial add) { + if (add.isNull() || bounds.isNull()) { return; } for (int i = 0; i < dimensions; i++) { - float v = a.min(i); - if (v < b.min(i)) { - b.setMin(i, v); + float v = add.min(i); + if (v < bounds.min(i)) { + bounds.setMin(i, v); } - v = a.max(i); - if (v > b.max(i)) { - b.setMax(i, v); + v = add.max(i); + if (v > bounds.max(i)) { + bounds.setMax(i, v); } } } @@ -176,28 +172,26 @@ public void increaseBounds(Object bounds, Object add) { /** * Get the area increase by extending a to contain b. * - * @param objA the bounding box - * @param objB the object + * @param bounds the bounding box + * @param add the object * @return the area */ - public float getAreaIncrease(Object objA, Object objB) { - Spatial b = (Spatial) objB; - Spatial a = (Spatial) objA; - if (a.isNull() || b.isNull()) { + public float getAreaIncrease(Spatial bounds, Spatial add) { + if (bounds.isNull() || add.isNull()) { return 0; } - float min = a.min(0); - float max = a.max(0); + float min = bounds.min(0); + float max = bounds.max(0); float areaOld = max - min; - min = Math.min(min, b.min(0)); - max = Math.max(max, b.max(0)); + min = Math.min(min, add.min(0)); + max = Math.max(max, add.max(0)); float areaNew = max - min; for (int i = 1; i < dimensions; i++) { - min = a.min(i); - max = a.max(i); + min = bounds.min(i); + max = bounds.max(i); areaOld *= max - min; - min = Math.min(min, b.min(i)); - max = Math.max(max, b.max(i)); + min = Math.min(min, add.min(i)); + max = Math.max(max, add.max(i)); areaNew *= max - min; } return areaNew - areaOld; @@ -206,13 +200,11 @@ public float getAreaIncrease(Object objA, Object objB) { /** * Get the combined area of both objects. * - * @param objA the first object - * @param objB the second object + * @param a the first object + * @param b the second object * @return the area */ - float getCombinedArea(Object objA, Object objB) { - Spatial a = (Spatial) objA; - Spatial b = (Spatial) objB; + float getCombinedArea(Spatial a, Spatial b) { if (a.isNull()) { return getArea(b); } else if (b.isNull()) { @@ -239,20 +231,18 @@ private float getArea(Spatial a) { } /** - * Check whether a contains b. + * Check whether bounds contains object. * - * @param objA the bounding box - * @param objB the object + * @param bounds the bounding box + * @param object the object * @return the area */ - public boolean contains(Object objA, Object objB) { - Spatial a = (Spatial) objA; - Spatial b = (Spatial) objB; - if (a.isNull() || b.isNull()) { + public boolean contains(Spatial bounds, Spatial object) { + if (bounds.isNull() || object.isNull()) { return false; } for (int i = 0; i < dimensions; i++) { - if (a.min(i) > b.min(i) || a.max(i) < b.max(i)) { + if (bounds.min(i) > object.min(i) || bounds.max(i) < object.max(i)) { return false; } } @@ -260,21 +250,18 @@ public boolean contains(Object objA, Object objB) { } /** - * Check whether a is completely inside b and does not touch the - * given bound. + * Check whether object is completely inside bounds and does not touch them. * - * @param objA the object to check - * @param objB the bounds + * @param object the object to check + * @param bounds the bounds * @return true if a is completely inside b */ - public boolean isInside(Object objA, Object objB) { - Spatial a = (Spatial) objA; - Spatial b = (Spatial) objB; - if (a.isNull() || b.isNull()) { + public boolean isInside(Spatial object, Spatial bounds) { + if (object.isNull() || bounds.isNull()) { return false; } for (int i = 0; i < dimensions; i++) { - if (a.min(i) <= b.min(i) || a.max(i) >= b.max(i)) { + if (object.min(i) <= bounds.min(i) || object.max(i) >= bounds.max(i)) { return false; } } @@ -284,15 +271,14 @@ public boolean isInside(Object objA, Object objB) { /** * Create a bounding box starting with the given object. * - * @param objA the object + * @param object the object * @return the bounding box */ - Spatial createBoundingBox(Object objA) { - Spatial a = (Spatial) objA; - if (a.isNull()) { - return a; + Spatial createBoundingBox(Spatial object) { + if (object.isNull()) { + return object; } - return a.clone(0); + return object.clone(0); } /** @@ -303,7 +289,7 @@ Spatial createBoundingBox(Object objA) { * @param list the objects * @return the indexes of the extremes */ - public int[] getExtremes(ArrayList list) { + public int[] getExtremes(ArrayList list) { list = getNotNull(list); if (list.isEmpty()) { return null; @@ -315,7 +301,7 @@ public int[] getExtremes(ArrayList list) { boundsInner.setMin(i, boundsInner.max(i)); boundsInner.setMax(i, t); } - for (Object o : list) { + for (Spatial o : list) { increaseBounds(bounds, o); increaseMaxInnerBounds(boundsInner, o); } @@ -341,7 +327,7 @@ public int[] getExtremes(ArrayList list) { int firstIndex = -1, lastIndex = -1; for (int i = 0; i < list.size() && (firstIndex < 0 || lastIndex < 0); i++) { - Spatial o = (Spatial) list.get(i); + Spatial o = list.get(i); if (firstIndex < 0 && o.max(bestDim) == min) { firstIndex = i; } else if (lastIndex < 0 && o.min(bestDim) == max) { @@ -351,11 +337,10 @@ public int[] getExtremes(ArrayList list) { return new int[] { firstIndex, lastIndex }; } - private static ArrayList getNotNull(ArrayList list) { + private static ArrayList getNotNull(ArrayList list) { boolean foundNull = false; - for (Object o : list) { - Spatial a = (Spatial) o; - if (a.isNull()) { + for (Spatial o : list) { + if (o.isNull()) { foundNull = true; break; } @@ -363,22 +348,19 @@ private static ArrayList getNotNull(ArrayList list) { if (!foundNull) { return list; } - ArrayList result = new ArrayList<>(); - for (Object o : list) { - Spatial a = (Spatial) o; - if (!a.isNull()) { - result.add(a); + ArrayList result = new ArrayList<>(); + for (Spatial o : list) { + if (!o.isNull()) { + result.add(o); } } return result; } - private void increaseMaxInnerBounds(Object bounds, Object add) { - Spatial b = (Spatial) bounds; - Spatial a = (Spatial) add; + private void increaseMaxInnerBounds(Spatial bounds, Spatial add) { for (int i = 0; i < dimensions; i++) { - b.setMin(i, Math.min(b.min(i), a.max(i))); - b.setMax(i, Math.max(b.max(i), a.min(i))); + bounds.setMin(i, Math.min(bounds.min(i), add.max(i))); + bounds.setMax(i, Math.max(bounds.max(i), add.min(i))); } } From 9bbb5056768849b30ed4aef1b72103042299712e Mon Sep 17 00:00:00 2001 From: Evgenij Ryazanov Date: Sun, 5 Jun 2022 15:45:31 +0800 Subject: [PATCH 04/13] Add default implementation of DbObject.getCreateSQLForCopy() --- h2/src/main/org/h2/constraint/ConstraintDomain.java | 5 ----- h2/src/main/org/h2/engine/Comment.java | 6 ------ h2/src/main/org/h2/engine/DbObject.java | 4 +++- h2/src/main/org/h2/engine/Role.java | 7 ------- h2/src/main/org/h2/engine/Setting.java | 6 ------ h2/src/main/org/h2/engine/User.java | 5 ----- h2/src/main/org/h2/schema/Constant.java | 7 ------- h2/src/main/org/h2/schema/Domain.java | 7 ------- h2/src/main/org/h2/schema/Schema.java | 5 ----- h2/src/main/org/h2/schema/Sequence.java | 6 ------ h2/src/main/org/h2/schema/UserDefinedFunction.java | 6 ------ h2/src/main/org/h2/table/Table.java | 5 ----- 12 files changed, 3 insertions(+), 66 deletions(-) diff --git a/h2/src/main/org/h2/constraint/ConstraintDomain.java b/h2/src/main/org/h2/constraint/ConstraintDomain.java index c866c808bb..c0d825045f 100644 --- a/h2/src/main/org/h2/constraint/ConstraintDomain.java +++ b/h2/src/main/org/h2/constraint/ConstraintDomain.java @@ -75,11 +75,6 @@ public void setExpression(SessionLocal session, Expression expr) { this.expr = expr; } - @Override - public String getCreateSQLForCopy(Table forTable, String quotedName) { - throw DbException.getInternalError(toString()); - } - @Override public String getCreateSQLWithoutIndexes() { return getCreateSQL(); diff --git a/h2/src/main/org/h2/engine/Comment.java b/h2/src/main/org/h2/engine/Comment.java index e3af80fb67..d8d66ac691 100644 --- a/h2/src/main/org/h2/engine/Comment.java +++ b/h2/src/main/org/h2/engine/Comment.java @@ -7,7 +7,6 @@ import org.h2.message.DbException; import org.h2.message.Trace; -import org.h2.table.Table; import org.h2.util.StringUtils; /** @@ -25,11 +24,6 @@ public Comment(Database database, int id, DbObject obj) { this.quotedObjectName = obj.getSQL(DEFAULT_SQL_FLAGS); } - @Override - public String getCreateSQLForCopy(Table table, String quotedName) { - throw DbException.getInternalError(toString()); - } - private static String getTypeName(int type) { switch (type) { case DbObject.CONSTANT: diff --git a/h2/src/main/org/h2/engine/DbObject.java b/h2/src/main/org/h2/engine/DbObject.java index 7464f97794..38c0bad802 100644 --- a/h2/src/main/org/h2/engine/DbObject.java +++ b/h2/src/main/org/h2/engine/DbObject.java @@ -228,7 +228,9 @@ public final boolean isValid() { * @param quotedName the quoted name * @return the SQL statement */ - public abstract String getCreateSQLForCopy(Table table, String quotedName); + public String getCreateSQLForCopy(Table table, String quotedName) { + throw DbException.getInternalError(toString()); + } /** * Construct the CREATE ... SQL statement for this object for meta table. diff --git a/h2/src/main/org/h2/engine/Role.java b/h2/src/main/org/h2/engine/Role.java index 7fec06ca11..e7364814fd 100644 --- a/h2/src/main/org/h2/engine/Role.java +++ b/h2/src/main/org/h2/engine/Role.java @@ -7,10 +7,8 @@ import java.util.ArrayList; -import org.h2.message.DbException; import org.h2.message.Trace; import org.h2.schema.Schema; -import org.h2.table.Table; /** * Represents a role. Roles can be granted to users, and to other roles. @@ -24,11 +22,6 @@ public Role(Database database, int id, String roleName, boolean system) { this.system = system; } - @Override - public String getCreateSQLForCopy(Table table, String quotedName) { - throw DbException.getInternalError(toString()); - } - /** * Get the CREATE SQL statement for this object. * diff --git a/h2/src/main/org/h2/engine/Setting.java b/h2/src/main/org/h2/engine/Setting.java index 3d8cc24576..d2de710aa4 100644 --- a/h2/src/main/org/h2/engine/Setting.java +++ b/h2/src/main/org/h2/engine/Setting.java @@ -7,7 +7,6 @@ import org.h2.message.DbException; import org.h2.message.Trace; -import org.h2.table.Table; /** * A persistent database setting. @@ -47,11 +46,6 @@ public String getStringValue() { return stringValue; } - @Override - public String getCreateSQLForCopy(Table table, String quotedName) { - throw DbException.getInternalError(toString()); - } - @Override public String getCreateSQL() { StringBuilder buff = new StringBuilder("SET "); diff --git a/h2/src/main/org/h2/engine/User.java b/h2/src/main/org/h2/engine/User.java index 281d691ec1..80e1c3c4b5 100644 --- a/h2/src/main/org/h2/engine/User.java +++ b/h2/src/main/org/h2/engine/User.java @@ -74,11 +74,6 @@ public void setUserPasswordHash(byte[] userPasswordHash) { } } - @Override - public String getCreateSQLForCopy(Table table, String quotedName) { - throw DbException.getInternalError(toString()); - } - @Override public String getCreateSQL() { return getCreateSQL(true); diff --git a/h2/src/main/org/h2/schema/Constant.java b/h2/src/main/org/h2/schema/Constant.java index bcf523ab79..c7feff95e3 100644 --- a/h2/src/main/org/h2/schema/Constant.java +++ b/h2/src/main/org/h2/schema/Constant.java @@ -8,9 +8,7 @@ import org.h2.engine.DbObject; import org.h2.engine.SessionLocal; import org.h2.expression.ValueExpression; -import org.h2.message.DbException; import org.h2.message.Trace; -import org.h2.table.Table; import org.h2.value.Value; /** @@ -26,11 +24,6 @@ public Constant(Schema schema, int id, String name) { super(schema, id, name, Trace.SCHEMA); } - @Override - public String getCreateSQLForCopy(Table table, String quotedName) { - throw DbException.getInternalError(toString()); - } - @Override public String getCreateSQL() { StringBuilder builder = new StringBuilder("CREATE CONSTANT "); diff --git a/h2/src/main/org/h2/schema/Domain.java b/h2/src/main/org/h2/schema/Domain.java index 1003a2105a..297ecf301e 100644 --- a/h2/src/main/org/h2/schema/Domain.java +++ b/h2/src/main/org/h2/schema/Domain.java @@ -12,10 +12,8 @@ import org.h2.engine.SessionLocal; import org.h2.expression.Expression; import org.h2.expression.ValueExpression; -import org.h2.message.DbException; import org.h2.message.Trace; import org.h2.table.ColumnTemplate; -import org.h2.table.Table; import org.h2.util.Utils; import org.h2.value.TypeInfo; import org.h2.value.Value; @@ -42,11 +40,6 @@ public Domain(Schema schema, int id, String name) { super(schema, id, name, Trace.SCHEMA); } - @Override - public String getCreateSQLForCopy(Table table, String quotedName) { - throw DbException.getInternalError(toString()); - } - @Override public String getDropSQL() { StringBuilder builder = new StringBuilder("DROP DOMAIN IF EXISTS "); diff --git a/h2/src/main/org/h2/schema/Schema.java b/h2/src/main/org/h2/schema/Schema.java index 9002a5c8a9..bb508605f3 100644 --- a/h2/src/main/org/h2/schema/Schema.java +++ b/h2/src/main/org/h2/schema/Schema.java @@ -92,11 +92,6 @@ public boolean canDrop() { return !system; } - @Override - public String getCreateSQLForCopy(Table table, String quotedName) { - throw DbException.getInternalError(toString()); - } - @Override public String getCreateSQL() { if (system) { diff --git a/h2/src/main/org/h2/schema/Sequence.java b/h2/src/main/org/h2/schema/Sequence.java index f21b918132..3dceeda1da 100644 --- a/h2/src/main/org/h2/schema/Sequence.java +++ b/h2/src/main/org/h2/schema/Sequence.java @@ -11,7 +11,6 @@ import org.h2.engine.SessionLocal; import org.h2.message.DbException; import org.h2.message.Trace; -import org.h2.table.Table; import org.h2.value.TypeInfo; import org.h2.value.Value; import org.h2.value.ValueBigint; @@ -350,11 +349,6 @@ public String getDropSQL() { return getSQL(builder, DEFAULT_SQL_FLAGS).toString(); } - @Override - public String getCreateSQLForCopy(Table table, String quotedName) { - throw DbException.getInternalError(toString()); - } - @Override public String getCreateSQL() { StringBuilder builder = getSQL(new StringBuilder("CREATE SEQUENCE "), DEFAULT_SQL_FLAGS); diff --git a/h2/src/main/org/h2/schema/UserDefinedFunction.java b/h2/src/main/org/h2/schema/UserDefinedFunction.java index 7a3c6c8954..f697f0ed32 100644 --- a/h2/src/main/org/h2/schema/UserDefinedFunction.java +++ b/h2/src/main/org/h2/schema/UserDefinedFunction.java @@ -6,7 +6,6 @@ package org.h2.schema; import org.h2.message.DbException; -import org.h2.table.Table; /** * User-defined Java function or aggregate function. @@ -19,11 +18,6 @@ public abstract class UserDefinedFunction extends SchemaObject { super(newSchema, id, name, traceModuleId); } - @Override - public final String getCreateSQLForCopy(Table table, String quotedName) { - throw DbException.getInternalError(toString()); - } - @Override public final void checkRename() { throw DbException.getUnsupportedException("RENAME"); diff --git a/h2/src/main/org/h2/table/Table.java b/h2/src/main/org/h2/table/Table.java index d194c64015..92974e8781 100644 --- a/h2/src/main/org/h2/table/Table.java +++ b/h2/src/main/org/h2/table/Table.java @@ -401,11 +401,6 @@ public Column getRowIdColumn() { return null; } - @Override - public String getCreateSQLForCopy(Table table, String quotedName) { - throw DbException.getInternalError(toString()); - } - /** * Check whether the table (or view) contains no columns that prevent index * conditions to be used. For example, a view that contains the ROWNUM() From 1ac7a24a092bc0e8dc654046f0b134ab2f857ac1 Mon Sep 17 00:00:00 2001 From: Evgenij Ryazanov Date: Sun, 5 Jun 2022 16:25:25 +0800 Subject: [PATCH 05/13] Require ADMIN privileges for tables with table engines --- h2/src/main/org/h2/command/ddl/CreateTable.java | 7 +++++-- h2/src/main/org/h2/res/help.csv | 4 ++++ .../test/org/h2/test/db/TestTableEngines.java | 17 +++++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/h2/src/main/org/h2/command/ddl/CreateTable.java b/h2/src/main/org/h2/command/ddl/CreateTable.java index 213b178702..ede7e6ce96 100644 --- a/h2/src/main/org/h2/command/ddl/CreateTable.java +++ b/h2/src/main/org/h2/command/ddl/CreateTable.java @@ -71,10 +71,13 @@ public void setIfNotExists(boolean ifNotExists) { public long update() { Schema schema = getSchema(); boolean isSessionTemporary = data.temporary && !data.globalTemporary; - if (!isSessionTemporary) { + Database db = session.getDatabase(); + String tableEngine = data.tableEngine; + if (tableEngine != null || db.getSettings().defaultTableEngine != null) { + session.getUser().checkAdmin(); + } else if (!isSessionTemporary) { session.getUser().checkSchemaOwner(schema); } - Database db = session.getDatabase(); if (!db.isPersistent()) { data.persistIndexes = false; } diff --git a/h2/src/main/org/h2/res/help.csv b/h2/src/main/org/h2/res/help.csv index 791a92bc82..6d6026aec0 100644 --- a/h2/src/main/org/h2/res/help.csv +++ b/h2/src/main/org/h2/res/help.csv @@ -961,6 +961,10 @@ TABLE @h2@ [ IF NOT EXISTS ] [schemaName.]tableName [ AS ( query ) [ WITH [ NO ] DATA ] ]"," Creates a new table. +Admin rights are required to execute this command +if and only if ENGINE option is used or custom default table engine is configured in the database. +Schema owner rights or ALTER ANY SCHEMA rights are required for creation of regular tables and GLOBAL TEMPORARY tables. + Cached tables (the default for regular tables) are persistent, and the number of rows is not limited by the main memory. Memory tables (the default for temporary tables) are persistent, diff --git a/h2/src/test/org/h2/test/db/TestTableEngines.java b/h2/src/test/org/h2/test/db/TestTableEngines.java index a87646f7e3..3afd8cb47c 100644 --- a/h2/src/test/org/h2/test/db/TestTableEngines.java +++ b/h2/src/test/org/h2/test/db/TestTableEngines.java @@ -19,6 +19,7 @@ import java.util.Set; import java.util.TreeSet; +import org.h2.api.ErrorCode; import org.h2.api.TableEngine; import org.h2.command.ddl.CreateTableData; import org.h2.command.query.AllColumnsForPlan; @@ -60,6 +61,7 @@ public static void main(String[] a) throws Exception { @Override public void test() throws Exception { + testAdminPrivileges(); testQueryExpressionFlag(); testSubQueryInfo(); testEngineParams(); @@ -68,6 +70,21 @@ public void test() throws Exception { testMultiColumnTreeSetIndex(); } + private void testAdminPrivileges() throws SQLException { + deleteDb("tableEngine"); + Connection conn = getConnection("tableEngine"); + Statement stat = conn.createStatement(); + stat.execute("CREATE USER U PASSWORD '1'"); + stat.execute("GRANT ALTER ANY SCHEMA TO U"); + Connection connUser = getConnection("tableEngine", "U", "1"); + Statement statUser = connUser.createStatement(); + assertThrows(ErrorCode.ADMIN_RIGHTS_REQUIRED, statUser) + .execute("CREATE TABLE T(ID INT, NAME VARCHAR) ENGINE \"" + EndlessTableEngine.class.getName() + '"'); + connUser.close(); + conn.close(); + deleteDb("tableEngine"); + } + private void testEngineParams() throws SQLException { deleteDb("tableEngine"); Connection conn = getConnection("tableEngine"); From 98de3b0c5ce461a411b72f4305869a73c5ad447b Mon Sep 17 00:00:00 2001 From: Evgenij Ryazanov Date: Sun, 5 Jun 2022 16:39:08 +0800 Subject: [PATCH 06/13] Avoid generation of too long names in Database.getTempTableName() --- h2/src/main/org/h2/engine/Database.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/h2/src/main/org/h2/engine/Database.java b/h2/src/main/org/h2/engine/Database.java index 28aa24d2ab..f9d3566eff 100644 --- a/h2/src/main/org/h2/engine/Database.java +++ b/h2/src/main/org/h2/engine/Database.java @@ -1699,10 +1699,13 @@ public Role getPublicRole() { * @return a unique name */ public synchronized String getTempTableName(String baseName, SessionLocal session) { + int maxBaseLength = Constants.MAX_IDENTIFIER_LENGTH - (7 + ValueInteger.DISPLAY_SIZE * 2); + if (baseName.length() > maxBaseLength) { + baseName = baseName.substring(0, maxBaseLength); + } String tempName; do { - tempName = baseName + "_COPY_" + session.getId() + - "_" + nextTempTableId++; + tempName = baseName + "_COPY_" + session.getId() + '_' + nextTempTableId++; } while (mainSchema.findTableOrView(session, tempName) != null); return tempName; } From bc3258692e975725eb2ac5481469391c366dfb23 Mon Sep 17 00:00:00 2001 From: Evgenij Ryazanov Date: Sun, 5 Jun 2022 17:37:39 +0800 Subject: [PATCH 07/13] Remove ROUNDMAGIC from documentation --- h2/src/main/org/h2/res/help.csv | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/h2/src/main/org/h2/res/help.csv b/h2/src/main/org/h2/res/help.csv index 6d6026aec0..0967c0314e 100644 --- a/h2/src/main/org/h2/res/help.csv +++ b/h2/src/main/org/h2/res/help.csv @@ -5126,19 +5126,6 @@ This method returns value of the same type as argument, but with adjusted precis ROUND(N, 2) " -"Functions (Numeric)","ROUNDMAGIC"," -@h2@ ROUNDMAGIC(numeric) -"," -This function rounds numbers in a good way, but it is slow. -It has a special handling for numbers around 0. -Only numbers smaller or equal +/-1000000000000 are supported. -The value is converted to a String internally, and then the last 4 characters are checked. -'000x' becomes '0000' and '999x' becomes '999999', which is rounded automatically. -This method returns a double. -"," -ROUNDMAGIC(N/3*3) -" - "Functions (Numeric)","SECURE_RAND"," @h2@ SECURE_RAND(int) "," From a20ed6b6e1f899420087c0bcb2a83922c3c5f218 Mon Sep 17 00:00:00 2001 From: Evgenij Ryazanov Date: Sun, 5 Jun 2022 18:07:20 +0800 Subject: [PATCH 08/13] Fix building of documentation --- h2/src/main/org/h2/mvstore/FileStore.java | 6 ++++-- h2/src/main/org/h2/mvstore/db/MVTempResult.java | 3 ++- h2/src/tools/org/h2/build/doc/dictionary.txt | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/h2/src/main/org/h2/mvstore/FileStore.java b/h2/src/main/org/h2/mvstore/FileStore.java index 291394dfb4..ef30c36fa9 100644 --- a/h2/src/main/org/h2/mvstore/FileStore.java +++ b/h2/src/main/org/h2/mvstore/FileStore.java @@ -125,8 +125,10 @@ public void writeFully(long pos, ByteBuffer src) { * used */ public void open(String fileName, boolean readOnly, char[] encryptionKey) { - open(fileName, readOnly, encryptionKey == null ? null : - fileChannel -> new FileEncrypt(fileName, FilePathEncrypt.getPasswordBytes(encryptionKey), fileChannel)); + open(fileName, readOnly, + encryptionKey == null ? null + : fileChannel -> new FileEncrypt(fileName, FilePathEncrypt.getPasswordBytes(encryptionKey), + fileChannel)); } public FileStore open(String fileName, boolean readOnly) { diff --git a/h2/src/main/org/h2/mvstore/db/MVTempResult.java b/h2/src/main/org/h2/mvstore/db/MVTempResult.java index 53ec8e0a2a..26735b4fa5 100644 --- a/h2/src/main/org/h2/mvstore/db/MVTempResult.java +++ b/h2/src/main/org/h2/mvstore/db/MVTempResult.java @@ -178,7 +178,8 @@ public static ResultExternal of(Database database, Expression[] expressions, boo String fileName = FileUtils.createTempFile("h2tmp", Constants.SUFFIX_TEMP_FILE, true); FileStore fileStore = database.getStore().getMvStore().getFileStore().open(fileName, false); - MVStore.Builder builder = new MVStore.Builder().adoptFileStore(fileStore).cacheSize(0).autoCommitDisabled(); + MVStore.Builder builder = new MVStore.Builder().adoptFileStore(fileStore).cacheSize(0) + .autoCommitDisabled(); store = builder.open(); this.expressions = expressions; this.visibleColumnCount = visibleColumnCount; diff --git a/h2/src/tools/org/h2/build/doc/dictionary.txt b/h2/src/tools/org/h2/build/doc/dictionary.txt index ebc7a03276..3724b9c4cf 100644 --- a/h2/src/tools/org/h2/build/doc/dictionary.txt +++ b/h2/src/tools/org/h2/build/doc/dictionary.txt @@ -849,4 +849,4 @@ entirely skeleton discouraged pearson coefficient squares covariance mytab debug filestore backstop tie breaker lockable lobtx btx waiter accounted aiobe spf resolvers generators abandoned accidental approximately cited competitive configuring drastically happier hasn interactions journal journaling ldt occasional odt officially pragma ration recognising rnrn rough seemed sonatype supplementary subtree ver -wal wbr worse xerial won +wal wbr worse xerial won symlink respected adopted From 5f73ad3d05e2c2024964df209645a8bb3c747f2f Mon Sep 17 00:00:00 2001 From: Evgenij Ryazanov Date: Sun, 5 Jun 2022 18:08:42 +0800 Subject: [PATCH 09/13] Restore support of LTRIM and RTRIM with two arguments --- h2/src/main/org/h2/command/Parser.java | 4 ++-- h2/src/main/org/h2/res/help.csv | 8 ++++---- .../test/org/h2/test/scripts/functions/string/ltrim.sql | 3 +++ .../test/org/h2/test/scripts/functions/string/rtrim.sql | 3 +++ 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/h2/src/main/org/h2/command/Parser.java b/h2/src/main/org/h2/command/Parser.java index cc9ce677c7..ffec3ec791 100644 --- a/h2/src/main/org/h2/command/Parser.java +++ b/h2/src/main/org/h2/command/Parser.java @@ -4062,9 +4062,9 @@ private Expression readCompatibilityFunction(String name) { return readSubstringFunction(); // TRIM case "LTRIM": - return new TrimFunction(readSingleArgument(), null, TrimFunction.LEADING); + return new TrimFunction(readExpression(), readIfArgument(), TrimFunction.LEADING); case "RTRIM": - return new TrimFunction(readSingleArgument(), null, TrimFunction.TRAILING); + return new TrimFunction(readExpression(), readIfArgument(), TrimFunction.TRAILING); // UPPER case "UCASE": return new StringFunction1(readSingleArgument(), StringFunction1.UPPER); diff --git a/h2/src/main/org/h2/res/help.csv b/h2/src/main/org/h2/res/help.csv index 0967c0314e..fa6b620fa3 100644 --- a/h2/src/main/org/h2/res/help.csv +++ b/h2/src/main/org/h2/res/help.csv @@ -5411,9 +5411,9 @@ RPAD(TEXT, 10, '-') " "Functions (String)","LTRIM"," -@c@ LTRIM(string) +@c@ LTRIM(string [, characterToTrimString]) "," -Removes all leading spaces from a string. +Removes all leading spaces or other specified characters from a string. This function is deprecated, use [TRIM](https://h2database.com/html/functions.html#trim) instead of it. "," @@ -5421,9 +5421,9 @@ LTRIM(NAME) " "Functions (String)","RTRIM"," -@c@ RTRIM(string) +@c@ RTRIM(string [, characterToTrimString]) "," -Removes all trailing spaces from a string. +Removes all trailing spaces or other specified characters from a string. This function is deprecated, use [TRIM](https://h2database.com/html/functions.html#trim) instead of it. "," diff --git a/h2/src/test/org/h2/test/scripts/functions/string/ltrim.sql b/h2/src/test/org/h2/test/scripts/functions/string/ltrim.sql index daf8e3e101..ca5732f08a 100644 --- a/h2/src/test/org/h2/test/scripts/functions/string/ltrim.sql +++ b/h2/src/test/org/h2/test/scripts/functions/string/ltrim.sql @@ -8,3 +8,6 @@ select ltrim(null) en, '>' || ltrim('a') || '<' ea, '>' || ltrim(' a ') || '<' e > ---- --- ---- > null >a< >a < > rows: 1 + +VALUES LTRIM('__A__', '_'); +>> A__ diff --git a/h2/src/test/org/h2/test/scripts/functions/string/rtrim.sql b/h2/src/test/org/h2/test/scripts/functions/string/rtrim.sql index a216fd6805..2bab3a06fa 100644 --- a/h2/src/test/org/h2/test/scripts/functions/string/rtrim.sql +++ b/h2/src/test/org/h2/test/scripts/functions/string/rtrim.sql @@ -11,3 +11,6 @@ select rtrim(null) en, '>' || rtrim('a') || '<' ea, '>' || rtrim(' a ') || '<' e select rtrim() from dual; > exception SYNTAX_ERROR_2 + +VALUES RTRIM('__A__', '_'); +>> __A From 1bacc48da7894330bfe1e6913d59c08cef7247b2 Mon Sep 17 00:00:00 2001 From: Evgenij Ryazanov Date: Sun, 5 Jun 2022 19:12:26 +0800 Subject: [PATCH 10/13] Improve support for ROW in NON_KEYWORDS list --- h2/src/docsrc/html/changelog.html | 2 + h2/src/main/org/h2/command/Parser.java | 196 +++++++++++++++---------- 2 files changed, 118 insertions(+), 80 deletions(-) diff --git a/h2/src/docsrc/html/changelog.html b/h2/src/docsrc/html/changelog.html index 23bd7bc41f..9a6628ea81 100644 --- a/h2/src/docsrc/html/changelog.html +++ b/h2/src/docsrc/html/changelog.html @@ -21,6 +21,8 @@

Change Log

Next Version (unreleased)

    +
  • Issue #3390: "ROW" cannot be set as a non keyword in 2.x +
  • Issue #3448: With linked table to postgreSQL, case-sensitive column names not respected in where part
  • Issue #3434: JavaTableFunction is not closing underlying ResultSet when reading column list diff --git a/h2/src/main/org/h2/command/Parser.java b/h2/src/main/org/h2/command/Parser.java index ffec3ec791..f158b3dea2 100644 --- a/h2/src/main/org/h2/command/Parser.java +++ b/h2/src/main/org/h2/command/Parser.java @@ -1788,8 +1788,7 @@ private void parseValuesForCommand(CommandWithValues command) { do { values.clear(); boolean multiColumn; - if (readIf(ROW)) { - read(OPEN_PAREN); + if (readIf(ROW, OPEN_PAREN)) { multiColumn = true; } else { multiColumn = readIf(OPEN_PAREN); @@ -4968,6 +4967,45 @@ private Parameter readParameter() { } private Expression readTerm() { + Expression r = currentTokenType == IDENTIFIER ? readTermWithIdentifier() : readTermWithoutIdentifier(); + if (readIf(OPEN_BRACKET)) { + r = new ArrayElementReference(r, readExpression()); + read(CLOSE_BRACKET); + } + if (readIf(COLON_COLON)) { + r = readColonColonAfterTerm(r); + } + for (;;) { + TypeInfo ti = readIntervalQualifier(); + if (ti != null) { + r = new CastSpecification(r, ti); + } + int index = tokenIndex; + if (readIf("AT")) { + if (readIf("TIME")) { + read("ZONE"); + r = new TimeZoneOperation(r, readExpression()); + continue; + } else if (readIf("LOCAL")) { + r = new TimeZoneOperation(r, null); + continue; + } else { + setTokenIndex(index); + } + } else if (readIf("FORMAT")) { + if (readIf("JSON")) { + r = new Format(r, FormatEnum.JSON); + continue; + } else { + setTokenIndex(index); + } + } + break; + } + return r; + } + + private Expression readTermWithoutIdentifier() { Expression r; switch (currentTokenType) { case AT: @@ -5064,20 +5102,21 @@ private Expression readTerm() { read(); r = readInterval(); break; - case ROW: { - read(); - read(OPEN_PAREN); - if (readIf(CLOSE_PAREN)) { - r = ValueExpression.get(ValueRow.EMPTY); + case ROW: + if (readIf(ROW, OPEN_PAREN)) { + if (readIf(CLOSE_PAREN)) { + r = ValueExpression.get(ValueRow.EMPTY); + } else { + ArrayList list = Utils.newSmallArrayList(); + do { + list.add(readExpression()); + } while (readIfMore()); + r = new ExpressionList(list.toArray(new Expression[0]), false); + } } else { - ArrayList list = Utils.newSmallArrayList(); - do { - list.add(readExpression()); - } while (readIfMore()); - r = new ExpressionList(list.toArray(new Expression[0]), false); + r = readTermWithIdentifier(); } break; - } case TRUE: read(); r = ValueExpression.TRUE; @@ -5140,17 +5179,21 @@ private Expression readTerm() { break; } case CURRENT_CATALOG: - return readCurrentGeneralValueSpecification(CurrentGeneralValueSpecification.CURRENT_CATALOG); + r = readCurrentGeneralValueSpecification(CurrentGeneralValueSpecification.CURRENT_CATALOG); + break; case CURRENT_DATE: read(); r = readCurrentDateTimeValueFunction(CurrentDateTimeValueFunction.CURRENT_DATE, readIf(OPEN_PAREN), null); break; case CURRENT_PATH: - return readCurrentGeneralValueSpecification(CurrentGeneralValueSpecification.CURRENT_PATH); + r = readCurrentGeneralValueSpecification(CurrentGeneralValueSpecification.CURRENT_PATH); + break; case CURRENT_ROLE: - return readCurrentGeneralValueSpecification(CurrentGeneralValueSpecification.CURRENT_ROLE); + r = readCurrentGeneralValueSpecification(CurrentGeneralValueSpecification.CURRENT_ROLE); + break; case CURRENT_SCHEMA: - return readCurrentGeneralValueSpecification(CurrentGeneralValueSpecification.CURRENT_SCHEMA); + r = readCurrentGeneralValueSpecification(CurrentGeneralValueSpecification.CURRENT_SCHEMA); + break; case CURRENT_TIME: read(); r = readCurrentDateTimeValueFunction(CurrentDateTimeValueFunction.CURRENT_TIME, readIf(OPEN_PAREN), null); @@ -5162,16 +5205,20 @@ private Expression readTerm() { break; case CURRENT_USER: case USER: - return readCurrentGeneralValueSpecification(CurrentGeneralValueSpecification.CURRENT_USER); + r = readCurrentGeneralValueSpecification(CurrentGeneralValueSpecification.CURRENT_USER); + break; case SESSION_USER: - return readCurrentGeneralValueSpecification(CurrentGeneralValueSpecification.SESSION_USER); + r = readCurrentGeneralValueSpecification(CurrentGeneralValueSpecification.SESSION_USER); + break; case SYSTEM_USER: - return readCurrentGeneralValueSpecification(CurrentGeneralValueSpecification.SYSTEM_USER); + r = readCurrentGeneralValueSpecification(CurrentGeneralValueSpecification.SYSTEM_USER); + break; case ANY: case SOME: read(); read(OPEN_PAREN); - return readAggregate(AggregateType.ANY, "ANY"); + r = readAggregate(AggregateType.ANY, "ANY"); + break; case DAY: case HOUR: case MINUTE: @@ -5218,68 +5265,40 @@ private Expression readTerm() { if (!isIdentifier()) { throw getSyntaxError(); } - //$FALL-THROUGH$ - case IDENTIFIER: - String name = currentToken; - boolean quoted = token.isQuoted(); - read(); - if (readIf(OPEN_PAREN)) { - r = readFunction(null, name); - } else if (readIf(DOT)) { - r = readTermObjectDot(name); - } else if (quoted) { - r = new ExpressionColumn(database, null, null, name); - } else { - r = readTermWithIdentifier(name, quoted); - } + r = readTermWithIdentifier(); break; } - if (readIf(OPEN_BRACKET)) { - r = new ArrayElementReference(r, readExpression()); - read(CLOSE_BRACKET); - } - colonColon: if (readIf(COLON_COLON)) { - if (database.getMode().getEnum() == ModeEnum.PostgreSQL) { - // PostgreSQL compatibility - if (isToken("PG_CATALOG")) { - read("PG_CATALOG"); - read(DOT); - } - if (readIf("REGCLASS")) { - r = new Regclass(r); - break colonColon; - } - } - r = new CastSpecification(r, parseColumnWithType(null)); + return r; + } + + private Expression readTermWithIdentifier() { + Expression r; + String name = currentToken; + boolean quoted = token.isQuoted(); + read(); + if (readIf(OPEN_PAREN)) { + r = readFunction(null, name); + } else if (readIf(DOT)) { + r = readTermObjectDot(name); + } else if (quoted) { + r = new ExpressionColumn(database, null, null, name); + } else { + r = readTermWithIdentifier(name, quoted); } - for (;;) { - TypeInfo ti = readIntervalQualifier(); - if (ti != null) { - r = new CastSpecification(r, ti); + return r; + } + + private Expression readColonColonAfterTerm(Expression r) { + if (database.getMode().getEnum() == ModeEnum.PostgreSQL) { + // PostgreSQL compatibility + if (readIf("PG_CATALOG")) { + read(DOT); } - int index = tokenIndex; - if (readIf("AT")) { - if (readIf("TIME")) { - read("ZONE"); - r = new TimeZoneOperation(r, readExpression()); - continue; - } else if (readIf("LOCAL")) { - r = new TimeZoneOperation(r, null); - continue; - } else { - setTokenIndex(index); - } - } else if (readIf("FORMAT")) { - if (readIf("JSON")) { - r = new Format(r, FormatEnum.JSON); - continue; - } else { - setTokenIndex(index); - } + if (readIf("REGCLASS")) { + return new Regclass(r); } - break; } - return r; + return new CastSpecification(r, parseColumnWithType(null)); } private Expression readCurrentGeneralValueSpecification(int specification) { @@ -5763,6 +5782,19 @@ private boolean readIf(int tokenType) { return false; } + private boolean readIf(int tokenType1, int tokenType2) { + int size = tokens.size(); + if (tokenType1 == currentTokenType) { + int i = tokenIndex + 1; + if (i < size && tokens.get(i).tokenType() == tokenType2) { + setTokenIndex(i + 1); + return true; + } + } + addExpected(tokenType1, tokenType2); + return false; + } + private boolean isToken(String tokenName) { if (!token.isQuoted() && equalsToken(tokenName, currentToken)) { return true; @@ -5802,6 +5834,12 @@ private void addExpected(int tokenType) { } } + private void addExpected(int tokenType1, int tokenType2) { + if (expectedList != null) { + expectedList.add(TOKENS[tokenType1] + ' ' + TOKENS[tokenType2]); + } + } + private void addMultipleExpected(int ... tokenTypes) { for (int tokenType : tokenTypes) { expectedList.add(TOKENS[tokenType]); @@ -6919,9 +6957,7 @@ private TableValueConstructor parseValues() { } private ArrayList parseValuesRow(ArrayList row) { - if (readIf(ROW)) { - read(OPEN_PAREN); - } else if (!readIf(OPEN_PAREN)) { + if (!readIf(ROW, OPEN_PAREN) && !readIf(OPEN_PAREN)) { row.add(readExpression()); return row; } From 6a5fe627307da41397dd1c0223c7ba32cb3f3307 Mon Sep 17 00:00:00 2001 From: Evgenij Ryazanov Date: Sun, 5 Jun 2022 21:54:10 +0800 Subject: [PATCH 11/13] Improve NON_KEYWORDS and remove FILTER from context-sensitive keywords --- h2/src/docsrc/html/advanced.html | 2 - h2/src/main/org/h2/command/Parser.java | 455 ++++++++++-------------- h2/src/main/org/h2/util/ParserUtil.java | 1 - 3 files changed, 187 insertions(+), 271 deletions(-) diff --git a/h2/src/docsrc/html/advanced.html b/h2/src/docsrc/html/advanced.html index b4e1d3fc55..05b0b96581 100644 --- a/h2/src/docsrc/html/advanced.html +++ b/h2/src/docsrc/html/advanced.html @@ -529,8 +529,6 @@

    Keywords / Reserved Words

    +++++++ FETCH +++++++ -FILTER -CS++++ FOR +++++++ FOREIGN diff --git a/h2/src/main/org/h2/command/Parser.java b/h2/src/main/org/h2/command/Parser.java index f158b3dea2..3a84d0ea45 100644 --- a/h2/src/main/org/h2/command/Parser.java +++ b/h2/src/main/org/h2/command/Parser.java @@ -143,6 +143,7 @@ import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.StringJoiner; import java.util.TreeSet; import org.h2.api.ErrorCode; import org.h2.api.IntervalQualifier; @@ -969,8 +970,7 @@ private TransactionCommand parseRollback() { return command; } readIf("WORK"); - if (readIf(TO)) { - read("SAVEPOINT"); + if (readIf(TO, "SAVEPOINT")) { command = new TransactionCommand(session, CommandInterface.ROLLBACK_TO_SAVEPOINT); command.setSavepointName(readIdentifier()); } else { @@ -1380,7 +1380,7 @@ private Prepared parseShow() { } else if (readIf("DATABASES") || readIf("SCHEMAS")) { // for MySQL compatibility buff.append("SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA"); - } else if (database.getMode().getEnum() == ModeEnum.PostgreSQL && readIf("ALL")) { + } else if (database.getMode().getEnum() == ModeEnum.PostgreSQL && readIf(ALL)) { // for PostgreSQL compatibility buff.append("NAME, SETTING FROM PG_CATALOG.PG_SETTINGS"); } @@ -1554,8 +1554,7 @@ private Prepared parseMergeInto(TableFilter targetTableFilter, int start) { } command.setColumns(parseColumnList(table)); } - if (readIf(KEY)) { - read(OPEN_PAREN); + if (readIf(KEY, OPEN_PAREN)) { command.setKeys(parseColumnList(table)); } if (readIf(VALUES)) { @@ -1662,8 +1661,7 @@ private Insert parseInsert(int start) { } readValues: { if (!requireQuery) { - if (overridingSystem == null && readIf(DEFAULT)) { - read(VALUES); + if (overridingSystem == null && readIf(DEFAULT, VALUES)) { command.addRow(new Expression[0]); break readValues; } @@ -1687,14 +1685,10 @@ private Insert parseInsert(int start) { private Boolean readIfOverriding() { Boolean overridingSystem = null; - if (readIf("OVERRIDING")) { - if (readIf(USER)) { - overridingSystem = Boolean.FALSE; - } else { - read("SYSTEM"); - overridingSystem = Boolean.TRUE; - } - read(VALUE); + if (readIf("OVERRIDING", USER, VALUE)) { + overridingSystem = Boolean.FALSE; + } else if (readIf("OVERRIDING", "SYSTEM", VALUE)) { + overridingSystem = Boolean.TRUE; } return overridingSystem; } @@ -1716,10 +1710,7 @@ private void parseInsertSet(Insert command, Table table, Column[] columns) { private void parseInsertCompatibility(Insert command, Table table, Mode mode) { if (mode.onDuplicateKeyUpdate) { - if (readIf(ON)) { - read("DUPLICATE"); - read(KEY); - read("UPDATE"); + if (readIf(ON, "DUPLICATE", KEY, "UPDATE")) { do { String columnName = readIdentifier(); if (readIf(DOT)) { @@ -1745,10 +1736,7 @@ private void parseInsertCompatibility(Insert command, Table table, Mode mode) { } } if (mode.insertOnConflict) { - if (readIf(ON)) { - read("CONFLICT"); - read("DO"); - read("NOTHING"); + if (readIf(ON, "CONFLICT", "DO", "NOTHING")) { command.setIgnore(true); } } @@ -1823,9 +1811,8 @@ private TableFilter readTablePrimary() { TableValueConstructor query = parseValues(); alias = session.getNextSystemIdentifier(sqlCommand); table = query.toTable(alias, null, parameters, createView != null, currentSelect); - } else if (readIf(TABLE)) { + } else if (readIf(TABLE, OPEN_PAREN)) { // Table function derived table - read(OPEN_PAREN); ArrayTableFunction function = readTableFunction(ArrayTableFunction.TABLE); table = new FunctionTable(database.getMainSchema(), session, function); } else { @@ -2084,8 +2071,7 @@ private ArrayList readDerivedColumnNames() { } private void discardWithTableHints() { - if (readIf(WITH)) { - read(OPEN_PAREN); + if (readIf(WITH, OPEN_PAREN)) { do { discardTableHint(); } while (readIfMore()); @@ -2111,11 +2097,9 @@ private Prepared parseTruncate() { read(TABLE); Table table = readTableOrView(); boolean restart = database.getMode().truncateTableRestartIdentity; - if (readIf("CONTINUE")) { - read("IDENTITY"); + if (readIf("CONTINUE", "IDENTITY")) { restart = false; - } else if (readIf("RESTART")) { - read("IDENTITY"); + } else if (readIf("RESTART", "IDENTITY")) { restart = true; } TruncateTable command = new TruncateTable(session); @@ -2125,8 +2109,7 @@ private Prepared parseTruncate() { } private boolean readIfExists(boolean ifExists) { - if (readIf(IF)) { - read(EXISTS); + if (readIf(IF, EXISTS)) { ifExists = true; } return ifExists; @@ -2305,12 +2288,10 @@ private Prepared parseDrop() { command.setDropAction(dropAction); } return command; - } else if (readIf(ALL)) { - read("OBJECTS"); + } else if (readIf(ALL, "OBJECTS")) { DropDatabase command = new DropDatabase(session); command.setDropAllObjects(true); - if (readIf("DELETE")) { - read("FILES"); + if (readIf("DELETE", "FILES")) { command.setDeleteFiles(true); } return command; @@ -2430,8 +2411,7 @@ private Expression readJoinSpecification(TableFilter filter1, TableFilter filter Expression on = null; if (readIf(ON)) { on = readExpression(); - } else if (readIf(USING)) { - read(OPEN_PAREN); + } else if (readIf(USING, OPEN_PAREN)) { do { String columnName = readIdentifier(); on = addJoinColumn(on, filter1, filter2, filter1.getColumn(columnName, false), @@ -2671,8 +2651,7 @@ private Query parseQueryTerm() { } private void parseEndOfQuery(Query command) { - if (readIf(ORDER)) { - read("BY"); + if (readIf(ORDER, "BY")) { Select oldSelect = currentSelect; if (command instanceof Select) { currentSelect = (Select) command; @@ -2725,8 +2704,7 @@ private void parseEndOfQuery(Query command) { read("ROWS"); } } - if (readIf(WITH)) { - read("TIES"); + if (readIf(WITH, "TIES")) { command.setWithTies(true); } else { read("ONLY"); @@ -2773,9 +2751,7 @@ private void parseIsolationClause() { if (readIf(WITH)) { if (readIf("RR") || readIf("RS")) { // concurrent-access-resolution clause - if (readIf("USE")) { - read(AND); - read("KEEP"); + if (readIf("USE", AND, "KEEP")) { if (readIf("SHARE") || readIf("UPDATE") || readIf("EXCLUSIVE")) { // ignore @@ -2849,15 +2825,13 @@ private void parseSelectExpressions(Select command) { if (readIf("PERCENT")) { command.setFetchPercent(true); } - if (readIf(WITH)) { - read("TIES"); + if (readIf(WITH, "TIES")) { command.setWithTies(true); } currentSelect = temp; } if (readIf(DISTINCT)) { - if (readIf(ON)) { - read(OPEN_PAREN); + if (readIf(ON, OPEN_PAREN)) { ArrayList distinctExpressions = Utils.newSmallArrayList(); do { distinctExpressions.add(readExpression()); @@ -2921,8 +2895,7 @@ private Select parseSelect(int start) { // the group by is read for the outer select (or not a select) // so that columns that are not grouped can be used currentSelect = oldSelect; - if (readIf(GROUP)) { - read("BY"); + if (readIf(GROUP, "BY")) { command.setGroupQuery(); ArrayList list = Utils.newSmallArrayList(); do { @@ -3156,17 +3129,12 @@ private Expression readCondition() { return new UniquePredicate(query); } default: - int index = tokenIndex; - if (readIf("INTERSECTS")) { - if (readIf(OPEN_PAREN)) { - Expression r1 = readConcat(); - read(COMMA); - Expression r2 = readConcat(); - read(CLOSE_PAREN); - return new Comparison(Comparison.SPATIAL_INTERSECTS, r1, r2, false); - } else { - setTokenIndex(index); - } + if (readIf("INTERSECTS", OPEN_PAREN)) { + Expression r1 = readConcat(); + read(COMMA); + Expression r2 = readConcat(); + read(CLOSE_PAREN); + return new Comparison(Comparison.SPATIAL_INTERSECTS, r1, r2, false); } if (expectedList != null) { addMultipleExpected(NOT, EXISTS, UNIQUE); @@ -3337,12 +3305,10 @@ private IsJsonPredicate readJsonPredicate(Expression left, boolean not, boolean itemType = JSONItemType.VALUE; } boolean unique = false; - if (readIf(WITH)) { - read(UNIQUE); + if (readIf(WITH, UNIQUE)) { readIf("KEYS"); unique = true; - } else if (readIf("WITHOUT")) { - read(UNIQUE); + } else if (readIf("WITHOUT", UNIQUE)) { readIf("KEYS"); } return new IsJsonPredicate(left, not, whenOperand, unique, itemType); @@ -3357,8 +3323,7 @@ private Expression readLikePredicate(Expression left, LikeType likeType, boolean private Expression readComparison(Expression left, int compareType, boolean whenOperand) { int start = tokenIndex; - if (readIf(ALL)) { - read(OPEN_PAREN); + if (readIf(ALL, OPEN_PAREN)) { if (isQuery()) { Query query = parseQuery(); left = new ConditionInQuery(left, false, whenOperand, query, true, compareType); @@ -3367,21 +3332,27 @@ private Expression readComparison(Expression left, int compareType, boolean when setTokenIndex(start); left = new Comparison(compareType, left, readConcat(), whenOperand); } - } else if (readIf(ANY) || readIf(SOME)) { - read(OPEN_PAREN); - if (currentTokenType == PARAMETER && compareType == Comparison.EQUAL) { - Parameter p = readParameter(); - left = new ConditionInParameter(left, false, whenOperand, p); - read(CLOSE_PAREN); - } else if (isQuery()) { - Query query = parseQuery(); - left = new ConditionInQuery(left, false, whenOperand, query, false, compareType); - read(CLOSE_PAREN); - } else { - setTokenIndex(start); - left = new Comparison(compareType, left, readConcat(), whenOperand); - } + } else if (readIf(ANY, OPEN_PAREN)) { + left = readAnyComparison(left, compareType, whenOperand, start); + } else if (readIf(SOME, OPEN_PAREN)) { + left = readAnyComparison(left, compareType, whenOperand, start); + } else { + left = new Comparison(compareType, left, readConcat(), whenOperand); + } + return left; + } + + private Expression readAnyComparison(Expression left, int compareType, boolean whenOperand, int start) { + if (currentTokenType == PARAMETER && compareType == Comparison.EQUAL) { + Parameter p = readParameter(); + left = new ConditionInParameter(left, false, whenOperand, p); + read(CLOSE_PAREN); + } else if (isQuery()) { + Query query = parseQuery(); + left = new ConditionInQuery(left, false, whenOperand, query, false, compareType); + read(CLOSE_PAREN); } else { + setTokenIndex(start); left = new Comparison(compareType, left, readConcat(), whenOperand); } return left; @@ -3668,8 +3639,7 @@ private void readAggregateOrder(Aggregate r, Expression expr, boolean parseSortT } private ArrayList readIfOrderBy() { - if (readIf(ORDER)) { - read("BY"); + if (readIf(ORDER, "BY")) { return parseSortSpecificationList(); } return null; @@ -3726,9 +3696,7 @@ private boolean readDistinctAgg() { } private void readFilterAndOver(AbstractAggregate aggregate) { - if (readIf("FILTER")) { - read(OPEN_PAREN); - read(WHERE); + if (readIf("FILTER", OPEN_PAREN, WHERE)) { Expression filterCondition = readExpression(); read(CLOSE_PAREN); aggregate.setFilterCondition(filterCondition); @@ -3766,8 +3734,7 @@ private Window readWindowSpecification() { } } ArrayList partitionBy = null; - if (readIf("PARTITION")) { - read("BY"); + if (readIf("PARTITION", "BY")) { partitionBy = Utils.newSmallArrayList(); do { Expression expr = readExpression(); @@ -3803,8 +3770,7 @@ private WindowFrame readWindowFrame() { int sqlIndex = token.start(); WindowFrameExclusion exclusion = WindowFrameExclusion.EXCLUDE_NO_OTHERS; if (readIf("EXCLUDE")) { - if (readIf("CURRENT")) { - read(ROW); + if (readIf("CURRENT", ROW)) { exclusion = WindowFrameExclusion.EXCLUDE_CURRENT_ROW; } else if (readIf(GROUP)) { exclusion = WindowFrameExclusion.EXCLUDE_GROUP; @@ -4531,8 +4497,7 @@ private ArrayTableFunction readUnnestFunction() { columns.add(new Column("C" + ++i, columnType)); } while (readIfMore()); } - if (readIf(WITH)) { - read("ORDINALITY"); + if (readIf(WITH, "ORDINALITY")) { columns.add(new Column("NORD", TypeInfo.TYPE_INTEGER)); } f.setColumns(columns); @@ -4739,61 +4704,38 @@ private WindowFunction readWindowFunction(String name) { } private void readFromFirstOrLast(WindowFunction function) { - if (readIf(FROM) && !readIf("FIRST")) { - read("LAST"); + if (readIf(FROM, "LAST")) { function.setFromLast(true); + } else { + readIf(FROM, "FIRST"); } } private void readRespectOrIgnoreNulls(WindowFunction function) { - if (readIf("RESPECT")) { - read("NULLS"); - } else if (readIf("IGNORE")) { - read("NULLS"); + if (readIf("IGNORE", "NULLS")) { function.setIgnoreNulls(true); + } else { + readIf("RESPECT", "NULLS"); } } private boolean readJsonObjectFunctionFlags(ExpressionWithFlags function, boolean forArray) { - int start = tokenIndex; boolean result = false; int flags = function.getFlags(); - if (readIf(NULL)) { - if (readIf(ON)) { - read(NULL); - flags &= ~JsonConstructorUtils.JSON_ABSENT_ON_NULL; - result = true; - } else { - setTokenIndex(start); - return false; - } - } else if (readIf("ABSENT")) { - if (readIf(ON)) { - read(NULL); - flags |= JsonConstructorUtils.JSON_ABSENT_ON_NULL; - result = true; - } else { - setTokenIndex(start); - return false; - } + if (readIf(NULL, ON, NULL)) { + flags &= ~JsonConstructorUtils.JSON_ABSENT_ON_NULL; + result = true; + } else if (readIf("ABSENT", ON, NULL)) { + flags |= JsonConstructorUtils.JSON_ABSENT_ON_NULL; + result = true; } if (!forArray) { - if (readIf(WITH)) { - read(UNIQUE); - read("KEYS"); + if (readIf(WITH, UNIQUE, "KEYS")) { flags |= JsonConstructorUtils.JSON_WITH_UNIQUE_KEYS; result = true; - } else if (readIf("WITHOUT")) { - if (readIf(UNIQUE)) { - read("KEYS"); - flags &= ~JsonConstructorUtils.JSON_WITH_UNIQUE_KEYS; - result = true; - } else if (result) { - throw getSyntaxError(); - } else { - setTokenIndex(start); - return false; - } + } else if (readIf("WITHOUT", UNIQUE, "KEYS")) { + flags &= ~JsonConstructorUtils.JSON_WITH_UNIQUE_KEYS; + result = true; } } if (result) { @@ -4853,8 +4795,7 @@ private Expression readIfWildcardRowidOrSequencePseudoColumn(String schema, Stri private Wildcard parseWildcard(String schema, String objectName) { Wildcard wildcard = new Wildcard(schema, objectName); - if (readIf(EXCEPT)) { - read(OPEN_PAREN); + if (readIf(EXCEPT, OPEN_PAREN)) { ArrayList exceptColumns = Utils.newSmallArrayList(); do { String s = null, t = null; @@ -5352,11 +5293,9 @@ private Expression readTermWithIdentifier(String name, boolean quoted) { switch (name.charAt(0) & 0xffdf) { case 'C': if (equalsToken("CURRENT", name)) { - int index = tokenIndex; - if (readIf(VALUE) && readIf(FOR)) { + if (readIf(VALUE, FOR)) { return new SequenceValue(readSequence()); } - setTokenIndex(index); if (database.getMode().getEnum() == ModeEnum.DB2) { return parseDB2SpecialRegisters(name); } @@ -5414,18 +5353,14 @@ && equalsToken("E", name)) { break; case 'N': if (equalsToken("NEXT", name)) { - int index = tokenIndex; - if (readIf(VALUE) && readIf(FOR)) { + if (readIf(VALUE, FOR)) { return new SequenceValue(readSequence(), getCurrentPreparedOrSelect()); } - setTokenIndex(index); } break; case 'T': if (equalsToken("TIME", name)) { - if (readIf(WITH)) { - read("TIME"); - read("ZONE"); + if (readIf(WITH, "TIME", "ZONE")) { if (currentTokenType != LITERAL || token.value(session).getValueType() != Value.VARCHAR) { throw getSyntaxError(); } @@ -5433,11 +5368,7 @@ && equalsToken("E", name)) { read(); return ValueExpression.get(ValueTimeTimeZone.parse(time)); } else { - boolean without = readIf("WITHOUT"); - if (without) { - read("TIME"); - read("ZONE"); - } + boolean without = readIf("WITHOUT", "TIME", "ZONE"); if (currentTokenType == LITERAL && token.value(session).getValueType() == Value.VARCHAR) { String time = token.value(session).getString(); read(); @@ -5447,9 +5378,7 @@ && equalsToken("E", name)) { } } } else if (equalsToken("TIMESTAMP", name)) { - if (readIf(WITH)) { - read("TIME"); - read("ZONE"); + if (readIf(WITH, "TIME", "ZONE")) { if (currentTokenType != LITERAL || token.value(session).getValueType() != Value.VARCHAR) { throw getSyntaxError(); } @@ -5457,11 +5386,7 @@ && equalsToken("E", name)) { read(); return ValueExpression.get(ValueTimestampTimeZone.parse(timestamp, session)); } else { - boolean without = readIf("WITHOUT"); - if (without) { - read("TIME"); - read("ZONE"); - } + boolean without = readIf("WITHOUT", "TIME", "ZONE"); if (currentTokenType == LITERAL && token.value(session).getValueType() == Value.VARCHAR) { String timestamp = token.value(session).getString(); read(); @@ -5526,9 +5451,7 @@ private Expression readInterval() { private Expression parseDB2SpecialRegisters(String name) { // Only "CURRENT" name is supported if (readIf("TIMESTAMP")) { - if (readIf(WITH)) { - read("TIME"); - read("ZONE"); + if (readIf(WITH, "TIME", "ZONE")) { return readCurrentDateTimeValueFunction(CurrentDateTimeValueFunction.CURRENT_TIMESTAMP, readIf(OPEN_PAREN), null); } @@ -5795,6 +5718,39 @@ private boolean readIf(int tokenType1, int tokenType2) { return false; } + private boolean readIf(int tokenType1, String tokenName2) { + int size = tokens.size(); + if (tokenType1 == currentTokenType) { + int i = tokenIndex + 1; + if (i < size) { + Token token2 = tokens.get(i); + if (!token2.isQuoted() && equalsToken(tokenName2, token2.asIdentifier())) { + setTokenIndex(i + 1); + return true; + } + } + } + addExpected(TOKENS[tokenType1], tokenName2); + return false; + } + + private boolean readIf(Object... tokensTypesOrNames) { + int count = tokensTypesOrNames.length; + int size = tokens.size(); + int i = tokenIndex; + check: if (i + count < size) { + for (Object tokenTypeOrName : tokensTypesOrNames) { + if (!testToken(tokenTypeOrName, tokens.get(i++))) { + break check; + } + } + setTokenIndex(i); + return true; + } + addExpected(tokensTypesOrNames); + return false; + } + private boolean isToken(String tokenName) { if (!token.isQuoted() && equalsToken(tokenName, currentToken)) { return true; @@ -5803,6 +5759,11 @@ private boolean isToken(String tokenName) { return false; } + private boolean testToken(Object expected, Token token) { + return expected instanceof Integer ? (int) expected == token.tokenType() + : !token.isQuoted() && equalsToken((String) expected, token.asIdentifier()); + } + private boolean isToken(int tokenType) { if (tokenType == currentTokenType) { return true; @@ -5840,6 +5801,22 @@ private void addExpected(int tokenType1, int tokenType2) { } } + private void addExpected(String tokenType1, String tokenType2) { + if (expectedList != null) { + expectedList.add(tokenType1 + ' ' + tokenType2); + } + } + + private void addExpected(Object... tokens) { + if (expectedList != null) { + StringJoiner j = new StringJoiner(" "); + for (Object token : tokens) { + j.add(token instanceof Integer ? TOKENS[(int) token] : (String) token); + } + expectedList.add(j.toString()); + } + } + private void addMultipleExpected(int ... tokenTypes) { for (int tokenType : tokenTypes) { expectedList.add(TOKENS[tokenType]); @@ -5945,8 +5922,7 @@ private Column parseColumnForTable(String columnName, boolean defaultNullable) { if (readIf(AS)) { column.setGeneratedExpression(readExpression()); } else if (readIf(DEFAULT)) { - if (readIf(ON)) { - read(NULL); + if (readIf(ON, NULL)) { defaultOnNull = true; break defaultIdentityGeneration; } @@ -5972,8 +5948,7 @@ private Column parseColumnForTable(String columnName, boolean defaultNullable) { column.setGeneratedExpression(readExpression()); } } - if (!column.isGenerated() && readIf(ON)) { - read("UPDATE"); + if (!column.isGenerated() && readIf(ON, "UPDATE")) { column.setOnUpdateExpression(session, readExpression()); } nullConstraint = parseNotNullConstraint(nullConstraint); @@ -6001,9 +5976,7 @@ private Column parseColumnForTable(String columnName, boolean defaultNullable) { "Internal Error - unhandled case: " + nullConstraint.name()); } if (!defaultOnNull) { - if (readIf(DEFAULT)) { - read(ON); - read(NULL); + if (readIf(DEFAULT, ON, NULL)) { defaultOnNull = true; } else if (readIf("NULL_TO_DEFAULT")) { defaultOnNull = true; @@ -6302,9 +6275,7 @@ private TypeInfo readIfDataType1() { readIf("UNSIGNED"); } if (mode.forBitData && DataType.isStringType(t)) { - if (readIf(FOR)) { - read("BIT"); - read("DATA"); + if (readIf(FOR, "BIT", "DATA")) { dataType = DataType.getDataType(t = Value.VARBINARY); } } @@ -6395,13 +6366,10 @@ private TypeInfo parseTimeType() { read(CLOSE_PAREN); } int type = Value.TIME; - if (readIf(WITH)) { - read("TIME"); - read("ZONE"); + if (readIf(WITH, "TIME", "ZONE")) { type = Value.TIME_TZ; - } else if (readIf("WITHOUT")) { - read("TIME"); - read("ZONE"); + } else { + readIf("WITHOUT", "TIME", "ZONE"); } return TypeInfo.getTypeInfo(type, -1L, scale, null); } @@ -6421,13 +6389,10 @@ private TypeInfo parseTimestampType() { read(CLOSE_PAREN); } int type = Value.TIMESTAMP; - if (readIf(WITH)) { - read("TIME"); - read("ZONE"); + if (readIf(WITH, "TIME", "ZONE")) { type = Value.TIMESTAMP_TZ; - } else if (readIf("WITHOUT")) { - read("TIME"); - read("ZONE"); + } else { + readIf("WITHOUT", "TIME", "ZONE"); } return TypeInfo.getTypeInfo(type, -1L, scale, null); } @@ -6460,8 +6425,7 @@ private TypeInfo readIntervalQualifier() { precision = readNonNegativeInt(); read(CLOSE_PAREN); } - if (readIf(TO)) { - read(MONTH); + if (readIf(TO, MONTH)) { qualifier = IntervalQualifier.YEAR_TO_MONTH; } else { qualifier = IntervalQualifier.YEAR; @@ -6539,8 +6503,7 @@ private TypeInfo readIntervalQualifier() { precision = readNonNegativeInt(); read(CLOSE_PAREN); } - if (readIf(TO)) { - read(SECOND); + if (readIf(TO, SECOND)) { if (readIf(OPEN_PAREN)) { scale = readNonNegativeInt(); read(CLOSE_PAREN); @@ -6721,8 +6684,7 @@ private long readPrecision(int valueType) { private Prepared parseCreate() { boolean orReplace = false; - if (readIf(OR)) { - read("REPLACE"); + if (readIf(OR, "REPLACE")) { orReplace = true; } boolean force = readIf("FORCE"); @@ -6756,15 +6718,13 @@ private Prepared parseCreate() { } else if (readIf("CACHED")) { cached = true; } - if (readIf("LOCAL")) { - read("TEMPORARY"); + if (readIf("LOCAL", "TEMPORARY")) { if (readIf("LINKED")) { return parseCreateLinkedTable(true, false, force); } read(TABLE); return parseCreateTable(true, false, cached); - } else if (readIf("GLOBAL")) { - read("TEMPORARY"); + } else if (readIf("GLOBAL", "TEMPORARY")) { if (readIf("LINKED")) { return parseCreateLinkedTable(true, true, force); } @@ -6789,8 +6749,7 @@ private Prepared parseCreate() { String indexName = null; Schema oldSchema = null; boolean ifNotExists = false; - if (session.isQuirksMode() && readIf(PRIMARY)) { - read(KEY); + if (session.isQuirksMode() && readIf(PRIMARY, KEY)) { if (readIf("HASH")) { hash = true; } @@ -6970,20 +6929,12 @@ private ArrayList parseValuesRow(ArrayList row) { private Call parseCall() { Call command = new Call(session); currentPrepared = command; - int index = tokenIndex; - boolean canBeFunction; - switch (currentTokenType) { - case IDENTIFIER: - canBeFunction = true; - break; - case TABLE: - read(); - read(OPEN_PAREN); + if (readIf(TABLE, OPEN_PAREN)) { command.setTableFunction(readTableFunction(ArrayTableFunction.TABLE)); return command; - default: - canBeFunction = false; } + int index = tokenIndex; + boolean canBeFunction = isIdentifier(); try { command.setExpression(readExpression()); } catch (DbException e) { @@ -7060,9 +7011,7 @@ private CreateSequence parseCreateSequence() { } private boolean readIfNotExists() { - if (readIf(IF)) { - read(NOT); - read(EXISTS); + if (readIf(IF, NOT, EXISTS)) { return true; } return false; @@ -7119,8 +7068,7 @@ private CreateDomain parseCreateDomain() { if (readIf(DEFAULT)) { command.setDefaultExpression(readExpression()); } - if (readIf(ON)) { - read("UPDATE"); + if (readIf(ON, "UPDATE")) { command.setOnUpdateExpression(readExpression()); } // Compatibility with 1.4.200 and older versions @@ -7160,8 +7108,7 @@ private CreateTrigger parseCreateTrigger(boolean force) { String triggerName = readIdentifierWithSchema(null); Schema schema = getSchema(); boolean insteadOf, isBefore; - if (readIf("INSTEAD")) { - read("OF"); + if (readIf("INSTEAD", "OF")) { isBefore = true; insteadOf = true; } else if (readIf("BEFORE")) { @@ -7202,8 +7149,7 @@ private CreateTrigger parseCreateTrigger(boolean force) { command.setOnRollback(onRollback); command.setTypeMask(typeMask); command.setTableName(tableName); - if (readIf(FOR)) { - read("EACH"); + if (readIf(FOR, "EACH")) { if (readIf(ROW)) { command.setRowBased(true); } else { @@ -7645,8 +7591,7 @@ private DefineCommand parseAlterDomain() { command.setIfDomainExists(ifDomainExists); command.setExpression(null); return command; - } else if (readIf(ON)) { - read("UPDATE"); + } else if (readIf(ON, "UPDATE")) { AlterDomainExpressions command = new AlterDomainExpressions(session, schema, CommandInterface.ALTER_DOMAIN_ON_UPDATE); command.setDomainName(domainName); @@ -7683,8 +7628,7 @@ private DefineCommand parseAlterDomain() { command.setIfDomainExists(ifDomainExists); command.setExpression(readExpression()); return command; - } else if (readIf(ON)) { - read("UPDATE"); + } else if (readIf(ON, "UPDATE")) { AlterDomainExpressions command = new AlterDomainExpressions(session, schema, CommandInterface.ALTER_DOMAIN_ON_UPDATE); command.setDomainName(domainName); @@ -7704,8 +7648,7 @@ private DefineCommand parseAlterView() { if (!(tableView instanceof TableView) && !ifExists) { throw DbException.get(ErrorCode.VIEW_NOT_FOUND_1, viewName); } - if (readIf("RENAME")) { - read(TO); + if (readIf("RENAME", TO)) { String newName = readIdentifierWithSchema(schema.getName()); checkSchema(schema); AlterTableRename command = new AlterTableRename(session, getSchema()); @@ -7767,8 +7710,7 @@ private boolean parseSequenceOptions(SequenceOptions options, CreateSequence com .getSQL(new StringBuilder("CREATE SEQUENCE AS "), HasSQL.TRACE_SQL_FLAGS).toString()); } options.setDataType(dataType); - } else if (readIf("START")) { - read(WITH); + } else if (readIf("START", WITH)) { options.setStartValue(readExpression()); } else if (readIf("RESTART")) { options.setRestartValue(readIf(WITH) ? readExpression() : ValueExpression.DEFAULT); @@ -7805,6 +7747,7 @@ private boolean parseCreateSequenceOption(CreateSequence command) { private boolean parseBasicSequenceOption(SequenceOptions options) { if (readIf("INCREMENT")) { + // TODO Why BY is optional? readIf("BY"); options.setIncrement(readExpression()); } else if (readIf("MINVALUE")) { @@ -7860,8 +7803,7 @@ private AlterUser parseAlterUser() { throw getSyntaxError(); } return command; - } else if (readIf("RENAME")) { - read(TO); + } else if (readIf("RENAME", TO)) { AlterUser command = new AlterUser(session); command.setType(CommandInterface.ALTER_USER_RENAME); command.setUser(database.getUser(userName)); @@ -8530,8 +8472,7 @@ private Prepared parseAlterTableAlter(Schema schema, String tableName, boolean i return command; } else if (readIf("DROP")) { if (readIf(DEFAULT)) { - if (readIf(ON)) { - read(NULL); + if (readIf(ON, NULL)) { AlterTableAlterColumn command = new AlterTableAlterColumn(session, schema); command.setTableName(tableName); command.setIfTableExists(ifTableExists); @@ -8549,8 +8490,7 @@ private Prepared parseAlterTableAlter(Schema schema, String tableName, boolean i return getAlterTableAlterColumnDropDefaultExpression(schema, tableName, ifTableExists, column, CommandInterface.ALTER_TABLE_ALTER_COLUMN_DROP_IDENTITY); } - if (readIf(ON)) { - read("UPDATE"); + if (readIf(ON, "UPDATE")) { AlterTableAlterColumn command = new AlterTableAlterColumn(session, schema); command.setTableName(tableName); command.setIfTableExists(ifTableExists); @@ -8604,9 +8544,8 @@ private Prepared getAlterTableAlterColumnDropDefaultExpression(Schema schema, St private Prepared parseAlterTableAlterColumnIdentity(Schema schema, String tableName, boolean ifTableExists, Column column) { - int index = tokenIndex; Boolean always = null; - if (readIf(SET) && readIf("GENERATED")) { + if (readIf(SET, "GENERATED")) { if (readIf("ALWAYS")) { always = true; } else { @@ -8614,8 +8553,6 @@ private Prepared parseAlterTableAlterColumnIdentity(Schema schema, String tableN read(DEFAULT); always = false; } - } else { - setTokenIndex(index); } SequenceOptions options = new SequenceOptions(); if (!parseSequenceOptions(options, null, false, true) && always == null) { @@ -8644,8 +8581,7 @@ private Prepared parseAlterTableAlterColumnIdentity(Schema schema, String tableN private Prepared parseAlterTableAlterColumnSet(Schema schema, String tableName, boolean ifTableExists, boolean ifExists, String columnName, Column column) { - if (readIf("DATA")) { - read("TYPE"); + if (readIf("DATA", "TYPE")) { return parseAlterTableAlterColumnDataType(schema, tableName, columnName, ifTableExists, ifExists); } AlterTableAlterColumn command = new AlterTableAlterColumn( @@ -8663,8 +8599,7 @@ private Prepared parseAlterTableAlterColumnSet(Schema schema, String tableName, break; case NO_NULL_CONSTRAINT_FOUND: if (readIf(DEFAULT)) { - if (readIf(ON)) { - read(NULL); + if (readIf(ON, NULL)) { command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_DEFAULT_ON_NULL); command.setBooleanFlag(true); break; @@ -8672,8 +8607,7 @@ private Prepared parseAlterTableAlterColumnSet(Schema schema, String tableName, Expression defaultExpression = readExpression(); command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_DEFAULT); command.setDefaultExpression(defaultExpression); - } else if (readIf(ON)) { - read("UPDATE"); + } else if (readIf(ON, "UPDATE")) { Expression onUpdateExpression = readExpression(); command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_ON_UPDATE); command.setDefaultExpression(onUpdateExpression); @@ -8707,8 +8641,7 @@ private Prepared parseAlterTableDrop(Schema schema, String tableName, boolean if command.setDropAction(dropAction); } return command; - } else if (readIf(PRIMARY)) { - read(KEY); + } else if (readIf(PRIMARY, KEY)) { Table table = tableIfTableExists(schema, tableName, ifTableExists); if (table == null) { return new NoOperation(session); @@ -8754,8 +8687,7 @@ private Prepared parseAlterTableDrop(Schema schema, String tableName, boolean if } private Prepared parseAlterTableDropCompatibility(Schema schema, String tableName, boolean ifTableExists) { - if (readIf(FOREIGN)) { - read(KEY); + if (readIf(FOREIGN, KEY)) { // For MariaDB boolean ifExists = readIfExists(false); String constraintName = readIdentifierWithSchema(schema.getName()); @@ -9048,8 +8980,7 @@ private ConstraintActionType parseAction() { if (result != null) { return result; } - if (readIf("NO")) { - read("ACTION"); + if (readIf("NO", "ACTION")) { return ConstraintActionType.RESTRICT; } read(SET); @@ -9111,8 +9042,7 @@ private DefineCommand parseTableConstraintIf(String tableName, Schema schema, bo read(OPEN_PAREN); command = new AlterTableAddConstraint(session, schema, CommandInterface.ALTER_TABLE_ADD_CONSTRAINT_UNIQUE, ifNotExists); - if (readIf(VALUE)) { - read(CLOSE_PAREN); + if (readIf(VALUE, CLOSE_PAREN)) { command.setIndexColumns(null); } else { command.setIndexColumns(parseIndexColumnList()); @@ -9121,16 +9051,16 @@ private DefineCommand parseTableConstraintIf(String tableName, Schema schema, bo String indexName = readIdentifierWithSchema(); command.setIndex(getSchema().findIndex(session, indexName)); } - if (compatibility && readIf(USING)) { - read("BTREE"); + if (compatibility) { + readIf(USING, "BTREE"); } break; case FOREIGN: read(); - command = new AlterTableAddConstraint(session, schema, - CommandInterface.ALTER_TABLE_ADD_CONSTRAINT_REFERENTIAL, ifNotExists); read(KEY); read(OPEN_PAREN); + command = new AlterTableAddConstraint(session, schema, + CommandInterface.ALTER_TABLE_ADD_CONSTRAINT_REFERENTIAL, ifNotExists); command.setIndexColumns(parseIndexColumnList()); if (readIf("INDEX")) { String indexName = readIdentifierWithSchema(); @@ -9221,11 +9151,7 @@ private void parseReferences(AlterTableAddConstraint command, command.setUpdateAction(parseAction()); } } - if (readIf(NOT)) { - if (!readIf("DEFERRABLE")) { - setTokenIndex(tokenIndex - 1); - } - } else { + if (!readIf(NOT, "DEFERRABLE")) { readIf("DEFERRABLE"); } } @@ -9258,8 +9184,7 @@ private CreateLinkedTable parseCreateLinkedTable(boolean temp, } command.setOriginalTable(originalTable); read(CLOSE_PAREN); - if (readIf("EMIT")) { - read("UPDATES"); + if (readIf("EMIT", "UPDATES")) { command.setEmitUpdates(true); } else if (readIf("READONLY")) { command.setReadOnly(true); @@ -9313,8 +9238,7 @@ private CreateTable parseCreateTable(boolean temp, boolean globalTemp, command.setTableEngineParams(readTableEngineParams()); } if (temp) { - if (readIf(ON)) { - read("COMMIT"); + if (readIf(ON, "COMMIT")) { if (readIf("DROP")) { command.setOnCommitDrop(); } else if (readIf("DELETE")) { @@ -9331,8 +9255,7 @@ private CreateTable parseCreateTable(boolean temp, boolean globalTemp, if (readIf("TRANSACTIONAL")) { command.setTransactional(true); } - } else if (!persistIndexes && readIf(NOT)) { - read("PERSISTENT"); + } else if (!persistIndexes && readIf(NOT, "PERSISTENT")) { command.setPersistData(false); } if (readIf("HIDDEN")) { @@ -9404,8 +9327,7 @@ private void readColumnConstraints(CommandWithColumns command, Schema schema, St } else { constraintName = null; } - if (!hasPrimaryKey && readIf(PRIMARY)) { - read(KEY); + if (!hasPrimaryKey && readIf(PRIMARY, KEY)) { hasPrimaryKey = true; boolean hash = readIf("HASH"); AlterTableAddConstraint pk = new AlterTableAddConstraint(session, schema, @@ -9495,9 +9417,7 @@ private void parseCreateTableMySQLTableOptions(CreateTable command) { throw DbException.get(ErrorCode.COLUMN_NOT_FOUND_1, "AUTO_INCREMENT PRIMARY KEY"); } } else if (readIf(DEFAULT)) { - if (readIf("CHARACTER")) { - read(SET); - } else { + if (!readIf("CHARACTER", SET)) { readIf("CHARSET"); readIf("COLLATE"); } @@ -9548,8 +9468,7 @@ private NullConstraintType parseNotNullConstraint(NullConstraintType nullConstra private NullConstraintType parseNotNullConstraint() { NullConstraintType nullConstraint; - if (readIf(NOT)) { - read(NULL); + if (readIf(NOT, NULL)) { nullConstraint = NullConstraintType.NULL_IS_NOT_ALLOWED; } else if (readIf(NULL)) { nullConstraint = NullConstraintType.NULL_IS_ALLOWED; diff --git a/h2/src/main/org/h2/util/ParserUtil.java b/h2/src/main/org/h2/util/ParserUtil.java index 95498a4a26..0083887d9e 100644 --- a/h2/src/main/org/h2/util/ParserUtil.java +++ b/h2/src/main/org/h2/util/ParserUtil.java @@ -573,7 +573,6 @@ public class ParserUtil { map.put("_ROWID_", _ROWID_); // Additional keywords map.put("BOTH", KEYWORD); - map.put("FILTER", KEYWORD); map.put("GROUPS", KEYWORD); map.put("ILIKE", KEYWORD); map.put("LEADING", KEYWORD); From 936a385f068a393f31d893ff25cbc27d3a98a94d Mon Sep 17 00:00:00 2001 From: Evgenij Ryazanov Date: Sun, 5 Jun 2022 22:44:08 +0800 Subject: [PATCH 12/13] Fix password in test --- h2/src/test/org/h2/test/db/TestTableEngines.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h2/src/test/org/h2/test/db/TestTableEngines.java b/h2/src/test/org/h2/test/db/TestTableEngines.java index 3afd8cb47c..e81d72d7f3 100644 --- a/h2/src/test/org/h2/test/db/TestTableEngines.java +++ b/h2/src/test/org/h2/test/db/TestTableEngines.java @@ -76,7 +76,7 @@ private void testAdminPrivileges() throws SQLException { Statement stat = conn.createStatement(); stat.execute("CREATE USER U PASSWORD '1'"); stat.execute("GRANT ALTER ANY SCHEMA TO U"); - Connection connUser = getConnection("tableEngine", "U", "1"); + Connection connUser = getConnection("tableEngine", "U", getPassword("1")); Statement statUser = connUser.createStatement(); assertThrows(ErrorCode.ADMIN_RIGHTS_REQUIRED, statUser) .execute("CREATE TABLE T(ID INT, NAME VARCHAR) ENGINE \"" + EndlessTableEngine.class.getName() + '"'); From 05d5999c0a065b9974c1698cfb494a69fd06a4ef Mon Sep 17 00:00:00 2001 From: Evgenij Ryazanov Date: Tue, 7 Jun 2022 20:02:44 +0800 Subject: [PATCH 13/13] Add more helper methods --- h2/src/main/org/h2/command/Parser.java | 53 ++++++++++++++++++-------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/h2/src/main/org/h2/command/Parser.java b/h2/src/main/org/h2/command/Parser.java index 3a84d0ea45..8e77bab415 100644 --- a/h2/src/main/org/h2/command/Parser.java +++ b/h2/src/main/org/h2/command/Parser.java @@ -1822,7 +1822,7 @@ private TableFilter readTablePrimary() { schemaName = null; if (readIf(DOT)) { tableName = readIdentifierWithSchema2(tableName); - } else if (!quoted && readIf(TABLE)) { + } else if (!quoted && readIf(TABLE, OPEN_PAREN)) { table = readDataChangeDeltaTable(upperName(tableName), backupIndex); break label; } @@ -1949,7 +1949,6 @@ private TableFilter buildTableFilter(Table table, String alias, ArrayList