提交 33a523b9 authored 作者: thomasmueller's avatar thomasmueller

Formatting

上级 dc8b67b8
...@@ -21,15 +21,15 @@ Change Log ...@@ -21,15 +21,15 @@ Change Log
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul> <ul>
<li>Issue #654: List ENUM type values in INFORMATION_SCHEMA.COLUMNS <li>Issue #654: List ENUM type values in INFORMATION_SCHEMA.COLUMNS
</li> </li>
<li>Issue #668: Fail of an update command on large table with ENUM column <li>Issue #668: Fail of an update command on large table with ENUM column
</li> </li>
<li>Issue #662: column called CONSTRAINT is not properly escaped when storing to metadata <li>Issue #662: column called CONSTRAINT is not properly escaped when storing to metadata
</li> </li>
<li>Issue #660: Outdated java version mentioned on http://h2database.com/html/build.html#providing_patches <li>Issue #660: Outdated java version mentioned on http://h2database.com/html/build.html#providing_patches
</li> </li>
<li>Issue #643: H2 doesn't use index when I use IN and EQUAL in one query <li>Issue #643: H2 doesn't use index when I use IN and EQUAL in one query
</li> </li>
<li>Reset transaction start timestamp on ROLLBACK <li>Reset transaction start timestamp on ROLLBACK
</li> </li>
......
...@@ -90,7 +90,7 @@ Java implementations (such as Swing) are not used, or only used for optional fea ...@@ -90,7 +90,7 @@ Java implementations (such as Swing) are not used, or only used for optional fea
<h2 id="supporters">Supporters</h2> <h2 id="supporters">Supporters</h2>
<p> <p>
Many thanks for those who reported bugs, gave valuable feedback, Many thanks for those who reported bugs, gave valuable feedback,
spread the word, and translated this project. spread the word, and translated this project.
</p> </p>
<p> <p>
Also many thanks to the donors. Also many thanks to the donors.
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -4,7 +4,8 @@ ...@@ -4,7 +4,8 @@
08000=Error opening database: {0} 08000=Error opening database: {0}
21S02=Column count does not match 21S02=Column count does not match
22001=Value too long for column {0}: {1} 22001=Value too long for column {0}: {1}
22003=Numeric value out of range: {0} {1} 22003=Numeric value out of range: {0}
22004=Numeric value out of range: {0} in column {1}
22007=Cannot parse {0} constant {1} 22007=Cannot parse {0} constant {1}
22012=Division by zero: {0} 22012=Division by zero: {0}
22018=Data conversion error converting {0} 22018=Data conversion error converting {0}
......
...@@ -333,26 +333,20 @@ public class AlterTableAddConstraint extends SchemaCommand { ...@@ -333,26 +333,20 @@ public class AlterTableAddConstraint extends SchemaCommand {
// all cols must be in the index key, the order doesn't matter and there // all cols must be in the index key, the order doesn't matter and there
// must be no other fields in the index key // must be no other fields in the index key
private static boolean canUseUniqueIndex(Index idx, Table table, private static boolean canUseUniqueIndex(Index idx, Table table, IndexColumn[] cols) {
IndexColumn[] cols) {
if (idx.getTable() != table || !idx.getIndexType().isUnique()) { if (idx.getTable() != table || !idx.getIndexType().isUnique()) {
return false; return false;
} }
Column[] indexCols = idx.getColumns(); Column[] indexCols = idx.getColumns();
HashSet<Column> indexColsSet = New.hashSet(); HashSet<Column> indexColsSet = New.hashSet();
for (Column c : indexCols) { for (Column c : indexCols) {
indexColsSet.add(c); indexColsSet.add(c);
} }
HashSet<Column> colsSet = New.hashSet(); HashSet<Column> colsSet = New.hashSet();
for (IndexColumn c : cols) { for (IndexColumn c : cols) {
colsSet.add(c.column); colsSet.add(c.column);
} }
return colsSet.equals(indexColsSet);
return colsSet.equals(indexColsSet);
} }
private static boolean canUseIndex(Index existingIndex, Table table, private static boolean canUseIndex(Index existingIndex, Table table,
......
...@@ -52,7 +52,7 @@ public class Delete extends Prepared { ...@@ -52,7 +52,7 @@ public class Delete extends Prepared {
public void setCondition(Expression condition) { public void setCondition(Expression condition) {
this.condition = condition; this.condition = condition;
} }
public Expression getCondition( ) { public Expression getCondition( ) {
return this.condition; return this.condition;
} }
...@@ -137,7 +137,7 @@ public class Delete extends Prepared { ...@@ -137,7 +137,7 @@ public class Delete extends Prepared {
if (condition != null) { if (condition != null) {
condition.mapColumns(targetTableFilter, 0); condition.mapColumns(targetTableFilter, 0);
if(sourceTableFilter!=null){ if(sourceTableFilter!=null){
condition.mapColumns(sourceTableFilter, 0); condition.mapColumns(sourceTableFilter, 0);
} }
condition = condition.optimize(session); condition = condition.optimize(session);
condition.createIndexConditions(session, targetTableFilter); condition.createIndexConditions(session, targetTableFilter);
...@@ -180,7 +180,7 @@ public class Delete extends Prepared { ...@@ -180,7 +180,7 @@ public class Delete extends Prepared {
} }
public void setSourceTableFilter(TableFilter sourceTableFilter) { public void setSourceTableFilter(TableFilter sourceTableFilter) {
this.sourceTableFilter = sourceTableFilter; this.sourceTableFilter = sourceTableFilter;
} }
public TableFilter getTableFilter() { public TableFilter getTableFilter() {
......
...@@ -49,7 +49,7 @@ public class Insert extends Prepared implements ResultTarget { ...@@ -49,7 +49,7 @@ public class Insert extends Prepared implements ResultTarget {
private boolean insertFromSelect; private boolean insertFromSelect;
/** /**
* This table filter is for MERGE..USING support - not used in stand-alone DML * This table filter is for MERGE..USING support - not used in stand-alone DML
*/ */
private TableFilter sourceTableFilter; private TableFilter sourceTableFilter;
/** /**
...@@ -404,7 +404,7 @@ public class Insert extends Prepared implements ResultTarget { ...@@ -404,7 +404,7 @@ public class Insert extends Prepared implements ResultTarget {
} }
public void setSourceTableFilter(TableFilter sourceTableFilter) { public void setSourceTableFilter(TableFilter sourceTableFilter) {
this.sourceTableFilter = sourceTableFilter; this.sourceTableFilter = sourceTableFilter;
} }
} }
...@@ -68,7 +68,7 @@ public class Merge extends Prepared { ...@@ -68,7 +68,7 @@ public class Merge extends Prepared {
public void setQuery(Query query) { public void setQuery(Query query) {
this.query = query; this.query = query;
} }
/** /**
* Add a row to this merge statement. * Add a row to this merge statement.
* *
...@@ -154,10 +154,10 @@ public class Merge extends Prepared { ...@@ -154,10 +154,10 @@ public class Merge extends Prepared {
Parameter p = k.get(columns.length + i); Parameter p = k.get(columns.length + i);
p.setValue(v); p.setValue(v);
} }
// try and update // try and update
int count = update.update(); int count = update.update();
// if update fails try an insert // if update fails try an insert
if (count == 0) { if (count == 0) {
try { try {
...@@ -315,11 +315,11 @@ public class Merge extends Prepared { ...@@ -315,11 +315,11 @@ public class Merge extends Prepared {
public Table getTargetTable() { public Table getTargetTable() {
return targetTable; return targetTable;
} }
public TableFilter getTargetTableFilter() { public TableFilter getTargetTableFilter() {
return targetTableFilter; return targetTableFilter;
} }
public void setTargetTableFilter(TableFilter targetTableFilter) { public void setTargetTableFilter(TableFilter targetTableFilter) {
this.targetTableFilter = targetTableFilter; this.targetTableFilter = targetTableFilter;
setTargetTable(targetTableFilter.getTable()); setTargetTable(targetTableFilter.getTable());
......
...@@ -840,7 +840,7 @@ public class Select extends Query { ...@@ -840,7 +840,7 @@ public class Select extends Query {
sort = prepareOrder(orderList, expressions.size()); sort = prepareOrder(orderList, expressions.size());
orderList = null; orderList = null;
} }
ColumnNamer columnNamer= new ColumnNamer(session); ColumnNamer columnNamer= new ColumnNamer(session);
for (int i = 0; i < expressions.size(); i++) { for (int i = 0; i < expressions.size(); i++) {
Expression e = expressions.get(i); Expression e = expressions.get(i);
String proposedColumnName = e.getAlias(); String proposedColumnName = e.getAlias();
......
...@@ -36,7 +36,7 @@ public class SelectListColumnResolver implements ColumnResolver { ...@@ -36,7 +36,7 @@ public class SelectListColumnResolver implements ColumnResolver {
columns = new Column[columnCount]; columns = new Column[columnCount];
expressions = new Expression[columnCount]; expressions = new Expression[columnCount];
ArrayList<Expression> columnList = select.getExpressions(); ArrayList<Expression> columnList = select.getExpressions();
ColumnNamer columnNamer= new ColumnNamer(select.getSession()); ColumnNamer columnNamer= new ColumnNamer(select.getSession());
for (int i = 0; i < columnCount; i++) { for (int i = 0; i < columnCount; i++) {
Expression expr = columnList.get(i); Expression expr = columnList.get(i);
String columnName = columnNamer.getColumnName(expr, i, expr.getAlias()); String columnName = columnNamer.getColumnName(expr, i, expr.getAlias());
......
...@@ -330,7 +330,7 @@ public class SelectUnion extends Query { ...@@ -330,7 +330,7 @@ public class SelectUnion extends Query {
expressions = New.arrayList(); expressions = New.arrayList();
ArrayList<Expression> le = left.getExpressions(); ArrayList<Expression> le = left.getExpressions();
ArrayList<Expression> re = right.getExpressions(); ArrayList<Expression> re = right.getExpressions();
ColumnNamer columnNamer= new ColumnNamer(session); ColumnNamer columnNamer= new ColumnNamer(session);
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
Expression l = le.get(i); Expression l = le.get(i);
Expression r = re.get(i); Expression r = re.get(i);
......
...@@ -535,13 +535,11 @@ public class Set extends Prepared { ...@@ -535,13 +535,11 @@ public class Set extends Prepared {
database.setAllowBuiltinAliasOverride(value == 1); database.setAllowBuiltinAliasOverride(value == 1);
break; break;
} }
case SetTypes.COLUMN_NAME_RULES: { case SetTypes.COLUMN_NAME_RULES: {
session.getUser().checkAdmin(); session.getUser().checkAdmin();
session.getColumnNamerConfiguration().configure(expression.getColumnName()); session.getColumnNamerConfiguration().configure(expression.getColumnName());
break; break;
} }
default: default:
DbException.throwInternalError("type="+type); DbException.throwInternalError("type="+type);
} }
......
...@@ -251,7 +251,7 @@ public class SetTypes { ...@@ -251,7 +251,7 @@ public class SetTypes {
* The type of a SET COLUMN_NAME_RULES statement. * The type of a SET COLUMN_NAME_RULES statement.
*/ */
public static final int COLUMN_NAME_RULES = 48; public static final int COLUMN_NAME_RULES = 48;
private static final ArrayList<String> TYPES = New.arrayList(); private static final ArrayList<String> TYPES = New.arrayList();
...@@ -310,7 +310,7 @@ public class SetTypes { ...@@ -310,7 +310,7 @@ public class SetTypes {
list.add(LAZY_QUERY_EXECUTION, "LAZY_QUERY_EXECUTION"); list.add(LAZY_QUERY_EXECUTION, "LAZY_QUERY_EXECUTION");
list.add(BUILTIN_ALIAS_OVERRIDE, "BUILTIN_ALIAS_OVERRIDE"); list.add(BUILTIN_ALIAS_OVERRIDE, "BUILTIN_ALIAS_OVERRIDE");
list.add(COLUMN_NAME_RULES, "COLUMN_NAME_RULES"); list.add(COLUMN_NAME_RULES, "COLUMN_NAME_RULES");
} }
/** /**
......
...@@ -41,7 +41,7 @@ public class Update extends Prepared { ...@@ -41,7 +41,7 @@ public class Update extends Prepared {
private TableFilter targetTableFilter;// target of update private TableFilter targetTableFilter;// target of update
/** /**
* This table filter is for MERGE..USING support - not used in stand-alone DML * This table filter is for MERGE..USING support - not used in stand-alone DML
*/ */
private TableFilter sourceTableFilter; private TableFilter sourceTableFilter;
/** The limit expression as specified in the LIMIT clause. */ /** The limit expression as specified in the LIMIT clause. */
...@@ -61,7 +61,7 @@ public class Update extends Prepared { ...@@ -61,7 +61,7 @@ public class Update extends Prepared {
public void setCondition(Expression condition) { public void setCondition(Expression condition) {
this.condition = condition; this.condition = condition;
} }
public Expression getCondition( ) { public Expression getCondition( ) {
return this.condition; return this.condition;
} }
......
...@@ -2453,10 +2453,10 @@ public class Database implements DataHandler { ...@@ -2453,10 +2453,10 @@ public class Database implements DataHandler {
* @param closeOthers whether other sessions are closed * @param closeOthers whether other sessions are closed
*/ */
public void setExclusiveSession(Session session, boolean closeOthers) { public void setExclusiveSession(Session session, boolean closeOthers) {
this.exclusiveSession.set(session); this.exclusiveSession.set(session);
if (closeOthers) { if (closeOthers) {
closeAllSessionsException(session); closeAllSessionsException(session);
} }
} }
@Override @Override
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
08000=Chyba při otevírání databáze: {0} 08000=Chyba při otevírání databáze: {0}
21S02=Počet sloupců nesouhlasí 21S02=Počet sloupců nesouhlasí
22001=Příliš dlouhá hodnota pro sloupec {0}: {1} 22001=Příliš dlouhá hodnota pro sloupec {0}: {1}
22003=Číselná hodnota je mimo rozsah: {0} 22003=#Numeric value out of range: {0}
22004=Numeric value out of range: {0} in column {1} 22004=Numeric value out of range: {0} in column {1}
22007=Nelze zpracovat konstantu {0} {1} 22007=Nelze zpracovat konstantu {0} {1}
22012=Dělení nulou: {0} 22012=Dělení nulou: {0}
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
08000=Fehler beim Öffnen der Datenbank: {0} 08000=Fehler beim Öffnen der Datenbank: {0}
21S02=Anzahl der Felder stimmt nicht überein 21S02=Anzahl der Felder stimmt nicht überein
22001=Wert zu gross / lang für Feld {0}: {1} 22001=Wert zu gross / lang für Feld {0}: {1}
22003=Zahlenwert ausserhalb des Bereichs: {0} 22003=#Numeric value out of range: {0}
22004=Numeric value out of range: {0} in column {1} 22004=Numeric value out of range: {0} in column {1}
22007=Kann {0} {1} nicht umwandeln 22007=Kann {0} {1} nicht umwandeln
22012=Division durch 0: {0} 22012=Division durch 0: {0}
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
08000=Error abriendo la base de datos: {0} 08000=Error abriendo la base de datos: {0}
21S02=La cantidad de columnas no coincide 21S02=La cantidad de columnas no coincide
22001=Valor demasiado largo para la columna {0}: {1} 22001=Valor demasiado largo para la columna {0}: {1}
22003=Valor numerico fuera de rango: {0} 22003=#Numeric value out of range: {0}
22004=Numeric value out of range: {0} in column {1} 22004=Numeric value out of range: {0} in column {1}
22007=Imposible interpretar la constante {0} {1} 22007=Imposible interpretar la constante {0} {1}
22012=División por cero: {0} 22012=División por cero: {0}
......
...@@ -4,7 +4,8 @@ ...@@ -4,7 +4,8 @@
08000=Une erreur est survenue lors de l'ouverture de la base de données: {0} 08000=Une erreur est survenue lors de l'ouverture de la base de données: {0}
21S02=Le nombre de colonnes ne correspond pas 21S02=Le nombre de colonnes ne correspond pas
22001=Valeur trop longue pour la colonne {0}: {1} 22001=Valeur trop longue pour la colonne {0}: {1}
22003=Valeur numérique hors de portée: {0} 22003=#Numeric value out of range: {0}
22004=#Numeric value out of range: {0} in column {1}
22007=Impossible d'analyser {0} constante {1} 22007=Impossible d'analyser {0} constante {1}
22012=Division par zéro: {0} 22012=Division par zéro: {0}
22018=Erreur lors de la conversion de données {0} 22018=Erreur lors de la conversion de données {0}
...@@ -100,7 +101,7 @@ ...@@ -100,7 +101,7 @@
90066=Propriété dupliquée {0} 90066=Propriété dupliquée {0}
90067=La connexion est cassée: {0} 90067=La connexion est cassée: {0}
90068=L'expression Order by {0} doit être dans ce cas dans la liste des résultats 90068=L'expression Order by {0} doit être dans ce cas dans la liste des résultats
90069Le rôle {0} existe déjà 90069=#Role {0} already exists
90070=Rôle {0} non trouvé 90070=Rôle {0} non trouvé
90071=Utilisateur ou rôle {0} non trouvé 90071=Utilisateur ou rôle {0} non trouvé
90072=Les rôles et les droits ne peuvent être mélangés 90072=Les rôles et les droits ne peuvent être mélangés
...@@ -171,8 +172,8 @@ ...@@ -171,8 +172,8 @@
90140='ensemble des résultats est en lecture seule. Vous pouvez avoir besoin d'utiliser conn.createStatement(.., ResultSet.CONCUR_UPDATABLE). 90140='ensemble des résultats est en lecture seule. Vous pouvez avoir besoin d'utiliser conn.createStatement(.., ResultSet.CONCUR_UPDATABLE).
90141=Le sérialiseur ne peut être changé parce que il y a des données dans la table: {0} 90141=Le sérialiseur ne peut être changé parce que il y a des données dans la table: {0}
90142=La taille de l'étape ne doit pas être de 0 90142=La taille de l'étape ne doit pas être de 0
90143=#Ligne {1} non trouvée dans l'index principal {0} 90143=##Row {1} not found in primary index {0}
HY000=Erreur générale: {0} HY000=Erreur générale: {0}
HY004=Type de données inconnu: {0} HY004=Type de données inconnu: {0}
HYC00=Fonctionnalité non supportée: {0} HYC00=Fonctionnalité non supportée: {0}
HYT00=Dépassement du temps lors du vérrouillage de la table {0} HYT00=Dépassement du temps lors du vérrouillage de la table {0}
\ No newline at end of file
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
08000=データベースオープンエラー: {0} 08000=データベースオープンエラー: {0}
21S02=列番号が一致しません 21S02=列番号が一致しません
22001=列 {0} の値が長過ぎます: {1} 22001=列 {0} の値が長過ぎます: {1}
22003=範囲外の数値です: {0} 22003=#Numeric value out of range: {0}
22004=Numeric value out of range: {0} in column {1} 22004=Numeric value out of range: {0} in column {1}
22007={0} 定数 {1} を解析できません 22007={0} 定数 {1} を解析できません
22012=ゼロで除算しました: {0} 22012=ゼロで除算しました: {0}
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
08000=Błąd otwarcia bazy danych: {0} 08000=Błąd otwarcia bazy danych: {0}
21S02=Niezgodna ilość kolumn 21S02=Niezgodna ilość kolumn
22001=Wartość za długa dla kolumny {0}: {1} 22001=Wartość za długa dla kolumny {0}: {1}
22003=Wartość numeryczna poza zakresem: {0} 22003=#Numeric value out of range: {0}
22004=Numeric value out of range: {0} in column {1} 22004=Numeric value out of range: {0} in column {1}
22007=Nie można odczytać {0} jako {1} 22007=Nie można odczytać {0} jako {1}
22012=Dzielenie przez zero: {0} 22012=Dzielenie przez zero: {0}
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
08000=Erro ao abrir a base de dados: {0} 08000=Erro ao abrir a base de dados: {0}
21S02=A quantidade de colunas não corresponde 21S02=A quantidade de colunas não corresponde
22001=Valor muito longo para a coluna {0}: {1} 22001=Valor muito longo para a coluna {0}: {1}
22003=Valor númerico não esta dentro do limite: {0} 22003=#Numeric value out of range: {0}
22004=Numeric value out of range: {0} in column {1} 22004=Numeric value out of range: {0} in column {1}
22007=Não é possível converter {1} para {0} 22007=Não é possível converter {1} para {0}
22012=Divisão por zero: {0} 22012=Divisão por zero: {0}
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
08000=Ошибка при открытии базы данных: {0} 08000=Ошибка при открытии базы данных: {0}
21S02=Неверное количество столбцов 21S02=Неверное количество столбцов
22001=Значение слишком длинное для поля {0}: {1} 22001=Значение слишком длинное для поля {0}: {1}
22003=Численное значение вне допустимого диапазона: {0} 22003=#Numeric value out of range: {0}
22004=Numeric value out of range: {0} in column {1} 22004=Numeric value out of range: {0} in column {1}
22007=Невозможно преобразование строки {1} в тип {0} 22007=Невозможно преобразование строки {1} в тип {0}
22012=Деление на ноль: {0} 22012=Деление на ноль: {0}
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
08000=Chyba otvorenia databázy: {0} 08000=Chyba otvorenia databázy: {0}
21S02=Počet stĺpcov sa nezhoduje 21S02=Počet stĺpcov sa nezhoduje
22001=Hodnota je príliš dlhá pre stĺpec {0}: {1} 22001=Hodnota je príliš dlhá pre stĺpec {0}: {1}
22003=Číselná hodnota mimo rozsah: {0} 22003=#Numeric value out of range: {0}
22004=Numeric value out of range: {0} in column {1} 22004=Numeric value out of range: {0} in column {1}
22007=Nemožem rozobrať {0} konštantu {1} 22007=Nemožem rozobrať {0} konštantu {1}
22012=Delenie nulou: {0} 22012=Delenie nulou: {0}
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
08000=开启数据库错误: {0} 08000=开启数据库错误: {0}
21S02=字段数目不匹配 21S02=字段数目不匹配
22001=字段 {0}数值太大: {1} 22001=字段 {0}数值太大: {1}
22003=数值超出范围: {0} 22003=#Numeric value out of range: {0}
22004=Numeric value out of range: {0} in column {1} 22004=Numeric value out of range: {0} in column {1}
22007=不能解析字段 {0} 的数值 :{1} 22007=不能解析字段 {0} 的数值 :{1}
22012=除数为零: {0} 22012=除数为零: {0}
......
...@@ -103,16 +103,13 @@ public class CreateCluster extends Tool { ...@@ -103,16 +103,13 @@ public class CreateCluster extends Tool {
String user, String password, String serverList) throws SQLException { String user, String password, String serverList) throws SQLException {
org.h2.Driver.load(); org.h2.Driver.load();
try (Connection connSource = DriverManager.getConnection( // use cluster='' so connecting is possible
// use cluster='' so connecting is possible // even if the cluster is enabled
// even if the cluster is enabled try (Connection connSource = DriverManager.getConnection(urlSource + ";CLUSTER=''", user, password);
urlSource + ";CLUSTER=''", user, password); Statement statSource = connSource.createStatement()) {
Statement statSource = connSource.createStatement())
{
// enable the exclusive mode and close other connections, // enable the exclusive mode and close other connections,
// so that data can't change while restoring the second database // so that data can't change while restoring the second database
statSource.execute("SET EXCLUSIVE 2"); statSource.execute("SET EXCLUSIVE 2");
try { try {
performTransfer(statSource, urlTarget, user, password, serverList); performTransfer(statSource, urlTarget, user, password, serverList);
} finally { } finally {
...@@ -122,14 +119,12 @@ public class CreateCluster extends Tool { ...@@ -122,14 +119,12 @@ public class CreateCluster extends Tool {
} }
} }
private static void performTransfer(Statement statSource, String urlTarget, private static void performTransfer(Statement statSource, String urlTarget, String user, String password,
String user, String password, String serverList) throws SQLException { String serverList) throws SQLException {
// Delete the target database first. // Delete the target database first.
try (Connection connTarget = DriverManager.getConnection( try (Connection connTarget = DriverManager.getConnection(urlTarget + ";CLUSTER=''", user, password);
urlTarget + ";CLUSTER=''", user, password); Statement statTarget = connTarget.createStatement()) {
Statement statTarget = connTarget.createStatement())
{
statTarget.execute("DROP ALL OBJECTS DELETE FILES"); statTarget.execute("DROP ALL OBJECTS DELETE FILES");
} }
...@@ -137,10 +132,8 @@ public class CreateCluster extends Tool { ...@@ -137,10 +132,8 @@ public class CreateCluster extends Tool {
Future<?> threadFuture = startWriter(pipeReader, statSource); Future<?> threadFuture = startWriter(pipeReader, statSource);
// Read data from pipe reader, restore on target. // Read data from pipe reader, restore on target.
try (Connection connTarget = DriverManager.getConnection( try (Connection connTarget = DriverManager.getConnection(urlTarget, user, password);
urlTarget, user, password); Statement statTarget = connTarget.createStatement()) {
Statement statTarget = connTarget.createStatement())
{
RunScript.execute(connTarget, pipeReader); RunScript.execute(connTarget, pipeReader);
// Check if the writer encountered any exception // Check if the writer encountered any exception
...@@ -170,17 +163,13 @@ public class CreateCluster extends Tool { ...@@ -170,17 +163,13 @@ public class CreateCluster extends Tool {
Future<?> threadFuture = thread.submit(new Runnable() { Future<?> threadFuture = thread.submit(new Runnable() {
@Override @Override
public void run() { public void run() {
/* // If the creation of the piped writer fails, the reader will
* If the creation of the piped writer fails, the reader will // throw an IOException as soon as read() is called: IOException
* throw an IOException as soon as read() is called: // - if the pipe is broken, unconnected, closed, or an I/O error
* IOException - if the pipe is broken, unconnected, closed, // occurs. The reader's IOException will then trigger the
* or an I/O error occurs. // finally{} that releases exclusive mode on the source DB.
* The reader's IOException will then trigger the finally{} that
* releases exclusive mode on the source DB.
*/
try (final PipedWriter pipeWriter = new PipedWriter(pipeReader); try (final PipedWriter pipeWriter = new PipedWriter(pipeReader);
final ResultSet rs = statSource.executeQuery("SCRIPT")) final ResultSet rs = statSource.executeQuery("SCRIPT")) {
{
while (rs.next()) { while (rs.next()) {
pipeWriter.write(rs.getString(1) + "\n"); pipeWriter.write(rs.getString(1) + "\n");
} }
......
/*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
*/
package org.h2.util; package org.h2.util;
import java.util.HashSet; import java.util.HashSet;
...@@ -6,7 +10,9 @@ import java.util.regex.Matcher; ...@@ -6,7 +10,9 @@ import java.util.regex.Matcher;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.expression.Expression; import org.h2.expression.Expression;
/**
* A factory for column names.
*/
public class ColumnNamer { public class ColumnNamer {
private static final String DEFAULT_COLUMN_NAME = "DEFAULT"; private static final String DEFAULT_COLUMN_NAME = "DEFAULT";
...@@ -17,14 +23,13 @@ public class ColumnNamer { ...@@ -17,14 +23,13 @@ public class ColumnNamer {
public ColumnNamer(Session session) { public ColumnNamer(Session session) {
this.session = session; this.session = session;
if(this.session!=null && this.session.getColumnNamerConfiguration()!=null){ if (this.session != null && this.session.getColumnNamerConfiguration() != null) {
// use original from session // use original from session
this.configuration = this.session.getColumnNamerConfiguration(); this.configuration = this.session.getColumnNamerConfiguration();
} } else {
else{
// detached namer, create new // detached namer, create new
this.configuration = ColumnNamerConfiguration.getDefault(); this.configuration = ColumnNamerConfiguration.getDefault();
if(session!=null){ if (session != null) {
session.setColumnNamerConfiguration(this.configuration); session.setColumnNamerConfiguration(this.configuration);
} }
} }
...@@ -36,7 +41,7 @@ public class ColumnNamer { ...@@ -36,7 +41,7 @@ public class ColumnNamer {
* @param indexOfColumn index of column in below array * @param indexOfColumn index of column in below array
*/ */
public String getColumnName(Expression expr, int indexOfColumn) { public String getColumnName(Expression expr, int indexOfColumn) {
return getColumnName(expr,indexOfColumn,(String) null); return getColumnName(expr, indexOfColumn, (String) null);
} }
/** /**
...@@ -64,18 +69,17 @@ public class ColumnNamer { ...@@ -64,18 +69,17 @@ public class ColumnNamer {
public String getColumnName(Expression columnExp, int indexOfColumn, String columnNameOverride) { public String getColumnName(Expression columnExp, int indexOfColumn, String columnNameOverride) {
// try a name from the column name override // try a name from the column name override
String columnName = null; String columnName = null;
if (columnNameOverride != null){ if (columnNameOverride != null) {
columnName = columnNameOverride; columnName = columnNameOverride;
if(!isAllowableColumnName(columnName)){ if (!isAllowableColumnName(columnName)) {
columnName = fixColumnName(columnName); columnName = fixColumnName(columnName);
} }
if(!isAllowableColumnName(columnName)){ if (!isAllowableColumnName(columnName)) {
columnName = null; columnName = null;
} }
} }
// try a name from the column alias // try a name from the column alias
if (columnName==null && columnExp.getAlias()!=null && if (columnName == null && columnExp.getAlias() != null && !DEFAULT_COLUMN_NAME.equals(columnExp.getAlias())) {
!DEFAULT_COLUMN_NAME.equals(columnExp.getAlias())) {
columnName = columnExp.getAlias(); columnName = columnExp.getAlias();
if (!isAllowableColumnName(columnName)) { if (!isAllowableColumnName(columnName)) {
columnName = fixColumnName(columnName); columnName = fixColumnName(columnName);
...@@ -85,9 +89,9 @@ public class ColumnNamer { ...@@ -85,9 +89,9 @@ public class ColumnNamer {
} }
} }
// try a name derived from the column expression SQL // try a name derived from the column expression SQL
if (columnName == null && columnExp.getColumnName() != null && if (columnName == null && columnExp.getColumnName() != null
!DEFAULT_COLUMN_NAME.equals(columnExp.getColumnName())) { && !DEFAULT_COLUMN_NAME.equals(columnExp.getColumnName())) {
columnName = columnExp.getColumnName(); columnName = columnExp.getColumnName();
if (!isAllowableColumnName(columnName)) { if (!isAllowableColumnName(columnName)) {
columnName = fixColumnName(columnName); columnName = fixColumnName(columnName);
} }
...@@ -96,9 +100,8 @@ public class ColumnNamer { ...@@ -96,9 +100,8 @@ public class ColumnNamer {
} }
} }
// try a name derived from the column expression plan SQL // try a name derived from the column expression plan SQL
if (columnName == null && columnExp.getSQL() != null && if (columnName == null && columnExp.getSQL() != null && !DEFAULT_COLUMN_NAME.equals(columnExp.getSQL())) {
!DEFAULT_COLUMN_NAME.equals(columnExp.getSQL())) { columnName = columnExp.getSQL();
columnName = columnExp.getSQL();
if (!isAllowableColumnName(columnName)) { if (!isAllowableColumnName(columnName)) {
columnName = fixColumnName(columnName); columnName = fixColumnName(columnName);
} }
...@@ -121,7 +124,7 @@ public class ColumnNamer { ...@@ -121,7 +124,7 @@ public class ColumnNamer {
String newColumnName = columnName; String newColumnName = columnName;
int loopCount = 2; int loopCount = 2;
while (existingColumnNames.contains(newColumnName)) { while (existingColumnNames.contains(newColumnName)) {
String loopCountString = "_"+loopCount; String loopCountString = "_" + loopCount;
newColumnName = columnName.substring(0, newColumnName = columnName.substring(0,
Math.min(columnName.length(), configuration.getMaxIdentiferLength() - loopCountString.length())) Math.min(columnName.length(), configuration.getMaxIdentiferLength() - loopCountString.length()))
+ loopCountString; + loopCountString;
...@@ -130,10 +133,10 @@ public class ColumnNamer { ...@@ -130,10 +133,10 @@ public class ColumnNamer {
return newColumnName; return newColumnName;
} }
public boolean isAllowableColumnName(String proposedName){ public boolean isAllowableColumnName(String proposedName) {
// check null // check null
if (proposedName == null){ if (proposedName == null) {
return false; return false;
} }
// check size limits // check size limits
...@@ -152,8 +155,8 @@ public class ColumnNamer { ...@@ -152,8 +155,8 @@ public class ColumnNamer {
proposedName = match.replaceAll(""); proposedName = match.replaceAll("");
// check size limits - then truncate // check size limits - then truncate
if (proposedName.length() > configuration.getMaxIdentiferLength()){ if (proposedName.length() > configuration.getMaxIdentiferLength()) {
proposedName=proposedName.substring(0, configuration.getMaxIdentiferLength()); proposedName = proposedName.substring(0, configuration.getMaxIdentiferLength());
} }
return proposedName; return proposedName;
......
package org.h2.util; /*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
import java.util.regex.Pattern; * and the EPL 1.0 (http://h2database.com/html/license.html).
import org.h2.engine.Mode.ModeEnum; */
import static org.h2.engine.Mode.ModeEnum.*; package org.h2.util;
import org.h2.message.DbException;
import java.util.regex.Pattern;
public class ColumnNamerConfiguration { import org.h2.engine.Mode.ModeEnum;
private static final String DEFAULT_COMMAND = "DEFAULT"; import static org.h2.engine.Mode.ModeEnum.*;
private static final String REGULAR_EXPRESSION_MATCH_DISALLOWED = "REGULAR_EXPRESSION_MATCH_DISALLOWED = "; import org.h2.message.DbException;
private static final String REGULAR_EXPRESSION_MATCH_ALLOWED = "REGULAR_EXPRESSION_MATCH_ALLOWED = ";
private static final String DEFAULT_COLUMN_NAME_PATTERN = "DEFAULT_COLUMN_NAME_PATTERN = "; /**
private static final String MAX_IDENTIFIER_LENGTH = "MAX_IDENTIFIER_LENGTH = "; * The configuration for the allowed column names.
private static final String EMULATE_COMMAND = "EMULATE = "; */
private static final String GENERATE_UNIQUE_COLUMN_NAMES = "GENERATE_UNIQUE_COLUMN_NAMES = "; public class ColumnNamerConfiguration {
private int maxIdentiferLength; private static final String DEFAULT_COMMAND = "DEFAULT";
private String regularExpressionMatchAllowed; private static final String REGULAR_EXPRESSION_MATCH_DISALLOWED = "REGULAR_EXPRESSION_MATCH_DISALLOWED = ";
private String regularExpressionMatchDisallowed; private static final String REGULAR_EXPRESSION_MATCH_ALLOWED = "REGULAR_EXPRESSION_MATCH_ALLOWED = ";
private String defaultColumnNamePattern; private static final String DEFAULT_COLUMN_NAME_PATTERN = "DEFAULT_COLUMN_NAME_PATTERN = ";
private boolean generateUniqueColumnNames; private static final String MAX_IDENTIFIER_LENGTH = "MAX_IDENTIFIER_LENGTH = ";
private Pattern compiledRegularExpressionMatchAllowed; private static final String EMULATE_COMMAND = "EMULATE = ";
private Pattern compiledRegularExpressionMatchDisallowed; private static final String GENERATE_UNIQUE_COLUMN_NAMES = "GENERATE_UNIQUE_COLUMN_NAMES = ";
public ColumnNamerConfiguration(int maxIdentiferLength, String regularExpressionMatchAllowed, private int maxIdentiferLength;
String regularExpressionMatchDisallowed, String defaultColumnNamePattern, private String regularExpressionMatchAllowed;
boolean generateUniqueColumnNames) { private String regularExpressionMatchDisallowed;
private String defaultColumnNamePattern;
this.maxIdentiferLength = maxIdentiferLength; private boolean generateUniqueColumnNames;
this.regularExpressionMatchAllowed = regularExpressionMatchAllowed; private Pattern compiledRegularExpressionMatchAllowed;
this.regularExpressionMatchDisallowed = regularExpressionMatchDisallowed; private Pattern compiledRegularExpressionMatchDisallowed;
this.defaultColumnNamePattern = defaultColumnNamePattern;
this.generateUniqueColumnNames = generateUniqueColumnNames; public ColumnNamerConfiguration(int maxIdentiferLength, String regularExpressionMatchAllowed,
String regularExpressionMatchDisallowed, String defaultColumnNamePattern,
compiledRegularExpressionMatchAllowed = Pattern.compile(regularExpressionMatchAllowed); boolean generateUniqueColumnNames) {
compiledRegularExpressionMatchDisallowed = Pattern.compile(regularExpressionMatchDisallowed);
} this.maxIdentiferLength = maxIdentiferLength;
this.regularExpressionMatchAllowed = regularExpressionMatchAllowed;
public int getMaxIdentiferLength() { this.regularExpressionMatchDisallowed = regularExpressionMatchDisallowed;
return maxIdentiferLength; this.defaultColumnNamePattern = defaultColumnNamePattern;
} this.generateUniqueColumnNames = generateUniqueColumnNames;
public void setMaxIdentiferLength(int maxIdentiferLength) { compiledRegularExpressionMatchAllowed = Pattern.compile(regularExpressionMatchAllowed);
this.maxIdentiferLength = Math.max(30,maxIdentiferLength); compiledRegularExpressionMatchDisallowed = Pattern.compile(regularExpressionMatchDisallowed);
if (maxIdentiferLength != getMaxIdentiferLength()) { }
throw DbException.getInvalidValueException("Illegal value (<30) in SET COLUMN_NAME_RULES",
"MAX_IDENTIFIER_LENGTH=" + maxIdentiferLength); public int getMaxIdentiferLength() {
} return maxIdentiferLength;
} }
public String getRegularExpressionMatchAllowed() { public void setMaxIdentiferLength(int maxIdentiferLength) {
return regularExpressionMatchAllowed; this.maxIdentiferLength = Math.max(30, maxIdentiferLength);
} if (maxIdentiferLength != getMaxIdentiferLength()) {
throw DbException.getInvalidValueException("Illegal value (<30) in SET COLUMN_NAME_RULES",
public void setRegularExpressionMatchAllowed(String regularExpressionMatchAllowed) { "MAX_IDENTIFIER_LENGTH=" + maxIdentiferLength);
this.regularExpressionMatchAllowed = regularExpressionMatchAllowed; }
} }
public String getRegularExpressionMatchDisallowed() { public String getRegularExpressionMatchAllowed() {
return regularExpressionMatchDisallowed; return regularExpressionMatchAllowed;
} }
public void setRegularExpressionMatchDisallowed(String regularExpressionMatchDisallowed) { public void setRegularExpressionMatchAllowed(String regularExpressionMatchAllowed) {
this.regularExpressionMatchDisallowed = regularExpressionMatchDisallowed; this.regularExpressionMatchAllowed = regularExpressionMatchAllowed;
} }
public String getDefaultColumnNamePattern() { public String getRegularExpressionMatchDisallowed() {
return defaultColumnNamePattern; return regularExpressionMatchDisallowed;
} }
public void setDefaultColumnNamePattern(String defaultColumnNamePattern) { public void setRegularExpressionMatchDisallowed(String regularExpressionMatchDisallowed) {
this.defaultColumnNamePattern = defaultColumnNamePattern; this.regularExpressionMatchDisallowed = regularExpressionMatchDisallowed;
} }
public Pattern getCompiledRegularExpressionMatchAllowed() { public String getDefaultColumnNamePattern() {
return compiledRegularExpressionMatchAllowed; return defaultColumnNamePattern;
} }
public void setCompiledRegularExpressionMatchAllowed(Pattern compiledRegularExpressionMatchAllowed) { public void setDefaultColumnNamePattern(String defaultColumnNamePattern) {
this.compiledRegularExpressionMatchAllowed = compiledRegularExpressionMatchAllowed; this.defaultColumnNamePattern = defaultColumnNamePattern;
} }
public Pattern getCompiledRegularExpressionMatchDisallowed() { public Pattern getCompiledRegularExpressionMatchAllowed() {
return compiledRegularExpressionMatchDisallowed; return compiledRegularExpressionMatchAllowed;
} }
public void setCompiledRegularExpressionMatchDisallowed(Pattern compiledRegularExpressionMatchDisallowed) { public void setCompiledRegularExpressionMatchAllowed(Pattern compiledRegularExpressionMatchAllowed) {
this.compiledRegularExpressionMatchDisallowed = compiledRegularExpressionMatchDisallowed; this.compiledRegularExpressionMatchAllowed = compiledRegularExpressionMatchAllowed;
} }
public void configure(String stringValue) { public Pattern getCompiledRegularExpressionMatchDisallowed() {
try{ return compiledRegularExpressionMatchDisallowed;
if (stringValue.equalsIgnoreCase(DEFAULT_COMMAND)) { }
configure(REGULAR);
} else if (stringValue.startsWith(EMULATE_COMMAND)) { public void setCompiledRegularExpressionMatchDisallowed(Pattern compiledRegularExpressionMatchDisallowed) {
configure(ModeEnum.valueOf( this.compiledRegularExpressionMatchDisallowed = compiledRegularExpressionMatchDisallowed;
unquoteString(stringValue.substring(EMULATE_COMMAND.length())))); }
} else if (stringValue.startsWith(MAX_IDENTIFIER_LENGTH)) {
int maxLength = Integer.parseInt(stringValue.substring(MAX_IDENTIFIER_LENGTH.length())); public void configure(String stringValue) {
setMaxIdentiferLength(maxLength); try {
} else if (stringValue.startsWith(GENERATE_UNIQUE_COLUMN_NAMES)) { if (stringValue.equalsIgnoreCase(DEFAULT_COMMAND)) {
setGenerateUniqueColumnNames( configure(REGULAR);
Integer.parseInt(stringValue.substring(GENERATE_UNIQUE_COLUMN_NAMES.length())) == 1); } else if (stringValue.startsWith(EMULATE_COMMAND)) {
} else if (stringValue.startsWith(DEFAULT_COLUMN_NAME_PATTERN)) { configure(ModeEnum.valueOf(unquoteString(stringValue.substring(EMULATE_COMMAND.length()))));
setDefaultColumnNamePattern( } else if (stringValue.startsWith(MAX_IDENTIFIER_LENGTH)) {
unquoteString(stringValue.substring(DEFAULT_COLUMN_NAME_PATTERN.length()))); int maxLength = Integer.parseInt(stringValue.substring(MAX_IDENTIFIER_LENGTH.length()));
} else if (stringValue.startsWith(REGULAR_EXPRESSION_MATCH_ALLOWED)) { setMaxIdentiferLength(maxLength);
setRegularExpressionMatchAllowed( } else if (stringValue.startsWith(GENERATE_UNIQUE_COLUMN_NAMES)) {
unquoteString(stringValue.substring(REGULAR_EXPRESSION_MATCH_ALLOWED.length()))); setGenerateUniqueColumnNames(
} else if (stringValue.startsWith(REGULAR_EXPRESSION_MATCH_DISALLOWED)) { Integer.parseInt(stringValue.substring(GENERATE_UNIQUE_COLUMN_NAMES.length())) == 1);
setRegularExpressionMatchDisallowed( } else if (stringValue.startsWith(DEFAULT_COLUMN_NAME_PATTERN)) {
unquoteString(stringValue.substring(REGULAR_EXPRESSION_MATCH_DISALLOWED.length()))); setDefaultColumnNamePattern(
} else { unquoteString(stringValue.substring(DEFAULT_COLUMN_NAME_PATTERN.length())));
throw DbException.getInvalidValueException("SET COLUMN_NAME_RULES: unknown id:" + stringValue, } else if (stringValue.startsWith(REGULAR_EXPRESSION_MATCH_ALLOWED)) {
stringValue); setRegularExpressionMatchAllowed(
unquoteString(stringValue.substring(REGULAR_EXPRESSION_MATCH_ALLOWED.length())));
} } else if (stringValue.startsWith(REGULAR_EXPRESSION_MATCH_DISALLOWED)) {
setRegularExpressionMatchDisallowed(
recompilePatterns(); unquoteString(stringValue.substring(REGULAR_EXPRESSION_MATCH_DISALLOWED.length())));
} } else {
//Including NumberFormatException|PatternSyntaxException throw DbException.getInvalidValueException("SET COLUMN_NAME_RULES: unknown id:" + stringValue,
catch(RuntimeException e){ stringValue);
throw DbException.getInvalidValueException("SET COLUMN_NAME_RULES:"+e.getMessage(), }
stringValue); recompilePatterns();
}
} // Including NumberFormatException|PatternSyntaxException
} catch (RuntimeException e) {
throw DbException.getInvalidValueException("SET COLUMN_NAME_RULES:" + e.getMessage(), stringValue);
private void recompilePatterns() {
try{ }
// recompile RE patterns }
setCompiledRegularExpressionMatchAllowed(Pattern.compile(getRegularExpressionMatchAllowed()));
setCompiledRegularExpressionMatchDisallowed(Pattern.compile(getRegularExpressionMatchDisallowed())); private void recompilePatterns() {
} try {
catch(Exception e){ // recompile RE patterns
configure(REGULAR); setCompiledRegularExpressionMatchAllowed(Pattern.compile(getRegularExpressionMatchAllowed()));
throw e; setCompiledRegularExpressionMatchDisallowed(Pattern.compile(getRegularExpressionMatchDisallowed()));
} } catch (Exception e) {
} configure(REGULAR);
throw e;
public static ColumnNamerConfiguration getDefault(){ }
return new ColumnNamerConfiguration(Integer.MAX_VALUE, "(?m)(?s).+", "(?m)(?s)[\\x00]", "_UNNAMED_$$",false); }
}
public static ColumnNamerConfiguration getDefault() {
private static String unquoteString(String s){ return new ColumnNamerConfiguration(Integer.MAX_VALUE, "(?m)(?s).+", "(?m)(?s)[\\x00]", "_UNNAMED_$$", false);
if(s.startsWith("'") && s.endsWith("'")){ }
s = s.substring(1, s.length()-1);
return s; private static String unquoteString(String s) {
} if (s.startsWith("'") && s.endsWith("'")) {
return s; s = s.substring(1, s.length() - 1);
} return s;
}
public boolean isGenerateUniqueColumnNames() { return s;
return generateUniqueColumnNames; }
}
public boolean isGenerateUniqueColumnNames() {
public void setGenerateUniqueColumnNames(boolean generateUniqueColumnNames) { return generateUniqueColumnNames;
this.generateUniqueColumnNames = generateUniqueColumnNames; }
}
public void setGenerateUniqueColumnNames(boolean generateUniqueColumnNames) {
public void configure(ModeEnum modeEnum) { this.generateUniqueColumnNames = generateUniqueColumnNames;
switch(modeEnum){ }
case Oracle:
// Nonquoted identifiers can contain only alphanumeric characters public void configure(ModeEnum modeEnum) {
// from your database character set and the underscore (_), dollar switch (modeEnum) {
// sign ($), and pound sign (#). case Oracle:
setMaxIdentiferLength(128); // Nonquoted identifiers can contain only alphanumeric characters
setRegularExpressionMatchAllowed("(?m)(?s)\"?[A-Za-z0-9_\\$#]+\"?"); // from your database character set and the underscore (_), dollar
setRegularExpressionMatchDisallowed("(?m)(?s)[^A-Za-z0-9_\"\\$#]"); // sign ($), and pound sign (#).
setDefaultColumnNamePattern("_UNNAMED_$$"); setMaxIdentiferLength(128);
setGenerateUniqueColumnNames(false); setRegularExpressionMatchAllowed("(?m)(?s)\"?[A-Za-z0-9_\\$#]+\"?");
break; setRegularExpressionMatchDisallowed("(?m)(?s)[^A-Za-z0-9_\"\\$#]");
setDefaultColumnNamePattern("_UNNAMED_$$");
case MSSQLServer: setGenerateUniqueColumnNames(false);
// https://docs.microsoft.com/en-us/sql/sql-server/maximum-capacity-specifications-for-sql-server break;
setMaxIdentiferLength(128);
setRegularExpressionMatchAllowed("(?m)(?s)[A-Za-z0-9_\\[\\]]+");// allows [] around names case MSSQLServer:
setRegularExpressionMatchDisallowed("(?m)(?s)[^A-Za-z0-9_\\[\\]]"); // https://docs.microsoft.com/en-us/sql/sql-server/maximum-capacity-specifications-for-sql-server
setDefaultColumnNamePattern("_UNNAMED_$$"); setMaxIdentiferLength(128);
setGenerateUniqueColumnNames(false); // allows [] around names
break; setRegularExpressionMatchAllowed("(?m)(?s)[A-Za-z0-9_\\[\\]]+");
setRegularExpressionMatchDisallowed("(?m)(?s)[^A-Za-z0-9_\\[\\]]");
case PostgreSQL: setDefaultColumnNamePattern("_UNNAMED_$$");
setMaxIdentiferLength(63);// this default can be changed to 128 by postgres config setGenerateUniqueColumnNames(false);
setRegularExpressionMatchAllowed("(?m)(?s)[A-Za-z0-9_\\$]+"); break;
setRegularExpressionMatchDisallowed("(?m)(?s)[^A-Za-z0-9_\\$]");
setDefaultColumnNamePattern("_UNNAMED_$$"); case PostgreSQL:
setGenerateUniqueColumnNames(false); // this default can be changed to 128 by postgres config
break; setMaxIdentiferLength(63);
setRegularExpressionMatchAllowed("(?m)(?s)[A-Za-z0-9_\\$]+");
case MySQL: setRegularExpressionMatchDisallowed("(?m)(?s)[^A-Za-z0-9_\\$]");
//https://dev.mysql.com/doc/refman/5.7/en/identifiers.html setDefaultColumnNamePattern("_UNNAMED_$$");
setMaxIdentiferLength(64); setGenerateUniqueColumnNames(false);
setRegularExpressionMatchAllowed("(?m)(?s)`?[A-Za-z0-9_`\\$]+`?"); break;
setRegularExpressionMatchDisallowed("(?m)(?s)[^A-Za-z0-9_`\\$]");
setDefaultColumnNamePattern("_UNNAMED_$$"); case MySQL:
setGenerateUniqueColumnNames(false); // https://dev.mysql.com/doc/refman/5.7/en/identifiers.html
break; setMaxIdentiferLength(64);
setRegularExpressionMatchAllowed("(?m)(?s)`?[A-Za-z0-9_`\\$]+`?");
default: setRegularExpressionMatchDisallowed("(?m)(?s)[^A-Za-z0-9_`\\$]");
case REGULAR: setDefaultColumnNamePattern("_UNNAMED_$$");
case DB2: setGenerateUniqueColumnNames(false);
case Derby: break;
case HSQLDB:
case Ignite: default:
setMaxIdentiferLength(Integer.MAX_VALUE); case REGULAR:
setRegularExpressionMatchAllowed("(?m)(?s).+"); case DB2:
setRegularExpressionMatchDisallowed("(?m)(?s)[\\x00]"); case Derby:
setDefaultColumnNamePattern("_UNNAMED_$$"); case HSQLDB:
setGenerateUniqueColumnNames(false); case Ignite:
break; setMaxIdentiferLength(Integer.MAX_VALUE);
} setRegularExpressionMatchAllowed("(?m)(?s).+");
recompilePatterns(); setRegularExpressionMatchDisallowed("(?m)(?s)[\\x00]");
} setDefaultColumnNamePattern("_UNNAMED_$$");
setGenerateUniqueColumnNames(false);
break;
}
recompilePatterns();
}
} }
\ No newline at end of file
...@@ -227,10 +227,10 @@ import org.h2.test.unit.TestValueHashMap; ...@@ -227,10 +227,10 @@ import org.h2.test.unit.TestValueHashMap;
import org.h2.test.unit.TestValueMemory; import org.h2.test.unit.TestValueMemory;
import org.h2.test.utils.OutputCatcher; import org.h2.test.utils.OutputCatcher;
import org.h2.test.utils.SelfDestructor; import org.h2.test.utils.SelfDestructor;
import org.h2.test.utils.TestColumnNamer;
import org.h2.tools.DeleteDbFiles; import org.h2.tools.DeleteDbFiles;
import org.h2.tools.Server; import org.h2.tools.Server;
import org.h2.util.AbbaLockingDetector; import org.h2.util.AbbaLockingDetector;
import org.h2.util.TestColumnNamer;
import org.h2.util.New; import org.h2.util.New;
import org.h2.util.Profiler; import org.h2.util.Profiler;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
...@@ -784,7 +784,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -784,7 +784,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
addTest(new TestReplace()); addTest(new TestReplace());
addTest(new TestSynonymForTable()); addTest(new TestSynonymForTable());
addTest(new TestColumnNamer()); addTest(new TestColumnNamer());
// jaqu // jaqu
addTest(new AliasMapTest()); addTest(new AliasMapTest());
......
...@@ -133,6 +133,12 @@ public class BenchB implements Bench, Runnable { ...@@ -133,6 +133,12 @@ public class BenchB implements Bench, Runnable {
// db.end(); // db.end();
} }
/**
* Get the number of transactions per client.
*
* @param size test size
* @return the transactions per client
*/
protected int getTransactionsPerClient(int size) { protected int getTransactionsPerClient(int size) {
return size / 8; return size / 8;
} }
......
...@@ -124,7 +124,7 @@ public class TestAlter extends TestBase { ...@@ -124,7 +124,7 @@ public class TestAlter extends TestBase {
execute("alter table test drop column b"); execute("alter table test drop column b");
stat.execute("alter table test drop (d)"); stat.execute("alter table test drop (d)");
stat.execute("drop table test"); stat.execute("drop table test");
// Test-Case: Error if dropping all columns // Test-Case: Error if dropping all columns
stat.execute("create table test(id int, name varchar, name2 varchar)"); stat.execute("create table test(id int, name varchar, name2 varchar)");
assertThrows(ErrorCode.CANNOT_DROP_LAST_COLUMN, stat). assertThrows(ErrorCode.CANNOT_DROP_LAST_COLUMN, stat).
execute("alter table test drop column id, name, name2"); execute("alter table test drop column id, name, name2");
...@@ -218,7 +218,7 @@ public class TestAlter extends TestBase { ...@@ -218,7 +218,7 @@ public class TestAlter extends TestBase {
} }
// column and field names must be upper-case due to getMetaData sensitivity // column and field names must be upper-case due to getMetaData sensitivity
private void testAlterTableAddMultipleColumnsBefore() throws SQLException { private void testAlterTableAddMultipleColumnsBefore() throws SQLException {
stat.execute("create table T(X varchar)"); stat.execute("create table T(X varchar)");
...@@ -294,10 +294,10 @@ public class TestAlter extends TestBase { ...@@ -294,10 +294,10 @@ public class TestAlter extends TestBase {
stat.execute("insert into t values('Hello')"); stat.execute("insert into t values('Hello')");
stat.execute("drop table t"); stat.execute("drop table t");
} }
/** /**
* Test for fix "Change not-null / null -constraint to existing column" * Test for fix "Change not-null / null -constraint to existing column"
* (MySql/ORACLE - SQL style) that failed silently corrupting the changed * (MySql/ORACLE - SQL style) that failed silently corrupting the changed
* column.<br/> * column.<br/>
* Before the change (added after v1.4.196) following was observed: * Before the change (added after v1.4.196) following was observed:
* <pre> * <pre>
...@@ -311,7 +311,7 @@ public class TestAlter extends TestBase { ...@@ -311,7 +311,7 @@ public class TestAlter extends TestBase {
stat.execute("alter table T modify C int null"); stat.execute("alter table T modify C int null");
stat.execute("insert into T values(null)"); stat.execute("insert into T values(null)");
stat.execute("drop table T"); stat.execute("drop table T");
// This failed in v1.4.196 // This failed in v1.4.196
stat.execute("create table T (C int not null)"); stat.execute("create table T (C int not null)");
stat.execute("alter table T modify C null"); // Silently corrupted column C stat.execute("alter table T modify C null"); // Silently corrupted column C
stat.execute("insert into T values(null)"); // <- Fixed in v1.4.196 - NULL is allowed stat.execute("insert into T values(null)"); // <- Fixed in v1.4.196 - NULL is allowed
......
...@@ -34,7 +34,7 @@ public class TestCompatibilityOracle extends TestBase { ...@@ -34,7 +34,7 @@ public class TestCompatibilityOracle extends TestBase {
@Override @Override
public void test() throws Exception { public void test() throws Exception {
testNotNullSyntax(); testNotNullSyntax();
testTreatEmptyStringsAsNull(); testTreatEmptyStringsAsNull();
testDecimalScale(); testDecimalScale();
testPoundSymbolInColumnName(); testPoundSymbolInColumnName();
......
...@@ -1417,23 +1417,19 @@ public class TestFunctions extends TestBase implements AggregateFunction { ...@@ -1417,23 +1417,19 @@ public class TestFunctions extends TestBase implements AggregateFunction {
assertEquals(date, ToDateParser.toDate("113029", "J")); assertEquals(date, ToDateParser.toDate("113029", "J"));
if (Locale.getDefault() == Locale.ENGLISH) { if (Locale.getDefault() == Locale.ENGLISH) {
date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss") date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse("9999-12-31T23:59:59");
.parse("9999-12-31T23:59:59"); assertEquals(date, ToDateParser.toDate("31-DEC-9999 23:59:59", "DD-MON-YYYY HH24:MI:SS"));
assertEquals(date, ToDateParser.toDate("31-DEC-9999 23:59:59", assertEquals(date, ToDateParser.toDate("31-DEC-9999 23:59:59", "DD-MON-RRRR HH24:MI:SS"));
"DD-MON-YYYY HH24:MI:SS")); SimpleDateFormat ymd = new SimpleDateFormat("yyyy-MM-dd");
assertEquals(date, ToDateParser.toDate("31-DEC-9999 23:59:59", assertEquals(ymd.parse("0001-03-01"), ToDateParser.toDate("1-MAR-0001", "DD-MON-RRRR"));
"DD-MON-RRRR HH24:MI:SS")); assertEquals(ymd.parse("9999-03-01"), ToDateParser.toDate("1-MAR-9999", "DD-MON-RRRR"));
assertEquals(ymd.parse("2000-03-01"), ToDateParser.toDate("1-MAR-000", "DD-MON-RRRR"));
SimpleDateFormat ymd = new SimpleDateFormat("yyyy-MM-dd"); assertEquals(ymd.parse("1999-03-01"), ToDateParser.toDate("1-MAR-099", "DD-MON-RRRR"));
assertEquals(ymd.parse("0001-03-01"), ToDateParser.toDate("1-MAR-0001", "DD-MON-RRRR")); assertEquals(ymd.parse("0100-03-01"), ToDateParser.toDate("1-MAR-100", "DD-MON-RRRR"));
assertEquals(ymd.parse("9999-03-01"), ToDateParser.toDate("1-MAR-9999", "DD-MON-RRRR")); assertEquals(ymd.parse("2000-03-01"), ToDateParser.toDate("1-MAR-00", "DD-MON-RRRR"));
assertEquals(ymd.parse("2000-03-01"), ToDateParser.toDate("1-MAR-000", "DD-MON-RRRR")); assertEquals(ymd.parse("2049-03-01"), ToDateParser.toDate("1-MAR-49", "DD-MON-RRRR"));
assertEquals(ymd.parse("1999-03-01"), ToDateParser.toDate("1-MAR-099", "DD-MON-RRRR")); assertEquals(ymd.parse("1950-03-01"), ToDateParser.toDate("1-MAR-50", "DD-MON-RRRR"));
assertEquals(ymd.parse("0100-03-01"), ToDateParser.toDate("1-MAR-100", "DD-MON-RRRR")); assertEquals(ymd.parse("1999-03-01"), ToDateParser.toDate("1-MAR-99", "DD-MON-RRRR"));
assertEquals(ymd.parse("2000-03-01"), ToDateParser.toDate("1-MAR-00", "DD-MON-RRRR"));
assertEquals(ymd.parse("2049-03-01"), ToDateParser.toDate("1-MAR-49", "DD-MON-RRRR"));
assertEquals(ymd.parse("1950-03-01"), ToDateParser.toDate("1-MAR-50", "DD-MON-RRRR"));
assertEquals(ymd.parse("1999-03-01"), ToDateParser.toDate("1-MAR-99", "DD-MON-RRRR"));
} }
} }
...@@ -1734,22 +1730,22 @@ public class TestFunctions extends TestBase implements AggregateFunction { ...@@ -1734,22 +1730,22 @@ public class TestFunctions extends TestBase implements AggregateFunction {
"SELECT TO_CHAR(12345, '$9999') FROM DUAL"); "SELECT TO_CHAR(12345, '$9999') FROM DUAL");
String expected = String.format("%,d", 12345); String expected = String.format("%,d", 12345);
if (Locale.getDefault() == Locale.ENGLISH) { if (Locale.getDefault() == Locale.ENGLISH) {
assertResult(String.format("%5s12345", cs), stat, assertResult(String.format("%5s12345", cs), stat,
"SELECT TO_CHAR(12345, '$99999999') FROM DUAL"); "SELECT TO_CHAR(12345, '$99999999') FROM DUAL");
assertResult(String.format("%6s12,345.35", cs), stat, assertResult(String.format("%6s12,345.35", cs), stat,
"SELECT TO_CHAR(12345.345, '$99,999,999.99') FROM DUAL"); "SELECT TO_CHAR(12345.345, '$99,999,999.99') FROM DUAL");
assertResult(String.format("%5s%s", cs, expected), stat, assertResult(String.format("%5s%s", cs, expected), stat,
"SELECT TO_CHAR(12345.345, '$99g999g999') FROM DUAL"); "SELECT TO_CHAR(12345.345, '$99g999g999') FROM DUAL");
assertResult(" " + cs + "123.45", stat, assertResult(" " + cs + "123.45", stat,
"SELECT TO_CHAR(123.45, 'L999.99') FROM DUAL"); "SELECT TO_CHAR(123.45, 'L999.99') FROM DUAL");
assertResult(" -" + cs + "123.45", stat, assertResult(" -" + cs + "123.45", stat,
"SELECT TO_CHAR(-123.45, 'L999.99') FROM DUAL"); "SELECT TO_CHAR(-123.45, 'L999.99') FROM DUAL");
assertResult(cs + "123.45", stat, assertResult(cs + "123.45", stat,
"SELECT TO_CHAR(123.45, 'FML999.99') FROM DUAL"); "SELECT TO_CHAR(123.45, 'FML999.99') FROM DUAL");
assertResult(" " + cs + "123.45", stat, assertResult(" " + cs + "123.45", stat,
"SELECT TO_CHAR(123.45, 'U999.99') FROM DUAL"); "SELECT TO_CHAR(123.45, 'U999.99') FROM DUAL");
assertResult(" " + cs + "123.45", stat, assertResult(" " + cs + "123.45", stat,
"SELECT TO_CHAR(123.45, 'u999.99') FROM DUAL"); "SELECT TO_CHAR(123.45, 'u999.99') FROM DUAL");
} }
assertResult(" 12,345.35", stat, assertResult(" 12,345.35", stat,
...@@ -2130,12 +2126,12 @@ public class TestFunctions extends TestBase implements AggregateFunction { ...@@ -2130,12 +2126,12 @@ public class TestFunctions extends TestBase implements AggregateFunction {
getClass().getName() + ".currentTimestamp\""); getClass().getName() + ".currentTimestamp\"");
stat.execute("set BUILTIN_ALIAS_OVERRIDE true"); stat.execute("set BUILTIN_ALIAS_OVERRIDE true");
stat.execute("create alias CURRENT_TIMESTAMP for \"" + stat.execute("create alias CURRENT_TIMESTAMP for \"" +
getClass().getName() + ".currentTimestampOverride\""); getClass().getName() + ".currentTimestampOverride\"");
assertCallResult("3141", stat, "CURRENT_TIMESTAMP"); assertCallResult("3141", stat, "CURRENT_TIMESTAMP");
conn.close(); conn.close();
} }
...@@ -2456,7 +2452,7 @@ public class TestFunctions extends TestBase implements AggregateFunction { ...@@ -2456,7 +2452,7 @@ public class TestFunctions extends TestBase implements AggregateFunction {
} }
return new Object[] { buff.toString() }; return new Object[] { buff.toString() };
} }
/** /**
* This method is called via reflection from the database. * This method is called via reflection from the database.
*/ */
......
...@@ -500,7 +500,7 @@ public class TestGeneralCommonTableQueries extends TestBase { ...@@ -500,7 +500,7 @@ public class TestGeneralCommonTableQueries extends TestBase {
ResultSet rs; ResultSet rs;
String SETUP_SQL = String SETUP_SQL =
"DROP TABLE IF EXISTS A; " "DROP TABLE IF EXISTS A; "
+"DROP TABLE IF EXISTS B; " +"DROP TABLE IF EXISTS B; "
+"DROP TABLE IF EXISTS C; " +"DROP TABLE IF EXISTS C; "
+"CREATE TABLE A(VAL VARCHAR(255)); " +"CREATE TABLE A(VAL VARCHAR(255)); "
...@@ -520,18 +520,17 @@ public class TestGeneralCommonTableQueries extends TestBase { ...@@ -520,18 +520,17 @@ public class TestGeneralCommonTableQueries extends TestBase {
+"INSERT INTO C VALUES(null, 'banapple'); " +"INSERT INTO C VALUES(null, 'banapple'); "
+"INSERT INTO A VALUES('meat'); " +"INSERT INTO A VALUES('meat'); "
; ;
String WITH_QUERY = String WITH_QUERY = "WITH BB as (SELECT \n" +
"WITH BB as (SELECT \n" "sum(1) as X, \n" +
+"sum(1) as X, \n" "a \n" +
+"a \n" "FROM B \n" +
+"FROM B \n" "JOIN C ON B.val=C.b \n" +
+"JOIN C ON B.val=C.b \n" "GROUP BY a) \n" +
+"GROUP BY a) \n" "SELECT \n" +
+"SELECT \n" "A.val, \n" +
+"A.val, \n" "sum(SELECT X FROM BB WHERE BB.a IS A.val)\n" +
+"sum(SELECT X FROM BB WHERE BB.a IS A.val)\n"//AS SUM_X // AS SUM_X
+"FROM A \n" "FROM A \n" + "GROUP BY A.val";
+"GROUP BY A.val";
for(int queryRunTries=1;queryRunTries<4;queryRunTries++){ for(int queryRunTries=1;queryRunTries<4;queryRunTries++){
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
......
/* /*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html). * and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group * Initial Developer: H2 Group
*/ */
package org.h2.test.db; package org.h2.test.db;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import org.h2.api.Trigger; import org.h2.api.Trigger;
import org.h2.test.TestBase; import org.h2.test.TestBase;
/** /**
* Test merge using syntax. * Test merge using syntax.
*/ */
public class TestMergeUsing extends TestBase implements Trigger { public class TestMergeUsing extends TestBase implements Trigger {
private static final String GATHER_ORDERED_RESULTS_SQL = "SELECT ID, NAME FROM PARENT ORDER BY ID ASC"; private static final String GATHER_ORDERED_RESULTS_SQL = "SELECT ID, NAME FROM PARENT ORDER BY ID ASC";
private static int triggerTestingUpdateCount = 0; private static int triggerTestingUpdateCount = 0;
/** /**
* Run just this test. * Run just this test.
* *
* @param a ignored * @param a ignored
*/ */
public static void main(String... a) throws Exception { public static void main(String... a) throws Exception {
TestBase.createCaller().init().test(); TestBase.createCaller().init().test();
} }
private String triggerName; private String triggerName;
@Override @Override
public void test() throws Exception { public void test() throws Exception {
// Simple ID,NAME inserts, target table with PK initially empty // Simple ID,NAME inserts, target table with PK initially empty
testMergeUsing( testMergeUsing(
"CREATE TABLE PARENT(ID INT, NAME VARCHAR, PRIMARY KEY(ID) );", "CREATE TABLE PARENT(ID INT, NAME VARCHAR, PRIMARY KEY(ID) );",
"MERGE INTO PARENT AS P USING (SELECT X AS ID, 'Marcy'||X AS 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) " + "FROM SYSTEM_RANGE(1,2) ) AS S ON (P.ID = S.ID AND 1=1 AND S.ID = P.ID) " +
"WHEN MATCHED THEN " + "WHEN MATCHED THEN " +
"UPDATE SET P.NAME = S.NAME WHERE 2 = 2 WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (S.ID, S.NAME)", "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, GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2)", 2); "SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2)", 2);
// Simple NAME updates, target table missing PK // Simple NAME updates, target table missing PK
testMergeUsing( testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );", "CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );",
"MERGE INTO PARENT AS P USING (" + "MERGE INTO PARENT AS P USING (" +
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) ) AS S " + "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) " + "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 " + "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)", "INSERT (ID, NAME) VALUES (S.ID, S.NAME)",
GATHER_ORDERED_RESULTS_SQL, GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X||X AS NAME FROM SYSTEM_RANGE(1,2)", "SELECT X AS ID, 'Marcy'||X||X AS NAME FROM SYSTEM_RANGE(1,2)",
2); 2);
// No NAME updates, WHERE clause is always false, insert clause missing // No NAME updates, WHERE clause is always false, insert clause missing
testMergeUsing( testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );", "CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );",
"MERGE INTO PARENT AS P USING (" + "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) " + "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", "WHEN MATCHED THEN UPDATE SET P.NAME = S.NAME||S.ID WHERE 1 = 2",
GATHER_ORDERED_RESULTS_SQL, GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2)", 0); "SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2)", 0);
// No NAME updates, no WHERE clause, insert clause missing // No NAME updates, no WHERE clause, insert clause missing
testMergeUsing( testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );", "CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );",
"MERGE INTO PARENT AS P USING (" + "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) " + "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", "WHEN MATCHED THEN UPDATE SET P.NAME = S.NAME||S.ID",
GATHER_ORDERED_RESULTS_SQL, GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X||X AS NAME FROM SYSTEM_RANGE(1,2)", "SELECT X AS ID, 'Marcy'||X||X AS NAME FROM SYSTEM_RANGE(1,2)",
2); 2);
// Two delete updates done, no WHERE clause, insert clause missing // Two delete updates done, no WHERE clause, insert clause missing
testMergeUsing( testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );", "CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );",
"MERGE INTO PARENT AS P USING (" + "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) " + "SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) ) AS S ON (P.ID = S.ID) " +
"WHEN MATCHED THEN DELETE", "WHEN MATCHED THEN DELETE",
GATHER_ORDERED_RESULTS_SQL, GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) WHERE 1=0", "SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) WHERE 1=0",
2); 2);
// One insert, one update one delete happens, target table missing PK // One insert, one update one delete happens, target table missing PK
testMergeUsing( testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );", "CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );",
"MERGE INTO PARENT AS P USING (" + "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) " + "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 " + "WHEN MATCHED THEN UPDATE SET P.NAME = S.NAME||S.ID WHERE P.ID = 2 " +
"DELETE WHERE P.ID = 1 WHEN NOT MATCHED THEN " + "DELETE WHERE P.ID = 1 WHEN NOT MATCHED THEN " +
"INSERT (ID, NAME) VALUES (S.ID, S.NAME)", "INSERT (ID, NAME) VALUES (S.ID, S.NAME)",
GATHER_ORDERED_RESULTS_SQL, 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||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 AS NAME FROM SYSTEM_RANGE(3,3)",
3); 3);
// No updates happen: No insert defined, no update or delete happens due // No updates happen: No insert defined, no update or delete happens due
// to ON condition failing always, target table missing PK // to ON condition failing always, target table missing PK
testMergeUsing( testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );", "CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );",
"MERGE INTO PARENT AS P USING (" + "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) " + "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 " + "WHEN MATCHED THEN " +
"UPDATE SET P.NAME = S.NAME||S.ID WHERE P.ID = 2 DELETE WHERE P.ID = 1", "UPDATE SET P.NAME = S.NAME||S.ID WHERE P.ID = 2 DELETE WHERE P.ID = 1",
GATHER_ORDERED_RESULTS_SQL, GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2)", 0); "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 // One insert, one update one delete happens, target table missing PK
testMergeUsing( testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );" + "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) );", "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 " + "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 " + "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)", "INSERT (ID, NAME) VALUES (S.ID, S.NAME)",
GATHER_ORDERED_RESULTS_SQL, 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||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 AS NAME FROM SYSTEM_RANGE(3,3)",
3); 3);
// One insert, one update one delete happens, target table missing PK, // One insert, one update one delete happens, target table missing PK,
// no source alias // no source alias
testMergeUsing( testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );" + "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) );", "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 " + "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 " + "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)", "WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)",
GATHER_ORDERED_RESULTS_SQL, 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||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 AS NAME FROM SYSTEM_RANGE(3,3)",
3); 3);
// One insert, one update one delete happens, target table missing PK, // One insert, one update one delete happens, target table missing PK,
// no source or target alias // no source or target alias
testMergeUsing( testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );" + "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) );", "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 " + "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 " + "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)", "DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)",
GATHER_ORDERED_RESULTS_SQL, 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||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 AS NAME FROM SYSTEM_RANGE(3,3)",
3); 3);
// Only insert clause, no update or delete clauses // Only insert clause, no update or delete clauses
testMergeUsing( testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,1) );" + "CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,1) );" +
"DELETE FROM PARENT;", "DELETE FROM PARENT;",
"MERGE INTO PARENT AS P USING (" + "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) " + "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)", "WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (S.ID, S.NAME)",
GATHER_ORDERED_RESULTS_SQL, GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3)", 3); "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 // no insert, no update, no delete clauses - essentially a no-op
testMergeUsingException( testMergeUsingException(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,1) );" "CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,1) );"
+ "DELETE FROM PARENT;", + "DELETE FROM PARENT;",
"MERGE INTO PARENT AS P USING (" + "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)", "SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) ) AS S ON (P.ID = S.ID)",
GATHER_ORDERED_RESULTS_SQL, GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) WHERE X<0", "SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) WHERE X<0",
0, 0,
"At least UPDATE, DELETE or INSERT embedded statement must be supplied."); "At least UPDATE, DELETE or INSERT embedded statement must be supplied.");
// Two updates to same row - update and delete together - emptying the // Two updates to same row - update and delete together - emptying the
// parent table // parent table
testMergeUsing( testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,1) )", "CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,1) )",
"MERGE INTO PARENT AS P USING (" + "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) " + "SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) ) AS S ON (P.ID = S.ID) " +
"WHEN MATCHED THEN " + "WHEN MATCHED THEN " +
"UPDATE SET P.NAME = P.NAME||S.ID WHERE P.ID = 1 DELETE WHERE P.ID = 1 AND P.NAME = 'Marcy11'", "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, GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,1) WHERE X<0", "SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,1) WHERE X<0",
2); 2);
// Duplicate source keys but different ROWID update - so no error // Duplicate source keys but different ROWID update - so no error
// SQL standard says duplicate or repeated updates of same row in same // SQL standard says duplicate or repeated updates of same row in same
// statement should cause errors - but because first row is updated, // statement should cause errors - but because first row is updated,
// deleted (on source row 1) then inserted (on source row 2) // deleted (on source row 1) then inserted (on source row 2)
// it's considered different - with respect to to ROWID - so no error // it's considered different - with respect to to ROWID - so no error
// One insert, one update one delete happens (on same row) , target // One insert, one update one delete happens (on same row) , target
// table missing PK, no source or target alias // table missing PK, no source or target alias
testMergeUsing( testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,1) );" + "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) );", "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 " + "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 " + "UPDATE SET PARENT.NAME = SOURCE.NAME||SOURCE.ID WHERE PARENT.ID = 2 " +
"DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN " + "DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN " +
"INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)", "INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)",
GATHER_ORDERED_RESULTS_SQL, GATHER_ORDERED_RESULTS_SQL,
"SELECT 1 AS ID, 'Marcy'||X||X UNION ALL SELECT 1 AS ID, 'Marcy2'", "SELECT 1 AS ID, 'Marcy'||X||X UNION ALL SELECT 1 AS ID, 'Marcy2'",
2); 2);
// Multiple update on same row: SQL standard says duplicate or repeated // Multiple update on same row: SQL standard says duplicate or repeated
// updates in same statement should cause errors -but because first row // updates in same statement should cause errors -but because first row
// is updated, delete then insert it's considered different // is updated, delete then insert it's considered different
// One insert, one update one delete happens (on same row, which is // One insert, one update one delete happens (on same row, which is
// okay), then another update (which is illegal)target table missing PK, // okay), then another update (which is illegal)target table missing PK,
// no source or target alias // no source or target alias
testMergeUsingException( testMergeUsingException(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,1) );" "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) );", + "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 " + "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 " + "UPDATE SET PARENT.NAME = SOURCE.NAME||SOURCE.ID WHERE PARENT.ID = 2 " +
"DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN " + "DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN " +
"INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)", "INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)",
GATHER_ORDERED_RESULTS_SQL, GATHER_ORDERED_RESULTS_SQL,
"SELECT 1 AS ID, 'Marcy'||X||X UNION ALL SELECT 1 AS ID, 'Marcy2'", "SELECT 1 AS ID, 'Marcy'||X||X UNION ALL SELECT 1 AS ID, 'Marcy2'",
3, 3,
"Unique index or primary key violation: \"Merge using " + "Unique index or primary key violation: \"Merge using " +
"ON column expression, duplicate _ROWID_ target record " + "ON column expression, duplicate _ROWID_ target record " +
"already updated, deleted or inserted:_ROWID_=2:in:PUBLIC.PARENT:conflicting source row number:2"); "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 // Duplicate key updated 3 rows at once, only 1 expected
testMergeUsingException( testMergeUsingException(
"CREATE TABLE PARENT AS (SELECT 1 AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) );" "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) );", + "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 " + "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 " + "UPDATE SET PARENT.NAME = SOURCE.NAME||SOURCE.ID WHERE PARENT.ID = 2 " +
"DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN " + "DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN " +
"INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)", "INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)",
GATHER_ORDERED_RESULTS_SQL, 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||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 AS NAME FROM SYSTEM_RANGE(3,3)",
3, "Duplicate key updated 3 rows at once, only 1 expected"); 3, "Duplicate key updated 3 rows at once, only 1 expected");
// Missing target columns in ON expression // Missing target columns in ON expression
testMergeUsingException( testMergeUsingException(
"CREATE TABLE PARENT AS (SELECT 1 AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) );" "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) );", + "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 " + "MERGE INTO PARENT USING SOURCE ON (1 = SOURCE.ID) WHEN MATCHED THEN " +
"UPDATE SET PARENT.NAME = SOURCE.NAME||SOURCE.ID WHERE PARENT.ID = 2 " + "UPDATE SET PARENT.NAME = SOURCE.NAME||SOURCE.ID WHERE PARENT.ID = 2 " +
"DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN " + "DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN " +
"INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)", "INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)",
GATHER_ORDERED_RESULTS_SQL, 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||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 AS NAME FROM SYSTEM_RANGE(3,3)",
3, "No references to target columns found in ON clause"); 3, "No references to target columns found in ON clause");
// Missing source columns in ON expression // Missing source columns in ON expression
testMergeUsingException( testMergeUsingException(
"CREATE TABLE PARENT AS (SELECT 1 AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) );" "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) );", + "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 " + "MERGE INTO PARENT USING SOURCE ON (PARENT.ID = 1) WHEN MATCHED THEN " +
"UPDATE SET PARENT.NAME = SOURCE.NAME||SOURCE.ID WHERE PARENT.ID = 2 " + "UPDATE SET PARENT.NAME = SOURCE.NAME||SOURCE.ID WHERE PARENT.ID = 2 " +
"DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN " + "DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN " +
"INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)", "INSERT (ID, NAME) VALUES (SOURCE.ID, SOURCE.NAME)",
GATHER_ORDERED_RESULTS_SQL, 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||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 AS NAME FROM SYSTEM_RANGE(3,3)",
3, "No references to source columns found in ON clause"); 3, "No references to source columns found in ON clause");
// Insert does not insert correct values with respect to ON condition // Insert does not insert correct values with respect to ON condition
// (inserts ID value above 100, instead) // (inserts ID value above 100, instead)
testMergeUsingException( testMergeUsingException(
"CREATE TABLE PARENT AS (SELECT 1 AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(4,4) );" "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) );", + "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 " + "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 " + "UPDATE SET PARENT.NAME = SOURCE.NAME||SOURCE.ID WHERE PARENT.ID = 2 " +
"DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN " + "DELETE WHERE PARENT.ID = 1 WHEN NOT MATCHED THEN " +
"INSERT (ID, NAME) VALUES (SOURCE.ID+100, SOURCE.NAME)", "INSERT (ID, NAME) VALUES (SOURCE.ID+100, SOURCE.NAME)",
GATHER_ORDERED_RESULTS_SQL, GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(4,4)", 1, "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."); "Expected to find key after row inserted, but none found. Insert does not match ON condition.");
// One insert, one update one delete happens, target table missing PK, // One insert, one update one delete happens, target table missing PK,
// triggers update all NAME fields // triggers update all NAME fields
triggerTestingUpdateCount = 0; triggerTestingUpdateCount = 0;
testMergeUsing( testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2));" "CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2));"
+ getCreateTriggerSQL(), + getCreateTriggerSQL(),
"MERGE INTO PARENT AS P USING " + "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) " + "(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 " + "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)", "DELETE WHERE P.ID = 1 WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (S.ID, S.NAME)",
GATHER_ORDERED_RESULTS_SQL, GATHER_ORDERED_RESULTS_SQL,
"SELECT 2 AS ID, 'Marcy22-updated2' AS NAME UNION ALL " + "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 X AS ID, 'Marcy'||X||'-inserted'||X AS NAME FROM SYSTEM_RANGE(3,4)",
4); 4);
} }
/** /**
* Run a test case of the merge using syntax * Run a test case of the merge using syntax
* *
* @param setupSQL - one or more SQL statements to setup the case * @param setupSQL - one or more SQL statements to setup the case
* @param statementUnderTest - the merge statement being tested * @param statementUnderTest - the merge statement being tested
* @param gatherResultsSQL - a select which gathers the results of the merge * @param gatherResultsSQL - a select which gathers the results of the merge
* from the target table * from the target table
* @param expectedResultsSQL - a select which returns the expected results * @param expectedResultsSQL - a select which returns the expected results
* in the target table * in the target table
* @param expectedRowUpdateCount - how many updates should be expected from * @param expectedRowUpdateCount - how many updates should be expected from
* the merge using * the merge using
*/ */
private void testMergeUsing(String setupSQL, String statementUnderTest, private void testMergeUsing(String setupSQL, String statementUnderTest,
String gatherResultsSQL, String expectedResultsSQL, String gatherResultsSQL, String expectedResultsSQL,
int expectedRowUpdateCount) throws Exception { int expectedRowUpdateCount) throws Exception {
deleteDb("mergeUsingQueries"); deleteDb("mergeUsingQueries");
Connection conn = getConnection("mergeUsingQueries"); Connection conn = getConnection("mergeUsingQueries");
Statement stat; Statement stat;
PreparedStatement prep; PreparedStatement prep;
ResultSet rs; ResultSet rs;
int rowCountUpdate; int rowCountUpdate;
try { try {
stat = conn.createStatement(); stat = conn.createStatement();
stat.execute(setupSQL); stat.execute(setupSQL);
prep = conn.prepareStatement(statementUnderTest); prep = conn.prepareStatement(statementUnderTest);
rowCountUpdate = prep.executeUpdate(); rowCountUpdate = prep.executeUpdate();
// compare actual results from SQL result set with expected results // compare actual results from SQL result set with expected results
// - by diffing (aka set MINUS operation) // - by diffing (aka set MINUS operation)
rs = stat.executeQuery("( " + gatherResultsSQL + " ) MINUS ( " rs = stat.executeQuery("( " + gatherResultsSQL + " ) MINUS ( "
+ expectedResultsSQL + " )"); + expectedResultsSQL + " )");
int rowCount = 0; int rowCount = 0;
StringBuffer diffBuffer = new StringBuffer(""); StringBuffer diffBuffer = new StringBuffer("");
while (rs.next()) { while (rs.next()) {
rowCount++; rowCount++;
diffBuffer.append("|"); diffBuffer.append("|");
for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) { for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
diffBuffer.append(rs.getObject(i)); diffBuffer.append(rs.getObject(i));
diffBuffer.append("|\n"); diffBuffer.append("|\n");
} }
} }
assertEquals("Differences between expected and actual output found:" assertEquals("Differences between expected and actual output found:"
+ diffBuffer, 0, rowCount); + diffBuffer, 0, rowCount);
assertEquals("Expected update counts differ", assertEquals("Expected update counts differ",
expectedRowUpdateCount, rowCountUpdate); expectedRowUpdateCount, rowCountUpdate);
} finally { } finally {
conn.close(); conn.close();
deleteDb("mergeUsingQueries"); deleteDb("mergeUsingQueries");
} }
} }
/** /**
* Run a test case of the merge using syntax * Run a test case of the merge using syntax
* *
* @param setupSQL - one or more SQL statements to setup the case * @param setupSQL - one or more SQL statements to setup the case
* @param statementUnderTest - the merge statement being tested * @param statementUnderTest - the merge statement being tested
* @param gatherResultsSQL - a select which gathers the results of the merge * @param gatherResultsSQL - a select which gathers the results of the merge
* from the target table * from the target table
* @param expectedResultsSQL - a select which returns the expected results * @param expectedResultsSQL - a select which returns the expected results
* in the target table * in the target table
* @param expectedRowUpdateCount - how many updates should be expected from * @param expectedRowUpdateCount - how many updates should be expected from
* the merge using * the merge using
* @param exceptionMessage - the exception message expected * @param exceptionMessage - the exception message expected
*/ */
private void testMergeUsingException(String setupSQL, private void testMergeUsingException(String setupSQL,
String statementUnderTest, String gatherResultsSQL, String statementUnderTest, String gatherResultsSQL,
String expectedResultsSQL, int expectedRowUpdateCount, String expectedResultsSQL, int expectedRowUpdateCount,
String exceptionMessage) throws Exception { String exceptionMessage) throws Exception {
try { try {
testMergeUsing(setupSQL, statementUnderTest, gatherResultsSQL, testMergeUsing(setupSQL, statementUnderTest, gatherResultsSQL,
expectedResultsSQL, expectedRowUpdateCount); expectedResultsSQL, expectedRowUpdateCount);
} catch (RuntimeException | org.h2.jdbc.JdbcSQLException e) { } catch (RuntimeException | org.h2.jdbc.JdbcSQLException e) {
if (!e.getMessage().contains(exceptionMessage)) { if (!e.getMessage().contains(exceptionMessage)) {
e.printStackTrace(); e.printStackTrace();
} }
assertContains(e.getMessage(), exceptionMessage); assertContains(e.getMessage(), exceptionMessage);
return; return;
} }
fail("Failed to see exception with message:" + exceptionMessage); fail("Failed to see exception with message:" + exceptionMessage);
} }
@Override @Override
public void fire(Connection conn, Object[] oldRow, Object[] newRow) public void fire(Connection conn, Object[] oldRow, Object[] newRow)
throws SQLException { throws SQLException {
if (conn == null) { if (conn == null) {
throw new AssertionError("connection is null"); throw new AssertionError("connection is null");
} }
if (triggerName.startsWith("INS_BEFORE")) { if (triggerName.startsWith("INS_BEFORE")) {
newRow[1] = newRow[1] + "-inserted" + (++triggerTestingUpdateCount); newRow[1] = newRow[1] + "-inserted" + (++triggerTestingUpdateCount);
} else if (triggerName.startsWith("UPD_BEFORE")) { } else if (triggerName.startsWith("UPD_BEFORE")) {
newRow[1] = newRow[1] + "-updated" + (++triggerTestingUpdateCount); newRow[1] = newRow[1] + "-updated" + (++triggerTestingUpdateCount);
} else if (triggerName.startsWith("DEL_BEFORE")) { } else if (triggerName.startsWith("DEL_BEFORE")) {
oldRow[1] = oldRow[1] + "-deleted" + (++triggerTestingUpdateCount); oldRow[1] = oldRow[1] + "-deleted" + (++triggerTestingUpdateCount);
} }
} }
@Override @Override
public void close() { public void close() {
// ignore // ignore
} }
@Override @Override
public void remove() { public void remove() {
// ignore // ignore
} }
@Override @Override
public void init(Connection conn, String schemaName, String trigger, public void init(Connection conn, String schemaName, String trigger,
String tableName, boolean before, int type) { String tableName, boolean before, int type) {
this.triggerName = trigger; this.triggerName = trigger;
if (!"PARENT".equals(tableName)) { if (!"PARENT".equals(tableName)) {
throw new AssertionError("supposed to be PARENT"); throw new AssertionError("supposed to be PARENT");
} }
if ((trigger.endsWith("AFTER") && before) if ((trigger.endsWith("AFTER") && before)
|| (trigger.endsWith("BEFORE") && !before)) { || (trigger.endsWith("BEFORE") && !before)) {
throw new AssertionError( throw new AssertionError(
"triggerName: " + trigger + " before:" + before); "triggerName: " + trigger + " before:" + before);
} }
if ((trigger.startsWith("UPD") && type != UPDATE) if ((trigger.startsWith("UPD") && type != UPDATE)
|| (trigger.startsWith("INS") && type != INSERT) || (trigger.startsWith("INS") && type != INSERT)
|| (trigger.startsWith("DEL") && type != DELETE)) { || (trigger.startsWith("DEL") && type != DELETE)) {
throw new AssertionError( throw new AssertionError(
"triggerName: " + trigger + " type:" + type); "triggerName: " + trigger + " type:" + type);
} }
} }
private String getCreateTriggerSQL() { private String getCreateTriggerSQL() {
StringBuffer buf = new StringBuffer(); StringBuffer buf = new StringBuffer();
buf.append("CREATE TRIGGER INS_BEFORE " + "BEFORE INSERT ON PARENT " buf.append("CREATE TRIGGER INS_BEFORE " + "BEFORE INSERT ON PARENT "
+ "FOR EACH ROW NOWAIT CALL \"" + getClass().getName() + "\";"); + "FOR EACH ROW NOWAIT CALL \"" + getClass().getName() + "\";");
buf.append("CREATE TRIGGER UPD_BEFORE " + "BEFORE UPDATE ON PARENT " buf.append("CREATE TRIGGER UPD_BEFORE " + "BEFORE UPDATE ON PARENT "
+ "FOR EACH ROW NOWAIT CALL \"" + getClass().getName() + "\";"); + "FOR EACH ROW NOWAIT CALL \"" + getClass().getName() + "\";");
buf.append("CREATE TRIGGER DEL_BEFORE " + "BEFORE DELETE ON PARENT " buf.append("CREATE TRIGGER DEL_BEFORE " + "BEFORE DELETE ON PARENT "
+ "FOR EACH ROW NOWAIT CALL \"" + getClass().getName() + "\";"); + "FOR EACH ROW NOWAIT CALL \"" + getClass().getName() + "\";");
return buf.toString(); return buf.toString();
} }
} }
...@@ -58,28 +58,20 @@ public class TestConnection extends TestBase { ...@@ -58,28 +58,20 @@ public class TestConnection extends TestBase {
* This is needed when using h2 in websphere liberty. * This is needed when using h2 in websphere liberty.
*/ */
private void testSetInternalPropertyToInitialValue() throws SQLException { private void testSetInternalPropertyToInitialValue() throws SQLException {
// Use MySQL-mode since this allows all property names // Use MySQL-mode since this allows all property names
// (apart from h2 internal names). // (apart from h2 internal names).
Connection conn = getConnection("clientInfoMySQL;MODE=MySQL"); Connection conn = getConnection("clientInfoMySQL;MODE=MySQL");
String numServersPropertyName = "numServers";
String numServersPropertyName = "numServers"; String numServers = conn.getClientInfo(numServersPropertyName);
conn.setClientInfo(numServersPropertyName, numServers);
String numServers = conn.getClientInfo(numServersPropertyName); assertEquals(conn.getClientInfo(numServersPropertyName), numServers);
conn.close();
conn.setClientInfo(numServersPropertyName, numServers); }
assertEquals(conn.getClientInfo(numServersPropertyName), numServers);
conn.close();
}
private void testSetUnsupportedClientInfoProperties() throws SQLException { private void testSetUnsupportedClientInfoProperties() throws SQLException {
Connection conn = getConnection("clientInfo"); Connection conn = getConnection("clientInfo");
Properties properties = new Properties(); Properties properties = new Properties();
properties.put("ClientUser", "someUser"); properties.put("ClientUser", "someUser");
assertThrows(SQLClientInfoException.class, conn).setClientInfo(properties); assertThrows(SQLClientInfoException.class, conn).setClientInfo(properties);
conn.close(); conn.close();
} }
......
...@@ -77,7 +77,7 @@ public class TestScript extends TestBase { ...@@ -77,7 +77,7 @@ public class TestScript extends TestBase {
return; return;
} }
reconnectOften = !config.memory && config.big; reconnectOften = !config.memory && config.big;
testScript("testScript.sql"); testScript("testScript.sql");
testScript("altertable-index-reuse.sql"); testScript("altertable-index-reuse.sql");
testScript("query-optimisations.sql"); testScript("query-optimisations.sql");
......
...@@ -13,11 +13,11 @@ CREATE TABLE "users" ("id" bigint NOT NULL auto_increment PRIMARY KEY,"username" ...@@ -13,11 +13,11 @@ CREATE TABLE "users" ("id" bigint NOT NULL auto_increment PRIMARY KEY,"username"
ALTER TABLE "users" ADD CONSTRAINT "domainusername_key" UNIQUE ("domain","username"); ALTER TABLE "users" ADD CONSTRAINT "domainusername_key" UNIQUE ("domain","username");
> ok > ok
-- adds foreign key on domain - if domainusername_key didn't exist it would create unique index on domain, but it reuses the existing index
ALTER TABLE "users" ADD CONSTRAINT "udomain_fkey" FOREIGN KEY ("domain") REFERENCES "domains"("id") ON DELETE RESTRICT; ALTER TABLE "users" ADD CONSTRAINT "udomain_fkey" FOREIGN KEY ("domain") REFERENCES "domains"("id") ON DELETE RESTRICT;
> ok > ok
-- now we drop the domainusername_key, but domainusername_key_INDEX_xxx is used by udomain_fkey and was not being dropped
-- this was an issue, because it's a unique index and still enforcing constraint on (domain,username) -- this was an issue, because it's a unique index and still enforcing constraint on (domain,username)
ALTER TABLE "users" DROP CONSTRAINT "domainusername_key"; ALTER TABLE "users" DROP CONSTRAINT "domainusername_key";
> ok > ok
...@@ -29,5 +29,5 @@ insert into "users" ("username","domain","desc") VALUES ('test',1,'first user'); ...@@ -29,5 +29,5 @@ insert into "users" ("username","domain","desc") VALUES ('test',1,'first user');
> update count: 1 > update count: 1
-- should work,because we dropped domainusername_key, but failed: Unique index or primary key violation -- should work,because we dropped domainusername_key, but failed: Unique index or primary key violation
INSERT INTO "users" ("username","domain","desc") VALUES ('test',1,'second user'); INSERT INTO "users" ("username","domain","desc") VALUES ('test',1,'second user');
> update count: 1 > update count: 1
...@@ -19,7 +19,7 @@ SET COLUMN_NAME_RULES=DEFAULT_COLUMN_NAME_PATTERN = 'noName$$'; ...@@ -19,7 +19,7 @@ SET COLUMN_NAME_RULES=DEFAULT_COLUMN_NAME_PATTERN = 'noName$$';
SET COLUMN_NAME_RULES=GENERATE_UNIQUE_COLUMN_NAMES = 1; SET COLUMN_NAME_RULES=GENERATE_UNIQUE_COLUMN_NAMES = 1;
> ok > ok
SELECT 1 AS VERY_VERY_VERY_LONG_ID_VERY_VERY_VERY_LONG_ID, SUM(X)+1 AS _123456789012345, SUM(X)+1 , SUM(X)+1 SELECT 1 AS VERY_VERY_VERY_LONG_ID_VERY_VERY_VERY_LONG_ID, SUM(X)+1 AS _123456789012345, SUM(X)+1 , SUM(X)+1
+47, 'x' , '!!!' , '!!!!' FROM SYSTEM_RANGE(1,2); +47, 'x' , '!!!' , '!!!!' FROM SYSTEM_RANGE(1,2);
> VERY_VERY_VERY_LONG_ID_VERY_VE _123456789012345 SUMX1 SUMX147 x noName6 noName7 > VERY_VERY_VERY_LONG_ID_VERY_VE _123456789012345 SUMX1 SUMX147 x noName6 noName7
> ------------------------------ ---------------- ----- ------- - ------- ------- > ------------------------------ ---------------- ----- ------- - ------- -------
...@@ -28,7 +28,7 @@ SELECT 1 AS VERY_VERY_VERY_LONG_ID_VERY_VERY_VERY_LONG_ID, SUM(X)+1 AS _12345678 ...@@ -28,7 +28,7 @@ SELECT 1 AS VERY_VERY_VERY_LONG_ID_VERY_VERY_VERY_LONG_ID, SUM(X)+1 AS _12345678
SET COLUMN_NAME_RULES=EMULATE='Oracle'; SET COLUMN_NAME_RULES=EMULATE='Oracle';
> ok > ok
SELECT 1 AS VERY_VERY_VERY_LONG_ID, SUM(X)+1 AS _123456789012345, SUM(X)+1 , SUM(X)+1 SELECT 1 AS VERY_VERY_VERY_LONG_ID, SUM(X)+1 AS _123456789012345, SUM(X)+1 , SUM(X)+1
+47, 'x' , '!!!' , '!!!!' FROM SYSTEM_RANGE(1,2); +47, 'x' , '!!!' , '!!!!' FROM SYSTEM_RANGE(1,2);
> VERY_VERY_VERY_LONG_ID _123456789012345 SUMX1 SUMX147 x _UNNAMED_6 _UNNAMED_7 > VERY_VERY_VERY_LONG_ID _123456789012345 SUMX1 SUMX147 x _UNNAMED_6 _UNNAMED_7
> ---------------------- ---------------- ----- ------- - ---------- ---------- > ---------------------- ---------------- ----- ------- - ---------- ----------
...@@ -37,7 +37,7 @@ SELECT 1 AS VERY_VERY_VERY_LONG_ID, SUM(X)+1 AS _123456789012345, SUM(X)+1 , SUM ...@@ -37,7 +37,7 @@ SELECT 1 AS VERY_VERY_VERY_LONG_ID, SUM(X)+1 AS _123456789012345, SUM(X)+1 , SUM
SET COLUMN_NAME_RULES=EMULATE='Oracle'; SET COLUMN_NAME_RULES=EMULATE='Oracle';
> ok > ok
SELECT 1 AS VERY_VERY_VERY_LONG_ID, SUM(X)+1 AS _123456789012345, SUM(X)+1 , SUM(X)+1 SELECT 1 AS VERY_VERY_VERY_LONG_ID, SUM(X)+1 AS _123456789012345, SUM(X)+1 , SUM(X)+1
+47, 'x' , '!!!' , '!!!!', 'Very Long' AS _23456789012345678901234567890XXX FROM SYSTEM_RANGE(1,2); +47, 'x' , '!!!' , '!!!!', 'Very Long' AS _23456789012345678901234567890XXX FROM SYSTEM_RANGE(1,2);
> VERY_VERY_VERY_LONG_ID _123456789012345 SUMX1 SUMX147 x _UNNAMED_6 _UNNAMED_7 _23456789012345678901234567890XXX > VERY_VERY_VERY_LONG_ID _123456789012345 SUMX1 SUMX147 x _UNNAMED_6 _UNNAMED_7 _23456789012345678901234567890XXX
> ---------------------- ---------------- ----- ------- - ---------- ---------- --------------------------------- > ---------------------- ---------------- ----- ------- - ---------- ---------- ---------------------------------
...@@ -46,7 +46,7 @@ SELECT 1 AS VERY_VERY_VERY_LONG_ID, SUM(X)+1 AS _123456789012345, SUM(X)+1 , SUM ...@@ -46,7 +46,7 @@ SELECT 1 AS VERY_VERY_VERY_LONG_ID, SUM(X)+1 AS _123456789012345, SUM(X)+1 , SUM
SET COLUMN_NAME_RULES=EMULATE='PostgreSQL'; SET COLUMN_NAME_RULES=EMULATE='PostgreSQL';
> ok > ok
SELECT 1 AS VERY_VERY_VERY_LONG_ID, SUM(X)+1 AS _123456789012345, SUM(X)+1 , SUM(X)+1 SELECT 1 AS VERY_VERY_VERY_LONG_ID, SUM(X)+1 AS _123456789012345, SUM(X)+1 , SUM(X)+1
+47, 'x' , '!!!' , '!!!!', 999 AS "QuotedColumnId" FROM SYSTEM_RANGE(1,2); +47, 'x' , '!!!' , '!!!!', 999 AS "QuotedColumnId" FROM SYSTEM_RANGE(1,2);
> VERY_VERY_VERY_LONG_ID _123456789012345 SUMX1 SUMX147 x _UNNAMED_6 _UNNAMED_7 QuotedColumnId > VERY_VERY_VERY_LONG_ID _123456789012345 SUMX1 SUMX147 x _UNNAMED_6 _UNNAMED_7 QuotedColumnId
> ---------------------- ---------------- ----- ------- - ---------- ---------- -------------- > ---------------------- ---------------- ----- ------- - ---------- ---------- --------------
......
...@@ -14,7 +14,7 @@ insert into person select convert(x,varchar) as firstname, (convert(x,varchar) | ...@@ -14,7 +14,7 @@ insert into person select convert(x,varchar) as firstname, (convert(x,varchar) |
-- Issue #643: verify that when using an index, we use the IN part of the query, if that part of the query -- Issue #643: verify that when using an index, we use the IN part of the query, if that part of the query
-- can directly use the index. -- can directly use the index.
-- --
explain analyze SELECT * FROM person WHERE firstname IN ('FirstName1', 'FirstName2') AND lastname='LastName1'; explain analyze SELECT * FROM person WHERE firstname IN ('FirstName1', 'FirstName2') AND lastname='LastName1';
> PLAN > PLAN
> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- > -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
......
package org.h2.util; /*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
import org.h2.expression.Expression; */
import org.h2.expression.ValueExpression; package org.h2.test.utils;
import org.h2.test.TestBase;
import org.h2.expression.Expression;
public class TestColumnNamer extends TestBase { import org.h2.expression.ValueExpression;
import org.h2.test.TestBase;
private String[] ids = new String[]{ import org.h2.util.ColumnNamer;
"ABC"
,"123" /**
,"a\n2" * Tests the column name factory.
,"a$c%d#e@f!." */
,null public class TestColumnNamer extends TestBase {
,"VERYVERYVERYVERYVERYVERYLONGVERYVERYVERYVERYVERYVERYLONGVERYVERYVERYVERYVERYVERYLONG"
,"'!!!'" private String[] ids = new String[] { "ABC", "123", "a\n2", "a$c%d#e@f!.", null,
,"'!!!!'" "VERYVERYVERYVERYVERYVERYLONGVERYVERYVERYVERYVERYVERYLONGVERYVERYVERYVERYVERYVERYLONG", "'!!!'", "'!!!!'",
,"3.1415" "3.1415", "\r", "col1", "col1", "col1",
,"\r" "col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2",
,"col1" "col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2" };
,"col1"
,"col1" private String[] expectedColumnName = { "ABC", "123", "a2", "acdef", "colName6", "VERYVERYVERYVERYVERYVERYLONGVE",
,"col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2" "colName8", "colName9", "31415", "colName11", "col1", "col1_2", "col1_3", "col2col2col2col2col2col2col2co",
,"col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2col2" "col2col2col2col2col2col2col2_2" };
};
/**
private String[] expectedColumnName= {"ABC" * Run just this test.
,"123" *
,"a2" * @param a ignored
,"acdef" */
,"colName6" public static void main(String[] args) {
,"VERYVERYVERYVERYVERYVERYLONGVE" new TestColumnNamer().test();
,"colName8" }
,"colName9"
,"31415" public void test() {
,"colName11" ColumnNamer columnNamer = new ColumnNamer(null);
,"col1" columnNamer.getConfiguration().configure("MAX_IDENTIFIER_LENGTH = 30");
,"col1_2" columnNamer.getConfiguration().configure("REGULAR_EXPRESSION_MATCH_ALLOWED = '[A-Za-z0-9_]+'");
,"col1_3" columnNamer.getConfiguration().configure("REGULAR_EXPRESSION_MATCH_DISALLOWED = '[^A-Za-z0-9_]+'");
,"col2col2col2col2col2col2col2co" columnNamer.getConfiguration().configure("DEFAULT_COLUMN_NAME_PATTERN = 'colName$$'");
,"col2col2col2col2col2col2col2_2"}; columnNamer.getConfiguration().configure("GENERATE_UNIQUE_COLUMN_NAMES = 1");
public static void main(String[] args){
int index = 0;
new TestColumnNamer().test(); for (String id : ids) {
} Expression columnExp = ValueExpression.getDefault();
String newColumnName = columnNamer.getColumnName(columnExp, index + 1, id);
public void test() { assertTrue(newColumnName != null);
ColumnNamer columnNamer = new ColumnNamer(null); assertTrue(newColumnName.length() <= 30);
columnNamer.getConfiguration().configure("MAX_IDENTIFIER_LENGTH = 30"); assertTrue(newColumnName.length() >= 1);
columnNamer.getConfiguration().configure("REGULAR_EXPRESSION_MATCH_ALLOWED = '[A-Za-z0-9_]+'"); assertEquals(newColumnName, expectedColumnName[index]);
columnNamer.getConfiguration().configure("REGULAR_EXPRESSION_MATCH_DISALLOWED = '[^A-Za-z0-9_]+'"); index++;
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();
String newColumnName = columnNamer.getColumnName(columnExp , index+1, id);
assertTrue(newColumnName!=null);
assertTrue(newColumnName.length()<=30);
assertTrue(newColumnName.length()>=1);
assertEquals(newColumnName,expectedColumnName[index]);
index++;
}
}
}
...@@ -20,8 +20,8 @@ public class CheckTextFiles { ...@@ -20,8 +20,8 @@ public class CheckTextFiles {
private static final int MAX_SOURCE_LINE_SIZE = 120; private static final int MAX_SOURCE_LINE_SIZE = 120;
// must contain "+" otherwise this here counts as well // must contain "+" otherwise this here counts as well
private static final String COPYRIGHT = "Copyright 2004-2014 " + private static final String COPYRIGHT1 = "Copyright 2004-201";
"H2 Group."; private static final String COPYRIGHT2 = "H2 Group.";
private static final String LICENSE = "Multiple-Licensed " + private static final String LICENSE = "Multiple-Licensed " +
"under the MPL 2.0"; "under the MPL 2.0";
...@@ -144,10 +144,13 @@ public class CheckTextFiles { ...@@ -144,10 +144,13 @@ public class CheckTextFiles {
in.readFully(data); in.readFully(data);
in.close(); in.close();
if (checkLicense) { if (checkLicense) {
if (data.length > COPYRIGHT.length() + LICENSE.length()) { if (data.length > COPYRIGHT1.length() + LICENSE.length()) {
// don't check tiny files // don't check tiny files
String text = new String(data); String text = new String(data);
if (text.indexOf(COPYRIGHT) < 0) { if (text.indexOf(COPYRIGHT1) < 0) {
fail(file, "copyright is missing", 0);
}
if (text.indexOf(COPYRIGHT2) < 0) {
fail(file, "copyright is missing", 0); fail(file, "copyright is missing", 0);
} }
if (text.indexOf(LICENSE) < 0) { if (text.indexOf(LICENSE) < 0) {
......
...@@ -746,4 +746,11 @@ rowids searchers tcnt enforcing timeanddate nullifies determines believe giving ...@@ -746,4 +746,11 @@ rowids searchers tcnt enforcing timeanddate nullifies determines believe giving
vectorwise preparation corrupting cubrid diffing unrestricted cleanups warns vectorwise preparation corrupting cubrid diffing unrestricted cleanups warns
rowspan specifically unoptimized stand emphasize cascaded exasol minimize rnum figure rowspan specifically unoptimized stand emphasize cascaded exasol minimize rnum figure
emptying goal gathers multithread amend raised iter gathered gather especially requiring emptying goal gathers multithread amend raised iter gathered gather especially requiring
collaboration thank essentially bunch vmlens subroutines nulled collaboration thank essentially bunch vmlens subroutines nulled
\ No newline at end of file
ndx quoss isn nonquoted pippin variation pierre allowable granny liberty fkey kervin veg banapple unconnected
alphanumeric england acdef landry arun mederp detached lyderic imperator morocco sumx websphere fruit
joaquim overides altertable novalidate udomain managed rewritten unquote identifer jake innocuous golay
bellotti clemens donators domainusername
veryveryveryveryveryverylongveryveryveryveryveryverylongveryveryveryveryveryverylong namer veryveryveryveryveryverylongve
chittanoor carrot
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论