提交 3e77d2fb authored 作者: Noel Grandin's avatar Noel Grandin

couple of cleanups to the CTE merge

上级 5a428e25
...@@ -21,6 +21,8 @@ Change Log ...@@ -21,6 +21,8 @@ Change Log
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul> <ul>
<li>Issue#479 Allow non-recursive CTEs (WITH statements), patch from stumc
</li>
<li>Fix startup issue when using "CHECK" as a column name. <li>Fix startup issue when using "CHECK" as a column name.
</li> </li>
<li>Issue #423: ANALYZE performed multiple times on one table during execution of the same statement. <li>Issue #423: ANALYZE performed multiple times on one table during execution of the same statement.
......
...@@ -18,7 +18,6 @@ import java.util.Comparator; ...@@ -18,7 +18,6 @@ import java.util.Comparator;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.api.Trigger; import org.h2.api.Trigger;
import org.h2.command.ddl.AlterIndexRename; import org.h2.command.ddl.AlterIndexRename;
...@@ -4857,97 +4856,99 @@ public class Parser { ...@@ -4857,97 +4856,99 @@ public class Parser {
} }
private Query parseWith() { private Query parseWith() {
List<TableView> viewsCreated = new ArrayList<TableView>(); List<TableView> viewsCreated = new ArrayList<TableView>();
readIf("RECURSIVE"); readIf("RECURSIVE");
do{ do {
viewsCreated.add(parseSingleCommonTableExpression()); viewsCreated.add(parseSingleCommonTableExpression());
} while(readIf(",")); } while (readIf(","));
Query q = parseSelectUnion(); Query q = parseSelectUnion();
q.setPrepareAlways(true); q.setPrepareAlways(true);
return q; return q;
} }
private TableView parseSingleCommonTableExpression() { private TableView parseSingleCommonTableExpression() {
String tempViewName = readIdentifierWithSchema(); String tempViewName = readIdentifierWithSchema();
Schema schema = getSchema(); Schema schema = getSchema();
Table recursiveTable; Table recursiveTable;
ArrayList<Column> columns = New.arrayList(); ArrayList<Column> columns = New.arrayList();
String[] cols = null; String[] cols = null;
// 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 // query if not supplied
if(readIf("(")){ if (readIf("(")) {
cols = parseColumnList(); cols = parseColumnList();
for (String c : cols) { for (String c : cols) {
// we dont really know the type of the column, so string will have to do // we dont really know the type of the column, so string will
columns.add(new Column(c, Value.STRING)); // have to do
} columns.add(new Column(c, Value.STRING));
} }
Table old = session.findLocalTempTable(tempViewName); }
if (old != null) { Table old = session.findLocalTempTable(tempViewName);
if (!(old instanceof TableView)) { if (old != null) {
throw DbException.get(ErrorCode.TABLE_OR_VIEW_ALREADY_EXISTS_1, if (!(old instanceof TableView)) {
tempViewName); throw DbException.get(ErrorCode.TABLE_OR_VIEW_ALREADY_EXISTS_1,
} tempViewName);
TableView tv = (TableView) old; }
if (!tv.isTableExpression()) { TableView tv = (TableView) old;
throw DbException.get(ErrorCode.TABLE_OR_VIEW_ALREADY_EXISTS_1, if (!tv.isTableExpression()) {
tempViewName); throw DbException.get(ErrorCode.TABLE_OR_VIEW_ALREADY_EXISTS_1,
} tempViewName);
session.removeLocalTempTable(old); }
} session.removeLocalTempTable(old);
// this table is created as a work around because recursive }
// table expressions need to reference something that look like themselves // this table is created as a work around because recursive
// to work (its removed after creation in this method) // table expressions need to reference something that look like
CreateTableData data = new CreateTableData(); // themselves
data.id = database.allocateObjectId(); // to work (its removed after creation in this method)
data.columns = columns; CreateTableData data = new CreateTableData();
data.tableName = tempViewName; data.id = database.allocateObjectId();
data.temporary = true; data.columns = columns;
data.persistData = true; data.tableName = tempViewName;
data.persistIndexes = false; data.temporary = true;
data.create = true; data.persistData = true;
data.session = session; data.persistIndexes = false;
recursiveTable = schema.createTable(data); data.create = true;
session.addLocalTempTable(recursiveTable); data.session = session;
String querySQL; recursiveTable = schema.createTable(data);
List<Column> columnTemplateList = new ArrayList<Column>(); session.addLocalTempTable(recursiveTable);
try { String querySQL;
read("AS"); List<Column> columnTemplateList = new ArrayList<Column>();
read("("); try {
Query withQuery = parseSelect(); read("AS");
read(")"); read("(");
withQuery.prepare(); Query withQuery = parseSelect();
querySQL = StringUtils.cache(withQuery.getPlanSQL()); read(")");
ArrayList<Expression> withExpressions = withQuery.getExpressions(); withQuery.prepare();
for (int i = 0; i < withExpressions.size(); ++i) { querySQL = StringUtils.cache(withQuery.getPlanSQL());
String columnName = cols != null ? cols[i] : withExpressions.get(i).getColumnName(); ArrayList<Expression> withExpressions = withQuery.getExpressions();
columnTemplateList.add(new Column(columnName, withExpressions.get(i).getType())); for (int i = 0; i < withExpressions.size(); ++i) {
} String columnName = cols != null ? cols[i]
} finally { : withExpressions.get(i).getColumnName();
session.removeLocalTempTable(recursiveTable); columnTemplateList.add(new Column(columnName,
} withExpressions.get(i).getType()));
int id = database.allocateObjectId(); }
boolean isRecursive = true; } finally {
TableView view = null; session.removeLocalTempTable(recursiveTable);
do{ }
view = new TableView(schema, id, tempViewName, querySQL, int id = database.allocateObjectId();
parameters, columnTemplateList.toArray(new Column[0]), session, // No easy way to determine if this is a recursive query up front, so we just compile
isRecursive); // it twice - once without the flag set, and if we didn't see a recursive term,
if(view.isRecursiveQueryDetected()!=isRecursive){ // then we just compile it again.
isRecursive = view.isRecursiveQueryDetected(); TableView view = new TableView(schema, id, tempViewName, querySQL,
view = null; parameters, columnTemplateList.toArray(new Column[0]), session,
continue; true/* recursive */);
} if (!view.isRecursiveQueryDetected()) {
view = new TableView(schema, id, tempViewName, querySQL, parameters,
} while(view==null); columnTemplateList.toArray(new Column[0]), session,
view.setTableExpression(true); false/* recursive */);
view.setTemporary(true); }
session.addLocalTempTable(view); view.setTableExpression(true);
view.setOnCommitDrop(true); view.setTemporary(true);
return view; session.addLocalTempTable(view);
} view.setOnCommitDrop(true);
return view;
}
private CreateView parseCreateView(boolean force, boolean orReplace) { private CreateView parseCreateView(boolean force, boolean orReplace) {
boolean ifNotExists = readIfNotExists(); boolean ifNotExists = readIfNotExists();
......
...@@ -57,7 +57,7 @@ public class TableView extends Table { ...@@ -57,7 +57,7 @@ public class TableView extends Table {
private Query topQuery; private Query topQuery;
private ResultInterface recursiveResult; private ResultInterface recursiveResult;
private boolean tableExpression; private boolean tableExpression;
private boolean isRecursiveQueryDetected; private boolean isRecursiveQueryDetected;
public TableView(Schema schema, int id, String name, String querySQL, public TableView(Schema schema, int id, String name, String querySQL,
ArrayList<Parameter> params, Column[] columnTemplates, Session session, ArrayList<Parameter> params, Column[] columnTemplates, Session session,
...@@ -205,13 +205,12 @@ public class TableView extends Table { ...@@ -205,13 +205,12 @@ public class TableView extends Table {
} catch (DbException e) { } catch (DbException e) {
e.addSQL(getCreateSQL()); e.addSQL(getCreateSQL());
createException = e; createException = e;
// if it can't be compiled, then it's a 'zero column table' // If it can't be compiled, then it's a 'zero column table'
// this avoids problems when creating the view when opening the // this avoids problems when creating the view when opening the
// database // database.
// If it can not be compiled - it could also be a recursive common table expression query.
// if it can not be compiled - it could also be a recursive common table expression query if (isRecursiveQueryExceptionDetected(createException)) {
if(isRecursiveQueryExceptionDetected(createException)){ this.isRecursiveQueryDetected = true;
this.isRecursiveQueryDetected = true;
} }
tables = New.arrayList(); tables = New.arrayList();
cols = new Column[0]; cols = new Column[0];
...@@ -672,30 +671,28 @@ public class TableView extends Table { ...@@ -672,30 +671,28 @@ public class TableView extends Table {
return true; return true;
} }
} }
/** /**
* If query recursion is detected (for recursion detection) * Was query recursion detected during compiling.
* @return is Recursive Query Flag Set
*/ */
public boolean isRecursiveQueryDetected(){ public boolean isRecursiveQueryDetected() {
return isRecursiveQueryDetected; return isRecursiveQueryDetected;
} }
/** /**
* If query an exception indicates query recursion * Does exception indicate query recursion?
* @return is Recursive Query Exception Detected */
*/ private boolean isRecursiveQueryExceptionDetected(DbException exception) {
private boolean isRecursiveQueryExceptionDetected(DbException exception){ if (exception == null) {
if (exception==null){ return false;
return false; }
} if (exception.getErrorCode() != ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1) {
if (exception.getErrorCode()!=ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1){ return false;
return false; }
} if (!exception.getMessage().contains("\"" + this.getName() + "\"")) {
if (! exception.getMessage().contains("\""+this.getName()+"\"")){ return false;
return false; }
} return true;
return true;
} }
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论