Unverified 提交 3a7253d5 authored 作者: Noel Grandin's avatar Noel Grandin 提交者: GitHub

Merge pull request #749 from katzyn/build_errors

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