diff --git a/h2/src/main/org/h2/command/Parser.java b/h2/src/main/org/h2/command/Parser.java index 55e81095161b0b2c5f817dbc67cac150092a3d04..b8c1568d9d87f0cfda64517535c26571cb247cb8 100644 --- a/h2/src/main/org/h2/command/Parser.java +++ b/h2/src/main/org/h2/command/Parser.java @@ -199,7 +199,7 @@ public class Parser { return o1 == o2 ? 0 : compareTableFilters(o1, o2); } }; - + private final Database database; private final Session session; /** @@ -1136,7 +1136,8 @@ public class Parser { command.setQueryAlias(readFromAlias(null, Arrays.asList("ON"))); String[] querySQLOutput = new String[]{null}; - List<Column> columnTemplateList = TableView.createQueryColumnTemplateList(null, command.getQuery(), querySQLOutput); + List<Column> columnTemplateList = TableView.createQueryColumnTemplateList(null, command.getQuery(), + querySQLOutput); TableView temporarySourceTableView = createCTEView( command.getQueryAlias(), querySQLOutput[0], columnTemplateList, false/* no recursion */, @@ -5153,24 +5154,24 @@ public class Parser { private Prepared parseWith() { List<TableView> viewsCreated = new ArrayList<>(); readIf("RECURSIVE"); - - // this WITH statement might not be a temporary view - allow optional keyword to tell us that - // this keyword. This feature will not be documented - H2 internal use only. + + // this WITH statement might not be a temporary view - allow optional keyword to + // tell us that this keyword. This feature will not be documented - H2 internal use only. boolean isPersistent = readIf("PERSISTENT"); - + // this WITH statement is not a temporary view - it is part of a persistent view // as in CREATE VIEW abc AS WITH my_cte - this auto detects that condition if (session.isParsingCreateView()) { isPersistent = true; } - + do { viewsCreated.add(parseSingleCommonTableExpression(isPersistent)); } while (readIf(",")); Prepared p = null; // reverse the order of constructed CTE views - as the destruction order - // (since later created view may depend on previously created views - + // (since later created view may depend on previously created views - // we preserve that dependency order in the destruction sequence ) // used in setCteCleanups Collections.reverse(viewsCreated); @@ -5220,7 +5221,7 @@ public class Parser { ArrayList<Column> columns = New.arrayList(); String[] cols = null; Database db = session.getDatabase(); - + // column names are now optional - they can be inferred from the named // query, if not supplied by user if (readIf("(")) { @@ -5231,7 +5232,7 @@ public class Parser { columns.add(new Column(c, Value.STRING)); } } - + Table oldViewFound = null; if (isPersistent) { oldViewFound = getSchema().findTableOrView(session, cteViewName); @@ -5251,8 +5252,8 @@ public class Parser { } if (isPersistent) { oldViewFound.lock(session, true, true); - session.getDatabase().removeSchemaObject(session, oldViewFound); - + session.getDatabase().removeSchemaObject(session, oldViewFound); + } else { session.removeLocalTempTable(oldViewFound); } @@ -5275,7 +5276,7 @@ public class Parser { Query withQuery = parseSelect(); if (isPersistent) { withQuery.session = session; - } + } read(")"); columnTemplateList = TableView.createQueryColumnTemplateList(cols, withQuery, querySQLOutput); @@ -5285,43 +5286,45 @@ public class Parser { TableView view = createCTEView(cteViewName, querySQLOutput[0], columnTemplateList, - true/* allowRecursiveQueryDetection */, - true/* add to session */, + true/* allowRecursiveQueryDetection */, + true/* add to session */, isPersistent, session); - + return view; } private TableView createCTEView(String cteViewName, String querySQL, - List<Column> columnTemplateList, boolean allowRecursiveQueryDetection, + List<Column> columnTemplateList, boolean allowRecursiveQueryDetection, boolean addViewToSession, boolean isPersistent, Session targetSession) { Database db = targetSession.getDatabase(); Schema schema = getSchemaWithDefault(); int id = db.allocateObjectId(); Column[] columnTemplateArray = columnTemplateList.toArray(new Column[0]); - + // No easy way to determine if this is a recursive query up front, so we just compile // it twice - once without the flag set, and if we didn't see a recursive term, // then we just compile it again. TableView view; - synchronized (targetSession) { + synchronized (targetSession) { view = new TableView(schema, id, cteViewName, querySQL, parameters, columnTemplateArray, targetSession, - allowRecursiveQueryDetection, false /* literalsChecked */, true /* isTableExpression */, isPersistent); + allowRecursiveQueryDetection, false /* literalsChecked */, true /* isTableExpression */, + isPersistent); if (!view.isRecursiveQueryDetected() && allowRecursiveQueryDetection) { if (isPersistent) { db.addSchemaObject(targetSession, view); view.lock(targetSession, true, true); - targetSession.getDatabase().removeSchemaObject(targetSession, view); + targetSession.getDatabase().removeSchemaObject(targetSession, view); } else { - session.removeLocalTempTable(view); + session.removeLocalTempTable(view); } view = new TableView(schema, id, cteViewName, querySQL, parameters, columnTemplateArray, targetSession, - false/* assume recursive */, false /* literalsChecked */, true /* isTableExpression */, isPersistent); + false/* assume recursive */, false /* literalsChecked */, true /* isTableExpression */, + isPersistent); } // both removeSchemaObject and removeLocalTempTable hold meta locks - targetSession.getDatabase().unlockMeta(targetSession); + targetSession.getDatabase().unlockMeta(targetSession); } view.setTableExpression(true); view.setTemporary(!isPersistent); @@ -6104,7 +6107,7 @@ public class Parser { ArrayList<Column> columnsToRemove = New.arrayList(); Table table = tableIfTableExists(schema, tableName, ifTableExists); // For Oracle compatibility - open bracket required - boolean openingBracketDetected = readIf("("); + boolean openingBracketDetected = readIf("("); do { String columnName = readColumnIdentifier(); if (table == null) { @@ -6118,7 +6121,7 @@ public class Parser { } while (readIf(",")); if (openingBracketDetected) { // For Oracle compatibility - close bracket - read(")"); + read(")"); } command.setTableName(tableName); command.setIfTableExists(ifTableExists); @@ -6145,7 +6148,7 @@ public class Parser { // MySQL compatibility (optional) readIf("COLUMN"); // Oracle specifies (but will not require) an opening parenthesis - boolean hasOpeningBracket = readIf("("); + boolean hasOpeningBracket = readIf("("); String columnName = readColumnIdentifier(); AlterTableAlterColumn command = null; NullConstraintType nullConstraint = parseNotNullConstraint(); @@ -6754,7 +6757,7 @@ public class Parser { /** * Enumeration describing null constraints */ - private enum NullConstraintType { + private enum NullConstraintType { NULL_IS_ALLOWED, NULL_IS_NOT_ALLOWED, NO_NULL_CONSTRAINT_FOUND } @@ -6771,19 +6774,19 @@ public class Parser { if (database.getMode().getEnum() == ModeEnum.Oracle) { if (readIf("ENABLE")) { // Leave constraint 'as is' - readIf("VALIDATE"); + readIf("VALIDATE"); // Turn off constraint, allow NULLs - if (readIf("NOVALIDATE")) { + if (readIf("NOVALIDATE")) { nullConstraint = NullConstraintType.NULL_IS_ALLOWED; } } // Turn off constraint, allow NULLs - if (readIf("DISABLE")) { + if (readIf("DISABLE")) { nullConstraint = NullConstraintType.NULL_IS_ALLOWED; // ignore validate - readIf("VALIDATE"); + readIf("VALIDATE"); // ignore novalidate - readIf("NOVALIDATE"); + readIf("NOVALIDATE"); } } } diff --git a/h2/src/main/org/h2/command/ddl/CreateView.java b/h2/src/main/org/h2/command/ddl/CreateView.java index b003d9ed70f2303d2f8af7d397b47bd7c5a2fe23..5f64ef38acc17c2c914a7d00b7d12be4c79f1303 100644 --- a/h2/src/main/org/h2/command/ddl/CreateView.java +++ b/h2/src/main/org/h2/command/ddl/CreateView.java @@ -71,10 +71,10 @@ public class CreateView extends SchemaCommand { public void setForce(boolean force) { this.force = force; } - + public void setTableExpression(boolean isTableExpression) { this.isTableExpression = isTableExpression; - } + } @Override public int update() { @@ -117,9 +117,12 @@ public class CreateView extends SchemaCommand { } if (view == null) { if (isTableExpression) { - view = TableView.createTableViewMaybeRecursive(getSchema(), id, viewName, querySQL, null, columnTemplatesAsStrings, session, false /* literalsChecked */, isTableExpression, true /*isPersistent*/, db); + view = TableView.createTableViewMaybeRecursive(getSchema(), id, viewName, querySQL, null, + columnTemplatesAsStrings, session, false /* literalsChecked */, isTableExpression, + true /* isPersistent */, db); } else { - view = new TableView(getSchema(), id, viewName, querySQL, null, columnTemplatesAsUnknowns, session, false/* allow recursive */, false/* literalsChecked */, isTableExpression, true); + view = new TableView(getSchema(), id, viewName, querySQL, null, columnTemplatesAsUnknowns, session, + false/* allow recursive */, false/* literalsChecked */, isTableExpression, true); } } else { // TODO support isTableExpression in replace function... @@ -135,10 +138,10 @@ public class CreateView extends SchemaCommand { } else { db.updateMeta(session, view); } - + // TODO: if we added any table expressions that aren't used by this view, detect them // and drop them - otherwise they will leak and never get cleaned up. - + return 0; } diff --git a/h2/src/main/org/h2/command/ddl/DropView.java b/h2/src/main/org/h2/command/ddl/DropView.java index 1a5f96c81a8016767b6bd3e76841c6f6cfe4f378..c975ff13a1c6bc01737573a4449371d777babfad 100644 --- a/h2/src/main/org/h2/command/ddl/DropView.java +++ b/h2/src/main/org/h2/command/ddl/DropView.java @@ -68,13 +68,13 @@ public class DropView extends SchemaCommand { } } } - - // TODO: Where is the ConstraintReferential.CASCADE style drop processing ? It's - // supported from imported keys - but not for dependent db objects + + // TODO: Where is the ConstraintReferential.CASCADE style drop processing ? It's + // supported from imported keys - but not for dependent db objects TableView tableView = (TableView) view; ArrayList<Table> copyOfDependencies = new ArrayList<Table>(tableView.getTables()); - + view.lock(session, true, true); session.getDatabase().removeSchemaObject(session, view); diff --git a/h2/src/main/org/h2/command/dml/MergeUsing.java b/h2/src/main/org/h2/command/dml/MergeUsing.java index 5f946292c04d0dbe43ee0d633f1079adce07634b..cd8eac90bbde79398c82993448b46ae8b13ee179 100644 --- a/h2/src/main/org/h2/command/dml/MergeUsing.java +++ b/h2/src/main/org/h2/command/dml/MergeUsing.java @@ -80,7 +80,7 @@ import org.h2.value.Value; * action is always triggered. This is because the embedded UPDATE and DELETE statement's * returned update row count is used to detect a matching join. * If neither of the two the statements are provided, no matching join is NEVER detected. - * + * * A fix for this is now implemented as described below: * We now generate a "matchSelect" query and use that to always detect * a match join - rather than relying on UPDATE or DELETE statements. diff --git a/h2/src/main/org/h2/command/dml/Select.java b/h2/src/main/org/h2/command/dml/Select.java index 2107e567b49ad971f2020f5e127374630e1ede19..67c6a83ad2f0c2e6e7cb7377c32a2c5638c4049e 100644 --- a/h2/src/main/org/h2/command/dml/Select.java +++ b/h2/src/main/org/h2/command/dml/Select.java @@ -1062,10 +1062,11 @@ public class Select extends Query { Table t = f.getTable(); TableView tableView = t.isView() ? (TableView) t : null; if (tableView != null && tableView.isRecursive() && tableView.isTableExpression()) { - + if (tableView.isPersistent()) { - // skip the generation of plan SQL for this already recursive persistent ctes, since using a with - // statement will re-create the common table expression views. + // skip the generation of plan SQL for this already recursive persistent CTEs, + // since using a with statement will re-create the common table expression + // views. continue; } else { buff.append("WITH RECURSIVE ").append(t.getName()).append('('); @@ -1074,7 +1075,7 @@ public class Select extends Query { buff.appendExceptFirst(","); buff.append(c.getName()); } - buff.append(") AS ").append(t.getSQL()).append("\n"); + buff.append(") AS ").append(t.getSQL()).append("\n"); } } } diff --git a/h2/src/main/org/h2/engine/Database.java b/h2/src/main/org/h2/engine/Database.java index b9dc7279bb819acacc47ccdb15f3bbb1d5098694..6c62d804618a10a99d583d6ce8e1a1d57522578b 100644 --- a/h2/src/main/org/h2/engine/Database.java +++ b/h2/src/main/org/h2/engine/Database.java @@ -90,7 +90,7 @@ import org.h2.value.ValueInt; public class Database implements DataHandler { private static int initialPowerOffCount; - + private static final ThreadLocal<Session> META_LOCK_DEBUGGING = new ThreadLocal<Session>(); private static final ThreadLocal<Throwable> META_LOCK_DEBUGGING_STACK = new ThreadLocal<Throwable>(); @@ -768,7 +768,7 @@ public class Database implements DataHandler { Collections.sort(records); synchronized (systemSession) { for (MetaRecord rec : records) { - rec.execute(this, systemSession, eventListener); + rec.execute(this, systemSession, eventListener); } } if (mvStore != null) { @@ -902,7 +902,7 @@ public class Database implements DataHandler { } } - /** + /** * Lock the metadata table for updates. * * @param session the session @@ -933,7 +933,7 @@ public class Database implements DataHandler { boolean wasLocked = meta.lock(session, true, true); return wasLocked; } - + /** * Unlock the metadata table. * @@ -1912,7 +1912,7 @@ public class Database implements DataHandler { t.getSQL()); } obj.removeChildrenAndResources(session); - + } removeMeta(session, id); } diff --git a/h2/src/main/org/h2/engine/Session.java b/h2/src/main/org/h2/engine/Session.java index 3e0c1e25dce4110d54b76edd13a15cbb1857404d..a2ee0b4798c7a26d6855de08bf0202075dec3e21 100644 --- a/h2/src/main/org/h2/engine/Session.java +++ b/h2/src/main/org/h2/engine/Session.java @@ -123,7 +123,7 @@ public class Session extends SessionWithState { private long modificationMetaID = -1; private SubQueryInfo subQueryInfo; private int parsingView; - private Deque<String> viewNameStack = new ArrayDeque<String>(); + private Deque<String> viewNameStack = new ArrayDeque<String>(); private int preparingQueryExpression; private volatile SmallLRUCache<Object, ViewIndex> viewIndexCache; private HashMap<Object, ViewIndex> subQueryIndexCache; @@ -229,6 +229,16 @@ public class Session extends SessionWithState { return subQueryInfo; } + /** + * Stores name of currently parsed view in a stack so it can be determined + * during {@code prepare()}. + * + * @param parsingView + * {@code true} to store one more name, {@code false} to remove it + * from stack + * @param viewName + * name of the view + */ public void setParsingCreateView(boolean parsingView, String viewName) { // It can be recursive, thus implemented as counter. this.parsingView += parsingView ? 1 : -1; @@ -238,7 +248,7 @@ public class Session extends SessionWithState { } else { assert viewName.equals(viewNameStack.peek()); viewNameStack.pop(); - } + } } public String getParsingCreateViewName() { if (viewNameStack.size() == 0) { @@ -695,7 +705,7 @@ public class Session extends SessionWithState { Analyze.analyzeTable(this, table, rows, false); } // analyze can lock the meta - database.unlockMeta(this); + database.unlockMeta(this); } tablesToAnalyze = null; } diff --git a/h2/src/main/org/h2/jdbc/JdbcStatementBackwardsCompat.java b/h2/src/main/org/h2/jdbc/JdbcStatementBackwardsCompat.java index 7d8c93212e977f603fe3686953cbb544fd12c90a..eb2ee4a3d05f1a79ead33eaad113ab02ea36b020 100644 --- a/h2/src/main/org/h2/jdbc/JdbcStatementBackwardsCompat.java +++ b/h2/src/main/org/h2/jdbc/JdbcStatementBackwardsCompat.java @@ -121,7 +121,7 @@ public interface JdbcStatementBackwardsCompat { /** * Enquotes the specified identifier. * - * @param identifier + * @param identifier * identifier to quote if required * @param alwaysQuote * if {@code true} identifier will be quoted unconditionally diff --git a/h2/src/main/org/h2/mvstore/db/MVTable.java b/h2/src/main/org/h2/mvstore/db/MVTable.java index 4fe210e016e3d652f9e76dbef7a9187d44d07848..48ea210965d09e6dedfc9577fc59d4c9f9959d20 100644 --- a/h2/src/main/org/h2/mvstore/db/MVTable.java +++ b/h2/src/main/org/h2/mvstore/db/MVTable.java @@ -62,12 +62,12 @@ public class MVTable extends TableBase { * The tables names this thread has a shared lock on. */ public static final DebuggingThreadLocal<ArrayList<String>> SHARED_LOCKS; - + /** * The type of trace lock events */ private enum TraceLockEvent{ - + TRACE_LOCK_OK("ok"), TRACE_LOCK_WAITING_FOR("waiting for"), TRACE_LOCK_REQUESTING_FOR("requesting for"), @@ -81,13 +81,13 @@ public class MVTable extends TableBase { TraceLockEvent(String eventText) { this.eventText = eventText; } - + public String getEventText() { return eventText; } } private static final String NO_EXTRA_INFO = ""; - + static { if (SysProperties.THREAD_DEADLOCK_DETECTOR) { WAITING_FOR_LOCK = new DebuggingThreadLocal<>(); @@ -428,7 +428,7 @@ public class MVTable extends TableBase { public boolean isLockedExclusivelyBy(Session session) { return lockExclusiveSession == session; } - + @Override public void unlock(Session s) { if (database != null) { diff --git a/h2/src/main/org/h2/store/fs/FilePathDisk.java b/h2/src/main/org/h2/store/fs/FilePathDisk.java index 684359399545ab694264799c24a501b3dfa3aaac..40de30d0bcc5f39e7f04a3f6b08de72cb952aef6 100644 --- a/h2/src/main/org/h2/store/fs/FilePathDisk.java +++ b/h2/src/main/org/h2/store/fs/FilePathDisk.java @@ -299,7 +299,7 @@ public class FilePathDisk extends FilePath { // file name with a colon if (name.startsWith(CLASSPATH_PREFIX)) { String fileName = name.substring(CLASSPATH_PREFIX.length()); - // Force absolute resolution in Class.getResourceAsStream + // Force absolute resolution in Class.getResourceAsStream if (!fileName.startsWith("/")) { fileName = "/" + fileName; } diff --git a/h2/src/main/org/h2/table/Table.java b/h2/src/main/org/h2/table/Table.java index f92f503d68a8c9206845644c26fa8d2a6ac5ddee..e5fde48acb7e2d0c159ed6635c14d654c418fe7b 100644 --- a/h2/src/main/org/h2/table/Table.java +++ b/h2/src/main/org/h2/table/Table.java @@ -1239,7 +1239,7 @@ public abstract class Table extends SchemaObjectBase { public boolean isMVStore() { return false; } - + public void setTableExpression(boolean tableExpression) { this.tableExpression = tableExpression; } diff --git a/h2/src/main/org/h2/table/TableView.java b/h2/src/main/org/h2/table/TableView.java index bacc8b928dfd044c0fbd45a58f05b46885c6b8c0..fb4b6b4d80dfab926d6136570e850d5d6509d00b 100644 --- a/h2/src/main/org/h2/table/TableView.java +++ b/h2/src/main/org/h2/table/TableView.java @@ -67,7 +67,8 @@ public class TableView extends Table { ArrayList<Parameter> params, Column[] columnTemplates, Session session, boolean allowRecursive, boolean literalsChecked, boolean isTableExpression, boolean isPersistent) { super(schema, id, name, false, true); - init(querySQL, params, columnTemplates, session, allowRecursive, literalsChecked, isTableExpression, isPersistent); + init(querySQL, params, columnTemplates, session, allowRecursive, literalsChecked, isTableExpression, + isPersistent); } /** @@ -90,7 +91,7 @@ public class TableView extends Table { session, recursive, literalsChecked, isTableExpression, isPersistent); DbException e = recompile(session, force, true); if (e != null) { - init(oldQuerySQL, null, oldColumnTemplates, session, oldRecursive, + init(oldQuerySQL, null, oldColumnTemplates, session, oldRecursive, literalsChecked, isTableExpression, isPersistent); recompile(session, true, false); throw e; @@ -98,7 +99,7 @@ public class TableView extends Table { } private synchronized void init(String querySQL, ArrayList<Parameter> params, - Column[] columnTemplates, Session session, boolean allowRecursive, boolean literalsChecked, + Column[] columnTemplates, Session session, boolean allowRecursive, boolean literalsChecked, boolean isTableExpression, boolean isPersistent) { this.querySQL = querySQL; this.columnTemplates = columnTemplates; @@ -555,7 +556,7 @@ public class TableView extends Table { String querySQL = query.getPlanSQL(); TableView v = new TableView(mainSchema, 0, name, querySQL, query.getParameters(), null /* column templates */, session, - false/* allow recursive */, true /* literals have already been checked when parsing original query */, + false/* allow recursive */, true /* literals have already been checked when parsing original query */, false /* is table expression */, false/* is persistent*/); if (v.createException != null) { throw v.createException; @@ -705,7 +706,7 @@ public class TableView extends Table { } return true; } - + public List<Table> getTables() { return tables; } @@ -713,11 +714,11 @@ public class TableView extends Table { public boolean isPersistent() { return isPersistent; } - + public static TableView createTableViewMaybeRecursive(Schema schema, int id, String name, String querySQL, ArrayList<Parameter> parameters, Column[] columnTemplates, Session session, boolean literalsChecked, boolean isTableExpression, boolean isPersistent, Database db) { - + Table recursiveTable = TableView.createShadowTableForRecursiveTableExpression(isPersistent, session, name, schema, Arrays.asList(columnTemplates), db); @@ -728,42 +729,44 @@ public class TableView extends Table { for (Column columnTemplate: columnTemplates) { columnNames.add(columnTemplate.getName()); } - + try { Prepared withQuery = session.prepare(querySQL, false, false); if (isPersistent) { withQuery.setSession(session); - } - columnTemplateList = TableView.createQueryColumnTemplateList(columnNames.toArray(new String[1]), + } + columnTemplateList = TableView.createQueryColumnTemplateList(columnNames.toArray(new String[1]), (Query) withQuery, querySQLOutput); } finally { TableView.destroyShadowTableForRecursiveExpression(isPersistent, session, recursiveTable); } - + // build with recursion turned on TableView view = new TableView(schema, id, name, querySQL, parameters, columnTemplateList.toArray(columnTemplates), session, true/* try recursive */, literalsChecked, isTableExpression, isPersistent); - // is recursion really detected ? if not - recreate it without recursion flag and no recursive index + // is recursion really detected ? if not - recreate it without recursion flag + // and no recursive index if (!view.isRecursiveQueryDetected()) { if (isPersistent) { db.addSchemaObject(session, view); view.lock(session, true, true); session.getDatabase().removeSchemaObject(session, view); - - // during database startup - this method does not normally get called - and it needs to be - // to correctly un-register the table which the table expression uses... + + // during database startup - this method does not normally get called - and it + // needs to be to correctly un-register the table which the table expression + // uses... view.removeChildrenAndResources(session); } else { - session.removeLocalTempTable(view); + session.removeLocalTempTable(view); } view = new TableView(schema, id, name, querySQL, parameters, columnTemplates, session, - false/* detected not recursive */, literalsChecked, isTableExpression, isPersistent); + false/* detected not recursive */, literalsChecked, isTableExpression, isPersistent); } - + return view; } @@ -797,14 +800,14 @@ public class TableView extends Table { String columnName = columnNamer.getColumnName(columnExp, i, cols); columnTemplateList.add(new Column(columnName, columnExp.getType())); - + } return columnTemplateList; } public static Table createShadowTableForRecursiveTableExpression(boolean isPersistent, Session targetSession, String cteViewName, Schema schema, List<Column> columns, Database db) { - + // create table data object CreateTableData recursiveTableData = new CreateTableData(); recursiveTableData.id = db.allocateObjectId(); @@ -815,10 +818,10 @@ public class TableView extends Table { recursiveTableData.persistIndexes = isPersistent; recursiveTableData.create = true; recursiveTableData.session = targetSession; - + // this gets a meta table lock that is not released Table recursiveTable = schema.createTable(recursiveTableData); - + if (isPersistent) { // this unlock is to prevent lock leak from schema.createTable() db.unlockMeta(targetSession); @@ -837,11 +840,11 @@ public class TableView extends Table { if (isPersistent) { recursiveTable.lock(targetSession, true, true); targetSession.getDatabase().removeSchemaObject(targetSession, recursiveTable); - + } else { targetSession.removeLocalTempTable(recursiveTable); } - + // both removeSchemaObject and removeLocalTempTable hold meta locks - release them here targetSession.getDatabase().unlockMeta(targetSession); } diff --git a/h2/src/main/org/h2/util/SourceCompiler.java b/h2/src/main/org/h2/util/SourceCompiler.java index 44c65d985038a17784641d10f87d2871d286e1b2..9c81ce133774b2cab10347c5928dbb1fab3ccf61 100644 --- a/h2/src/main/org/h2/util/SourceCompiler.java +++ b/h2/src/main/org/h2/util/SourceCompiler.java @@ -190,7 +190,7 @@ public class SourceCompiler { /** * Whether the passed source can be compiled using {@link javax.script.ScriptEngineManager}. - * + * * @param source the source to test. * @return <code>true</code> if {@link #getCompiledScript(String)} can be called. */ diff --git a/h2/src/test/org/h2/test/db/AbstractBaseForCommonTableExpressions.java b/h2/src/test/org/h2/test/db/AbstractBaseForCommonTableExpressions.java index 59259168a7be393cd6cf8128fcb58a4dbd7ed150..75401c3ff6e21d6601295eb3e2de92b4bd52a369 100644 --- a/h2/src/test/org/h2/test/db/AbstractBaseForCommonTableExpressions.java +++ b/h2/src/test/org/h2/test/db/AbstractBaseForCommonTableExpressions.java @@ -1,72 +1,76 @@ -/* - * Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0, - * and the EPL 1.0 (http://h2database.com/html/license.html). - * Initial Developer: H2 Group - */ -package org.h2.test.db; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import org.h2.test.TestBase; -/** - * Base class for common table expression tests - */ -public abstract class AbstractBaseForCommonTableExpressions extends TestBase { - - protected void testRepeatedQueryWithSetup(int maxRetries, String[] expectedRowData, String[] expectedColumnNames, int expectedNumbeOfRows, String setupSQL, - String withQuery, int closeAndReopenDatabaseConnectionOnIteration, String[] expectedColumnTypes) throws SQLException { - - deleteDb("commonTableExpressionQueries"); - Connection conn = getConnection("commonTableExpressionQueries"); - PreparedStatement prep; - ResultSet rs; - - - for (int queryRunTries = 1; queryRunTries <= maxRetries; queryRunTries++) { - - Statement stat = conn.createStatement(); - stat.execute(setupSQL); - stat.close(); - - // close and re-open connection for one iteration to make sure the query work between connections - if (queryRunTries == closeAndReopenDatabaseConnectionOnIteration) { - conn.close(); - - conn = getConnection("commonTableExpressionQueries"); - } - prep = conn.prepareStatement(withQuery); - - rs = prep.executeQuery(); - for (int columnIndex = 1; columnIndex <= rs.getMetaData().getColumnCount(); columnIndex++) { - - assertTrue(rs.getMetaData().getColumnLabel(columnIndex) != null); - assertEquals(expectedColumnNames[columnIndex - 1], rs.getMetaData().getColumnLabel(columnIndex)); - assertEquals("wrongly type column "+rs.getMetaData().getColumnLabel(columnIndex)+" on iteration#"+queryRunTries, - expectedColumnTypes[columnIndex - 1], rs.getMetaData().getColumnTypeName(columnIndex)); - } - - int rowNdx = 0; - while (rs.next()) { - StringBuffer buf = new StringBuffer(); - for (int columnIndex = 1; columnIndex <= rs.getMetaData().getColumnCount(); columnIndex++) { - buf.append("|"+rs.getString(columnIndex)); - } - assertEquals(expectedRowData[rowNdx], buf.toString()); - rowNdx++; - } - - assertEquals(expectedNumbeOfRows, rowNdx); - - rs.close(); - prep.close(); - } - - conn.close(); - deleteDb("commonTableExpressionQueries"); - - } - -} +/* + * Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (http://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.h2.test.db; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import org.h2.test.TestBase; + +/** + * Base class for common table expression tests + */ +public abstract class AbstractBaseForCommonTableExpressions extends TestBase { + + protected void testRepeatedQueryWithSetup(int maxRetries, String[] expectedRowData, String[] expectedColumnNames, + int expectedNumberOfRows, String setupSQL, String withQuery, + int closeAndReopenDatabaseConnectionOnIteration, String[] expectedColumnTypes) throws SQLException { + + deleteDb("commonTableExpressionQueries"); + Connection conn = getConnection("commonTableExpressionQueries"); + PreparedStatement prep; + ResultSet rs; + + for (int queryRunTries = 1; queryRunTries <= maxRetries; queryRunTries++) { + + Statement stat = conn.createStatement(); + stat.execute(setupSQL); + stat.close(); + + // close and re-open connection for one iteration to make sure the query work + // between connections + if (queryRunTries == closeAndReopenDatabaseConnectionOnIteration) { + conn.close(); + + conn = getConnection("commonTableExpressionQueries"); + } + prep = conn.prepareStatement(withQuery); + + rs = prep.executeQuery(); + for (int columnIndex = 1; columnIndex <= rs.getMetaData().getColumnCount(); columnIndex++) { + + assertTrue(rs.getMetaData().getColumnLabel(columnIndex) != null); + assertEquals(expectedColumnNames[columnIndex - 1], rs.getMetaData().getColumnLabel(columnIndex)); + assertEquals( + "wrong type of column " + rs.getMetaData().getColumnLabel(columnIndex) + " on iteration #" + + queryRunTries, + expectedColumnTypes[columnIndex - 1], rs.getMetaData().getColumnTypeName(columnIndex)); + } + + int rowNdx = 0; + while (rs.next()) { + StringBuffer buf = new StringBuffer(); + for (int columnIndex = 1; columnIndex <= rs.getMetaData().getColumnCount(); columnIndex++) { + buf.append("|" + rs.getString(columnIndex)); + } + assertEquals(expectedRowData[rowNdx], buf.toString()); + rowNdx++; + } + + assertEquals(expectedNumberOfRows, rowNdx); + + rs.close(); + prep.close(); + } + + conn.close(); + deleteDb("commonTableExpressionQueries"); + + } + +} diff --git a/h2/src/test/org/h2/test/db/TestCases.java b/h2/src/test/org/h2/test/db/TestCases.java index 0d29d86db252ccb849341029e4d733d2c561c321..ccca18e36231ba6c68982751d5a5933091b186c0 100644 --- a/h2/src/test/org/h2/test/db/TestCases.java +++ b/h2/src/test/org/h2/test/db/TestCases.java @@ -302,7 +302,8 @@ public class TestCases extends TestBase { throws SQLException { assertThrows(expectedDropSuccess ? 0 : ErrorCode.CANNOT_DROP_2, stat) .execute("drop table test " + (restrict ? "restrict" : "cascade")); - assertThrows(expectedDropSuccess ? ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1 : 0, stat).execute("select * from test"); + assertThrows(expectedDropSuccess ? ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1 : 0, stat) + .execute("select * from test"); } private void testDropTableNoReference(final boolean stdDropTableRestrict, final boolean restrict) diff --git a/h2/src/test/org/h2/test/db/TestFunctions.java b/h2/src/test/org/h2/test/db/TestFunctions.java index bfa7e6c6e7dc522ab9d689fb037e1c6f48543235..f5f505eaf1c0cc97b4fe2723371998440e7240f0 100644 --- a/h2/src/test/org/h2/test/db/TestFunctions.java +++ b/h2/src/test/org/h2/test/db/TestFunctions.java @@ -2061,7 +2061,8 @@ public class TestFunctions extends TestBase implements AggregateFunction { assertThrows(ErrorCode.INVALID_VALUE_2, stat).execute("select signal('00145', 'success class is invalid')"); assertThrows(ErrorCode.INVALID_VALUE_2, stat).execute("select signal('foo', 'SQLSTATE has 5 chars')"); - assertThrows(ErrorCode.INVALID_VALUE_2, stat).execute("select signal('Ab123', 'SQLSTATE has only digits or upper-case letters')"); + assertThrows(ErrorCode.INVALID_VALUE_2, stat) + .execute("select signal('Ab123', 'SQLSTATE has only digits or upper-case letters')"); try { stat.execute("select signal('AB123', 'some custom error')"); fail("Should have thrown"); diff --git a/h2/src/test/org/h2/test/db/TestGeneralCommonTableQueries.java b/h2/src/test/org/h2/test/db/TestGeneralCommonTableQueries.java index ad9f7c7d1b3fab7a011b2f4d9338576fbdb309f4..501103c41a68e13c94ffd5b9ffe4b0fb112bce79 100644 --- a/h2/src/test/org/h2/test/db/TestGeneralCommonTableQueries.java +++ b/h2/src/test/org/h2/test/db/TestGeneralCommonTableQueries.java @@ -78,8 +78,8 @@ public class TestGeneralCommonTableQueries extends AbstractBaseForCommonTableExp "t1(n) as (select 2 as first) " + ",t2(n) as (select 3 as first) " + "select * from t1 union all select * from t2 where n<>?"); - - prep.setInt(1, 0); + + prep.setInt(1, 0); rs = prep.executeQuery(); assertTrue(rs.next()); assertEquals(2, rs.getInt(1)); @@ -93,7 +93,7 @@ public class TestGeneralCommonTableQueries extends AbstractBaseForCommonTableExp ",t3(n) as (select 4 as first) " + "select * from t1 union all select * from t2 union all select * from t3 where n<>?"); - prep.setInt(1, 4); + prep.setInt(1, 4); rs = prep.executeQuery(); assertTrue(rs.next()); assertEquals(2, rs.getInt(1)); @@ -116,8 +116,8 @@ public class TestGeneralCommonTableQueries extends AbstractBaseForCommonTableExp ",t2 as (select first_col+1 from t1) " + ",t3 as (select 4 as first_col) " + "select * from t1 union all select * from t2 union all select * from t3 where first_col<>?"); - - prep.setInt(1, 4); + + prep.setInt(1, 4); rs = prep.executeQuery(); assertTrue(rs.next()); assertEquals(2, rs.getInt(1)); @@ -474,13 +474,13 @@ public class TestGeneralCommonTableQueries extends AbstractBaseForCommonTableExp conn.close(); deleteDb("commonTableExpressionQueries"); } - + private void testSimple4RowRecursiveQuery() throws Exception { - + String[] expectedRowData = new String[]{"|1", "|2", "|3"}; String[] expectedColumnTypes = new String[]{"INTEGER"}; String[] expectedColumnNames = new String[]{"N"}; - + String setupSQL = "-- do nothing"; String withQuery = "with recursive r(n) as (\n"+ "(select 1) union all (select n+1 from r where n < 3)\n"+ @@ -489,18 +489,18 @@ public class TestGeneralCommonTableQueries extends AbstractBaseForCommonTableExp int maxRetries = 3; int expectedNumberOfRows = expectedRowData.length; - + testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumberOfRows, setupSQL, withQuery, maxRetries - 1, expectedColumnTypes); - - } - + + } + private void testSimple2By4RowRecursiveQuery() throws Exception { - + String[] expectedRowData = new String[]{"|0|1|10", "|1|2|11", "|2|3|12", "|3|4|13"}; String[] expectedColumnTypes = new String[]{"INTEGER", "INTEGER", "INTEGER"}; String[] expectedColumnNames = new String[]{"K", "N", "N2"}; - + String setupSQL = "-- do nothing"; String withQuery = "with \n"+ "r1(n,k) as ((select 1, 0) union all (select n+1,k+1 from r1 where n <= 3)),"+ @@ -509,31 +509,33 @@ public class TestGeneralCommonTableQueries extends AbstractBaseForCommonTableExp int maxRetries = 3; int expectedNumberOfRows = expectedRowData.length; - + testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumberOfRows, setupSQL, withQuery, maxRetries - 1, expectedColumnTypes); - - } - + + } + private void testSimple3RowRecursiveQueryWithLazyEval() throws Exception { - + String[] expectedRowData = new String[]{"|6"}; String[] expectedColumnTypes = new String[]{"BIGINT"}; String[] expectedColumnNames = new String[]{"SUM(N)"}; - + // back up the config - to restore it after this test TestAll backupConfig = config; config = new TestAll(); - + try { - //Test with settings: lazy mvStore memory mvcc multiThreaded - // connection url is =mem:script;MV_STORE=true;LOG=1;LOCK_TIMEOUT=50;MVCC=TRUE;MULTI_THREADED=TRUE;LAZY_QUERY_EXECUTION=1 + // Test with settings: lazy mvStore memory mvcc multiThreaded + // connection url is + // mem:script;MV_STORE=true;LOG=1;LOCK_TIMEOUT=50;MVCC=TRUE; + // MULTI_THREADED=TRUE;LAZY_QUERY_EXECUTION=1 config.lazy = true; config.mvStore = true; config.memory = true; config.mvcc = true; config.multiThreaded = true; - + String setupSQL = "--no config set"; String withQuery = "select sum(n) from (\n" +" with recursive r(n) as (\n" @@ -541,15 +543,15 @@ public class TestGeneralCommonTableQueries extends AbstractBaseForCommonTableExp +" )\n" +" select n from r \n" +")\n"; - + int maxRetries = 10; int expectedNumberOfRows = expectedRowData.length; - - testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumberOfRows, setupSQL, - withQuery, maxRetries - 1, expectedColumnTypes); + + testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumberOfRows, + setupSQL, withQuery, maxRetries - 1, expectedColumnTypes); } finally { config = backupConfig; } - - } + + } } diff --git a/h2/src/test/org/h2/test/db/TestPersistentCommonTableExpressions.java b/h2/src/test/org/h2/test/db/TestPersistentCommonTableExpressions.java index 764fd74dd3d3958d9224fac8d1f2e9f3bb7d7a1d..5a29c8904ef66d23cbc4fb245109872dbfab4d44 100644 --- a/h2/src/test/org/h2/test/db/TestPersistentCommonTableExpressions.java +++ b/h2/src/test/org/h2/test/db/TestPersistentCommonTableExpressions.java @@ -53,7 +53,7 @@ public class TestPersistentCommonTableExpressions extends AbstractBaseForCommonT " */\n" + " /* scanCount: 1 */\n" + "WHERE BB.A IS A.VAL)"}; - + String setupSQL = "DROP TABLE IF EXISTS A; " +"DROP TABLE IF EXISTS B; " @@ -74,7 +74,7 @@ public class TestPersistentCommonTableExpressions extends AbstractBaseForCommonT +"INSERT INTO C VALUES('carrot', 'imperator'); " +"INSERT INTO C VALUES(null, 'banapple'); " +"INSERT INTO A VALUES('meat'); "; - + String withQuery = "WITH BB as (SELECT \n" + "sum(1) as X, \n" + "a \n" + @@ -87,14 +87,14 @@ public class TestPersistentCommonTableExpressions extends AbstractBaseForCommonT "FROM A \n" + "GROUP BY A.val"; int maxRetries = 3; int expectedNumberOfRows = expectedRowData.length; - + testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumberOfRows, setupSQL, withQuery, maxRetries - 1, expectedColumnTypes); - + } - private void testPersistentRecursiveTableInCreateView() throws Exception { - String setuoSQL = "--SET TRACE_LEVEL_SYSTEM_OUT 3;\n" + private void testPersistentRecursiveTableInCreateView() throws Exception { + String setupSQL = "--SET TRACE_LEVEL_SYSTEM_OUT 3;\n" +"DROP TABLE IF EXISTS my_tree; \n" +"DROP VIEW IF EXISTS v_my_tree; \n" +"CREATE TABLE my_tree ( \n" @@ -119,7 +119,7 @@ public class TestPersistentCommonTableExpressions extends AbstractBaseForCommonT +"), \n" +"unused_cte AS ( SELECT 1 AS unUsedColumn ) \n" +"SELECT sub_tree_root_id, tree_level, parent_fk, child_fk FROM tree_cte; \n"; - + String withQuery = "SELECT * FROM v_my_tree"; int maxRetries = 4; String[] expectedRowData = new String[]{"|1|0|null|1", @@ -135,13 +135,13 @@ public class TestPersistentCommonTableExpressions extends AbstractBaseForCommonT "|1|2|null|121" }; String[] expectedColumnNames = new String[]{"SUB_TREE_ROOT_ID", "TREE_LEVEL", "PARENT_FK", "CHILD_FK"}; - String[] expectedColumnTypes = new String[]{"INTEGER", "INTEGER", "INTEGER", "INTEGER"}; + String[] expectedColumnTypes = new String[]{"INTEGER", "INTEGER", "INTEGER", "INTEGER"}; int expectedNumberOfRows = 11; - testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumberOfRows, setuoSQL, + testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumberOfRows, setupSQL, withQuery, maxRetries - 1, expectedColumnTypes); } - - private void testPersistentNonRecursiveTableInCreateView() throws Exception { + + private void testPersistentNonRecursiveTableInCreateView() throws Exception { String setupSQL = "" +"DROP VIEW IF EXISTS v_my_nr_tree; \n" +"DROP TABLE IF EXISTS my_table; \n" @@ -163,7 +163,7 @@ public class TestPersistentCommonTableExpressions extends AbstractBaseForCommonT +"), \n" +"unused_cte AS ( SELECT 1 AS unUsedColumn ) \n" +"SELECT sub_tree_root_id, tree_level, parent_fk, child_fk FROM tree_cte_nr; \n"; - + String withQuery = "SELECT * FROM v_my_nr_tree"; int maxRetries = 6; String[] expectedRowData = new String[]{ @@ -174,7 +174,7 @@ public class TestPersistentCommonTableExpressions extends AbstractBaseForCommonT "|121|0|12|121", }; String[] expectedColumnNames = new String[]{"SUB_TREE_ROOT_ID", "TREE_LEVEL", "PARENT_FK", "CHILD_FK"}; - String[] expectedColumnTypes = new String[]{"INTEGER", "INTEGER", "INTEGER", "INTEGER"}; + String[] expectedColumnTypes = new String[]{"INTEGER", "INTEGER", "INTEGER", "INTEGER"}; int expectedNumberOfRows = 5; testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumberOfRows, setupSQL, withQuery, maxRetries - 1, expectedColumnTypes); diff --git a/h2/src/test/org/h2/test/db/TestTriggersConstraints.java b/h2/src/test/org/h2/test/db/TestTriggersConstraints.java index d0d6458b98d4b67c1ced815f6ccbea2477fe3798..ac3e6e9c4f2e4b8f1c1c023ce78e1645dcfeec28 100644 --- a/h2/src/test/org/h2/test/db/TestTriggersConstraints.java +++ b/h2/src/test/org/h2/test/db/TestTriggersConstraints.java @@ -506,7 +506,8 @@ public class TestTriggersConstraints extends TestBase implements Trigger { } else if ("javascript".equals(sourceLang)) { String triggerClassName = this.getClass().getName() + "." + TestTriggerAlterTable.class.getSimpleName(); - final String body = "//javascript\nnew Packages." + triggerClassName + "();"; + final String body = "//javascript\n" + + "new Packages." + triggerClassName + "();"; stat.execute("create trigger test_upd before insert on test as $$" + body + " $$"); } else { diff --git a/h2/src/test/org/h2/test/mvcc/TestMvccMultiThreaded2.java b/h2/src/test/org/h2/test/mvcc/TestMvccMultiThreaded2.java index 473710b3dc2f0f6c048db1557ab61b3931b1372b..b5af1192cb41f1a32c92fb57642837d1bcc3fee2 100644 --- a/h2/src/test/org/h2/test/mvcc/TestMvccMultiThreaded2.java +++ b/h2/src/test/org/h2/test/mvcc/TestMvccMultiThreaded2.java @@ -75,11 +75,11 @@ public class TestMvccMultiThreaded2 extends TestBase { // give any of the 100 threads a chance to start by yielding the processor to them Thread.yield(); - + // gather stats on threads after they finished @SuppressWarnings("unused") int minProcessed = Integer.MAX_VALUE, maxProcessed = 0, totalProcessed = 0; - + for (SelectForUpdate sfu : threads) { // make sure all threads have stopped by joining with them sfu.join(); @@ -91,12 +91,13 @@ public class TestMvccMultiThreaded2 extends TestBase { minProcessed = sfu.iterationsProcessed; } } - + if (DISPLAY_STATS) { - System.out.println(String.format("+ INFO: TestMvccMultiThreaded2 RUN STATS threads=%d, minProcessed=%d, maxProcessed=%d, "+ - "totalProcessed=%d, averagePerThread=%d, averagePerThreadPerSecond=%d\n", - TEST_THREAD_COUNT, minProcessed, maxProcessed, totalProcessed, totalProcessed/TEST_THREAD_COUNT, - totalProcessed/(TEST_THREAD_COUNT * TEST_TIME_SECONDS))); + System.out.println(String.format( + "+ INFO: TestMvccMultiThreaded2 RUN STATS threads=%d, minProcessed=%d, maxProcessed=%d, " + + "totalProcessed=%d, averagePerThread=%d, averagePerThreadPerSecond=%d\n", + TEST_THREAD_COUNT, minProcessed, maxProcessed, totalProcessed, totalProcessed / TEST_THREAD_COUNT, + totalProcessed / (TEST_THREAD_COUNT * TEST_TIME_SECONDS))); } IOUtils.closeSilently(conn); @@ -107,7 +108,7 @@ public class TestMvccMultiThreaded2 extends TestBase { * Worker test thread selecting for update */ private class SelectForUpdate extends Thread { - + public int iterationsProcessed; @Override @@ -118,10 +119,10 @@ public class TestMvccMultiThreaded2 extends TestBase { try { conn = getConnection(getTestName() + URL); conn.setAutoCommit(false); - + // give the other threads a chance to start up before going into our work loop Thread.yield(); - + while (!done) { try { PreparedStatement ps = conn.prepareStatement( @@ -148,7 +149,7 @@ public class TestMvccMultiThreaded2 extends TestBase { } catch (Exception e) { TestBase.logError("General error from thread "+getName(), e); throw e; - } + } IOUtils.closeSilently(conn); } } diff --git a/h2/src/test/org/h2/test/scripts/TestScript.java b/h2/src/test/org/h2/test/scripts/TestScript.java index 567e180dc4b7e9af67621d6d67d7882e44fa642e..b7ad857d45ed5bafd023c60d6b1c37d5fceea933 100644 --- a/h2/src/test/org/h2/test/scripts/TestScript.java +++ b/h2/src/test/org/h2/test/scripts/TestScript.java @@ -135,7 +135,7 @@ public class TestScript extends TestBase { } for (String s : new String[] { "with", "mergeUsing" }) { testScript("dml/" + s + ".sql"); - } + } deleteDb("script"); System.out.flush(); } diff --git a/h2/src/test/org/h2/test/scripts/dml/mergeUsing.sql b/h2/src/test/org/h2/test/scripts/dml/mergeUsing.sql index 77acca6943155be04026c2ec9a82b6c51ad519dd..5940a3e245f12aa6c2c52c982b2dc2c8656e9cda 100644 --- a/h2/src/test/org/h2/test/scripts/dml/mergeUsing.sql +++ b/h2/src/test/org/h2/test/scripts/dml/mergeUsing.sql @@ -1,33 +1,33 @@ --- Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0, --- and the EPL 1.0 (http://h2database.com/html/license.html). --- Initial Developer: H2 Group --- -CREATE TABLE PARENT(ID INT, NAME VARCHAR, PRIMARY KEY(ID) ); -> ok - -MERGE INTO PARENT AS P - USING (SELECT X AS ID, 'Coco'||X AS NAME FROM SYSTEM_RANGE(1,2) ) AS S - ON (P.ID = S.ID AND 1=1 AND S.ID = P.ID) - WHEN MATCHED THEN - UPDATE SET P.NAME = S.NAME WHERE 2 = 2 WHEN NOT - MATCHED THEN - INSERT (ID, NAME) VALUES (S.ID, S.NAME); -> update count: 2 - -SELECT * FROM PARENT; -> ID NAME -> -- ----- -> 1 Coco1 -> 2 Coco2 - -EXPLAIN PLAN - MERGE INTO PARENT AS P - USING (SELECT X AS ID, 'Coco'||X AS NAME FROM SYSTEM_RANGE(1,2) ) AS S - ON (P.ID = S.ID AND 1=1 AND S.ID = P.ID) - WHEN MATCHED THEN - UPDATE SET P.NAME = S.NAME WHERE 2 = 2 WHEN NOT - MATCHED THEN - INSERT (ID, NAME) VALUES (S.ID, S.NAME); -> PLAN -> --------------------------------------------------------------------------------------------------------------------------------- -> MERGE INTO PUBLIC.PARENT(ID, NAME) KEY(ID) SELECT X AS ID, ('Coco' || X) AS NAME FROM SYSTEM_RANGE(1, 2) /* PUBLIC.RANGE_INDEX */ \ No newline at end of file +-- Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0, +-- and the EPL 1.0 (http://h2database.com/html/license.html). +-- Initial Developer: H2 Group +-- +CREATE TABLE PARENT(ID INT, NAME VARCHAR, PRIMARY KEY(ID) ); +> ok + +MERGE INTO PARENT AS P + USING (SELECT X AS ID, 'Coco'||X AS NAME FROM SYSTEM_RANGE(1,2) ) AS S + ON (P.ID = S.ID AND 1=1 AND S.ID = P.ID) + WHEN MATCHED THEN + UPDATE SET P.NAME = S.NAME WHERE 2 = 2 WHEN NOT + MATCHED THEN + INSERT (ID, NAME) VALUES (S.ID, S.NAME); +> update count: 2 + +SELECT * FROM PARENT; +> ID NAME +> -- ----- +> 1 Coco1 +> 2 Coco2 + +EXPLAIN PLAN + MERGE INTO PARENT AS P + USING (SELECT X AS ID, 'Coco'||X AS NAME FROM SYSTEM_RANGE(1,2) ) AS S + ON (P.ID = S.ID AND 1=1 AND S.ID = P.ID) + WHEN MATCHED THEN + UPDATE SET P.NAME = S.NAME WHERE 2 = 2 WHEN NOT + MATCHED THEN + INSERT (ID, NAME) VALUES (S.ID, S.NAME); +> PLAN +> --------------------------------------------------------------------------------------------------------------------------------- +> MERGE INTO PUBLIC.PARENT(ID, NAME) KEY(ID) SELECT X AS ID, ('Coco' || X) AS NAME FROM SYSTEM_RANGE(1, 2) /* PUBLIC.RANGE_INDEX */ \ No newline at end of file diff --git a/h2/src/test/org/h2/test/scripts/dml/with.sql b/h2/src/test/org/h2/test/scripts/dml/with.sql index efcdb1cb217d89a486437fedbcada9524b33c512..8ea464fc7db407f36b9f0cf0ac1dbdd9db4a6360 100644 --- a/h2/src/test/org/h2/test/scripts/dml/with.sql +++ b/h2/src/test/org/h2/test/scripts/dml/with.sql @@ -1,55 +1,55 @@ --- Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0, --- and the EPL 1.0 (http://h2database.com/html/license.html). --- Initial Developer: H2 Group --- -explain with recursive r(n) as ( - (select 1) union all (select n+1 from r where n < 3) -) -select n from r; -> PLAN -> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -> WITH RECURSIVE R(N) AS ( (SELECT 1 FROM SYSTEM_RANGE(1, 1) /* PUBLIC.RANGE_INDEX */) UNION ALL (SELECT (N + 1) FROM PUBLIC.R /* PUBLIC.R.tableScan */ WHERE N < 3) ) SELECT N FROM R R /* null */ -> rows: 1 - -select sum(n) from ( - with recursive r(n) as ( - (select 1) union all (select n+1 from r where n < 3) - ) - select n from r -); -> SUM(N) -> ------ -> 6 -> rows: 1 - -select sum(n) from (select 0) join ( - with recursive r(n) as ( - (select 1) union all (select n+1 from r where n < 3) - ) - select n from r -) on 1=1; -> SUM(N) -> ------ -> 6 -> rows: 1 - -select 0 from ( - select 0 where 0 in ( - with recursive r(n) as ( - (select 1) union all (select n+1 from r where n < 3) - ) - select n from r - ) -); -> 0 -> - -> rows: 0 -with - r0(n,k) as (select -1, 0), - r1(n,k) as ((select 1, 0) union all (select n+1,k+1 from r1 where n <= 3)), - r2(n,k) as ((select 10,0) union all (select n+1,k+1 from r2 where n <= 13)) - select r1.k, r0.n as N0, r1.n AS N1, r2.n AS n2 from r0 inner join r1 ON r1.k= r0.k inner join r2 ON r1.k= r2.k; -> K N0 N1 N2 -> - -- -- -- -> 0 -1 1 10 +-- Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0, +-- and the EPL 1.0 (http://h2database.com/html/license.html). +-- Initial Developer: H2 Group +-- +explain with recursive r(n) as ( + (select 1) union all (select n+1 from r where n < 3) +) +select n from r; +> PLAN +> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +> WITH RECURSIVE R(N) AS ( (SELECT 1 FROM SYSTEM_RANGE(1, 1) /* PUBLIC.RANGE_INDEX */) UNION ALL (SELECT (N + 1) FROM PUBLIC.R /* PUBLIC.R.tableScan */ WHERE N < 3) ) SELECT N FROM R R /* null */ +> rows: 1 + +select sum(n) from ( + with recursive r(n) as ( + (select 1) union all (select n+1 from r where n < 3) + ) + select n from r +); +> SUM(N) +> ------ +> 6 +> rows: 1 + +select sum(n) from (select 0) join ( + with recursive r(n) as ( + (select 1) union all (select n+1 from r where n < 3) + ) + select n from r +) on 1=1; +> SUM(N) +> ------ +> 6 +> rows: 1 + +select 0 from ( + select 0 where 0 in ( + with recursive r(n) as ( + (select 1) union all (select n+1 from r where n < 3) + ) + select n from r + ) +); +> 0 +> - +> rows: 0 +with + r0(n,k) as (select -1, 0), + r1(n,k) as ((select 1, 0) union all (select n+1,k+1 from r1 where n <= 3)), + r2(n,k) as ((select 10,0) union all (select n+1,k+1 from r2 where n <= 13)) + select r1.k, r0.n as N0, r1.n AS N1, r2.n AS n2 from r0 inner join r1 ON r1.k= r0.k inner join r2 ON r1.k= r2.k; +> K N0 N1 N2 +> - -- -- -- +> 0 -1 1 10 > rows: 1 \ No newline at end of file