提交 d4fb4c1c authored 作者: Owner's avatar Owner

Fixed recursive and non-recursive persistent views with CTE's

上级 15f6d85b
...@@ -1138,7 +1138,7 @@ public class Parser { ...@@ -1138,7 +1138,7 @@ public class Parser {
String[] querySQLOutput = new String[]{null}; String[] querySQLOutput = new String[]{null};
List<Column> columnTemplateList = createQueryColumnTemplateList(null, command.getQuery(), querySQLOutput); List<Column> columnTemplateList = createQueryColumnTemplateList(null, command.getQuery(), querySQLOutput);
TableView temporarySourceTableView = createTemporarySessionView( TableView temporarySourceTableView = createCTEView(
command.getQueryAlias(), querySQLOutput[0], command.getQueryAlias(), querySQLOutput[0],
columnTemplateList, false/* no recursion */, columnTemplateList, false/* no recursion */,
false/* do not add to session */, false/* do not add to session */,
...@@ -5130,12 +5130,12 @@ public class Parser { ...@@ -5130,12 +5130,12 @@ public class Parser {
List<TableView> viewsCreated = new ArrayList<>(); List<TableView> viewsCreated = new ArrayList<>();
readIf("RECURSIVE"); readIf("RECURSIVE");
// this WITH statement might not be temporary - allow optional keyword to tell us that // this WITH statement might not be a temporary view - allow optional keyword to tell us that
// this keyword is a work in progress feature and will not be documented // this keyword. This is a work in progress feature and will not be documented
boolean isPersistent = readIf("PERSISTENT"); boolean isPersistent = readIf("PERSISTENT");
// this WITH statement might not be temporary - it may part of a persistent view // this WITH statement is not be temporary - it is part of a persistent view
// as in CREATE VIEW abc AS WITH - this auto detects that condition // as in CREATE VIEW abc AS WITH my_cte - this auto detects that condition
if(session.isParsingView()){ if(session.isParsingView()){
isPersistent = true; isPersistent = true;
} }
...@@ -5145,10 +5145,11 @@ public class Parser { ...@@ -5145,10 +5145,11 @@ public class Parser {
} while (readIf(",")); } while (readIf(","));
Prepared p = null; Prepared p = null;
Collections.reverse(viewsCreated);
if(isToken("SELECT")) { if(isToken("SELECT")) {
Query query = parseSelectUnion(); Query query = parseSelectUnion();
query.setPrepareAlways(true); query.setPrepareAlways(!isPersistent);
query.setNeverLazy(true); query.setNeverLazy(true);
p = query; p = query;
} }
...@@ -5185,43 +5186,56 @@ public class Parser { ...@@ -5185,43 +5186,56 @@ public class Parser {
// clean up temporary views starting with last to first (in case of // clean up temporary views starting with last to first (in case of
// dependencies) - but only if they are not persistent // dependencies) - but only if they are not persistent
if(!isPersistent){ if(!isPersistent){
Collections.reverse(viewsCreated);
p.setCteCleanups(viewsCreated); p.setCteCleanups(viewsCreated);
} }
return p; return p;
} }
@SuppressWarnings("resource")// Eclipse thinks targetSession needs releasing
private TableView parseSingleCommonTableExpression(boolean isPersistent) { private TableView parseSingleCommonTableExpression(boolean isPersistent) {
Session targetSession = session; //isPersistent ? database.getSystemSession() : Session targetSession = isPersistent ? database.getSystemSession() : session;
String tempViewName = readIdentifierWithSchema(); String cteViewName = readIdentifierWithSchema();
Schema schema = getSchema(); Schema schema = getSchema();
Table recursiveTable=null; Table recursiveTable=null;
ArrayList<Column> columns = New.arrayList(); ArrayList<Column> columns = New.arrayList();
String[] cols = null; String[] cols = null;
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 // query, if not supplied by user
if (readIf("(")) { if (readIf("(")) {
cols = parseColumnList(); cols = parseColumnList();
for (String c : cols) { for (String c : cols) {
// we don't really know the type of the column, so string will // we don't really know the type of the column, so UNKNOWN will
// have to do // have to do
columns.add(new Column(c, Value.STRING)); columns.add(new Column(c, Value.STRING));
} }
} }
Table oldViewFound = targetSession.findLocalTempTable(tempViewName); Table oldViewFound = null;
if(isPersistent){
oldViewFound = getSchema().findTableOrView(session, cteViewName);
}
else{
oldViewFound = targetSession.findLocalTempTable(cteViewName);
}
if (oldViewFound != null && !isPersistent) { if (oldViewFound != null && !isPersistent) {
if (!(oldViewFound instanceof TableView)) { if (!(oldViewFound instanceof TableView)) {
throw DbException.get(ErrorCode.TABLE_OR_VIEW_ALREADY_EXISTS_1, throw DbException.get(ErrorCode.TABLE_OR_VIEW_ALREADY_EXISTS_1,
tempViewName); cteViewName);
} }
TableView tv = (TableView) oldViewFound; TableView tv = (TableView) oldViewFound;
if (!tv.isTableExpression()) { if (!tv.isTableExpression()) {
throw DbException.get(ErrorCode.TABLE_OR_VIEW_ALREADY_EXISTS_1, throw DbException.get(ErrorCode.TABLE_OR_VIEW_ALREADY_EXISTS_1,
tempViewName); cteViewName);
} }
if(isPersistent){
oldViewFound.lock(session, true, true);
session.getDatabase().removeSchemaObject(session, oldViewFound);
}else{
targetSession.removeLocalTempTable(oldViewFound); targetSession.removeLocalTempTable(oldViewFound);
}
oldViewFound=null; oldViewFound=null;
} }
// this table is created as a work around because recursive // this table is created as a work around because recursive
...@@ -5233,15 +5247,24 @@ public class Parser { ...@@ -5233,15 +5247,24 @@ public class Parser {
CreateTableData recursiveTableData = new CreateTableData(); CreateTableData recursiveTableData = new CreateTableData();
recursiveTableData.id = database.allocateObjectId(); recursiveTableData.id = database.allocateObjectId();
recursiveTableData.columns = columns; recursiveTableData.columns = columns;
recursiveTableData.tableName = tempViewName; recursiveTableData.tableName = cteViewName;
recursiveTableData.temporary = !isPersistent; recursiveTableData.temporary = !isPersistent;
recursiveTableData.persistData = true; recursiveTableData.persistData = true;
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
recursiveTable = schema.createTable(recursiveTableData); recursiveTable = schema.createTable(recursiveTableData);
if(isPersistent){
// this unlock is to prevent beed from schema.createTable()
database.unlockMeta(targetSession);
synchronized (targetSession) {
db.addSchemaObject(session, recursiveTable);
}
}else{
targetSession.addLocalTempTable(recursiveTable); targetSession.addLocalTempTable(recursiveTable);
} }
}
List<Column> columnTemplateList; List<Column> columnTemplateList;
String[] querySQLOutput = new String[]{null}; String[] querySQLOutput = new String[]{null};
try { try {
...@@ -5253,14 +5276,20 @@ public class Parser { ...@@ -5253,14 +5276,20 @@ public class Parser {
} finally { } finally {
if(recursiveTable!=null){ if(recursiveTable!=null){
if(isPersistent){
recursiveTable.lock(session, true, true);
session.getDatabase().removeSchemaObject(session, recursiveTable);
}else{
targetSession.removeLocalTempTable(recursiveTable); targetSession.removeLocalTempTable(recursiveTable);
} }
} }
}
// If it's persistent, a CTE and a TableView - return existing one, otherwise create new... // If it's persistent, a CTE and a TableView - return existing one, otherwise create new...
if(oldViewFound!=null && isPersistent && oldViewFound instanceof TableView && oldViewFound.isTableExpression()){ if(oldViewFound!=null && isPersistent && oldViewFound instanceof TableView && oldViewFound.isTableExpression()){
return (TableView) oldViewFound; return (TableView) oldViewFound;
} }
TableView view = createTemporarySessionView(tempViewName, TableView view = createCTEView(cteViewName,
querySQLOutput[0], columnTemplateList, querySQLOutput[0], columnTemplateList,
true/* allowRecursiveQueryDetection */, true/* add to session */, isPersistent); true/* allowRecursiveQueryDetection */, true/* add to session */, isPersistent);
...@@ -5283,7 +5312,7 @@ public class Parser { ...@@ -5283,7 +5312,7 @@ public class Parser {
Query theQuery, String[] querySQLOutput) { Query theQuery, String[] querySQLOutput) {
List<Column> columnTemplateList = new ArrayList<>(); List<Column> columnTemplateList = new ArrayList<>();
theQuery.prepare(); theQuery.prepare();
// array of length 1 to receive extra 'output' field in addition to // array of length 1 is to receive extra 'output' field in addition to
// return value // return value
querySQLOutput[0] = StringUtils.cache(theQuery.getPlanSQL()); querySQLOutput[0] = StringUtils.cache(theQuery.getPlanSQL());
ColumnNamer columnNamer = new ColumnNamer(theQuery.getSession()); ColumnNamer columnNamer = new ColumnNamer(theQuery.getSession());
...@@ -5300,31 +5329,47 @@ public class Parser { ...@@ -5300,31 +5329,47 @@ public class Parser {
return columnTemplateList; return columnTemplateList;
} }
private TableView createTemporarySessionView(String tempViewName, String querySQL, private TableView createCTEView(String cteViewName, String querySQL,
List<Column> columnTemplateList, boolean allowRecursiveQueryDetection, List<Column> columnTemplateList, boolean allowRecursiveQueryDetection,
boolean addViewToSession, boolean isPersistent) { boolean addViewToSession, boolean isPersistent) {
Session targetSession = session; //isPersistent ? database.getSystemSession() : Session targetSession = /*isPersistent ? database.getSystemSession() :*/session;
Database db = session.getDatabase();
Schema schema = getSchemaWithDefault(); Schema schema = getSchemaWithDefault();
int id = database.allocateObjectId(); int id = database.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 // 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 = new TableView(schema, id, tempViewName, querySQL, TableView view;
parameters, columnTemplateList.toArray(new Column[0]), targetSession, synchronized(targetSession){
allowRecursiveQueryDetection, false); view = new TableView(schema, id, cteViewName, querySQL,
parameters, columnTemplateArray, targetSession,
allowRecursiveQueryDetection, false /* literalsChecked */);
if (!view.isRecursiveQueryDetected() && allowRecursiveQueryDetection) { if (!view.isRecursiveQueryDetected() && allowRecursiveQueryDetection) {
targetSession.removeLocalTempTable(view); if(isPersistent){
view = new TableView(schema, id, tempViewName, querySQL, parameters, db.addSchemaObject(session, view);
columnTemplateList.toArray(new Column[0]), targetSession, view.lock(session, true, true);
false/* recursive */, false); session.getDatabase().removeSchemaObject(session, view);
}else{
session.removeLocalTempTable(view);
}
view = new TableView(schema, id, cteViewName, querySQL, parameters,
columnTemplateArray, targetSession,
false/* assume recursive */, false /* literalsChecked */);
}
} }
view.setTableExpression(true); view.setTableExpression(true);
view.setTemporary(!isPersistent); view.setTemporary(!isPersistent);
view.setHidden(true); view.setHidden(true);
view.setOnCommitDrop(false);
if(addViewToSession){ if(addViewToSession){
if(isPersistent){
db.addSchemaObject(session, view);
}
else{
targetSession.addLocalTempTable(view); targetSession.addLocalTempTable(view);
} }
view.setOnCommitDrop(false); }
return view; return view;
} }
......
...@@ -100,6 +100,7 @@ public class CreateTable extends SchemaCommand { ...@@ -100,6 +100,7 @@ public class CreateTable extends SchemaCommand {
@Override @Override
public int update() { public int update() {
boolean metaLockAquired = false;
if (!transactional) { if (!transactional) {
session.commit(true); session.commit(true);
} }
...@@ -108,8 +109,10 @@ public class CreateTable extends SchemaCommand { ...@@ -108,8 +109,10 @@ public class CreateTable extends SchemaCommand {
data.persistIndexes = false; data.persistIndexes = false;
} }
boolean isSessionTemporary = data.temporary && !data.globalTemporary; boolean isSessionTemporary = data.temporary && !data.globalTemporary;
try {
if (!isSessionTemporary) { if (!isSessionTemporary) {
db.lockMeta(session); db.lockMeta(session);
metaLockAquired = true;
} }
if (getSchema().resolveTableOrView(session, data.tableName) != null) { if (getSchema().resolveTableOrView(session, data.tableName) != null) {
if (ifNotExists) { if (ifNotExists) {
...@@ -225,6 +228,13 @@ public class CreateTable extends SchemaCommand { ...@@ -225,6 +228,13 @@ public class CreateTable extends SchemaCommand {
} }
throw e; throw e;
} }
}
finally{
if (!isSessionTemporary && metaLockAquired) {
db.unlockMeta(session);
}
}
return 0; return 0;
} }
......
...@@ -7,7 +7,6 @@ package org.h2.command.dml; ...@@ -7,7 +7,6 @@ package org.h2.command.dml;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.command.Prepared; import org.h2.command.Prepared;
import org.h2.engine.Database; import org.h2.engine.Database;
......
...@@ -1843,6 +1843,7 @@ public class Database implements DataHandler { ...@@ -1843,6 +1843,7 @@ public class Database implements DataHandler {
int type = obj.getType(); int type = obj.getType();
if (type == DbObject.TABLE_OR_VIEW) { if (type == DbObject.TABLE_OR_VIEW) {
Table table = (Table) obj; Table table = (Table) obj;
table.setBeingDropped(true);
if (table.isTemporary() && !table.isGlobalTemporary()) { if (table.isTemporary() && !table.isGlobalTemporary()) {
session.removeLocalTempTable(table); session.removeLocalTempTable(table);
return; return;
......
...@@ -639,9 +639,14 @@ public class Schema extends DbObjectBase { ...@@ -639,9 +639,14 @@ public class Schema extends DbObjectBase {
* @return the created {@link Table} object * @return the created {@link Table} object
*/ */
public Table createTable(CreateTableData data) { public Table createTable(CreateTableData data) {
Database acquiredMetaLockDatabase = null;
try{
synchronized (database) { synchronized (database) {
if (!data.temporary || data.globalTemporary) { if (!data.temporary || data.globalTemporary) {
database.lockMeta(data.session); database.lockMeta(data.session);
// remember to unlock the meta lock before we leave this method
acquiredMetaLockDatabase = database;
} }
data.schema = this; data.schema = this;
if (data.tableEngine == null) { if (data.tableEngine == null) {
...@@ -656,11 +661,24 @@ public class Schema extends DbObjectBase { ...@@ -656,11 +661,24 @@ public class Schema extends DbObjectBase {
if (data.tableEngineParams == null) { if (data.tableEngineParams == null) {
data.tableEngineParams = this.tableEngineParams; data.tableEngineParams = this.tableEngineParams;
} }
// the createTable method unlocks the meta - so turn off flag now
acquiredMetaLockDatabase=null;
return database.getTableEngine(data.tableEngine).createTable(data); return database.getTableEngine(data.tableEngine).createTable(data);
} }
// the RegularTable constructor unlocks the meta - so turn off flag now
acquiredMetaLockDatabase=null;
return new RegularTable(data); return new RegularTable(data);
} }
} }
finally{
if(acquiredMetaLockDatabase!=null && data.session!=null){
if(acquiredMetaLockDatabase.isSysTableLockedBy(data.session)){
acquiredMetaLockDatabase.unlockMeta(data.session);
}
}
}
}
public TableSynonym createSynonym(CreateSynonymData data) { public TableSynonym createSynonym(CreateSynonymData data) {
synchronized (database) { synchronized (database) {
......
...@@ -77,12 +77,13 @@ public abstract class Table extends SchemaObjectBase { ...@@ -77,12 +77,13 @@ public abstract class Table extends SchemaObjectBase {
private ArrayList<TriggerObject> triggers; private ArrayList<TriggerObject> triggers;
private ArrayList<Constraint> constraints; private ArrayList<Constraint> constraints;
private ArrayList<Sequence> sequences; private ArrayList<Sequence> sequences;
private ArrayList<TableView> views; private ArrayList<TableView> views; // remember which views are using this object
private ArrayList<TableSynonym> synonyms; private ArrayList<TableSynonym> synonyms;
private boolean checkForeignKeyConstraints = true; private boolean checkForeignKeyConstraints = true;
private boolean onCommitDrop, onCommitTruncate; private boolean onCommitDrop, onCommitTruncate;
private volatile Row nullRow; private volatile Row nullRow;
private boolean tableExpression; private boolean tableExpression;
private boolean isBeingDropped;
public Table(Schema schema, int id, String name, boolean persistIndexes, public Table(Schema schema, int id, String name, boolean persistIndexes,
...@@ -158,7 +159,6 @@ public abstract class Table extends SchemaObjectBase { ...@@ -158,7 +159,6 @@ public abstract class Table extends SchemaObjectBase {
* @param key the primary key * @param key the primary key
* @return the row * @return the row
*/ */
@SuppressWarnings("unused")
public Row getRow(Session session, long key) { public Row getRow(Session session, long key) {
return null; return null;
} }
...@@ -193,7 +193,6 @@ public abstract class Table extends SchemaObjectBase { ...@@ -193,7 +193,6 @@ public abstract class Table extends SchemaObjectBase {
* @param operation the operation * @param operation the operation
* @param row the row * @param row the row
*/ */
@SuppressWarnings("unused")
public void commit(short operation, Row row) { public void commit(short operation, Row row) {
// nothing to do // nothing to do
} }
...@@ -231,7 +230,6 @@ public abstract class Table extends SchemaObjectBase { ...@@ -231,7 +230,6 @@ public abstract class Table extends SchemaObjectBase {
* @param allColumnsSet all columns * @param allColumnsSet all columns
* @return the scan index * @return the scan index
*/ */
@SuppressWarnings("unused")
public Index getScanIndex(Session session, int[] masks, public Index getScanIndex(Session session, int[] masks,
TableFilter[] filters, int filter, SortOrder sortOrder, TableFilter[] filters, int filter, SortOrder sortOrder,
HashSet<Column> allColumnsSet) { HashSet<Column> allColumnsSet) {
...@@ -464,7 +462,6 @@ public abstract class Table extends SchemaObjectBase { ...@@ -464,7 +462,6 @@ public abstract class Table extends SchemaObjectBase {
* @param session the session * @param session the session
* @return true if it is * @return true if it is
*/ */
@SuppressWarnings("unused")
public boolean isLockedExclusivelyBy(Session session) { public boolean isLockedExclusivelyBy(Session session) {
return false; return false;
} }
...@@ -1168,7 +1165,6 @@ public abstract class Table extends SchemaObjectBase { ...@@ -1168,7 +1165,6 @@ public abstract class Table extends SchemaObjectBase {
* @return an object array with the sessions involved in the deadlock, or * @return an object array with the sessions involved in the deadlock, or
* null * null
*/ */
@SuppressWarnings("unused")
public ArrayList<Session> checkDeadlock(Session session, Session clash, public ArrayList<Session> checkDeadlock(Session session, Session clash,
Set<Session> visited) { Set<Session> visited) {
return null; return null;
...@@ -1253,4 +1249,12 @@ public abstract class Table extends SchemaObjectBase { ...@@ -1253,4 +1249,12 @@ public abstract class Table extends SchemaObjectBase {
return tableExpression; return tableExpression;
} }
public boolean isBeingDropped(){
return isBeingDropped;
}
public void setBeingDropped(boolean isBeingDropped){
this.isBeingDropped = isBeingDropped;
}
} }
...@@ -58,6 +58,7 @@ public class TableView extends Table { ...@@ -58,6 +58,7 @@ public class TableView extends Table {
private Query topQuery; private Query topQuery;
private ResultInterface recursiveResult; private ResultInterface recursiveResult;
private boolean isRecursiveQueryDetected; private boolean isRecursiveQueryDetected;
private Session session;
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,
...@@ -98,6 +99,7 @@ public class TableView extends Table { ...@@ -98,6 +99,7 @@ public class TableView extends Table {
this.columnTemplates = columnTemplates; this.columnTemplates = columnTemplates;
this.recursive = recursive; this.recursive = recursive;
this.isRecursiveQueryDetected = false; this.isRecursiveQueryDetected = false;
this.session = session;
index = new ViewIndex(this, querySQL, params, recursive); index = new ViewIndex(this, querySQL, params, recursive);
initColumnsAndTables(session, literalsChecked); initColumnsAndTables(session, literalsChecked);
} }
...@@ -157,13 +159,13 @@ public class TableView extends Table { ...@@ -157,13 +159,13 @@ public class TableView extends Table {
Column[] cols; Column[] cols;
removeViewFromTables(); removeViewFromTables();
try { try {
Query query = compileViewQuery(session, querySQL, literalsChecked); Query compiledQuery = compileViewQuery(session, querySQL, literalsChecked);
this.querySQL = query.getPlanSQL(); this.querySQL = compiledQuery.getPlanSQL();
tables = New.arrayList(query.getTables()); tables = New.arrayList(compiledQuery.getTables());
ArrayList<Expression> expressions = query.getExpressions(); ArrayList<Expression> expressions = compiledQuery.getExpressions();
ArrayList<Column> list = New.arrayList(); ArrayList<Column> list = New.arrayList();
ColumnNamer columnNamer= new ColumnNamer(session); ColumnNamer columnNamer= new ColumnNamer(session);
for (int i = 0, count = query.getColumnCount(); i < count; i++) { for (int i = 0, count = compiledQuery.getColumnCount(); i < count; i++) {
Expression expr = expressions.get(i); Expression expr = expressions.get(i);
String name = null; String name = null;
int type = Value.UNKNOWN; int type = Value.UNKNOWN;
...@@ -205,7 +207,7 @@ public class TableView extends Table { ...@@ -205,7 +207,7 @@ public class TableView extends Table {
cols = new Column[list.size()]; cols = new Column[list.size()];
list.toArray(cols); list.toArray(cols);
createException = null; createException = null;
viewQuery = query; viewQuery = compiledQuery;
} catch (DbException e) { } catch (DbException e) {
e.addSQL(getCreateSQL()); e.addSQL(getCreateSQL());
createException = e; createException = e;
...@@ -694,4 +696,16 @@ public class TableView extends Table { ...@@ -694,4 +696,16 @@ public class TableView extends Table {
return true; return true;
} }
@Override
public void removeView(TableView view){
super.removeView(view);
// if this is a table expression and the last view to use it is
// being dropped - then remove itself from the schema
if(isTableExpression() && getViews()!=null && view.isBeingDropped()){
// check if any database objects are left using this view
if(getViews().size()==0){
session.getDatabase().removeSchemaObject(session,this);
}
}
}
} }
...@@ -44,6 +44,7 @@ public class TestGeneralCommonTableQueries extends TestBase { ...@@ -44,6 +44,7 @@ public class TestGeneralCommonTableQueries extends TestBase {
testNestedSQL(); testNestedSQL();
testRecursiveTable(); testRecursiveTable();
testRecursiveTableInCreateView(); testRecursiveTableInCreateView();
testNonRecursiveTableInCreateView();
} }
private void testSimpleSelect() throws Exception { private void testSimpleSelect() throws Exception {
...@@ -608,4 +609,41 @@ public class TestGeneralCommonTableQueries extends TestBase { ...@@ -608,4 +609,41 @@ public class TestGeneralCommonTableQueries extends TestBase {
testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumbeOfRows, SETUP_SQL, testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumbeOfRows, SETUP_SQL,
WITH_QUERY); WITH_QUERY);
} }
private void testNonRecursiveTableInCreateView() throws Exception {
String SETUP_SQL = ""
+"DROP VIEW IF EXISTS v_my_nr_tree; \n"
+"DROP TABLE IF EXISTS my_table; \n"
+"CREATE TABLE my_table ( \n"
+" id INTEGER, \n"
+" parent_fk INTEGER \n"
+"); \n"
+" \n"
+"INSERT INTO my_table ( id, parent_fk) VALUES ( 1, NULL ); \n"
+"INSERT INTO my_table ( id, parent_fk) VALUES ( 11, 1 ); \n"
+"INSERT INTO my_table ( id, parent_fk) VALUES ( 111, 11 ); \n"
+"INSERT INTO my_table ( id, parent_fk) VALUES ( 12, 1 ); \n"
+"INSERT INTO my_table ( id, parent_fk) VALUES ( 121, 12 ); \n"
+" \n"
+"CREATE OR REPLACE VIEW v_my_nr_tree AS \n"
+"WITH tree_cte_nr (sub_tree_root_id, tree_level, parent_fk, child_fk) AS ( \n"
+" SELECT mt.ID AS sub_tree_root_id, CAST(0 AS INT) AS tree_level, mt.parent_fk, mt.id \n"
+" FROM my_table mt \n"
+") \n"
+"SELECT sub_tree_root_id, tree_level, parent_fk, child_fk FROM tree_cte_nr; \n"
;
String WITH_QUERY = "SELECT * FROM v_my_nr_tree";
int maxRetries = 4;
String[] expectedRowData =new String[]{
"|1|0|null|1",
"|11|0|1|11",
"|111|0|11|111",
"|12|0|1|12",
"|121|0|12|121",
};
String[] expectedColumnNames =new String[]{"SUB_TREE_ROOT_ID","TREE_LEVEL","PARENT_FK","CHILD_FK"};
int expectedNumbeOfRows = 5;
testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumbeOfRows, SETUP_SQL,
WITH_QUERY);
}
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论