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

Merged latest from master

......@@ -1991,7 +1991,7 @@ The following options are supported:
""fieldDelimiter"" (a double quote by default),
""fieldSeparator"" (a comma by default)),
""fieldSeparator"" (a comma by default),
""lineComment"" (disabled by default),
......
......@@ -49,7 +49,7 @@ http://www.h2database.com/html/roadmap.html
<p>
The development of H2 was started in May 2004,
but it was first published on December 14th 2005.
The main author of H2, Thomas Mueller, is also the original developer of Hypersonic SQL.
The original author of H2, Thomas Mueller, is also the original developer of Hypersonic SQL.
In 2001, he joined PointBase Inc. where he wrote PointBase Micro, a commercial Java SQL database.
At that point, he had to discontinue Hypersonic SQL. The HSQLDB Group was formed
to continued to work on the Hypersonic SQL codebase.
......@@ -90,8 +90,12 @@ Java implementations (such as Swing) are not used, or only used for optional fea
<h2 id="supporters">Supporters</h2>
<p>
Many thanks for those who reported bugs, gave valuable feedback,
spread the word, and translated this project. Also many thanks to the donors.
spread the word, and translated this project.
</p>
<p>
Also many thanks to the donors.
To become a donor, use PayPal (at the very bottom of the main web page).
Donators are:
</p>
<ul>
<li>Martin Wildam, Austria
......@@ -106,6 +110,7 @@ To become a donor, use PayPal (at the very bottom of the main web page).
</li><li><a href="http://lumber-mill.co.jp">Lumber-mill, Inc., Japan</a>
</li><li><a href="http://www.stockmarketeye.com">StockMarketEye, USA</a>
</li><li><a href="http://www.eckenfelder.de">Eckenfelder GmbH &amp; Co.KG, Germany</a>
</li><li>Jun Iyama, Japan
</li><li>Steven Branda, USA
</li><li>Anthony Goubard, Netherlands
</li><li>Richard Hickey, USA
......@@ -114,7 +119,6 @@ To become a donor, use PayPal (at the very bottom of the main web page).
</li><li>Donald Bleyl, USA
</li><li>Frank Berger, Germany
</li><li>Florent Ramiere, France
</li><li>Jun Iyama, Japan
</li><li>Antonio Casqueiro, Portugal
</li><li>Oliver Computing LLC, USA
</li><li>Harpal Grover Consulting Inc., USA
......@@ -134,7 +138,6 @@ To become a donor, use PayPal (at the very bottom of the main web page).
</li><li>Gustav Trede, Sweden
</li><li>Joonas Pulakka, Finland
</li><li>Bjorn Darri Sigurdsson, Iceland
</li><li>Iyama Jun, Japan
</li><li>Gray Watson, USA
</li><li>Erik Dick, Germany
</li><li>Pengxiang Shao, China
......@@ -166,7 +169,13 @@ To become a donor, use PayPal (at the very bottom of the main web page).
</li><li>Cristan Meijer, Netherlands
</li><li>Adam McMahon, USA
</li><li>F&aacute;bio Gomes Lisboa Gomes, Brasil
</li><li><a href="http://tao.works">Sam Blume, Switzerland</a>
</li><li>Lyderic Landry, England
</li><li>Mederp, Morocco
</li><li>Joaquim Golay, Switzerland
</li><li>Clemens Quoss, Germany
</li><li>Kervin Pierre, USA
</li><li>Jake Bellotti, Australia
</li><li>Arun Chittanoor, USA
</li></ul>
<!-- [close] { --></div></td></tr></table><!-- } --><!-- analytics --></body></html>
......
......@@ -670,6 +670,14 @@ Tune Backup</a><br />
Easy-to-use backup solution for your iTunes library.
</p>
<p><a href="http://www.timewriter.com">
TimeWriter</a><br />
TimeWriter is a very flexible program for time administration / time tracking.
The older versions used dBase tables.
The new version 5 is completely rewritten, now using the H2 database.
TimeWriter is delivered in Dutch and English.
</p>
<p><a href="http://www.weblica.ch">
weblica</a><br />
Desktop CMS.
......
......@@ -5346,8 +5346,9 @@ public class Parser {
ArrayList<Expression> withExpressions = theQuery.getExpressions();
for (int i = 0; i < withExpressions.size(); ++i) {
Expression columnExp = withExpressions.get(i);
// use the passed in column name if supplied, otherwise use alias (if found) otherwise use column name
// derived from column expression
// use the passed in column name if supplied, otherwise use alias
// (if found) otherwise use column name derived from column
// expression
String columnName = columnNamer.getColumnName(columnExp,i,cols);
columnTemplateList.add(new Column(columnName,
columnExp.getType()));
......
......@@ -329,9 +329,10 @@ public class AlterTableAddConstraint extends SchemaCommand {
}
return null;
}
// all cols must be in the index key, the order doesn't matter and there must be no other fields in the index key
// all cols must be in the index key, the order doesn't matter and there
// must be no other fields in the index key
private static boolean canUseUniqueIndex(Index idx, Table table,
IndexColumn[] cols) {
if (idx.getTable() != table || !idx.getIndexType().isUnique()) {
......@@ -350,9 +351,9 @@ public class AlterTableAddConstraint extends SchemaCommand {
for (IndexColumn c : cols) {
colsSet.add(c.column);
}
return colsSet.equals(indexColsSet);
}
}
private static boolean canUseIndex(Index existingIndex, Table table,
IndexColumn[] cols, boolean moreColumnsOk) {
......
......@@ -274,12 +274,9 @@ public class AlterTableAlterColumn extends SchemaCommand {
throw DbException.get(ErrorCode.VIEW_IS_INVALID_2, e, getSQL(), e.getMessage());
}
String tableName = table.getName();
ArrayList<TableView> views = table.getViews();
if (views != null) {
views = New.arrayList(views);
for (TableView view : views) {
table.removeView(view);
}
ArrayList<TableView> dependentViews = new ArrayList<>(table.getDependentViews());
for (TableView view : dependentViews) {
table.removeDependentView(view);
}
execute("DROP TABLE " + table.getSQL() + " IGNORE", true);
db.renameSchemaObject(session, newTable, tableName);
......@@ -306,11 +303,9 @@ public class AlterTableAlterColumn extends SchemaCommand {
db.renameSchemaObject(session, so, name);
}
}
if (views != null) {
for (TableView view : views) {
String sql = view.getCreateSQL(true, true);
execute(sql, true);
}
for (TableView view : dependentViews) {
String sql = view.getCreateSQL(true, true);
execute(sql, true);
}
}
......
......@@ -133,23 +133,7 @@ public class Analyze extends DefineCommand {
columns[j].setSelectivity(selectivity);
}
}
if (manual) {
db.updateMeta(session, table);
} else {
Session sysSession = db.getSystemSession();
if (sysSession != session) {
// if the current session is the system session
// (which is the case if we are within a trigger)
// then we can't update the statistics because
// that would unlock all locked objects
synchronized (sysSession) {
// can't take the db lock yet, updateMeta needs to call
// lockMeta, and then it will take the db lock
db.updateMeta(sysSession, table);
sysSession.commit(true);
}
}
}
db.updateMeta(session, table);
}
public void setTop(int top) {
......
......@@ -9,7 +9,6 @@ import java.util.ArrayList;
import org.h2.api.ErrorCode;
import org.h2.command.CommandInterface;
import org.h2.command.dml.Query;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.expression.Parameter;
......@@ -99,33 +98,19 @@ 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 (not the current session) down.
// Why is the SYS session used here ? I think it might cause a problem...
Session sysSession = db.getSystemSession();
synchronized (sysSession) {
try {
Column[] columnTemplates = null;
if (columnNames != null) {
columnTemplates = new Column[columnNames.length];
for (int i = 0; i < columnNames.length; ++i) {
columnTemplates[i] = new Column(columnNames[i], Value.UNKNOWN);
}
}
if (view == null) {
Schema schema = session.getDatabase().getSchema(
session.getCurrentSchemaName());
sysSession.setCurrentSchema(schema);
view = new TableView(getSchema(), id, viewName, querySQL, null,
columnTemplates, sysSession, false, false);
} else {
view.replace(querySQL, columnTemplates, sysSession, false, force, false);
view.setModified();
}
} finally {
sysSession.setCurrentSchema(db.getSchema(Constants.SCHEMA_MAIN));
Column[] columnTemplates = null;
if (columnNames != null) {
columnTemplates = new Column[columnNames.length];
for (int i = 0; i < columnNames.length; ++i) {
columnTemplates[i] = new Column(columnNames[i], Value.UNKNOWN);
}
}
if (view == null) {
view = new TableView(getSchema(), id, viewName, querySQL, null, columnTemplates, session, false, false);
} else {
view.replace(querySQL, columnTemplates, session, false, force, false);
view.setModified();
}
if (comment != null) {
view.setComment(comment);
}
......
......@@ -5,8 +5,7 @@
*/
package org.h2.command.ddl;
import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList;
import org.h2.api.ErrorCode;
import org.h2.command.CommandInterface;
import org.h2.constraint.ConstraintReferential;
......@@ -74,10 +73,10 @@ public class DropTable extends SchemaCommand {
throw DbException.get(ErrorCode.CANNOT_DROP_TABLE_1, tableName);
}
if (dropAction == ConstraintReferential.RESTRICT) {
ArrayList<TableView> views = table.getViews();
if (views != null && views.size() > 0) {
CopyOnWriteArrayList<TableView> dependentViews = table.getDependentViews();
if (dependentViews != null && dependentViews.size() > 0) {
StatementBuilder buff = new StatementBuilder();
for (TableView v : views) {
for (TableView v : dependentViews) {
buff.appendExceptFirst(", ");
buff.append(v.getName());
}
......
......@@ -114,16 +114,7 @@ public class AlterSequence extends SchemaCommand {
Long inc = getLong(increment);
sequence.modify(startValue, min, max, inc);
}
// need to use the system session, so that the update
// can be committed immediately - not committing it
// would keep other transactions from using the sequence
Session sysSession = db.getSystemSession();
synchronized (sysSession) {
synchronized (db) {
db.updateMeta(sysSession, sequence);
sysSession.commit(true);
}
}
db.updateMeta(session, sequence);
return 0;
}
......
......@@ -245,7 +245,8 @@ public class MergeUsing extends Prepared {
// throw and exception if we have processed this _ROWID_ before...
if (targetRowidsRemembered.containsKey(targetRowIdValue[0])) {
throw DbException.get(ErrorCode.DUPLICATE_KEY_1,
"Merge using ON column expression, duplicate _ROWID_ target record already updated, deleted or inserted:_ROWID_="
"Merge using ON column expression, " +
"duplicate _ROWID_ target record already updated, deleted or inserted:_ROWID_="
+ targetRowIdValue[0].toString() + ":in:"
+ targetTableFilter.getTable()
+ ":conflicting source row number:"
......
......@@ -472,7 +472,8 @@ public class Constants {
public static final String SUFFIX_TRACE_FILE = ".trace.db";
/**
* How often we check to see if we need to apply a throttling delay if SET THROTTLE has been used.
* How often we check to see if we need to apply a throttling delay if SET
* THROTTLE has been used.
*/
public static final int THROTTLE_DELAY = 50;
......
......@@ -974,6 +974,12 @@ public class Database implements DataHandler {
session.unlock(meta);
}
/**
* This method doesn't actually unlock the metadata table, all it does it
* reset the debugging flags.
*
* @param session the session
*/
public void unlockMetaDebug(Session session) {
if (SysProperties.CHECK2) {
if (metaLockDebugging.get() == session) {
......
......@@ -376,7 +376,8 @@ public class Session extends SessionWithState {
* @param table the table
*/
public void removeLocalTempTable(Table table) {
// Exception thrown in org.h2.engine.Database.removeMeta if line below is missing with TestGeneralCommonTableQueries
// Exception thrown in org.h2.engine.Database.removeMeta if line below
// is missing with TestGeneralCommonTableQueries
database.lockMeta(this);
modificationId++;
localTempTables.remove(table.getName());
......@@ -678,6 +679,7 @@ public class Session extends SessionWithState {
for (Table table : tablesToAnalyze) {
Analyze.analyzeTable(this, table, rows, false);
}
database.unlockMeta(this); // analyze can lock the meta
}
tablesToAnalyze = null;
}
......@@ -984,7 +986,8 @@ public class Session extends SessionWithState {
modificationId++;
table.setModified();
it.remove();
// Exception thrown in org.h2.engine.Database.removeMeta if line below is missing with TestDeadlock
// Exception thrown in org.h2.engine.Database.removeMeta
// if line below is missing with TestDeadlock
database.lockMeta(this);
table.removeChildrenAndResources(this);
if (closeSession) {
......
......@@ -114,8 +114,7 @@ public class SessionRemote extends SessionWithState implements DataHandler {
throws IOException {
Socket socket = NetUtils.createSocket(server,
Constants.DEFAULT_TCP_PORT, ci.isSSL());
Transfer trans = new Transfer(this);
trans.setSocket(socket);
Transfer trans = new Transfer(this, socket);
trans.setSSL(ci.isSSL());
trans.init();
trans.writeInt(Constants.TCP_PROTOCOL_VERSION_6);
......
......@@ -1739,11 +1739,12 @@ public class JdbcConnection extends TraceObject
+ ");");
}
checkClosed();
// no change to property: Ignore call. This early exit fixes a problem with websphere liberty
// resetting the client info of a pooled connection to its initial values.
// no change to property: Ignore call. This early exit fixes a
// problem with websphere liberty resetting the client info of a
// pooled connection to its initial values.
if (Objects.equals(value, getClientInfo(name))) {
return;
return;
}
if (isInternalProperty(name)) {
......
......@@ -5,7 +5,6 @@
*/
package org.h2.mvstore.db;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
......
......@@ -32,7 +32,6 @@ public class Sequence extends SchemaObjectBase {
private long maxValue;
private boolean cycle;
private boolean belongsToTable;
private final Object flushSync = new Object();
private boolean writeWithMargin;
/**
......@@ -306,18 +305,16 @@ public class Sequence extends SchemaObjectBase {
}
private void flushInternal(Session session) {
synchronized (flushSync) {
final boolean metaWasLocked = database.lockMeta(session);
// just for this case, use the value with the margin
try {
writeWithMargin = true;
database.updateMeta(session, this);
} finally {
writeWithMargin = false;
}
if (!metaWasLocked) {
database.unlockMeta(session);
}
final boolean metaWasLocked = database.lockMeta(session);
// just for this case, use the value with the margin
try {
writeWithMargin = true;
database.updateMeta(session, this);
} finally {
writeWithMargin = false;
}
if (!metaWasLocked) {
database.unlockMeta(session);
}
}
......
......@@ -62,8 +62,7 @@ public class TcpServerThread implements Runnable {
TcpServerThread(Socket socket, TcpServer server, int id) {
this.server = server;
this.threadId = id;
transfer = new Transfer(null);
transfer.setSocket(socket);
transfer = new Transfer(null, socket);
}
private void trace(String s) {
......@@ -78,6 +77,11 @@ public class TcpServerThread implements Runnable {
// TODO server: should support a list of allowed databases
// and a list of allowed clients
try {
Socket socket = transfer.getSocket();
if (socket == null) {
// the transfer is already closed, prevent NPE in TcpServer#allow(Socket)
return;
}
if (!server.allow(transfer.getSocket())) {
throw DbException.get(ErrorCode.REMOTE_CONNECTION_NOT_ALLOWED);
}
......
......@@ -234,8 +234,7 @@ public class FileLock implements Runnable {
try {
Socket socket = NetUtils.createSocket(server,
Constants.DEFAULT_TCP_PORT, false);
Transfer transfer = new Transfer(null);
transfer.setSocket(socket);
Transfer transfer = new Transfer(null, socket);
transfer.init();
transfer.writeInt(Constants.TCP_PROTOCOL_VERSION_6);
transfer.writeInt(Constants.TCP_PROTOCOL_VERSION_16);
......@@ -523,10 +522,15 @@ public class FileLock implements Runnable {
trace.debug(e, "watchdog");
}
}
while (serverSocket != null) {
while (true) {
// take a copy so we don't get an NPE between checking it and using it
ServerSocket local = serverSocket;
if (local == null) {
break;
}
try {
trace.debug("watchdog accept");
Socket s = serverSocket.accept();
Socket s = local.accept();
s.close();
} catch (Exception e) {
trace.debug(e, "watchdog");
......
......@@ -10,6 +10,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import org.h2.api.ErrorCode;
import org.h2.command.Prepared;
import org.h2.constraint.Constraint;
......@@ -77,7 +78,10 @@ public abstract class Table extends SchemaObjectBase {
private ArrayList<TriggerObject> triggers;
private ArrayList<Constraint> constraints;
private ArrayList<Sequence> sequences;
private ArrayList<TableView> views; // remember which views are using this object
/**
* views that depend on this table
*/
private final CopyOnWriteArrayList<TableView> dependentViews = new CopyOnWriteArrayList<>();
private ArrayList<TableSynonym> synonyms;
private boolean checkForeignKeyConstraints = true;
private boolean onCommitDrop, onCommitTruncate;
......@@ -339,6 +343,7 @@ public abstract class Table extends SchemaObjectBase {
return null;
}
@Override
public String getCreateSQLForCopy(Table table, String quotedName) {
throw DbException.throwInternalError(toString());
}
......@@ -398,9 +403,7 @@ public abstract class Table extends SchemaObjectBase {
if (sequences != null) {
children.addAll(sequences);
}
if (views != null) {
children.addAll(views);
}
children.addAll(dependentViews);
if (synonyms != null) {
children.addAll(synonyms);
}
......@@ -518,15 +521,15 @@ public abstract class Table extends SchemaObjectBase {
}
}
public ArrayList<TableView> getViews() {
return views;
public CopyOnWriteArrayList<TableView> getDependentViews() {
return dependentViews;
}
@Override
public void removeChildrenAndResources(Session session) {
while (views != null && views.size() > 0) {
TableView view = views.get(0);
views.remove(0);
while (dependentViews.size() > 0) {
TableView view = dependentViews.get(0);
dependentViews.remove(0);
database.removeSchemaObject(session, view);
}
while (synonyms != null && synonyms.size() > 0) {
......@@ -810,10 +813,7 @@ public abstract class Table extends SchemaObjectBase {
private static void remove(ArrayList<? extends DbObject> list, DbObject obj) {
if (list != null) {
int i = list.indexOf(obj);
if (i >= 0) {
list.remove(i);
}
list.remove(obj);
}
}
......@@ -839,8 +839,8 @@ public abstract class Table extends SchemaObjectBase {
*
* @param view the view to remove
*/
public void removeView(TableView view) {
remove(views, view);
public void removeDependentView(TableView view) {
dependentViews.remove(view);
}
/**
......@@ -884,8 +884,8 @@ public abstract class Table extends SchemaObjectBase {
*
* @param view the view to add
*/
public void addView(TableView view) {
views = add(views, view);
public void addDependentView(TableView view) {
dependentViews.add(view);
}
/**
......
......@@ -58,7 +58,7 @@ public class TableView extends Table {
private Query topQuery;
private ResultInterface recursiveResult;
private boolean isRecursiveQueryDetected;
private Session session;
// private Session session;
public TableView(Schema schema, int id, String name, String querySQL,
ArrayList<Parameter> params, Column[] columnTemplates, Session session,
......@@ -99,7 +99,7 @@ public class TableView extends Table {
this.columnTemplates = columnTemplates;
this.recursive = recursive;
this.isRecursiveQueryDetected = false;
this.session = session;
//this.session = session;
index = new ViewIndex(this, querySQL, params, recursive);
initColumnsAndTables(session, literalsChecked);
}
......@@ -136,17 +136,12 @@ public class TableView extends Table {
return e;
}
}
ArrayList<TableView> views = getViews();
if (views != null) {
views = New.arrayList(views);
}
ArrayList<TableView> dependentViews = new ArrayList<>(getDependentViews());
initColumnsAndTables(session, false);
if (views != null) {
for (TableView v : views) {
DbException e = v.recompile(session, force, false);
if (e != null && !force) {
return e;
}
for (TableView v : dependentViews) {
DbException e = v.recompile(session, force, false);
if (e != null && !force) {
return e;
}
}
if (clearIndexCache) {
......@@ -157,14 +152,14 @@ public class TableView extends Table {
private void initColumnsAndTables(Session session, boolean literalsChecked) {
Column[] cols;
removeViewFromTables();
removeDependentViewFromTables();
try {
Query compiledQuery = compileViewQuery(session, querySQL, literalsChecked);
this.querySQL = compiledQuery.getPlanSQL();
tables = New.arrayList(compiledQuery.getTables());
ArrayList<Expression> expressions = compiledQuery.getExpressions();
ArrayList<Column> list = New.arrayList();
ColumnNamer columnNamer= new ColumnNamer(session);
ColumnNamer columnNamer= new ColumnNamer(session);
for (int i = 0, count = compiledQuery.getColumnCount(); i < count; i++) {
Expression expr = expressions.get(i);
String name = null;
......@@ -232,7 +227,7 @@ public class TableView extends Table {
}
setColumns(cols);
if (getId() != 0) {
addViewToTables();
addDependentViewToTables();
}
}
......@@ -423,7 +418,7 @@ public class TableView extends Table {
@Override
public void removeChildrenAndResources(Session session) {
removeViewFromTables();
removeDependentViewFromTables();
super.removeChildrenAndResources(session);
database.removeMeta(session, getId());
querySQL = null;
......@@ -507,18 +502,18 @@ public class TableView extends Table {
return null;
}
private void removeViewFromTables() {
private void removeDependentViewFromTables() {
if (tables != null) {
for (Table t : tables) {
t.removeView(this);
t.removeDependentView(this);
}
tables.clear();
}
}
private void addViewToTables() {
private void addDependentViewToTables() {
for (Table t : tables) {
t.addView(this);
t.addDependentView(this);
}
}
......@@ -696,17 +691,17 @@ public class TableView extends Table {
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){
// check if any database objects are left using this view
if(getViews().size()==0 && !isBeingDropped()){
System.out.println("Detected unused CTE: Trying to remove="+this.getName()+",session="+session.toString()+",sessionId="+session.getId());
session.getDatabase().removeSchemaObject(session,this);
}
}
}
// @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){
// // check if any database objects are left using this view
// if(getViews().size()==0 && !isBeingDropped()){
// System.out.println("Detected unused CTE: Trying to remove="+this.getName()+",session="+session.toString()+",sessionId="+session.getId());
// session.getDatabase().removeSchemaObject(session,this);
// }
// }
// }
}
......@@ -29,6 +29,7 @@ public class ColumnNamer {
}
}
}
/**
* Create a standardized column name that isn't null and doesn't have a CR/LF in it.
* @param expr the column expression
......@@ -37,6 +38,7 @@ public class ColumnNamer {
public String getColumnName(Expression expr, int indexOfColumn) {
return getColumnName(expr,indexOfColumn,(String) null);
}
/**
* Create a standardized column name that isn't null and doesn't have a CR/LF in it.
* @param columnExp the column expression
......@@ -44,9 +46,9 @@ public class ColumnNamer {
* @param columnNameOverides array of overriding column names
* @return the new column name
*/
public String getColumnName(Expression columnExp, int indexOfColumn, String[] columnNameOverides){
public String getColumnName(Expression columnExp, int indexOfColumn, String[] columnNameOverides) {
String columnNameOverride = null;
if (columnNameOverides != null && columnNameOverides.length > indexOfColumn){
if (columnNameOverides != null && columnNameOverides.length > indexOfColumn) {
columnNameOverride = columnNameOverides[indexOfColumn];
}
return getColumnName(columnExp, indexOfColumn, columnNameOverride);
......@@ -72,40 +74,43 @@ public class ColumnNamer {
}
}
// try a name from the column alias
if (columnName==null && columnExp.getAlias()!=null && !DEFAULT_COLUMN_NAME.equals(columnExp.getAlias())){
if (columnName==null && columnExp.getAlias()!=null &&
!DEFAULT_COLUMN_NAME.equals(columnExp.getAlias())) {
columnName = columnExp.getAlias();
if(!isAllowableColumnName(columnName)){
if (!isAllowableColumnName(columnName)) {
columnName = fixColumnName(columnName);
}
if(!isAllowableColumnName(columnName)){
if (!isAllowableColumnName(columnName)) {
columnName = null;
}
}
// try a name derived from the column expression SQL
if (columnName==null && columnExp.getColumnName()!=null && !DEFAULT_COLUMN_NAME.equals(columnExp.getColumnName())){
if (columnName == null && columnExp.getColumnName() != null &&
!DEFAULT_COLUMN_NAME.equals(columnExp.getColumnName())) {
columnName = columnExp.getColumnName();
if(!isAllowableColumnName(columnName)){
columnName = fixColumnName(columnName);
}
if(!isAllowableColumnName(columnName)){
columnName = null;
}
if (!isAllowableColumnName(columnName)) {
columnName = fixColumnName(columnName);
}
if (!isAllowableColumnName(columnName)) {
columnName = null;
}
}
// try a name derived from the column expression plan SQL
if (columnName==null && columnExp.getSQL()!=null && !DEFAULT_COLUMN_NAME.equals(columnExp.getSQL())){
if (columnName == null && columnExp.getSQL() != null &&
!DEFAULT_COLUMN_NAME.equals(columnExp.getSQL())) {
columnName = columnExp.getSQL();
if(!isAllowableColumnName(columnName)){
columnName = fixColumnName(columnName);
}
if(!isAllowableColumnName(columnName)){
columnName = null;
}
if (!isAllowableColumnName(columnName)) {
columnName = fixColumnName(columnName);
}
if (!isAllowableColumnName(columnName)) {
columnName = null;
}
}
// go with a innocuous default name pattern
if (columnName==null){
columnName = configuration.getDefaultColumnNamePattern().replace("$$", ""+(indexOfColumn+1));
if (columnName == null) {
columnName = configuration.getDefaultColumnNamePattern().replace("$$", "" + (indexOfColumn + 1));
}
if(existingColumnNames.contains(columnName) && configuration.isGenerateUniqueColumnNames()){
if (existingColumnNames.contains(columnName) && configuration.isGenerateUniqueColumnNames()) {
columnName = generateUniqueName(columnName);
}
existingColumnNames.add(columnName);
......@@ -115,11 +120,11 @@ public class ColumnNamer {
private String generateUniqueName(String columnName) {
String newColumnName = columnName;
int loopCount = 2;
while(existingColumnNames.contains(newColumnName)){
while (existingColumnNames.contains(newColumnName)) {
String loopCountString = "_"+loopCount;
newColumnName = columnName.substring(0,Math.min(columnName.length(), configuration.getMaxIdentiferLength()-loopCountString.length()))+loopCountString;
newColumnName = columnName.substring(0,
Math.min(columnName.length(), configuration.getMaxIdentiferLength() - loopCountString.length()))
+ loopCountString;
loopCount++;
}
return newColumnName;
......@@ -132,11 +137,11 @@ public class ColumnNamer {
return false;
}
// check size limits
if (proposedName.length() > configuration.getMaxIdentiferLength() || proposedName.length()==0){
if (proposedName.length() > configuration.getMaxIdentiferLength() || proposedName.length() == 0) {
return false;
}
Matcher match = configuration.getCompiledRegularExpressionMatchAllowed().matcher(proposedName);
if(!match.matches()){
if (!match.matches()) {
return false;
}
return true;
......
......@@ -13,7 +13,7 @@ public class ColumnNamerConfiguration {
private static final String MAX_IDENTIFIER_LENGTH = "MAX_IDENTIFIER_LENGTH = ";
private static final String EMULATE_COMMAND = "EMULATE = ";
private static final String GENERATE_UNIQUE_COLUMN_NAMES = "GENERATE_UNIQUE_COLUMN_NAMES = ";
private int maxIdentiferLength;
private String regularExpressionMatchAllowed;
private String regularExpressionMatchDisallowed;
......@@ -23,14 +23,15 @@ public class ColumnNamerConfiguration {
private Pattern compiledRegularExpressionMatchDisallowed;
public ColumnNamerConfiguration(int maxIdentiferLength, String regularExpressionMatchAllowed,
String regularExpressionMatchDisallowed, String defaultColumnNamePattern, boolean generateUniqueColumnNames) {
String regularExpressionMatchDisallowed, String defaultColumnNamePattern,
boolean generateUniqueColumnNames) {
this.maxIdentiferLength = maxIdentiferLength;
this.regularExpressionMatchAllowed = regularExpressionMatchAllowed;
this.regularExpressionMatchDisallowed = regularExpressionMatchDisallowed;
this.defaultColumnNamePattern = defaultColumnNamePattern;
this.generateUniqueColumnNames = generateUniqueColumnNames;
compiledRegularExpressionMatchAllowed = Pattern.compile(regularExpressionMatchAllowed);
compiledRegularExpressionMatchDisallowed = Pattern.compile(regularExpressionMatchDisallowed);
}
......@@ -41,8 +42,9 @@ public class ColumnNamerConfiguration {
public void setMaxIdentiferLength(int maxIdentiferLength) {
this.maxIdentiferLength = Math.max(30,maxIdentiferLength);
if(maxIdentiferLength!=getMaxIdentiferLength()){
throw DbException.getInvalidValueException("Illegal value (<30) in SET COLUMN_NAME_RULES","MAX_IDENTIFIER_LENGTH="+maxIdentiferLength);
if (maxIdentiferLength != getMaxIdentiferLength()) {
throw DbException.getInvalidValueException("Illegal value (<30) in SET COLUMN_NAME_RULES",
"MAX_IDENTIFIER_LENGTH=" + maxIdentiferLength);
}
}
......@@ -85,32 +87,35 @@ public class ColumnNamerConfiguration {
public void setCompiledRegularExpressionMatchDisallowed(Pattern compiledRegularExpressionMatchDisallowed) {
this.compiledRegularExpressionMatchDisallowed = compiledRegularExpressionMatchDisallowed;
}
public void configure(String stringValue) {
try{
if(stringValue.equalsIgnoreCase(DEFAULT_COMMAND)){
if (stringValue.equalsIgnoreCase(DEFAULT_COMMAND)) {
configure(REGULAR);
} else if(stringValue.startsWith(EMULATE_COMMAND)){
configure(ModeEnum.valueOf(unquoteString(stringValue.substring(EMULATE_COMMAND.length()))));
} else if(stringValue.startsWith(MAX_IDENTIFIER_LENGTH)){
} else if (stringValue.startsWith(EMULATE_COMMAND)) {
configure(ModeEnum.valueOf(
unquoteString(stringValue.substring(EMULATE_COMMAND.length()))));
} else if (stringValue.startsWith(MAX_IDENTIFIER_LENGTH)) {
int maxLength = Integer.parseInt(stringValue.substring(MAX_IDENTIFIER_LENGTH.length()));
setMaxIdentiferLength(maxLength);
} else if(stringValue.startsWith(GENERATE_UNIQUE_COLUMN_NAMES)){
setGenerateUniqueColumnNames(Integer.parseInt(stringValue.substring(GENERATE_UNIQUE_COLUMN_NAMES.length()))==1);
} else if(stringValue.startsWith(DEFAULT_COLUMN_NAME_PATTERN)){
setDefaultColumnNamePattern(unquoteString(stringValue.substring(DEFAULT_COLUMN_NAME_PATTERN.length())));
} else if(stringValue.startsWith(REGULAR_EXPRESSION_MATCH_ALLOWED)){
setRegularExpressionMatchAllowed(unquoteString(stringValue.substring(REGULAR_EXPRESSION_MATCH_ALLOWED.length())));
} else if(stringValue.startsWith(REGULAR_EXPRESSION_MATCH_DISALLOWED)){
setRegularExpressionMatchDisallowed(unquoteString(stringValue.substring(REGULAR_EXPRESSION_MATCH_DISALLOWED.length())));
}
else
{
throw DbException.getInvalidValueException("SET COLUMN_NAME_RULES: unknown id:"+stringValue,
} else if (stringValue.startsWith(GENERATE_UNIQUE_COLUMN_NAMES)) {
setGenerateUniqueColumnNames(
Integer.parseInt(stringValue.substring(GENERATE_UNIQUE_COLUMN_NAMES.length())) == 1);
} else if (stringValue.startsWith(DEFAULT_COLUMN_NAME_PATTERN)) {
setDefaultColumnNamePattern(
unquoteString(stringValue.substring(DEFAULT_COLUMN_NAME_PATTERN.length())));
} else if (stringValue.startsWith(REGULAR_EXPRESSION_MATCH_ALLOWED)) {
setRegularExpressionMatchAllowed(
unquoteString(stringValue.substring(REGULAR_EXPRESSION_MATCH_ALLOWED.length())));
} else if (stringValue.startsWith(REGULAR_EXPRESSION_MATCH_DISALLOWED)) {
setRegularExpressionMatchDisallowed(
unquoteString(stringValue.substring(REGULAR_EXPRESSION_MATCH_DISALLOWED.length())));
} else {
throw DbException.getInvalidValueException("SET COLUMN_NAME_RULES: unknown id:" + stringValue,
stringValue);
}
recompilePatterns();
}
//Including NumberFormatException|PatternSyntaxException
......@@ -118,7 +123,7 @@ public class ColumnNamerConfiguration {
throw DbException.getInvalidValueException("SET COLUMN_NAME_RULES:"+e.getMessage(),
stringValue);
}
}
}
private void recompilePatterns() {
......@@ -131,12 +136,12 @@ public class ColumnNamerConfiguration {
configure(REGULAR);
throw e;
}
}
}
public static ColumnNamerConfiguration getDefault(){
return new ColumnNamerConfiguration(Integer.MAX_VALUE, "(?m)(?s).+", "(?m)(?s)[\\x00]", "_UNNAMED_$$",false);
}
private static String unquoteString(String s){
if(s.startsWith("'") && s.endsWith("'")){
s = s.substring(1, s.length()-1);
......@@ -156,30 +161,31 @@ public class ColumnNamerConfiguration {
public void configure(ModeEnum modeEnum) {
switch(modeEnum){
case Oracle:
// Nonquoted identifiers can contain only alphanumeric characters
// from your database character set and the underscore (_), dollar sign ($), and pound sign (#).
// Nonquoted identifiers can contain only alphanumeric characters
// from your database character set and the underscore (_), dollar
// sign ($), and pound sign (#).
setMaxIdentiferLength(128);
setRegularExpressionMatchAllowed("(?m)(?s)\"?[A-Za-z0-9_\\$#]+\"?");
setRegularExpressionMatchDisallowed("(?m)(?s)[^A-Za-z0-9_\"\\$#]");
setDefaultColumnNamePattern("_UNNAMED_$$");
setGenerateUniqueColumnNames(false);
setDefaultColumnNamePattern("_UNNAMED_$$");
setGenerateUniqueColumnNames(false);
break;
case MSSQLServer:
// https://docs.microsoft.com/en-us/sql/sql-server/maximum-capacity-specifications-for-sql-server
setMaxIdentiferLength(128);
setRegularExpressionMatchAllowed("(?m)(?s)[A-Za-z0-9_\\[\\]]+");// allows [] around names
setRegularExpressionMatchDisallowed("(?m)(?s)[^A-Za-z0-9_\\[\\]]");
setDefaultColumnNamePattern("_UNNAMED_$$");
setGenerateUniqueColumnNames(false);
setGenerateUniqueColumnNames(false);
break;
case PostgreSQL:
setMaxIdentiferLength(63);// this default can be changed to 128 by postgres config
setRegularExpressionMatchAllowed("(?m)(?s)[A-Za-z0-9_\\$]+");
setRegularExpressionMatchDisallowed("(?m)(?s)[^A-Za-z0-9_\\$]");
setDefaultColumnNamePattern("_UNNAMED_$$");
setGenerateUniqueColumnNames(false);
setGenerateUniqueColumnNames(false);
break;
case MySQL:
......@@ -190,7 +196,7 @@ public class ColumnNamerConfiguration {
setDefaultColumnNamePattern("_UNNAMED_$$");
setGenerateUniqueColumnNames(false);
break;
default:
case REGULAR:
case DB2:
......@@ -204,7 +210,7 @@ public class ColumnNamerConfiguration {
setGenerateUniqueColumnNames(false);
break;
}
recompilePatterns();
recompilePatterns();
}
}
\ No newline at end of file
......@@ -58,17 +58,9 @@ public class Transfer {
*
* @param session the session
*/
public Transfer(SessionInterface session) {
public Transfer(SessionInterface session, Socket s) {
this.session = session;
}
/**
* Set the socket this object uses.
*
* @param s the socket
*/
public void setSocket(Socket s) {
socket = s;
this.socket = s;
}
/**
......@@ -764,8 +756,7 @@ public class Transfer {
InetAddress address = socket.getInetAddress();
int port = socket.getPort();
Socket s2 = NetUtils.createSocket(address, port, ssl);
Transfer trans = new Transfer(null);
trans.setSocket(s2);
Transfer trans = new Transfer(null, s2);
trans.setSSL(ssl);
return trans;
}
......
......@@ -55,17 +55,19 @@ public class TestCompatibilityOracle extends TestBase {
stat.execute("insert into T values(1)");
stat.execute("drop table T");
// can set NULL
stat.execute("create table T (C int not null disable)"); // can set NULL even with 'not null syntax' (oracle)
// can set NULL even with 'not null syntax' (oracle)
stat.execute("create table T (C int not null disable)");
stat.execute("insert into T values(null)");
stat.execute("drop table T");
stat.execute("create table T (C int not null enable novalidate)"); // can set NULL even with 'not null syntax' (oracle)
// can set NULL even with 'not null syntax' (oracle)
stat.execute("create table T (C int not null enable novalidate)");
stat.execute("insert into T values(null)");
stat.execute("drop table T");
// Some other variation with oracle syntax
stat.execute("create table T (C int not null)");
stat.execute("insert into T values(1)");
stat.execute("alter table T modify C not null");
stat.execute("alter table T modify C not null");
stat.execute("insert into T values(1)");
stat.execute("alter table T modify C not null enable");
stat.execute("insert into T values(1)");
......@@ -78,17 +80,19 @@ public class TestCompatibilityOracle extends TestBase {
stat.execute("alter table T modify C null enable");
stat.execute("alter table T modify C null enable validate");
stat.execute("insert into T values(null)");
stat.execute("alter table T modify C not null disable"); // can set NULL even with 'not null syntax' (oracle)
// can set NULL even with 'not null syntax' (oracle)
stat.execute("alter table T modify C not null disable");
stat.execute("insert into T values(null)");
stat.execute("alter table T modify C not null enable novalidate"); // can set NULL even with 'not null syntax' (oracle)
// can set NULL even with 'not null syntax' (oracle)
stat.execute("alter table T modify C not null enable novalidate");
stat.execute("insert into T values(null)");
stat.execute("drop table T");
conn.close();
}
private void testSpecialTypes() throws SQLException {
// Test VARCHAR, VARCHAR2 with CHAR and BYTE
// Test VARCHAR, VARCHAR2 with CHAR and BYTE
deleteDb("oracle");
Connection conn = getConnection("oracle;MODE=Oracle");
Statement stat = conn.createStatement();
......@@ -96,7 +100,7 @@ public class TestCompatibilityOracle extends TestBase {
stat.execute("alter table T add A_1 VARCHAR(1)");
stat.execute("alter table T add A_2 VARCHAR2(1)");
stat.execute("alter table T add B_1 VARCHAR(1 byte)"); // with BYTE
stat.execute("alter table T add B_2 VARCHAR2(1 byte)");
stat.execute("alter table T add B_2 VARCHAR2(1 byte)");
stat.execute("alter table T add C_1 VARCHAR(1 char)"); // with CHAR
stat.execute("alter table T add C_2 VARCHAR2(1 char)");
stat.execute("alter table T add B_255 VARCHAR(255 byte)");
......@@ -104,7 +108,7 @@ public class TestCompatibilityOracle extends TestBase {
stat.execute("drop table T");
conn.close();
}
private void testTreatEmptyStringsAsNull() throws SQLException {
deleteDb("oracle");
Connection conn = getConnection("oracle;MODE=Oracle");
......
......@@ -29,20 +29,20 @@ public class TestGeneralCommonTableQueries extends TestBase {
@Override
public void test() throws Exception {
// testSimpleSelect();
// testImpliedColumnNames();
// testChainedQuery();
// testParameterizedQuery();
// testNumberedParameterizedQuery();
// testColumnNames();
//
// testInsert();
// testUpdate();
// testDelete();
// testMerge();
// testCreateTable();
// testNestedSQL();
// testRecursiveTable();
testSimpleSelect();
testImpliedColumnNames();
testChainedQuery();
testParameterizedQuery();
testNumberedParameterizedQuery();
testColumnNames();
testInsert();
testUpdate();
testDelete();
testMerge();
testCreateTable();
testNestedSQL();
testRecursiveTable();
// turn on special locking debug
System.setProperty("h2.check2", "true");
......@@ -398,7 +398,7 @@ public class TestGeneralCommonTableQueries extends TestBase {
conn.close();
deleteDb("commonTableExpressionQueries");
}
private void testNestedSQL() throws Exception {
deleteDb("commonTableExpressionQueries");
Connection conn = getConnection("commonTableExpressionQueries");
......@@ -446,7 +446,7 @@ public class TestGeneralCommonTableQueries extends TestBase {
}
conn.close();
deleteDb("commonTableExpressionQueries");
}
}
private void testColumnNames() throws Exception {
deleteDb("commonTableExpressionQueries");
......@@ -477,7 +477,7 @@ public class TestGeneralCommonTableQueries extends TestBase {
conn.close();
deleteDb("commonTableExpressionQueries");
}
private void testRecursiveTable() throws Exception {
int maxRetries = 4;
......@@ -619,6 +619,7 @@ public class TestGeneralCommonTableQueries extends TestBase {
Connection conn = getConnection("commonTableExpressionQueries");
PreparedStatement prep;
ResultSet rs;
for(int queryRunTries=1;queryRunTries<=maxRetries;queryRunTries++){
System.out.println("Iteration #"+queryRunTries);
......@@ -643,7 +644,7 @@ public class TestGeneralCommonTableQueries extends TestBase {
assertTrue(rs.getMetaData().getColumnLabel(columnIndex)!=null);
assertEquals(expectedColumnNames[columnIndex-1],rs.getMetaData().getColumnLabel(columnIndex));
}
int rowNdx=0;
while (rs.next()) {
StringBuffer buf = new StringBuffer();
......@@ -662,6 +663,6 @@ public class TestGeneralCommonTableQueries extends TestBase {
conn.close();
deleteDb("commonTableExpressionQueries");
}
}
}
......@@ -38,89 +38,122 @@ public class TestMergeUsing extends TestBase implements Trigger {
// Simple ID,NAME inserts, target table with PK initially empty
testMergeUsing(
"CREATE TABLE PARENT(ID INT, NAME VARCHAR, PRIMARY KEY(ID) );",
"MERGE INTO PARENT AS P USING (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) ) AS S ON (P.ID = S.ID AND 1=1 AND S.ID = P.ID) WHEN MATCHED THEN UPDATE SET P.NAME = S.NAME WHERE 2 = 2 WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (S.ID, S.NAME)",
"MERGE INTO PARENT AS P USING (SELECT X AS ID, 'Marcy'||X AS NAME " +
"FROM SYSTEM_RANGE(1,2) ) AS S ON (P.ID = S.ID AND 1=1 AND S.ID = P.ID) " +
"WHEN MATCHED THEN " +
"UPDATE SET P.NAME = S.NAME WHERE 2 = 2 WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (S.ID, S.NAME)",
GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2)", 2);
// Simple NAME updates, target table missing PK
testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );",
"MERGE INTO PARENT AS P USING (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) ) AS S ON (P.ID = S.ID AND 1=1 AND S.ID = P.ID) WHEN MATCHED THEN UPDATE SET P.NAME = S.NAME||S.ID WHERE 1 = 1 WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (S.ID, S.NAME)",
"MERGE INTO PARENT AS P USING (" +
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) ) AS S " +
"ON (P.ID = S.ID AND 1=1 AND S.ID = P.ID) " +
"WHEN MATCHED THEN UPDATE SET P.NAME = S.NAME||S.ID WHERE 1 = 1 WHEN NOT MATCHED THEN " +
"INSERT (ID, NAME) VALUES (S.ID, S.NAME)",
GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X||X AS NAME FROM SYSTEM_RANGE(1,2)",
2);
// No NAME updates, WHERE clause is always false, insert clause missing
testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );",
"MERGE INTO PARENT AS P USING (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) ) AS S ON (P.ID = S.ID) WHEN MATCHED THEN UPDATE SET P.NAME = S.NAME||S.ID WHERE 1 = 2",
"MERGE INTO PARENT AS P USING (" +
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) ) AS S ON (P.ID = S.ID) " +
"WHEN MATCHED THEN UPDATE SET P.NAME = S.NAME||S.ID WHERE 1 = 2",
GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2)", 0);
// No NAME updates, no WHERE clause, insert clause missing
testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );",
"MERGE INTO PARENT AS P USING (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) ) AS S ON (P.ID = S.ID) WHEN MATCHED THEN UPDATE SET P.NAME = S.NAME||S.ID",
"MERGE INTO PARENT AS P USING (" +
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) ) AS S ON (P.ID = S.ID) " +
"WHEN MATCHED THEN UPDATE SET P.NAME = S.NAME||S.ID",
GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X||X AS NAME FROM SYSTEM_RANGE(1,2)",
2);
// Two delete updates done, no WHERE clause, insert clause missing
testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );",
"MERGE INTO PARENT AS P USING (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) ) AS S ON (P.ID = S.ID) WHEN MATCHED THEN DELETE",
"MERGE INTO PARENT AS P USING (" +
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) ) AS S ON (P.ID = S.ID) " +
"WHEN MATCHED THEN DELETE",
GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) WHERE 1=0",
2);
// One insert, one update one delete happens, target table missing PK
testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );",
"MERGE INTO PARENT AS P USING (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) ) AS S ON (P.ID = S.ID) WHEN MATCHED THEN UPDATE SET P.NAME = S.NAME||S.ID WHERE P.ID = 2 DELETE WHERE P.ID = 1 WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (S.ID, S.NAME)",
"MERGE INTO PARENT AS P USING (" +
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) ) AS S ON (P.ID = S.ID) " +
"WHEN MATCHED THEN UPDATE SET P.NAME = S.NAME||S.ID WHERE P.ID = 2 " +
"DELETE WHERE P.ID = 1 WHEN NOT MATCHED THEN " +
"INSERT (ID, NAME) VALUES (S.ID, S.NAME)",
GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X||X AS NAME FROM SYSTEM_RANGE(2,2) UNION ALL SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(3,3)",
"SELECT X AS ID, 'Marcy'||X||X AS NAME FROM SYSTEM_RANGE(2,2) UNION ALL " +
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(3,3)",
3);
// No updates happen: No insert defined, no update or delete happens due
// to ON condition failing always, target table missing PK
testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );",
"MERGE INTO PARENT AS P USING (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) ) AS S ON (P.ID = S.ID AND 1=0) WHEN MATCHED THEN UPDATE SET P.NAME = S.NAME||S.ID WHERE P.ID = 2 DELETE WHERE P.ID = 1",
"MERGE INTO PARENT AS P USING (" +
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) ) AS S ON (P.ID = S.ID AND 1=0) " +
"WHEN MATCHED THEN " +
"UPDATE SET P.NAME = S.NAME||S.ID WHERE P.ID = 2 DELETE WHERE P.ID = 1",
GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2)", 0);
// One insert, one update one delete happens, target table missing PK
testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );"
+ "CREATE TABLE SOURCE AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) );",
"MERGE INTO PARENT AS P USING SOURCE AS S ON (P.ID = S.ID) WHEN MATCHED THEN UPDATE SET P.NAME = S.NAME||S.ID WHERE P.ID = 2 DELETE WHERE P.ID = 1 WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (S.ID, S.NAME)",
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );" +
"CREATE TABLE SOURCE AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) );",
"MERGE INTO PARENT AS P USING SOURCE AS S ON (P.ID = S.ID) WHEN MATCHED THEN " +
"UPDATE SET P.NAME = S.NAME||S.ID WHERE P.ID = 2 DELETE WHERE P.ID = 1 WHEN NOT MATCHED THEN " +
"INSERT (ID, NAME) VALUES (S.ID, S.NAME)",
GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X||X AS NAME FROM SYSTEM_RANGE(2,2) UNION ALL SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(3,3)",
"SELECT X AS ID, 'Marcy'||X||X AS NAME FROM SYSTEM_RANGE(2,2) UNION ALL " +
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(3,3)",
3);
// One insert, one update one delete happens, target table missing PK,
// no source alias
testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );"
+ "CREATE TABLE SOURCE AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) );",
"MERGE INTO PARENT AS P USING SOURCE ON (P.ID = SOURCE.ID) WHEN MATCHED THEN UPDATE SET P.NAME = SOURCE.NAME||SOURCE.ID WHERE P.ID = 2 DELETE WHERE P.ID = 1 WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)",
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );" +
"CREATE TABLE SOURCE AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) );",
"MERGE INTO PARENT AS P USING SOURCE ON (P.ID = SOURCE.ID) WHEN MATCHED THEN " +
"UPDATE SET P.NAME = SOURCE.NAME||SOURCE.ID WHERE P.ID = 2 DELETE WHERE P.ID = 1 " +
"WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)",
GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X||X AS NAME FROM SYSTEM_RANGE(2,2) UNION ALL SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(3,3)",
"SELECT X AS ID, 'Marcy'||X||X AS NAME FROM SYSTEM_RANGE(2,2) UNION ALL " +
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(3,3)",
3);
// One insert, one update one delete happens, target table missing PK,
// no source or target alias
testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );"
+ "CREATE TABLE SOURCE AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) );",
"MERGE INTO PARENT USING SOURCE ON (PARENT.ID = SOURCE.ID) WHEN MATCHED THEN UPDATE SET PARENT.NAME = SOURCE.NAME||SOURCE.ID WHERE PARENT.ID = 2 DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)",
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );" +
"CREATE TABLE SOURCE AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) );",
"MERGE INTO PARENT USING SOURCE ON (PARENT.ID = SOURCE.ID) WHEN MATCHED THEN " +
"UPDATE SET PARENT.NAME = SOURCE.NAME||SOURCE.ID WHERE PARENT.ID = 2 " +
"DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)",
GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X||X AS NAME FROM SYSTEM_RANGE(2,2) UNION ALL SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(3,3)",
"SELECT X AS ID, 'Marcy'||X||X AS NAME FROM SYSTEM_RANGE(2,2) UNION ALL " +
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(3,3)",
3);
// Only insert clause, no update or delete clauses
testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,1) );"
+ "DELETE FROM PARENT;",
"MERGE INTO PARENT AS P USING (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) ) AS S ON (P.ID = S.ID) WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (S.ID, S.NAME)",
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,1) );" +
"DELETE FROM PARENT;",
"MERGE INTO PARENT AS P USING (" +
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) ) AS S ON (P.ID = S.ID) " +
"WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (S.ID, S.NAME)",
GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3)", 3);
// no insert, no update, no delete clauses - essentially a no-op
testMergeUsingException(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,1) );"
+ "DELETE FROM PARENT;",
"MERGE INTO PARENT AS P USING (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) ) AS S ON (P.ID = S.ID)",
"MERGE INTO PARENT AS P USING (" +
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) ) AS S ON (P.ID = S.ID)",
GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) WHERE X<0",
0,
......@@ -129,7 +162,10 @@ public class TestMergeUsing extends TestBase implements Trigger {
// parent table
testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,1) )",
"MERGE INTO PARENT AS P USING (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) ) AS S ON (P.ID = S.ID) WHEN MATCHED THEN UPDATE SET P.NAME = P.NAME||S.ID WHERE P.ID = 1 DELETE WHERE P.ID = 1 AND P.NAME = 'Marcy11'",
"MERGE INTO PARENT AS P USING (" +
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) ) AS S ON (P.ID = S.ID) " +
"WHEN MATCHED THEN " +
"UPDATE SET P.NAME = P.NAME||S.ID WHERE P.ID = 1 DELETE WHERE P.ID = 1 AND P.NAME = 'Marcy11'",
GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,1) WHERE X<0",
2);
......@@ -141,9 +177,12 @@ public class TestMergeUsing extends TestBase implements Trigger {
// One insert, one update one delete happens (on same row) , target
// table missing PK, no source or target alias
testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,1) );"
+ "CREATE TABLE SOURCE AS (SELECT 1 AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );",
"MERGE INTO PARENT USING SOURCE ON (PARENT.ID = SOURCE.ID) WHEN MATCHED THEN UPDATE SET PARENT.NAME = SOURCE.NAME||SOURCE.ID WHERE PARENT.ID = 2 DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)",
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,1) );" +
"CREATE TABLE SOURCE AS (SELECT 1 AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );",
"MERGE INTO PARENT USING SOURCE ON (PARENT.ID = SOURCE.ID) WHEN MATCHED THEN " +
"UPDATE SET PARENT.NAME = SOURCE.NAME||SOURCE.ID WHERE PARENT.ID = 2 " +
"DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN " +
"INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)",
GATHER_ORDERED_RESULTS_SQL,
"SELECT 1 AS ID, 'Marcy'||X||X UNION ALL SELECT 1 AS ID, 'Marcy2'",
2);
......@@ -157,41 +196,61 @@ public class TestMergeUsing extends TestBase implements Trigger {
testMergeUsingException(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,1) );"
+ "CREATE TABLE SOURCE AS (SELECT 1 AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) );",
"MERGE INTO PARENT USING SOURCE ON (PARENT.ID = SOURCE.ID) WHEN MATCHED THEN UPDATE SET PARENT.NAME = SOURCE.NAME||SOURCE.ID WHERE PARENT.ID = 2 DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)",
"MERGE INTO PARENT USING SOURCE ON (PARENT.ID = SOURCE.ID) WHEN MATCHED THEN " +
"UPDATE SET PARENT.NAME = SOURCE.NAME||SOURCE.ID WHERE PARENT.ID = 2 " +
"DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN " +
"INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)",
GATHER_ORDERED_RESULTS_SQL,
"SELECT 1 AS ID, 'Marcy'||X||X UNION ALL SELECT 1 AS ID, 'Marcy2'",
3,
"Unique index or primary key violation: \"Merge using ON column expression, duplicate _ROWID_ target record already updated, deleted or inserted:_ROWID_=2:in:PUBLIC.PARENT:conflicting source row number:2");
"Unique index or primary key violation: \"Merge using " +
"ON column expression, duplicate _ROWID_ target record " +
"already updated, deleted or inserted:_ROWID_=2:in:PUBLIC.PARENT:conflicting source row number:2");
// Duplicate key updated 3 rows at once, only 1 expected
testMergeUsingException(
"CREATE TABLE PARENT AS (SELECT 1 AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) );"
+ "CREATE TABLE SOURCE AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) );",
"MERGE INTO PARENT USING SOURCE ON (PARENT.ID = SOURCE.ID) WHEN MATCHED THEN UPDATE SET PARENT.NAME = SOURCE.NAME||SOURCE.ID WHERE PARENT.ID = 2 DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)",
"MERGE INTO PARENT USING SOURCE ON (PARENT.ID = SOURCE.ID) WHEN MATCHED THEN " +
"UPDATE SET PARENT.NAME = SOURCE.NAME||SOURCE.ID WHERE PARENT.ID = 2 " +
"DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN " +
"INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)",
GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X||X AS NAME FROM SYSTEM_RANGE(2,2) UNION ALL SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(3,3)",
"SELECT X AS ID, 'Marcy'||X||X AS NAME FROM SYSTEM_RANGE(2,2) UNION ALL " +
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(3,3)",
3, "Duplicate key updated 3 rows at once, only 1 expected");
// Missing target columns in ON expression
testMergeUsingException(
"CREATE TABLE PARENT AS (SELECT 1 AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) );"
+ "CREATE TABLE SOURCE AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) );",
"MERGE INTO PARENT USING SOURCE ON (1 = SOURCE.ID) WHEN MATCHED THEN UPDATE SET PARENT.NAME = SOURCE.NAME||SOURCE.ID WHERE PARENT.ID = 2 DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)",
"MERGE INTO PARENT USING SOURCE ON (1 = SOURCE.ID) WHEN MATCHED THEN " +
"UPDATE SET PARENT.NAME = SOURCE.NAME||SOURCE.ID WHERE PARENT.ID = 2 " +
"DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN " +
"INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)",
GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X||X AS NAME FROM SYSTEM_RANGE(2,2) UNION ALL SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(3,3)",
"SELECT X AS ID, 'Marcy'||X||X AS NAME FROM SYSTEM_RANGE(2,2) UNION ALL " +
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(3,3)",
3, "No references to target columns found in ON clause");
// Missing source columns in ON expression
testMergeUsingException(
"CREATE TABLE PARENT AS (SELECT 1 AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) );"
+ "CREATE TABLE SOURCE AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) );",
"MERGE INTO PARENT USING SOURCE ON (PARENT.ID = 1) WHEN MATCHED THEN UPDATE SET PARENT.NAME = SOURCE.NAME||SOURCE.ID WHERE PARENT.ID = 2 DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)",
"MERGE INTO PARENT USING SOURCE ON (PARENT.ID = 1) WHEN MATCHED THEN " +
"UPDATE SET PARENT.NAME = SOURCE.NAME||SOURCE.ID WHERE PARENT.ID = 2 " +
"DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN " +
"INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)",
GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X||X AS NAME FROM SYSTEM_RANGE(2,2) UNION ALL SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(3,3)",
"SELECT X AS ID, 'Marcy'||X||X AS NAME FROM SYSTEM_RANGE(2,2) UNION ALL " +
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(3,3)",
3, "No references to source columns found in ON clause");
// Insert does not insert correct values with respect to ON condition
// (inserts ID value above 100, instead)
testMergeUsingException(
"CREATE TABLE PARENT AS (SELECT 1 AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(4,4) );"
+ "CREATE TABLE SOURCE AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) );",
"MERGE INTO PARENT USING SOURCE ON (PARENT.ID = SOURCE.ID) WHEN MATCHED THEN UPDATE SET PARENT.NAME = SOURCE.NAME||SOURCE.ID WHERE PARENT.ID = 2 DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (SOURCE.ID+100, SOURCE.NAME)",
"MERGE INTO PARENT USING SOURCE ON (PARENT.ID = SOURCE.ID) WHEN MATCHED THEN " +
"UPDATE SET PARENT.NAME = SOURCE.NAME||SOURCE.ID WHERE PARENT.ID = 2 " +
"DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN " +
"INSERT (ID, NAME) VALUES (SOURCE.ID+100, SOURCE.NAME)",
GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(4,4)", 1,
"Expected to find key after row inserted, but none found. Insert does not match ON condition.");
......@@ -201,9 +260,13 @@ public class TestMergeUsing extends TestBase implements Trigger {
testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2));"
+ getCreateTriggerSQL(),
"MERGE INTO PARENT AS P USING (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,4) ) AS S ON (P.ID = S.ID) WHEN MATCHED THEN UPDATE SET P.NAME = S.NAME||S.ID WHERE P.ID = 2 DELETE WHERE P.ID = 1 WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (S.ID, S.NAME)",
"MERGE INTO PARENT AS P USING " +
"(SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,4) ) AS S ON (P.ID = S.ID) " +
"WHEN MATCHED THEN UPDATE SET P.NAME = S.NAME||S.ID WHERE P.ID = 2 " +
"DELETE WHERE P.ID = 1 WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (S.ID, S.NAME)",
GATHER_ORDERED_RESULTS_SQL,
"SELECT 2 AS ID, 'Marcy22-updated2' AS NAME UNION ALL SELECT X AS ID, 'Marcy'||X||'-inserted'||X AS NAME FROM SYSTEM_RANGE(3,4)",
"SELECT 2 AS ID, 'Marcy22-updated2' AS NAME UNION ALL " +
"SELECT X AS ID, 'Marcy'||X||'-inserted'||X AS NAME FROM SYSTEM_RANGE(3,4)",
4);
}
......
......@@ -50,24 +50,24 @@ public class TestConnection extends TestBase {
assertThrows(SQLClientInfoException.class, conn).setClientInfo("server23", "SomeValue");
conn.close();
}
/**
* Test that no exception is thrown if the client info of a connection managed in a connection pool is reset
* to its initial values.
*
* Test that no exception is thrown if the client info of a connection
* managed in a connection pool is reset to its initial values.
*
* This is needed when using h2 in websphere liberty.
*/
private void testSetInternalPropertyToInitialValue() throws SQLException {
// Use MySQL-mode since this allows all property names
// (apart from h2 internal names).
Connection conn = getConnection("clientInfoMySQL;MODE=MySQL");
String numServersPropertyName = "numServers";
String numServers = conn.getClientInfo(numServersPropertyName);
conn.setClientInfo(numServersPropertyName, numServers);
assertEquals(conn.getClientInfo(numServersPropertyName), numServers);
conn.close();
......
......@@ -1495,7 +1495,8 @@ public class TestMVTableEngine extends TestBase {
String dbName = getTestName() + ";MV_STORE=TRUE";
try (Connection conn = getConnection(dbName)) {
Statement stat = conn.createStatement();
stat.execute("CREATE TABLE test(id INT PRIMARY KEY, name VARCHAR) AS SELECT x, x || space(1024) || x FROM system_range(1, 1000)");
stat.execute("CREATE TABLE test(id INT PRIMARY KEY, name VARCHAR) AS " +
"SELECT x, x || space(1024) || x FROM system_range(1, 1000)");
conn.setAutoCommit(false);
PreparedStatement prep = conn.prepareStatement("DELETE FROM test WHERE id = ?");
long start = System.nanoTime();
......
......@@ -9,7 +9,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
import org.h2.util.IOUtils;
/**
......@@ -51,29 +50,39 @@ public class OutputCatcher extends Thread {
@Override
public void run() {
StringBuilder buff = new StringBuilder();
while (true) {
try {
int x = in.read();
if (x < 0) {
break;
}
if (x < ' ') {
if (buff.length() > 0) {
String s = buff.toString();
buff.setLength(0);
synchronized (list) {
list.add(s);
list.notifyAll();
final StringBuilder buff = new StringBuilder();
try {
while (true) {
try {
int x = in.read();
if (x < 0) {
break;
}
if (x < ' ') {
if (buff.length() > 0) {
String s = buff.toString();
buff.setLength(0);
synchronized (list) {
list.add(s);
list.notifyAll();
}
}
} else {
buff.append((char) x);
}
} else {
buff.append((char) x);
} catch (IOException e) {
break;
}
}
IOUtils.closeSilently(in);
} finally {
// just in case something goes wrong, make sure we store any partial output we got
if (buff.length() > 0) {
synchronized (list) {
list.add(buff.toString());
list.notifyAll();
}
} catch (IOException e) {
break;
}
}
IOUtils.closeSilently(in);
}
}
......@@ -9,7 +9,6 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.h2.test.utils.SelfDestructor;
/**
......@@ -38,6 +37,7 @@ public class TestHaltApp extends TestHalt {
}
}
@Override
protected void execute(Statement stat, String sql) throws SQLException {
traceOperation("execute: " + sql);
super.execute(stat, sql);
......
......@@ -42,7 +42,7 @@ public class TestKillRestart extends TestBase {
int len = getSize(2, 15);
for (int i = 0; i < len; i++) {
Process p = Runtime.getRuntime().exec(procDef);
Process p = new ProcessBuilder().redirectErrorStream(true).command(procDef).start();
InputStream in = p.getInputStream();
OutputCatcher catcher = new OutputCatcher(in);
catcher.start();
......
......@@ -106,7 +106,8 @@ public class TestFileLockProcess extends TestBase {
}
proc.waitFor();
// The travis build somehow generates messages like this from javac. No idea where it is coming from.
// The travis build somehow generates messages like this from javac.
// No idea where it is coming from.
String processOutput = buff.toString();
processOutput = processOutput.replaceAll("Picked up _JAVA_OPTIONS: -Xmx2048m -Xms512m", "").trim();
......
......@@ -158,7 +158,9 @@ public class TestNetUtils extends TestBase {
*/
void closeSilently(Socket socket) {
try {
socket.close();
if (socket != null) {
socket.close();
}
} catch (Exception e) {
// ignore
}
......
......@@ -368,7 +368,8 @@ public class TestPgServer extends TestBase {
return;
}
// Sometimes the previous pg server has not finished shutting and we get "port in use", so sleep for a bit.
// Sometimes the previous pg server has not finished shutting and we get
// "port in use", so sleep for a bit.
Thread.sleep(100);
Server server = Server.createPgServer(
......
......@@ -6,7 +6,7 @@ import org.h2.expression.ValueExpression;
import org.h2.test.TestBase;
public class TestColumnNamer extends TestBase {
private String[] ids = new String[]{
"ABC"
,"123"
......@@ -24,7 +24,7 @@ public class TestColumnNamer extends TestBase {
,"col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2"
,"col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2"
};
private String[] expectedColumnName= {"ABC"
,"123"
,"a2"
......@@ -39,12 +39,13 @@ public class TestColumnNamer extends TestBase {
,"col1_2"
,"col1_3"
,"col2col2col2col2col2col2col2co"
,"col2col2col2col2col2col2col2_2"};
,"col2col2col2col2col2col2col2_2"};
public static void main(String[] args){
new TestColumnNamer().test();
}
@Override
public void test() {
ColumnNamer columnNamer = new ColumnNamer(null);
columnNamer.getConfiguration().configure("MAX_IDENTIFIER_LENGTH = 30");
......@@ -52,7 +53,7 @@ public class TestColumnNamer extends TestBase {
columnNamer.getConfiguration().configure("REGULAR_EXPRESSION_MATCH_DISALLOWED = '[^A-Za-z0-9_]+'");
columnNamer.getConfiguration().configure("DEFAULT_COLUMN_NAME_PATTERN = 'colName$$'");
columnNamer.getConfiguration().configure("GENERATE_UNIQUE_COLUMN_NAMES = 1");
int index =0;
for(String id : ids){
Expression columnExp = ValueExpression.getDefault();
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论