提交 5cad03b9 authored 作者: Owner's avatar Owner

Fix and test - but issue with meta lock and SYS )sysSession exist

上级 6e4dcce6
......@@ -1137,10 +1137,11 @@ public class Parser {
String[] querySQLOutput = new String[]{null};
List<Column> columnTemplateList = createQueryColumnTemplateList(null, command.getQuery(), querySQLOutput);
TableView temporarySourceTableView = createTemporarySessionView(
Table temporarySourceTableView = createTemporarySessionView(
command.getQueryAlias(), querySQLOutput[0],
columnTemplateList, false/* no recursion */,
false/* do not add to session */);
false/* do not add to session */,
false /* isPersistent */);
TableFilter sourceTableFilter = new TableFilter(session,
temporarySourceTableView, command.getQueryAlias(),
rightsChecked, (Select) command.getQuery(), 0, null);
......@@ -1450,8 +1451,8 @@ public class Parser {
}
}
}
// inherit alias for temporary views (usually CTE's) from table name
if(table.isView() && table.isTemporary() && alias==null){
// inherit alias for temporary views (usually CTE's) or explicity CTEs from table name
if(table.isView() && table.isTableExpression() && alias==null){
alias = table.getName();
}
return new TableFilter(session, table, alias, rightsChecked,
......@@ -1920,25 +1921,15 @@ public class Parser {
}
private Query parseSelect() {
// This method and its subroutines sometimes resets the schema name - the try-finally block
// makes sure it is reverted if nulled
//String savedSchemaName = schemaName;
Query command = null;
//try{
int paramIndex = parameters.size();
command = parseSelectUnion();
ArrayList<Parameter> params = New.arrayList();
for (int i = paramIndex, size = parameters.size(); i < size; i++) {
params.add(parameters.get(i));
}
command.setParameterList(params);
command.init();
//}
//finally{
//if(schemaName==null){
// schemaName = savedSchemaName;
//}
//}
int paramIndex = parameters.size();
command = parseSelectUnion();
ArrayList<Parameter> params = New.arrayList();
for (int i = paramIndex, size = parameters.size(); i < size; i++) {
params.add(parameters.get(i));
}
command.setParameterList(params);
command.init();
return command;
}
......@@ -5137,8 +5128,19 @@ public class Parser {
private Prepared parseWith() {
List<TableView> viewsCreated = new ArrayList<>();
readIf("RECURSIVE");
// this WITH statement might not be temporary - allow keyword to tell us that
boolean isPersistent = readIf("PERSISTENT");
// this WITH statement might not be temporary - it my part of a persistent view
// as in CREATE VIEW abc AS WITH
if(session.isParsingView()){
isPersistent = true;
}
do {
viewsCreated.add(parseSingleCommonTableExpression());
TableView newView = parseSingleCommonTableExpression(isPersistent);
viewsCreated.add(newView);
} while (readIf(","));
Prepared p = null;
......@@ -5179,17 +5181,20 @@ public class Parser {
WITH_STATEMENT_SUPPORTS_LIMITED_STATEMENTS);
}
// clean up temp views starting with last to first (in case of
// dependencies)
Collections.reverse(viewsCreated);
p.setCteCleanups(viewsCreated);
// clean up temporary views starting with last to first (in case of
// dependencies) - but only if they are not persistent
if(!isPersistent){
Collections.reverse(viewsCreated);
p.setCteCleanups(viewsCreated);
}
return p;
}
private TableView parseSingleCommonTableExpression() {
private TableView parseSingleCommonTableExpression(boolean isPersistent) {
Session targetSession = session; //isPersistent ? database.getSystemSession() :
String tempViewName = readIdentifierWithSchema();
Schema schema = getSchema();
Table recursiveTable;
Table recursiveTable=null;
ArrayList<Column> columns = New.arrayList();
String[] cols = null;
......@@ -5203,49 +5208,60 @@ public class Parser {
columns.add(new Column(c, Value.STRING));
}
}
Table old = session.findLocalTempTable(tempViewName);
if (old != null) {
if (!(old instanceof TableView)) {
Table oldViewFound = targetSession.findLocalTempTable(tempViewName);
if (oldViewFound != null && !isPersistent) {
if (!(oldViewFound instanceof TableView)) {
throw DbException.get(ErrorCode.TABLE_OR_VIEW_ALREADY_EXISTS_1,
tempViewName);
}
TableView tv = (TableView) old;
TableView tv = (TableView) oldViewFound;
if (!tv.isTableExpression()) {
throw DbException.get(ErrorCode.TABLE_OR_VIEW_ALREADY_EXISTS_1,
tempViewName);
}
session.removeLocalTempTable(old);
targetSession.removeLocalTempTable(oldViewFound);
oldViewFound=null;
}
// this table is created as a work around because recursive
// table expressions need to reference something that look like
// themselves
// to work (its removed after creation in this method)
CreateTableData data = new CreateTableData();
data.id = database.allocateObjectId();
data.columns = columns;
data.tableName = tempViewName;
data.temporary = true;
data.persistData = true;
data.persistIndexes = false;
data.create = true;
data.session = session;
recursiveTable = schema.createTable(data);
session.addLocalTempTable(recursiveTable);
if(oldViewFound == null){
CreateTableData recursiveTableData = new CreateTableData();
recursiveTableData.id = database.allocateObjectId();
recursiveTableData.columns = columns;
recursiveTableData.tableName = tempViewName;
recursiveTableData.temporary = !isPersistent;
recursiveTableData.persistData = true;
recursiveTableData.persistIndexes = isPersistent;
recursiveTableData.create = true;
recursiveTableData.session = targetSession;
recursiveTable = schema.createTable(recursiveTableData);
targetSession.addLocalTempTable(recursiveTable);
}
List<Column> columnTemplateList;
String[] querySQLOutput = new String[]{null};
try {
read("AS");
read("(");
Query withQuery = parseSelect();
//withQuery.setSession(targetSession);
read(")");
columnTemplateList = createQueryColumnTemplateList(cols, withQuery, querySQLOutput);
} finally {
session.removeLocalTempTable(recursiveTable);
if(recursiveTable!=null){
targetSession.removeLocalTempTable(recursiveTable);
}
}
// If it's persistent, a CTE and a TableView - return existing one
if(oldViewFound!=null && isPersistent && oldViewFound instanceof TableView && oldViewFound.isTableExpression()){
return (TableView) oldViewFound;
}
TableView view = createTemporarySessionView(tempViewName,
querySQLOutput[0], columnTemplateList,
true/* allowRecursiveQueryDetection */, true);
true/* allowRecursiveQueryDetection */, true, isPersistent);
return view;
}
......@@ -5290,25 +5306,28 @@ public class Parser {
}
private TableView createTemporarySessionView(String tempViewName, String querySQL,
List<Column> columnTemplateList, boolean allowRecursiveQueryDetection, boolean addViewToSession) {
List<Column> columnTemplateList, boolean allowRecursiveQueryDetection,
boolean addViewToSession, boolean isPersistent) {
Session targetSession = session; //isPersistent ? database.getSystemSession() :
Schema schema = getSchemaWithDefault();
int id = database.allocateObjectId();
// No easy way to determine if this is a recursive query up front, so we just compile
// it twice - once without the flag set, and if we didn't see a recursive term,
// then we just compile it again.
TableView view = new TableView(schema, id, tempViewName, querySQL,
parameters, columnTemplateList.toArray(new Column[0]), session,
parameters, columnTemplateList.toArray(new Column[0]), targetSession,
allowRecursiveQueryDetection, false);
if (!view.isRecursiveQueryDetected() && allowRecursiveQueryDetection) {
targetSession.removeLocalTempTable(view);
view = new TableView(schema, id, tempViewName, querySQL, parameters,
columnTemplateList.toArray(new Column[0]), session,
columnTemplateList.toArray(new Column[0]), targetSession,
false/* recursive */, false);
}
view.setTableExpression(true);
view.setTemporary(true);
view.setTemporary(!isPersistent);
view.setHidden(true);
if(addViewToSession){
session.addLocalTempTable(view);
targetSession.addLocalTempTable(view);
}
view.setOnCommitDrop(false);
return view;
......
......@@ -100,9 +100,10 @@ public class CreateView extends SchemaCommand {
querySQL = select.getPlanSQL();
}
// The view creates a Prepared command object, which belongs to a
// session, so we pass the system session down.
Session sysSession = db.getSystemSession();
synchronized (sysSession) {
// session, so we pass the system session (not the current session) down.
//Session viewSession = select.isTableExpression()!=null ? select.getSession() : db.getSystemSession();
Session viewSession = db.getSystemSession();
synchronized (viewSession) {
try {
Column[] columnTemplates = null;
if (columnNames != null) {
......@@ -114,15 +115,15 @@ public class CreateView extends SchemaCommand {
if (view == null) {
Schema schema = session.getDatabase().getSchema(
session.getCurrentSchemaName());
sysSession.setCurrentSchema(schema);
viewSession.setCurrentSchema(schema);
view = new TableView(getSchema(), id, viewName, querySQL, null,
columnTemplates, sysSession, false, false);
columnTemplates, viewSession, false, false);
} else {
view.replace(querySQL, columnTemplates, sysSession, false, force, false);
view.replace(querySQL, columnTemplates, viewSession, false, force, false);
view.setModified();
}
} finally {
sysSession.setCurrentSchema(db.getSchema(Constants.SCHEMA_MAIN));
viewSession.setCurrentSchema(db.getSchema(Constants.SCHEMA_MAIN));
}
}
if (comment != null) {
......
......@@ -1077,14 +1077,31 @@ public class Select extends Query {
StatementBuilder buff = new StatementBuilder();
for (TableFilter f : topFilters) {
Table t = f.getTable();
boolean isPersistent = !t.isTemporary();
if (t.isView() && ((TableView) t).isRecursive()) {
buff.append("WITH RECURSIVE ").append(t.getName()).append('(');
buff.append("WITH RECURSIVE ");
if(isPersistent){
buff.append("PERSISTENT ");
}
buff.append(t.getName()).append('(');
buff.resetCount();
for (Column c : t.getColumns()) {
buff.appendExceptFirst(",");
buff.append(c.getName());
}
buff.append(") AS ").append(t.getSQL()).append("\n");
String theSQL = t.getSQL();
if(!(theSQL.startsWith("(")&&theSQL.endsWith(")"))){
StatementBuilder buffSelect = new StatementBuilder();
buffSelect.append("( SELECT ");
buffSelect.resetCount();
for (Column c : t.getColumns()) {
buffSelect.appendExceptFirst(",");
buffSelect.append(c.getName());
}
buffSelect.append(" FROM "+t.getSQL()+") ");
theSQL = buffSelect.toString();
}
buff.append(") AS ").append(theSQL).append("\n");
}
}
buff.resetCount();
......
......@@ -47,6 +47,14 @@ MERGE INTO tableName [ ( columnName [,...] ) ]
{ VALUES { ( { DEFAULT | expression } [,...] ) } [,...] | select }
","
Updates existing rows, and insert rows that don't exist."
"Commands (DML)","MERGE USING","
MERGE INTO targetTableName [ [AS] targetAlias]
USING { ( select ) | sourceTableName }[ [AS] sourceAlias ]
ON ( expression )
[ WHEN MATCHED THEN [ update ] [ delete] ]
[ WHEN NOT MATCHED THEN insert ]
","
Updates or deletes existing rows, and insert rows that don't exist."
"Commands (DML)","RUNSCRIPT","
RUNSCRIPT FROM fileNameString scriptCompressionEncryption
[ CHARSET charsetString ]
......@@ -370,6 +378,11 @@ Switches auto commit on or off."
SET CACHE_SIZE int
","
Sets the size of the cache in KB (each KB being 1024 bytes) for the current database."
"Commands (Other)","SET COLUMN_NAMING_RULES","
SET COLUMN_NAMING_RULES = { DEFAULT | EMULATE=... | MAX_IDENTIFIER_LENGTH=... | REGULAR_EXPRESSION_MATCH_ALLOWED=... | REGULAR_EXPRESSION_MATCH_DISALLOWED=... | DEFAULT_COLUMN_NAME_PATTERN=... | GENERATE_UNIQUE_COLUMN_NAMES=...}
","
This setting controls the rules dealing with how column names are generated
in select statements."
"Commands (Other)","SET CLUSTER","
SET CLUSTER serverListString
","
......@@ -427,6 +440,10 @@ SET JAVA_OBJECT_SERIALIZER
{ null | className }
","
Sets the object used to serialize and deserialize java objects being stored in column of type OTHER."
"Commands (Other)","SET LAZY_QUERY_EXECUTION","
SET LAZY_QUERY_EXECUTION int
","
Sets the lazy query execution mode."
"Commands (Other)","SET LOG","
SET LOG int
","
......
......@@ -82,6 +82,8 @@ public abstract class Table extends SchemaObjectBase {
private boolean checkForeignKeyConstraints = true;
private boolean onCommitDrop, onCommitTruncate;
private volatile Row nullRow;
private boolean tableExpression;
public Table(Schema schema, int id, String name, boolean persistIndexes,
boolean persistData) {
......@@ -1242,5 +1244,13 @@ public abstract class Table extends SchemaObjectBase {
public boolean isMVStore() {
return false;
}
public void setTableExpression(boolean tableExpression) {
this.tableExpression = tableExpression;
}
public boolean isTableExpression() {
return tableExpression;
}
}
......@@ -56,7 +56,6 @@ public class TableView extends Table {
private User owner;
private Query topQuery;
private ResultInterface recursiveResult;
private boolean tableExpression;
private boolean isRecursiveQueryDetected;
public TableView(Schema schema, int id, String name, String querySQL,
......@@ -611,14 +610,6 @@ public class TableView extends Table {
return recursiveResult;
}
public void setTableExpression(boolean tableExpression) {
this.tableExpression = tableExpression;
}
public boolean isTableExpression() {
return tableExpression;
}
@Override
public void addDependencies(HashSet<DbObject> dependencies) {
super.addDependencies(dependencies);
......
......@@ -8,6 +8,7 @@ package org.h2.test.db;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.h2.jdbc.JdbcSQLException;
......@@ -42,6 +43,8 @@ public class TestGeneralCommonTableQueries extends TestBase {
testMerge();
testCreateTable();
testNestedSQL();
//testRecursiveTable();
testRecursiveTableInCreateView();
}
private void testSimpleSelect() throws Exception {
......@@ -469,4 +472,137 @@ public class TestGeneralCommonTableQueries extends TestBase {
conn.close();
deleteDb("commonTableExpressionQueries");
}
}
\ No newline at end of file
private void testRecursiveTable() throws Exception {
int maxRetries = 4;
String[] expectedRowData =new String[]{"|meat|null","|fruit|3","|veg|2"};
String[] expectedColumnNames =new String[]{"VAL",
"SUM(SELECT\n X\nFROM PUBLIC.\"\" BB\n /* SELECT\n SUM(1) AS X,\n A\n FROM PUBLIC.B\n /++ PUBLIC.B.tableScan ++/\n /++ WHERE A IS ?1\n ++/\n /++ scanCount: 4 ++/\n INNER JOIN PUBLIC.C\n /++ PUBLIC.C.tableScan ++/\n ON 1=1\n WHERE (A IS ?1)\n AND (B.VAL = C.B)\n GROUP BY A: A IS A.VAL\n */\n /* scanCount: 1 */\nWHERE BB.A IS A.VAL)"};
int expectedNumbeOfRows = 3;
String SETUP_SQL =
"DROP TABLE IF EXISTS A; "
+"DROP TABLE IF EXISTS B; "
+"DROP TABLE IF EXISTS C; "
+"CREATE TABLE A(VAL VARCHAR(255)); "
+"CREATE TABLE B(A VARCHAR(255), VAL VARCHAR(255)); "
+"CREATE TABLE C(B VARCHAR(255), VAL VARCHAR(255)); "
+" "
+"INSERT INTO A VALUES('fruit'); "
+"INSERT INTO B VALUES('fruit','apple'); "
+"INSERT INTO B VALUES('fruit','banana'); "
+"INSERT INTO C VALUES('apple', 'golden delicious');"
+"INSERT INTO C VALUES('apple', 'granny smith'); "
+"INSERT INTO C VALUES('apple', 'pippin'); "
+"INSERT INTO A VALUES('veg'); "
+"INSERT INTO B VALUES('veg', 'carrot'); "
+"INSERT INTO C VALUES('carrot', 'nantes'); "
+"INSERT INTO C VALUES('carrot', 'imperator'); "
+"INSERT INTO C VALUES(null, 'banapple'); "
+"INSERT INTO A VALUES('meat'); "
;
String WITH_QUERY =
"WITH BB as (SELECT \n"
+"sum(1) as X, \n"
+"a \n"
+"FROM B \n"
+"JOIN C ON B.val=C.b \n"
+"GROUP BY a) \n"
+"SELECT \n"
+"A.val, \n"
+"sum(SELECT X FROM BB WHERE BB.a IS A.val)\n"//AS SUM_X
+"FROM A \n"
+"GROUP BY A.val";
testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumbeOfRows, SETUP_SQL,
WITH_QUERY);
}
private void testRepeatedQueryWithSetup(int maxRetries, String[] expectedRowData, String[] expectedColumnNames,
int expectedNumbeOfRows, String SETUP_SQL, String WITH_QUERY) throws SQLException {
deleteDb("commonTableExpressionQueries");
Connection conn = getConnection("commonTableExpressionQueries");
PreparedStatement prep;
ResultSet rs;
for(int queryRunTries=1;queryRunTries<=maxRetries;queryRunTries++){
Statement stat = conn.createStatement();
stat.execute(SETUP_SQL);
stat.close();
prep = conn.prepareStatement(WITH_QUERY);
rs = prep.executeQuery();
for(int columnIndex = 1; columnIndex <= rs.getMetaData().getColumnCount(); columnIndex++){
assertTrue(rs.getMetaData().getColumnLabel(columnIndex)!=null);
assertEquals(expectedColumnNames[columnIndex-1],rs.getMetaData().getColumnLabel(columnIndex));
}
int rowNdx=0;
while (rs.next()) {
StringBuffer buf = new StringBuffer();
for(int columnIndex = 1; columnIndex <= rs.getMetaData().getColumnCount(); columnIndex++){
buf.append("|"+rs.getString(columnIndex));
}
assertEquals(expectedRowData[rowNdx], buf.toString());
rowNdx++;
}
assertEquals(expectedNumbeOfRows,rowNdx);
rs.close();
prep.close();
}
conn.close();
deleteDb("commonTableExpressionQueries");
}
private void testRecursiveTableInCreateView() throws Exception {
String SETUP_SQL = ""
+"DROP TABLE IF EXISTS my_tree; \n"
+"DROP VIEW IF EXISTS v_my_tree; \n"
+"CREATE TABLE my_tree ( \n"
+" id INTEGER, \n"
+" parent_fk INTEGER \n"
+"); \n"
+" \n"
+"INSERT INTO my_tree ( id, parent_fk) VALUES ( 1, NULL ); \n"
+"INSERT INTO my_tree ( id, parent_fk) VALUES ( 11, 1 ); \n"
+"INSERT INTO my_tree ( id, parent_fk) VALUES ( 111, 11 ); \n"
+"INSERT INTO my_tree ( id, parent_fk) VALUES ( 12, 1 ); \n"
+"INSERT INTO my_tree ( id, parent_fk) VALUES ( 121, 12 ); \n"
+" \n"
+"CREATE OR REPLACE VIEW v_my_tree AS \n"
+"WITH RECURSIVE tree_cte (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_tree mt \n"
+" UNION ALL \n"
+" SELECT sub_tree_root_id, mtc.tree_level + 1 AS tree_level, mtc.parent_fk, mt.id \n"
+" FROM my_tree mt \n"
+"INNER JOIN tree_cte mtc ON mtc.child_fk = mt.parent_fk \n"
+") \n"
+"SELECT sub_tree_root_id, tree_level, parent_fk, child_fk FROM tree_cte; \n"
;
String WITH_QUERY = "SELECT * FROM v_my_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",
"|1|1|null|11",
"|11|1|1|111",
"|1|1|null|12",
"|12|1|1|121",
"|1|2|null|111",
"|1|2|null|121"
};
String[] expectedColumnNames =new String[]{"SUB_TREE_ROOT_ID","TREE_LEVEL","PARENT_FK","CHILD_FK"};
int expectedNumbeOfRows = 11;
testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumbeOfRows, SETUP_SQL,
WITH_QUERY);
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论