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

Pre master merge commit

上级 b1359724
...@@ -14,3 +14,4 @@ test.out.txt ...@@ -14,3 +14,4 @@ test.out.txt
*.log *.log
target/ target/
src/main/org/h2/res/help.csv src/main/org/h2/res/help.csv
_tmp*
...@@ -5149,8 +5149,11 @@ public class Parser { ...@@ -5149,8 +5149,11 @@ public class Parser {
if(isToken("SELECT")) { if(isToken("SELECT")) {
Query query = parseSelectUnion(); Query query = parseSelectUnion();
query.setPrepareAlways(!isPersistent); query.setPrepareAlways(true);
query.setNeverLazy(true); query.setNeverLazy(true);
if(isPersistent){
query.session = session.getDatabase().getSystemSession();
}
p = query; p = query;
} }
else if(readIf("INSERT")) { else if(readIf("INSERT")) {
...@@ -5199,7 +5202,7 @@ public class Parser { ...@@ -5199,7 +5202,7 @@ public class Parser {
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(); Database db = targetSession.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
...@@ -5214,7 +5217,7 @@ public class Parser { ...@@ -5214,7 +5217,7 @@ public class Parser {
Table oldViewFound = null; Table oldViewFound = null;
if(isPersistent){ if(isPersistent){
oldViewFound = getSchema().findTableOrView(session, cteViewName); oldViewFound = getSchema().findTableOrView(targetSession, cteViewName);
} }
else{ else{
oldViewFound = targetSession.findLocalTempTable(cteViewName); oldViewFound = targetSession.findLocalTempTable(cteViewName);
...@@ -5230,8 +5233,8 @@ public class Parser { ...@@ -5230,8 +5233,8 @@ public class Parser {
cteViewName); cteViewName);
} }
if(isPersistent){ if(isPersistent){
oldViewFound.lock(session, true, true); oldViewFound.lock(targetSession, true, true);
session.getDatabase().removeSchemaObject(session, oldViewFound); targetSession.getDatabase().removeSchemaObject(targetSession, oldViewFound);
}else{ }else{
targetSession.removeLocalTempTable(oldViewFound); targetSession.removeLocalTempTable(oldViewFound);
...@@ -5256,10 +5259,10 @@ public class Parser { ...@@ -5256,10 +5259,10 @@ public class Parser {
// this gets a meta table lock that is not released // this gets a meta table lock that is not released
recursiveTable = schema.createTable(recursiveTableData); recursiveTable = schema.createTable(recursiveTableData);
if(isPersistent){ if(isPersistent){
// this unlock is to prevent beed from schema.createTable() // this unlock is to prevent lock leak from schema.createTable()
database.unlockMeta(targetSession); db.unlockMeta(targetSession);
synchronized (targetSession) { synchronized (targetSession) {
db.addSchemaObject(session, recursiveTable); db.addSchemaObject(targetSession, recursiveTable);
} }
}else{ }else{
targetSession.addLocalTempTable(recursiveTable); targetSession.addLocalTempTable(recursiveTable);
...@@ -5271,14 +5274,17 @@ public class Parser { ...@@ -5271,14 +5274,17 @@ public class Parser {
read("AS"); read("AS");
read("("); read("(");
Query withQuery = parseSelect(); Query withQuery = parseSelect();
if(isPersistent){
withQuery.session = targetSession;
}
read(")"); read(")");
columnTemplateList = createQueryColumnTemplateList(cols, withQuery, querySQLOutput); columnTemplateList = createQueryColumnTemplateList(cols, withQuery, querySQLOutput);
} finally { } finally {
if(recursiveTable!=null){ if(recursiveTable!=null){
if(isPersistent){ if(isPersistent){
recursiveTable.lock(session, true, true); recursiveTable.lock(targetSession, true, true);
session.getDatabase().removeSchemaObject(session, recursiveTable); targetSession.getDatabase().removeSchemaObject(targetSession, recursiveTable);
}else{ }else{
targetSession.removeLocalTempTable(recursiveTable); targetSession.removeLocalTempTable(recursiveTable);
...@@ -5312,7 +5318,7 @@ public class Parser { ...@@ -5312,7 +5318,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 is to receive extra 'output' field in addition to // String 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());
...@@ -5332,10 +5338,10 @@ public class Parser { ...@@ -5332,10 +5338,10 @@ public class Parser {
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) { boolean addViewToSession, boolean isPersistent) {
Session targetSession = /*isPersistent ? database.getSystemSession() :*/session; Session targetSession = isPersistent ? database.getSystemSession() : session;
Database db = session.getDatabase(); Database db = targetSession.getDatabase();
Schema schema = getSchemaWithDefault(); Schema schema = getSchemaWithDefault();
int id = database.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,
...@@ -5347,9 +5353,9 @@ public class Parser { ...@@ -5347,9 +5353,9 @@ public class Parser {
allowRecursiveQueryDetection, false /* literalsChecked */); allowRecursiveQueryDetection, false /* literalsChecked */);
if (!view.isRecursiveQueryDetected() && allowRecursiveQueryDetection) { if (!view.isRecursiveQueryDetected() && allowRecursiveQueryDetection) {
if(isPersistent){ if(isPersistent){
db.addSchemaObject(session, view); db.addSchemaObject(targetSession, view);
view.lock(session, true, true); view.lock(targetSession, true, true);
session.getDatabase().removeSchemaObject(session, view); targetSession.getDatabase().removeSchemaObject(targetSession, view);
}else{ }else{
session.removeLocalTempTable(view); session.removeLocalTempTable(view);
} }
...@@ -5364,7 +5370,9 @@ public class Parser { ...@@ -5364,7 +5370,9 @@ public class Parser {
view.setOnCommitDrop(false); view.setOnCommitDrop(false);
if(addViewToSession){ if(addViewToSession){
if(isPersistent){ if(isPersistent){
db.addSchemaObject(session, view); db.addSchemaObject(targetSession, view);
view.unlock(targetSession);
db.unlockMeta(targetSession);
} }
else{ else{
targetSession.addLocalTempTable(view); targetSession.addLocalTempTable(view);
......
...@@ -23,6 +23,7 @@ import org.h2.schema.Sequence; ...@@ -23,6 +23,7 @@ import org.h2.schema.Sequence;
import org.h2.table.Column; import org.h2.table.Column;
import org.h2.table.IndexColumn; import org.h2.table.IndexColumn;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.util.ColumnNamer;
import org.h2.util.New; import org.h2.util.New;
import org.h2.value.DataType; import org.h2.value.DataType;
import org.h2.value.Value; import org.h2.value.Value;
...@@ -230,10 +231,11 @@ public class CreateTable extends SchemaCommand { ...@@ -230,10 +231,11 @@ public class CreateTable extends SchemaCommand {
private void generateColumnsFromQuery() { private void generateColumnsFromQuery() {
int columnCount = asQuery.getColumnCount(); int columnCount = asQuery.getColumnCount();
ArrayList<Expression> expressions = asQuery.getExpressions(); ArrayList<Expression> expressions = asQuery.getExpressions();
ColumnNamer columnNamer= new ColumnNamer(session);
for (int i = 0; i < columnCount; i++) { for (int i = 0; i < columnCount; i++) {
Expression expr = expressions.get(i); Expression expr = expressions.get(i);
int type = expr.getType(); int type = expr.getType();
String name = expr.getAlias(); String name = columnNamer.getColumnName(expr,i,expr.getAlias());
long precision = expr.getPrecision(); long precision = expr.getPrecision();
int displaySize = expr.getDisplaySize(); int displaySize = expr.getDisplaySize();
DataType dt = DataType.getDataType(type); DataType dt = DataType.getDataType(type);
......
...@@ -101,6 +101,7 @@ public class DropTable extends SchemaCommand { ...@@ -101,6 +101,7 @@ public class DropTable extends SchemaCommand {
Database db = session.getDatabase(); Database db = session.getDatabase();
db.lockMeta(session); db.lockMeta(session);
db.removeSchemaObject(session, table); db.removeSchemaObject(session, table);
session.getDatabase().flushDeferredRemoveSchemaObject();
} }
if (next != null) { if (next != null) {
next.executeDrop(); next.executeDrop();
......
...@@ -67,9 +67,13 @@ public class DropView extends SchemaCommand { ...@@ -67,9 +67,13 @@ public class DropView extends SchemaCommand {
} }
} }
} }
// TODO: Where is the ConstraintReferential.CASCADE style drop processing ? It's
// supported from imported keys - but not for dependant
view.lock(session, true, true); view.lock(session, true, true);
session.getDatabase().removeSchemaObject(session, view); session.getDatabase().removeSchemaObject(session, view);
session.getDatabase().flushDeferredRemoveSchemaObject();
} }
return 0; return 0;
} }
......
...@@ -1087,8 +1087,13 @@ public class Select extends Query { ...@@ -1087,8 +1087,13 @@ public class Select extends Query {
for (TableFilter f : topFilters) { for (TableFilter f : topFilters) {
Table t = f.getTable(); Table t = f.getTable();
boolean isPersistent = !t.isTemporary(); boolean isPersistent = !t.isTemporary();
System.out.println("topFilters[]="+t.getName());
if (t.isView() && ((TableView) t).isRecursive()) { if (t.isView() && ((TableView) t).isRecursive()) {
buff.append("WITH RECURSIVE "); TableView tv = ((TableView) t);
buff.append("WITH ");
if(tv.isRecursive()){
buff.append("RECURSIVE ");
}
if(isPersistent){ if(isPersistent){
buff.append("PERSISTENT "); buff.append("PERSISTENT ");
} }
...@@ -1099,7 +1104,15 @@ public class Select extends Query { ...@@ -1099,7 +1104,15 @@ public class Select extends Query {
buff.append(c.getName()); buff.append(c.getName());
} }
String theSQL = t.getSQL(); String theSQL = t.getSQL();
if(!(theSQL.startsWith("(")&&theSQL.endsWith(")"))){ System.out.println("getPlanSQL.theSQL="+theSQL);
System.out.println("getPlanSQL.sqlStatement="+sqlStatement);
if(!sqlStatement.contains("?") && sqlStatement.toUpperCase().contains("SELECT") && session.isParsingView()){
theSQL = extractNamedCTEQueryFromSQL(t.getName(),sqlStatement);
if(!(theSQL.startsWith("(")&&theSQL.endsWith(")"))){
theSQL = "( "+theSQL+" )";
}
}
else if(!(theSQL.startsWith("(")&&theSQL.endsWith(")"))){
StatementBuilder buffSelect = new StatementBuilder(); StatementBuilder buffSelect = new StatementBuilder();
buffSelect.append("( SELECT "); buffSelect.append("( SELECT ");
buffSelect.resetCount(); buffSelect.resetCount();
...@@ -1107,7 +1120,7 @@ public class Select extends Query { ...@@ -1107,7 +1120,7 @@ public class Select extends Query {
buffSelect.appendExceptFirst(","); buffSelect.appendExceptFirst(",");
buffSelect.append(c.getName()); buffSelect.append(c.getName());
} }
buffSelect.append(" FROM "+t.getSQL()+") "); buffSelect.append(" FROM "+t.getSQL()+" ) ");
theSQL = buffSelect.toString(); theSQL = buffSelect.toString();
} }
buff.append(") AS ").append(theSQL).append("\n"); buff.append(") AS ").append(theSQL).append("\n");
...@@ -1223,6 +1236,13 @@ public class Select extends Query { ...@@ -1223,6 +1236,13 @@ public class Select extends Query {
return buff.toString(); return buff.toString();
} }
private String extractNamedCTEQueryFromSQL(String viewName, String sqlStatement) {
Table existingTableOrView = session.getDatabase().getSchema(session.getCurrentSchemaName()).findTableOrView(session, viewName);
TableView existingView = (TableView) existingTableOrView;
System.out.println("existingView.getSQL()="+existingView.getSQL());
return existingView.getSQL();
}
public void setHaving(Expression having) { public void setHaving(Expression having) {
this.having = having; this.having = having;
} }
......
...@@ -11,6 +11,8 @@ import java.util.ArrayList; ...@@ -11,6 +11,8 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import java.util.StringTokenizer; import java.util.StringTokenizer;
...@@ -202,6 +204,7 @@ public class Database implements DataHandler { ...@@ -202,6 +204,7 @@ public class Database implements DataHandler {
private int queryStatisticsMaxEntries = Constants.QUERY_STATISTICS_MAX_ENTRIES; private int queryStatisticsMaxEntries = Constants.QUERY_STATISTICS_MAX_ENTRIES;
private QueryStatisticsData queryStatisticsData; private QueryStatisticsData queryStatisticsData;
private RowFactory rowFactory = RowFactory.DEFAULT; private RowFactory rowFactory = RowFactory.DEFAULT;
private ConcurrentHashMap<SchemaObject, Session> removeSchemaObjectQueue = new ConcurrentHashMap<>();
public Database(ConnectionInfo ci, String cipher) { public Database(ConnectionInfo ci, String cipher) {
String name = ci.getName(); String name = ci.getName();
...@@ -917,6 +920,27 @@ public class Database implements DataHandler { ...@@ -917,6 +920,27 @@ public class Database implements DataHandler {
return wasLocked; return wasLocked;
} }
/**
* Lock the metadata table for updates - but don't wait if it's already locked...
*
* @param session the session
* @return whether it was already locked before by this session - or null if locked by other session
*/
public Boolean lockMetaNoWait(Session session) {
// this method can not be synchronized on the database object,
// as unlocking is also synchronized on the database object -
// so if locking starts just before unlocking, locking could
// never be successful
if (meta == null) {
return true;
}
if(meta.isLockedExclusively() && ! meta.isLockedExclusivelyBy(session)){
return null;
}
boolean wasLocked = meta.lock(session, true, true);
return wasLocked;
}
/** /**
* Unlock the metadata table. * Unlock the metadata table.
* *
...@@ -1864,7 +1888,12 @@ public class Database implements DataHandler { ...@@ -1864,7 +1888,12 @@ public class Database implements DataHandler {
} }
} }
checkWritingAllowed(); checkWritingAllowed();
lockMeta(session); Boolean wasLocked = lockMetaNoWait(session);
if(wasLocked==null){
removeSchemaObjectQueue.put(obj,session);
System.out.println("deferred removal scheduled="+obj.getName()+",wasLocked="+wasLocked);
return;
}
synchronized (this) { synchronized (this) {
Comment comment = findComment(obj); Comment comment = findComment(obj);
if (comment != null) { if (comment != null) {
...@@ -1881,7 +1910,24 @@ public class Database implements DataHandler { ...@@ -1881,7 +1910,24 @@ public class Database implements DataHandler {
} }
obj.removeChildrenAndResources(session); obj.removeChildrenAndResources(session);
} }
System.out.println("Removing meta lock");
removeMeta(session, id); removeMeta(session, id);
flushDeferredRemoveSchemaObject();
}
}
public void flushDeferredRemoveSchemaObject() {
Iterator<Entry<SchemaObject, Session>> i = removeSchemaObjectQueue.entrySet().iterator();
while(i.hasNext()){
Entry<SchemaObject, Session> pair = i.next();
i.remove();
System.out.println("re-attempting deferred removal="+pair.getKey().getName()+",size="+removeSchemaObjectQueue.size());
removeSchemaObject(pair.getValue(),pair.getKey());
if(!removeSchemaObjectQueue.contains(pair.getKey())){
System.out.println("completed deferred removal="+pair.getKey().getName()+",size="+removeSchemaObjectQueue.size());
}
} }
} }
......
...@@ -49,6 +49,14 @@ import org.h2.value.Value; ...@@ -49,6 +49,14 @@ import org.h2.value.Value;
*/ */
public class MVTable extends TableBase { public class MVTable extends TableBase {
private static final String TRACE_LOCK_OK = "ok";
private static final String TRACE_LOCK_WAITING_FOR = "waiting for";
private static final String TRACE_LOCK_REQUESTING_FOR = "requesting for";
private static final String TRACE_LOCK_TIMEOUT_AFTER = "timeout after ";
private static final String TRACE_LOCK_UNLOCK = "unlock";
private static final String TRACE_LOCK_ADDED_FOR = "added for";
private static final String TRACE_LOCK_ADD_UPGRADED_FOR = "add (upgraded) for ";
/** /**
* The table name this thread is waiting to lock. * The table name this thread is waiting to lock.
*/ */
...@@ -80,6 +88,7 @@ public class MVTable extends TableBase { ...@@ -80,6 +88,7 @@ public class MVTable extends TableBase {
private final ArrayList<Index> indexes = New.arrayList(); private final ArrayList<Index> indexes = New.arrayList();
private volatile long lastModificationId; private volatile long lastModificationId;
private volatile Session lockExclusiveSession; private volatile Session lockExclusiveSession;
private volatile Throwable lockExclusiveSessionStackTrace;
// using a ConcurrentHashMap as a set // using a ConcurrentHashMap as a set
private final ConcurrentHashMap<Session, Session> lockSharedSessions = private final ConcurrentHashMap<Session, Session> lockSharedSessions =
...@@ -192,7 +201,7 @@ public class MVTable extends TableBase { ...@@ -192,7 +201,7 @@ public class MVTable extends TableBase {
} }
private void doLock1(Session session, int lockMode, boolean exclusive) { private void doLock1(Session session, int lockMode, boolean exclusive) {
traceLock(session, exclusive, "requesting for"); traceLock(session, exclusive, TRACE_LOCK_REQUESTING_FOR);
// don't get the current time unless necessary // don't get the current time unless necessary
long max = 0; long max = 0;
boolean checkDeadlock = false; boolean checkDeadlock = false;
...@@ -219,11 +228,11 @@ public class MVTable extends TableBase { ...@@ -219,11 +228,11 @@ public class MVTable extends TableBase {
max = now + TimeUnit.MILLISECONDS.toNanos(session.getLockTimeout()); max = now + TimeUnit.MILLISECONDS.toNanos(session.getLockTimeout());
} else if (now >= max) { } else if (now >= max) {
traceLock(session, exclusive, traceLock(session, exclusive,
"timeout after " + session.getLockTimeout()); TRACE_LOCK_TIMEOUT_AFTER + session.getLockTimeout());
throw DbException.get(ErrorCode.LOCK_TIMEOUT_1, getName()); throw DbException.get(ErrorCode.LOCK_TIMEOUT_1, getName());
} }
try { try {
traceLock(session, exclusive, "waiting for"); traceLock(session, exclusive, TRACE_LOCK_WAITING_FOR);
if (database.getLockMode() == Constants.LOCK_MODE_TABLE_GC) { if (database.getLockMode() == Constants.LOCK_MODE_TABLE_GC) {
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
long free = Runtime.getRuntime().freeMemory(); long free = Runtime.getRuntime().freeMemory();
...@@ -251,7 +260,7 @@ public class MVTable extends TableBase { ...@@ -251,7 +260,7 @@ public class MVTable extends TableBase {
if (exclusive) { if (exclusive) {
if (lockExclusiveSession == null) { if (lockExclusiveSession == null) {
if (lockSharedSessions.isEmpty()) { if (lockSharedSessions.isEmpty()) {
traceLock(session, exclusive, "added for"); traceLock(session, exclusive, TRACE_LOCK_ADDED_FOR);
session.addLock(this); session.addLock(this);
lockExclusiveSession = session; lockExclusiveSession = session;
if (SysProperties.THREAD_DEADLOCK_DETECTOR) { if (SysProperties.THREAD_DEADLOCK_DETECTOR) {
...@@ -263,7 +272,7 @@ public class MVTable extends TableBase { ...@@ -263,7 +272,7 @@ public class MVTable extends TableBase {
return true; return true;
} else if (lockSharedSessions.size() == 1 && } else if (lockSharedSessions.size() == 1 &&
lockSharedSessions.containsKey(session)) { lockSharedSessions.containsKey(session)) {
traceLock(session, exclusive, "add (upgraded) for "); traceLock(session, exclusive, TRACE_LOCK_ADD_UPGRADED_FOR);
lockExclusiveSession = session; lockExclusiveSession = session;
if (SysProperties.THREAD_DEADLOCK_DETECTOR) { if (SysProperties.THREAD_DEADLOCK_DETECTOR) {
if (EXCLUSIVE_LOCKS.get() == null) { if (EXCLUSIVE_LOCKS.get() == null) {
...@@ -289,7 +298,7 @@ public class MVTable extends TableBase { ...@@ -289,7 +298,7 @@ public class MVTable extends TableBase {
} }
} }
if (!lockSharedSessions.containsKey(session)) { if (!lockSharedSessions.containsKey(session)) {
traceLock(session, exclusive, "ok"); traceLock(session, exclusive, TRACE_LOCK_OK);
session.addLock(this); session.addLock(this);
lockSharedSessions.put(session, session); lockSharedSessions.put(session, session);
if (SysProperties.THREAD_DEADLOCK_DETECTOR) { if (SysProperties.THREAD_DEADLOCK_DETECTOR) {
...@@ -387,11 +396,26 @@ public class MVTable extends TableBase { ...@@ -387,11 +396,26 @@ public class MVTable extends TableBase {
} }
} }
private void traceLock(Session session, boolean exclusive, String s) { private void traceLock(Session session, boolean exclusive, String statusText) {
if (traceLock.isDebugEnabled()) { if (traceLock.isDebugEnabled()) {
traceLock.debug("{0} {1} {2} {3}", session.getId(), traceLock.debug("{0} {1} {2} {3}", session.getId(),
exclusive ? "exclusive write lock" : "shared read lock", s, exclusive ? "exclusive write lock" : "shared read lock", statusText,
getName()); getName());
// create a stack trace when the lock is granted so we can debug where that was...
if(statusText.equals(TRACE_LOCK_ADDED_FOR) || statusText.equals(TRACE_LOCK_ADD_UPGRADED_FOR)){
lockExclusiveSessionStackTrace = new Throwable("trace lock - lock granted stack trace");
}
// clear the stack trace of the granted lock, on unlock
if(statusText.equals(TRACE_LOCK_UNLOCK)){
lockExclusiveSessionStackTrace = null;
}
// show the stack trace where the lock was granted, if a timeout happens...
if(statusText.contains(TRACE_LOCK_TIMEOUT_AFTER) && lockExclusiveSessionStackTrace!=null){
lockExclusiveSessionStackTrace.printStackTrace();
}
} }
} }
...@@ -402,13 +426,23 @@ public class MVTable extends TableBase { ...@@ -402,13 +426,23 @@ public class MVTable extends TableBase {
@Override @Override
public boolean isLockedExclusivelyBy(Session session) { public boolean isLockedExclusivelyBy(Session session) {
return lockExclusiveSession == session; Session localSession = lockExclusiveSession;
if(localSession!=null){
System.out.println("Meta was locked by "+localSession.getId()+" tested for "+session.getId());
if(lockExclusiveSessionStackTrace!=null){
lockExclusiveSessionStackTrace.printStackTrace();
}
}
else{
System.out.println("Meta was not locked by anyone, tested for "+session.getId());
}
return localSession == session;
} }
@Override @Override
public void unlock(Session s) { public void unlock(Session s) {
if (database != null) { if (database != null) {
traceLock(s, lockExclusiveSession == s, "unlock"); traceLock(s, lockExclusiveSession == s, TRACE_LOCK_UNLOCK);
if (lockExclusiveSession == s) { if (lockExclusiveSession == s) {
lockExclusiveSession = null; lockExclusiveSession = null;
if (SysProperties.THREAD_DEADLOCK_DETECTOR) { if (SysProperties.THREAD_DEADLOCK_DETECTOR) {
......
...@@ -43,8 +43,10 @@ public class TestGeneralCommonTableQueries extends TestBase { ...@@ -43,8 +43,10 @@ public class TestGeneralCommonTableQueries extends TestBase {
testCreateTable(); testCreateTable();
testNestedSQL(); testNestedSQL();
testRecursiveTable(); testRecursiveTable();
testRecursiveTableInCreateView();
testNonRecursiveTableInCreateView(); // persistent cte tests
testPersistentNonRecursiveTableInCreateView();
testPersistentRecursiveTableInCreateView();
} }
private void testSimpleSelect() throws Exception { private void testSimpleSelect() throws Exception {
...@@ -479,7 +481,7 @@ public class TestGeneralCommonTableQueries extends TestBase { ...@@ -479,7 +481,7 @@ public class TestGeneralCommonTableQueries extends TestBase {
String[] expectedRowData =new String[]{"|meat|null","|fruit|3","|veg|2"}; String[] expectedRowData =new String[]{"|meat|null","|fruit|3","|veg|2"};
String[] expectedColumnNames =new String[]{"VAL", 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)"}; "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; int expectedNumberOfRows = 3;
String SETUP_SQL = String SETUP_SQL =
"DROP TABLE IF EXISTS A; " "DROP TABLE IF EXISTS A; "
...@@ -516,64 +518,13 @@ public class TestGeneralCommonTableQueries extends TestBase { ...@@ -516,64 +518,13 @@ public class TestGeneralCommonTableQueries extends TestBase {
+"GROUP BY A.val"; +"GROUP BY A.val";
testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumbeOfRows, SETUP_SQL, testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumberOfRows, SETUP_SQL,
WITH_QUERY, maxRetries-1); WITH_QUERY, maxRetries-1);
} }
private void testRepeatedQueryWithSetup(int maxRetries, String[] expectedRowData, String[] expectedColumnNames,
int expectedNumbeOfRows, String SETUP_SQL, String WITH_QUERY, int closeAndReopenDatabaseConnectionOnIteration) throws SQLException {
deleteDb("commonTableExpressionQueries");
Connection conn = getConnection("commonTableExpressionQueries");
PreparedStatement prep;
ResultSet rs;
for(int queryRunTries=1;queryRunTries<=maxRetries;queryRunTries++){
System.out.println("Iteration #"+queryRunTries);
Statement stat = conn.createStatement(); private void testPersistentRecursiveTableInCreateView() throws Exception {
stat.execute(SETUP_SQL); String SETUP_SQL = "--SET TRACE_LEVEL_SYSTEM_OUT 3;\n"
stat.close();
// close and re-open connection for one iteration to make sure the query work between connections
if(queryRunTries==closeAndReopenDatabaseConnectionOnIteration){
System.out.println("Reconnecting to database on iteration#"+queryRunTries+" of "+maxRetries);
conn.close();
conn = getConnection("commonTableExpressionQueries");
}
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 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"
...@@ -588,7 +539,7 @@ public class TestGeneralCommonTableQueries extends TestBase { ...@@ -588,7 +539,7 @@ public class TestGeneralCommonTableQueries extends TestBase {
+"INSERT INTO my_tree ( id, parent_fk) VALUES ( 121, 12 ); \n" +"INSERT INTO my_tree ( id, parent_fk) VALUES ( 121, 12 ); \n"
+" \n" +" \n"
+"CREATE OR REPLACE VIEW v_my_tree AS \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" +"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" +" 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" +" FROM my_tree mt \n"
+" UNION ALL \n" +" UNION ALL \n"
...@@ -615,11 +566,11 @@ public class TestGeneralCommonTableQueries extends TestBase { ...@@ -615,11 +566,11 @@ public class TestGeneralCommonTableQueries extends TestBase {
"|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"};
int expectedNumbeOfRows = 11; int expectedNumberOfRows = 11;
testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumbeOfRows, SETUP_SQL, testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumberOfRows, SETUP_SQL,
WITH_QUERY, maxRetries-1); WITH_QUERY, maxRetries-1);
} }
private void testNonRecursiveTableInCreateView() throws Exception { private void testPersistentNonRecursiveTableInCreateView() throws Exception {
String SETUP_SQL = "" String SETUP_SQL = ""
+"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"
...@@ -653,8 +604,61 @@ public class TestGeneralCommonTableQueries extends TestBase { ...@@ -653,8 +604,61 @@ public class TestGeneralCommonTableQueries extends TestBase {
"|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"};
int expectedNumbeOfRows = 5; int expectedNumberOfRows = 5;
testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumbeOfRows, SETUP_SQL, testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumberOfRows, SETUP_SQL,
WITH_QUERY, maxRetries-1); WITH_QUERY, maxRetries-1);
} }
private void testRepeatedQueryWithSetup(int maxRetries, String[] expectedRowData, String[] expectedColumnNames,
int expectedNumbeOfRows, String SETUP_SQL, String WITH_QUERY, int closeAndReopenDatabaseConnectionOnIteration) throws SQLException {
deleteDb("commonTableExpressionQueries");
Connection conn = getConnection("commonTableExpressionQueries");
PreparedStatement prep;
ResultSet rs;
for(int queryRunTries=1;queryRunTries<=maxRetries;queryRunTries++){
System.out.println("Iteration #"+queryRunTries);
Statement stat = conn.createStatement();
stat.execute(SETUP_SQL);
stat.close();
// close and re-open connection for one iteration to make sure the query work between connections
if(queryRunTries==closeAndReopenDatabaseConnectionOnIteration){
System.out.println("Reconnecting to database on iteration#"+queryRunTries+" of "+maxRetries);
conn.close();
conn = getConnection("commonTableExpressionQueries");
}
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");
}
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论