提交 6e2cfde3 authored 作者: andrei's avatar andrei

Merge remote-tracking branch 'h2database/master' into non_blocking

...@@ -21,6 +21,32 @@ Change Log ...@@ -21,6 +21,32 @@ Change Log
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul> <ul>
<li>Issue #992: 1.4.196+ client cannot use DatabaseMetaData with 1.4.195- server
</li>
<li>Issue #1016: ResultSet.getObject() should return enum value, not ordinal
</li>
<li>Issue #1012: NPE when querying INFORMATION_SCHEMA.COLUMNS on a view that references an ENUM column
</li>
<li>Issue #1010: MERGE USING table not found with qualified table
</li>
<li>PR #1009: Fix ARRAY_AGG with ORDER BY and refactor aggregates
</li>
<li>Issue #1006: "Empty enums are not allowed" in 1.4.197 (mode=MYSQL)
</li>
<li>PR #1007: Copy also SRID in ValueGeometry.getGeometry()
</li>
<li>PR #1004: Preserve type names in more places especially for UUID
</li>
<li>Issue #1000: Regression in INFORMATION_SCHEMA.CONSTRAINTS.CONSTRAINT_TYPE content
</li>
<li>Issue #997: Can not delete from tables with enums
</li>
<li>Issue #994: Too much column in result set for GENERATED_KEYS on table with DEFAULT
</li>
<li>PR #993: Fix some compiler warnings and improve assert*() methods
</li>
<li>PR #991: Generate shorter queries in JdbcDatabaseMetaData.getTables() and remove some dead code
</li>
<li>PR #989: Fix more issues with range table and improve its documentation <li>PR #989: Fix more issues with range table and improve its documentation
</li> </li>
</ul> </ul>
...@@ -389,7 +415,7 @@ Change Log ...@@ -389,7 +415,7 @@ Change Log
</li> </li>
<li>PR #743: Change REGEXP_REPLACE mode for MariaDB and PostgreSQL <li>PR #743: Change REGEXP_REPLACE mode for MariaDB and PostgreSQL
</li> </li>
<li>Issue#646 NPE in CREATE VIEW WITH RECURSIVE & NON_RECURSIVE CTE <li>Issue#646 NPE in CREATE VIEW WITH RECURSIVE &amp; NON_RECURSIVE CTE
</li> </li>
<li>PR #738: Copy javadoc to *BackwardsCompat to fix building of documentation <li>PR #738: Copy javadoc to *BackwardsCompat to fix building of documentation
</li> </li>
...@@ -696,7 +722,7 @@ changed from Types.OTHER (1111) to Types.TIMESTAMP_WITH_TIMEZONE (2014) ...@@ -696,7 +722,7 @@ changed from Types.OTHER (1111) to Types.TIMESTAMP_WITH_TIMEZONE (2014)
</li> </li>
<li>Issue #266: Spatial index not updating, fixed by merging PR #267 <li>Issue #266: Spatial index not updating, fixed by merging PR #267
</li> </li>
<li>PR #302: add support for "with"-subqueries into "join" & "sub-query" statements <li>PR #302: add support for "with"-subqueries into "join" &amp; "sub-query" statements
</li> </li>
<li>Issue #299: Nested derived tables did not always work as expected. <li>Issue #299: Nested derived tables did not always work as expected.
</li> </li>
......
...@@ -1150,11 +1150,8 @@ public class Parser { ...@@ -1150,11 +1150,8 @@ public class Parser {
TableFilter sourceTableFilter = readSimpleTableFilter(0, excludeIdentifiers); TableFilter sourceTableFilter = readSimpleTableFilter(0, excludeIdentifiers);
command.setSourceTableFilter(sourceTableFilter); command.setSourceTableFilter(sourceTableFilter);
StringBuilder buff = new StringBuilder("SELECT * FROM ") StringBuilder buff = new StringBuilder("SELECT * FROM ");
.append(sourceTableFilter.getTable().getName()); appendTableWithSchemaAndAlias(buff, sourceTableFilter.getTable(), sourceTableFilter.getTableAlias());
if (sourceTableFilter.getTableAlias() != null) {
buff.append(" AS ").append(sourceTableFilter.getTableAlias());
}
Prepared preparedQuery = prepare(session, buff.toString(), null/*paramValues*/); Prepared preparedQuery = prepare(session, buff.toString(), null/*paramValues*/);
command.setQuery((Select) preparedQuery); command.setQuery((Select) preparedQuery);
...@@ -1197,11 +1194,9 @@ public class Parser { ...@@ -1197,11 +1194,9 @@ public class Parser {
// build and prepare the targetMatchQuery ready to test each rows // build and prepare the targetMatchQuery ready to test each rows
// existence in the target table (using source row to match) // existence in the target table (using source row to match)
StringBuilder targetMatchQuerySQL = new StringBuilder( StringBuilder targetMatchQuerySQL = new StringBuilder("SELECT _ROWID_ FROM ");
"SELECT _ROWID_ FROM " + command.getTargetTable().getName()); appendTableWithSchemaAndAlias(targetMatchQuerySQL, command.getTargetTable(),
if (command.getTargetTableFilter().getTableAlias() != null) { command.getTargetTableFilter().getTableAlias());
targetMatchQuerySQL.append(" AS ").append(command.getTargetTableFilter().getTableAlias());
}
targetMatchQuerySQL targetMatchQuerySQL
.append(" WHERE ").append(command.getOnCondition().getSQL()); .append(" WHERE ").append(command.getOnCondition().getSQL());
command.setTargetMatchQuery( command.setTargetMatchQuery(
...@@ -1210,6 +1205,14 @@ public class Parser { ...@@ -1210,6 +1205,14 @@ public class Parser {
return command; return command;
} }
private static void appendTableWithSchemaAndAlias(StringBuilder buff, Table table, String alias) {
buff.append(quoteIdentifier(table.getSchema().getName()))
.append('.').append(quoteIdentifier(table.getName()));
if (alias != null) {
buff.append(" AS ").append(quoteIdentifier(alias));
}
}
private Insert parseInsert() { private Insert parseInsert() {
Insert command = new Insert(session); Insert command = new Insert(session);
currentPrepared = command; currentPrepared = command;
...@@ -2628,7 +2631,7 @@ public class Parser { ...@@ -2628,7 +2631,7 @@ public class Parser {
readExpression(), currentSelect, distinct); readExpression(), currentSelect, distinct);
if (readIf("ORDER")) { if (readIf("ORDER")) {
read("BY"); read("BY");
r.setGroupConcatOrder(parseSimpleOrderList()); r.setOrderByList(parseSimpleOrderList());
} }
if (readIf("SEPARATOR")) { if (readIf("SEPARATOR")) {
...@@ -2642,7 +2645,7 @@ public class Parser { ...@@ -2642,7 +2645,7 @@ public class Parser {
r.setGroupConcatSeparator(readExpression()); r.setGroupConcatSeparator(readExpression());
if (readIf("ORDER")) { if (readIf("ORDER")) {
read("BY"); read("BY");
r.setGroupConcatOrder(parseSimpleOrderList()); r.setOrderByList(parseSimpleOrderList());
} }
} else { } else {
r = null; r = null;
...@@ -2654,7 +2657,7 @@ public class Parser { ...@@ -2654,7 +2657,7 @@ public class Parser {
readExpression(), currentSelect, distinct); readExpression(), currentSelect, distinct);
if (readIf("ORDER")) { if (readIf("ORDER")) {
read("BY"); read("BY");
r.setArrayAggOrder(parseSimpleOrderList()); r.setOrderByList(parseSimpleOrderList());
} }
} else { } else {
boolean distinct = readIf("DISTINCT"); boolean distinct = readIf("DISTINCT");
...@@ -3027,7 +3030,7 @@ public class Parser { ...@@ -3027,7 +3030,7 @@ public class Parser {
int index = currentValue.getInt() - 1; int index = currentValue.getInt() - 1;
if (index < 0 || index >= Constants.MAX_PARAMETER_INDEX) { if (index < 0 || index >= Constants.MAX_PARAMETER_INDEX) {
throw DbException.getInvalidValueException( throw DbException.getInvalidValueException(
"parameter index", index); "parameter index", index + 1);
} }
if (indexedParameterList.size() <= index) { if (indexedParameterList.size() <= index) {
indexedParameterList.ensureCapacity(index + 1); indexedParameterList.ensureCapacity(index + 1);
......
...@@ -6,12 +6,13 @@ ...@@ -6,12 +6,13 @@
package org.h2.expression; package org.h2.expression;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.command.dml.Select; import org.h2.command.dml.Select;
import org.h2.command.dml.SelectOrderBy; import org.h2.command.dml.SelectOrderBy;
import org.h2.engine.Database;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.index.Cursor; import org.h2.index.Cursor;
import org.h2.index.Index; import org.h2.index.Index;
...@@ -143,10 +144,8 @@ public class Aggregate extends Expression { ...@@ -143,10 +144,8 @@ public class Aggregate extends Expression {
private Expression on; private Expression on;
private Expression groupConcatSeparator; private Expression groupConcatSeparator;
private ArrayList<SelectOrderBy> groupConcatOrderList; private ArrayList<SelectOrderBy> orderByList;
private ArrayList<SelectOrderBy> arrayAggOrderList; private SortOrder orderBySort;
private SortOrder groupConcatSort;
private SortOrder arrayOrderSort;
private int dataType, scale; private int dataType, scale;
private long precision; private long precision;
private int displaySize; private int displaySize;
...@@ -220,21 +219,12 @@ public class Aggregate extends Expression { ...@@ -220,21 +219,12 @@ public class Aggregate extends Expression {
} }
/** /**
* Set the order for GROUP_CONCAT() aggregate. * Set the order for ARRAY_AGG() or GROUP_CONCAT() aggregate.
* *
* @param orderBy the order by list * @param orderByList the order by list
*/ */
public void setGroupConcatOrder(ArrayList<SelectOrderBy> orderBy) { public void setOrderByList(ArrayList<SelectOrderBy> orderByList) {
this.groupConcatOrderList = orderBy; this.orderByList = orderByList;
}
/**
* Set the order for ARRAY_AGG() aggregate.
*
* @param orderBy the order by list
*/
public void setArrayAggOrder(ArrayList<SelectOrderBy> orderBy) {
this.arrayAggOrderList = orderBy;
} }
/** /**
...@@ -255,12 +245,12 @@ public class Aggregate extends Expression { ...@@ -255,12 +245,12 @@ public class Aggregate extends Expression {
this.filterCondition = filterCondition; this.filterCondition = filterCondition;
} }
private SortOrder initOrder(ArrayList<SelectOrderBy> orderList, Session session) { private SortOrder initOrder(Session session) {
int size = orderList.size(); int size = orderByList.size();
int[] index = new int[size]; int[] index = new int[size];
int[] sortType = new int[size]; int[] sortType = new int[size];
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
SelectOrderBy o = orderList.get(i); SelectOrderBy o = orderByList.get(i);
index[i] = i + 1; index[i] = i + 1;
int order = o.descending ? SortOrder.DESCENDING : SortOrder.ASCENDING; int order = o.descending ? SortOrder.DESCENDING : SortOrder.ASCENDING;
sortType[i] = order; sortType[i] = order;
...@@ -268,6 +258,26 @@ public class Aggregate extends Expression { ...@@ -268,6 +258,26 @@ public class Aggregate extends Expression {
return new SortOrder(session.getDatabase(), index, sortType, null); return new SortOrder(session.getDatabase(), index, sortType, null);
} }
private void sortWithOrderBy(Value[] array) {
final SortOrder sortOrder = orderBySort;
if (sortOrder != null) {
Arrays.sort(array, new Comparator<Value>() {
@Override
public int compare(Value v1, Value v2) {
return sortOrder.compare(((ValueArray) v1).getList(), ((ValueArray) v2).getList());
}
});
} else {
final Database database = select.getSession().getDatabase();
Arrays.sort(array, new Comparator<Value> () {
@Override
public int compare(Value v1, Value v2) {
return database.compare(v1, v2);
}
});
}
}
@Override @Override
public void updateAggregate(Session session) { public void updateAggregate(Session session) {
// TODO aggregates: check nested MIN(MAX(ID)) and so on // TODO aggregates: check nested MIN(MAX(ID)) and so on
...@@ -296,12 +306,12 @@ public class Aggregate extends Expression { ...@@ -296,12 +306,12 @@ public class Aggregate extends Expression {
if (type == AggregateType.GROUP_CONCAT) { if (type == AggregateType.GROUP_CONCAT) {
if (v != ValueNull.INSTANCE) { if (v != ValueNull.INSTANCE) {
v = v.convertTo(Value.STRING); v = v.convertTo(Value.STRING);
if (groupConcatOrderList != null) { if (orderByList != null) {
int size = groupConcatOrderList.size(); int size = orderByList.size();
Value[] array = new Value[1 + size]; Value[] array = new Value[1 + size];
array[0] = v; array[0] = v;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
SelectOrderBy o = groupConcatOrderList.get(i); SelectOrderBy o = orderByList.get(i);
array[i + 1] = o.expression.getValue(session); array[i + 1] = o.expression.getValue(session);
} }
v = ValueArray.get(array); v = ValueArray.get(array);
...@@ -310,12 +320,12 @@ public class Aggregate extends Expression { ...@@ -310,12 +320,12 @@ public class Aggregate extends Expression {
} }
if (type == AggregateType.ARRAY_AGG) { if (type == AggregateType.ARRAY_AGG) {
if (v != ValueNull.INSTANCE) { if (v != ValueNull.INSTANCE) {
if (arrayAggOrderList != null) { if (orderByList != null) {
int size = arrayAggOrderList.size(); int size = orderByList.size();
Value[] array = new Value[1 + size]; Value[] array = new Value[1 + size];
array[0] = v; array[0] = v;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
SelectOrderBy o = arrayAggOrderList.get(i); SelectOrderBy o = orderByList.get(i);
array[i + 1] = o.expression.getValue(session); array[i + 1] = o.expression.getValue(session);
} }
v = ValueArray.get(array); v = ValueArray.get(array);
...@@ -371,27 +381,18 @@ public class Aggregate extends Expression { ...@@ -371,27 +381,18 @@ public class Aggregate extends Expression {
if (data == null) { if (data == null) {
data = AggregateData.create(type); data = AggregateData.create(type);
} }
Value v = data.getValue(session.getDatabase(), dataType, distinct);
if (type == AggregateType.GROUP_CONCAT) { if (type == AggregateType.GROUP_CONCAT) {
ArrayList<Value> list = ((AggregateDataArrayCollecting) data).getList(); Value[] array = ((AggregateDataCollecting) data).getArray();
if (list == null || list.isEmpty()) { if (array == null) {
return ValueNull.INSTANCE; return ValueNull.INSTANCE;
} }
if (groupConcatOrderList != null) { if (orderByList != null || distinct) {
final SortOrder sortOrder = groupConcatSort; sortWithOrderBy(array);
Collections.sort(list, new Comparator<Value>() {
@Override
public int compare(Value v1, Value v2) {
Value[] a1 = ((ValueArray) v1).getList();
Value[] a2 = ((ValueArray) v2).getList();
return sortOrder.compare(a1, a2);
}
});
} }
StatementBuilder buff = new StatementBuilder(); StatementBuilder buff = new StatementBuilder();
String sep = groupConcatSeparator == null ? String sep = groupConcatSeparator == null ?
"," : groupConcatSeparator.getValue(session).getString(); "," : groupConcatSeparator.getValue(session).getString();
for (Value val : list) { for (Value val : array) {
String s; String s;
if (val.getType() == Value.ARRAY) { if (val.getType() == Value.ARRAY) {
s = ((ValueArray) val).getList()[0].getString(); s = ((ValueArray) val).getList()[0].getString();
...@@ -406,26 +407,23 @@ public class Aggregate extends Expression { ...@@ -406,26 +407,23 @@ public class Aggregate extends Expression {
} }
buff.append(s); buff.append(s);
} }
v = ValueString.get(buff.toString()); return ValueString.get(buff.toString());
} else if (type == AggregateType.ARRAY_AGG) { } else if (type == AggregateType.ARRAY_AGG) {
ArrayList<Value> list = ((AggregateDataArrayCollecting) data).getList(); Value[] array = ((AggregateDataCollecting) data).getArray();
if (list == null || list.isEmpty()) { if (array == null) {
return ValueNull.INSTANCE; return ValueNull.INSTANCE;
} }
if (arrayAggOrderList != null) { if (orderByList != null || distinct) {
final SortOrder sortOrder = arrayOrderSort; sortWithOrderBy(array);
Collections.sort(list, new Comparator<Value>() { }
@Override if (orderByList != null) {
public int compare(Value v1, Value v2) { for (int i = 0; i < array.length; i++) {
Value[] a1 = ((ValueArray) v1).getList(); array[i] = ((ValueArray) array[i]).getList()[0];
Value[] a2 = ((ValueArray) v2).getList(); }
return sortOrder.compare(a1, a2);
}
});
} }
v = ValueArray.get(list.toArray(new Value[list.size()])); return ValueArray.get(array);
} }
return v; return data.getValue(session.getDatabase(), dataType, distinct);
} }
@Override @Override
...@@ -438,13 +436,8 @@ public class Aggregate extends Expression { ...@@ -438,13 +436,8 @@ public class Aggregate extends Expression {
if (on != null) { if (on != null) {
on.mapColumns(resolver, level); on.mapColumns(resolver, level);
} }
if (groupConcatOrderList != null) { if (orderByList != null) {
for (SelectOrderBy o : groupConcatOrderList) { for (SelectOrderBy o : orderByList) {
o.expression.mapColumns(resolver, level);
}
}
if (arrayAggOrderList != null) {
for (SelectOrderBy o : arrayAggOrderList) {
o.expression.mapColumns(resolver, level); o.expression.mapColumns(resolver, level);
} }
} }
...@@ -465,17 +458,11 @@ public class Aggregate extends Expression { ...@@ -465,17 +458,11 @@ public class Aggregate extends Expression {
precision = on.getPrecision(); precision = on.getPrecision();
displaySize = on.getDisplaySize(); displaySize = on.getDisplaySize();
} }
if (groupConcatOrderList != null) { if (orderByList != null) {
for (SelectOrderBy o : groupConcatOrderList) { for (SelectOrderBy o : orderByList) {
o.expression = o.expression.optimize(session); o.expression = o.expression.optimize(session);
} }
groupConcatSort = initOrder(groupConcatOrderList, session); orderBySort = initOrder(session);
}
if (arrayAggOrderList != null) {
for (SelectOrderBy o : arrayAggOrderList) {
o.expression = o.expression.optimize(session);
}
arrayOrderSort = initOrder(arrayAggOrderList, session);
} }
if (groupConcatSeparator != null) { if (groupConcatSeparator != null) {
groupConcatSeparator = groupConcatSeparator.optimize(session); groupConcatSeparator = groupConcatSeparator.optimize(session);
...@@ -564,13 +551,8 @@ public class Aggregate extends Expression { ...@@ -564,13 +551,8 @@ public class Aggregate extends Expression {
if (on != null) { if (on != null) {
on.setEvaluatable(tableFilter, b); on.setEvaluatable(tableFilter, b);
} }
if (groupConcatOrderList != null) { if (orderByList != null) {
for (SelectOrderBy o : groupConcatOrderList) { for (SelectOrderBy o : orderByList) {
o.expression.setEvaluatable(tableFilter, b);
}
}
if (arrayAggOrderList != null) {
for (SelectOrderBy o : arrayAggOrderList) {
o.expression.setEvaluatable(tableFilter, b); o.expression.setEvaluatable(tableFilter, b);
} }
} }
...@@ -603,9 +585,9 @@ public class Aggregate extends Expression { ...@@ -603,9 +585,9 @@ public class Aggregate extends Expression {
buff.append("DISTINCT "); buff.append("DISTINCT ");
} }
buff.append(on.getSQL()); buff.append(on.getSQL());
if (groupConcatOrderList != null) { if (orderByList != null) {
buff.append(" ORDER BY "); buff.append(" ORDER BY ");
for (SelectOrderBy o : groupConcatOrderList) { for (SelectOrderBy o : orderByList) {
buff.appendExceptFirst(", "); buff.appendExceptFirst(", ");
buff.append(o.expression.getSQL()); buff.append(o.expression.getSQL());
if (o.descending) { if (o.descending) {
...@@ -629,9 +611,9 @@ public class Aggregate extends Expression { ...@@ -629,9 +611,9 @@ public class Aggregate extends Expression {
buff.append("DISTINCT "); buff.append("DISTINCT ");
} }
buff.append(on.getSQL()); buff.append(on.getSQL());
if (arrayAggOrderList != null) { if (orderByList != null) {
buff.append(" ORDER BY "); buff.append(" ORDER BY ");
for (SelectOrderBy o : arrayAggOrderList) { for (SelectOrderBy o : orderByList) {
buff.appendExceptFirst(", "); buff.appendExceptFirst(", ");
buff.append(o.expression.getSQL()); buff.append(o.expression.getSQL());
if (o.descending) { if (o.descending) {
...@@ -765,15 +747,8 @@ public class Aggregate extends Expression { ...@@ -765,15 +747,8 @@ public class Aggregate extends Expression {
!groupConcatSeparator.isEverything(visitor)) { !groupConcatSeparator.isEverything(visitor)) {
return false; return false;
} }
if (groupConcatOrderList != null) { if (orderByList != null) {
for (SelectOrderBy o : groupConcatOrderList) { for (SelectOrderBy o : orderByList) {
if (!o.expression.isEverything(visitor)) {
return false;
}
}
}
if (arrayAggOrderList != null) {
for (SelectOrderBy o : arrayAggOrderList) {
if (!o.expression.isEverything(visitor)) { if (!o.expression.isEverything(visitor)) {
return false; return false;
} }
......
...@@ -21,21 +21,21 @@ abstract class AggregateData { ...@@ -21,21 +21,21 @@ abstract class AggregateData {
* @return the aggregate data object of the specified type * @return the aggregate data object of the specified type
*/ */
static AggregateData create(AggregateType aggregateType) { static AggregateData create(AggregateType aggregateType) {
if (aggregateType == AggregateType.SELECTIVITY) { switch (aggregateType) {
case SELECTIVITY:
return new AggregateDataSelectivity(); return new AggregateDataSelectivity();
} else if (aggregateType == AggregateType.GROUP_CONCAT) { case GROUP_CONCAT:
return new AggregateDataArrayCollecting(); case ARRAY_AGG:
} else if (aggregateType == AggregateType.ARRAY_AGG) { return new AggregateDataCollecting();
return new AggregateDataArrayCollecting(); case COUNT_ALL:
} else if (aggregateType == AggregateType.COUNT_ALL) {
return new AggregateDataCountAll(); return new AggregateDataCountAll();
} else if (aggregateType == AggregateType.COUNT) { case COUNT:
return new AggregateDataCount(); return new AggregateDataCount();
} else if (aggregateType == AggregateType.HISTOGRAM) { case HISTOGRAM:
return new AggregateDataHistogram(); return new AggregateDataHistogram();
} else if (aggregateType == AggregateType.MEDIAN) { case MEDIAN:
return new AggregateDataMedian(); return new AggregateDataMedian();
} else { default:
return new AggregateDataDefault(aggregateType); return new AggregateDataDefault(aggregateType);
} }
} }
......
...@@ -6,55 +6,54 @@ ...@@ -6,55 +6,54 @@
package org.h2.expression; package org.h2.expression;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import org.h2.engine.Database; import org.h2.engine.Database;
import org.h2.util.New;
import org.h2.util.ValueHashMap;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueNull; import org.h2.value.ValueNull;
/** /**
* Data stored while calculating a GROUP_CONCAT/ARRAY_AGG aggregate. * Data stored while calculating an aggregate that needs collecting of all
* values.
*
* <p>
* NULL values are not collected. {@link #getValue(Database, int, boolean)}
* method returns {@code null}. Use {@link #getArray()} for instances of this
* class instead. Notice that subclasses like {@link AggregateDataMedian} may
* override {@link #getValue(Database, int, boolean)} to return useful result.
* </p>
*/ */
class AggregateDataArrayCollecting extends AggregateData { class AggregateDataCollecting extends AggregateData {
private ArrayList<Value> list; Collection<Value> values;
private ValueHashMap<AggregateDataArrayCollecting> distinctValues;
@Override @Override
void add(Database database, int dataType, boolean distinct, Value v) { void add(Database database, int dataType, boolean distinct, Value v) {
if (v == ValueNull.INSTANCE) { if (v == ValueNull.INSTANCE) {
return; return;
} }
if (distinct) { Collection<Value> c = values;
if (distinctValues == null) { if (c == null) {
distinctValues = ValueHashMap.newInstance(); values = c = distinct ? new HashSet<Value>() : new ArrayList<Value>();
}
distinctValues.put(v, this);
return;
}
if (list == null) {
list = New.arrayList();
} }
list.add(v); c.add(v);
} }
@Override @Override
Value getValue(Database database, int dataType, boolean distinct) { Value getValue(Database database, int dataType, boolean distinct) {
if (distinct) {
distinct(database, dataType);
}
return null; return null;
} }
ArrayList<Value> getList() { /**
return list; * Returns array with values or {@code null}.
} *
* @return array with values or {@code null}
private void distinct(Database database, int dataType) { */
if (distinctValues == null) { Value[] getArray() {
return; Collection<Value> values = this.values;
} if (values == null) {
for (Value v : distinctValues.keys()) { return null;
add(database, dataType, false, v);
} }
return values.toArray(new Value[0]);
} }
} }
...@@ -8,9 +8,7 @@ package org.h2.expression; ...@@ -8,9 +8,7 @@ package org.h2.expression;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashSet;
import org.h2.engine.Database; import org.h2.engine.Database;
import org.h2.engine.Session; import org.h2.engine.Session;
...@@ -39,9 +37,7 @@ import org.h2.value.ValueTimestampTimeZone; ...@@ -39,9 +37,7 @@ import org.h2.value.ValueTimestampTimeZone;
/** /**
* Data stored while calculating a MEDIAN aggregate. * Data stored while calculating a MEDIAN aggregate.
*/ */
class AggregateDataMedian extends AggregateData { class AggregateDataMedian extends AggregateDataCollecting {
private Collection<Value> values;
private static boolean isNullsLast(Index index) { private static boolean isNullsLast(Index index) {
IndexColumn ic = index.getIndexColumns()[0]; IndexColumn ic = index.getIndexColumns()[0];
int sortType = ic.sortType; int sortType = ic.sortType;
...@@ -168,29 +164,12 @@ class AggregateDataMedian extends AggregateData { ...@@ -168,29 +164,12 @@ class AggregateDataMedian extends AggregateData {
return v; return v;
} }
@Override
void add(Database database, int dataType, boolean distinct, Value v) {
if (v == ValueNull.INSTANCE) {
return;
}
Collection<Value> c = values;
if (c == null) {
values = c = distinct ? new HashSet<Value>() : new ArrayList<Value>();
}
c.add(v);
}
@Override @Override
Value getValue(Database database, int dataType, boolean distinct) { Value getValue(Database database, int dataType, boolean distinct) {
Collection<Value> c = values; Value[] a = getArray();
// Non-null collection cannot be empty here if (a == null) {
if (c == null) {
return ValueNull.INSTANCE; return ValueNull.INSTANCE;
} }
if (distinct && c instanceof ArrayList) {
c = new HashSet<>(c);
}
Value[] a = c.toArray(new Value[0]);
final CompareMode mode = database.getCompareMode(); final CompareMode mode = database.getCompareMode();
Arrays.sort(a, new Comparator<Value>() { Arrays.sort(a, new Comparator<Value>() {
@Override @Override
......
...@@ -186,6 +186,8 @@ public class Operation extends Expression { ...@@ -186,6 +186,8 @@ public class Operation extends Expression {
dataType = left.getType(); dataType = left.getType();
if (dataType == Value.UNKNOWN) { if (dataType == Value.UNKNOWN) {
dataType = Value.DECIMAL; dataType = Value.DECIMAL;
} else if (dataType == Value.ENUM) {
dataType = Value.INT;
} }
break; break;
case CONCAT: case CONCAT:
...@@ -315,7 +317,9 @@ public class Operation extends Expression { ...@@ -315,7 +317,9 @@ public class Operation extends Expression {
DataType.getDataType(r).name); DataType.getDataType(r).name);
} else { } else {
dataType = Value.getHigherOrder(l, r); dataType = Value.getHigherOrder(l, r);
if (DataType.isStringType(dataType) && if (dataType == Value.ENUM) {
dataType = Value.INT;
} else if (DataType.isStringType(dataType) &&
session.getDatabase().getMode().allowPlusForStringConcat) { session.getDatabase().getMode().allowPlusForStringConcat) {
opType = OpType.CONCAT; opType = OpType.CONCAT;
} }
......
...@@ -135,14 +135,12 @@ public class TableFunction extends Function { ...@@ -135,14 +135,12 @@ public class TableFunction extends Function {
simple.setAutoClose(false); simple.setAutoClose(false);
for (int i = 0; i < columnCount; i++) { for (int i = 0; i < columnCount; i++) {
String name = rs.getColumnName(i); String name = rs.getColumnName(i);
/* DataType dataType = DataType.getDataType(rs.getColumnType(i));
* TODO Some types, such as Value.BYTES and Value.UUID are mapped to the same int sqlType = dataType.sqlType;
* SQL type and we can lose real type here. String sqlTypeName = dataType.name;
*/
int sqlType = DataType.convertTypeToSQLType(rs.getColumnType(i));
int precision = MathUtils.convertLongToInt(rs.getColumnPrecision(i)); int precision = MathUtils.convertLongToInt(rs.getColumnPrecision(i));
int scale = rs.getColumnScale(i); int scale = rs.getColumnScale(i);
simple.addColumn(name, sqlType, precision, scale); simple.addColumn(name, sqlType, sqlTypeName, precision, scale);
} }
rs.reset(); rs.reset();
for (int i = 0; i < maxrows && rs.next(); i++) { for (int i = 0; i < maxrows && rs.next(); i++) {
......
...@@ -14,11 +14,16 @@ import java.sql.SQLException; ...@@ -14,11 +14,16 @@ import java.sql.SQLException;
import java.sql.Types; import java.sql.Types;
import java.util.Arrays; import java.util.Arrays;
import java.util.Properties; import java.util.Properties;
import org.h2.command.CommandInterface;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.engine.SessionInterface;
import org.h2.engine.SessionRemote;
import org.h2.engine.SysProperties; import org.h2.engine.SysProperties;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.message.Trace; import org.h2.message.Trace;
import org.h2.message.TraceObject; import org.h2.message.TraceObject;
import org.h2.result.ResultInterface;
import org.h2.tools.SimpleResultSet; import org.h2.tools.SimpleResultSet;
import org.h2.util.StatementBuilder; import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
...@@ -31,6 +36,11 @@ public class JdbcDatabaseMetaData extends TraceObject implements ...@@ -31,6 +36,11 @@ public class JdbcDatabaseMetaData extends TraceObject implements
private final JdbcConnection conn; private final JdbcConnection conn;
/**
* Whether database has support for synonyms ({@code null} if not yet known).
*/
private Boolean hasSynonyms;
JdbcDatabaseMetaData(JdbcConnection conn, Trace trace, int id) { JdbcDatabaseMetaData(JdbcConnection conn, Trace trace, int id) {
setTrace(trace, TraceObject.DATABASE_META_DATA, id); setTrace(trace, TraceObject.DATABASE_META_DATA, id);
this.conn = conn; this.conn = conn;
...@@ -105,6 +115,33 @@ public class JdbcDatabaseMetaData extends TraceObject implements ...@@ -105,6 +115,33 @@ public class JdbcDatabaseMetaData extends TraceObject implements
return Constants.getFullVersion(); return Constants.getFullVersion();
} }
private boolean hasSynonyms() {
Boolean hasSynonyms = this.hasSynonyms;
if (hasSynonyms == null) {
SessionInterface si = conn.getSession();
if (si instanceof SessionRemote) {
SessionRemote sr = (SessionRemote) si;
int clientVersion = sr.getClientVersion();
if (clientVersion >= Constants.TCP_PROTOCOL_VERSION_17) {
hasSynonyms = true;
} else if (clientVersion <= Constants.TCP_PROTOCOL_VERSION_15) {
hasSynonyms = false;
} else { // 1.4.194-1.4.196
CommandInterface c = sr.prepareCommand("CALL H2VERSION()", Integer.MAX_VALUE);
ResultInterface result = c.executeQuery(0, false);
result.next();
String s = result.currentRow()[0].getString();
result.close();
hasSynonyms = "1.4.196".equals(s);
}
} else {
hasSynonyms = true;
}
this.hasSynonyms = hasSynonyms;
}
return hasSynonyms;
}
/** /**
* Gets the list of tables in the database. The result set is sorted by * Gets the list of tables in the database. The result set is sorted by
* TABLE_TYPE, TABLE_SCHEM, and TABLE_NAME. * TABLE_TYPE, TABLE_SCHEM, and TABLE_NAME.
...@@ -144,7 +181,7 @@ public class JdbcDatabaseMetaData extends TraceObject implements ...@@ -144,7 +181,7 @@ public class JdbcDatabaseMetaData extends TraceObject implements
} }
checkClosed(); checkClosed();
int typesLength = types != null ? types.length : 0; int typesLength = types != null ? types.length : 0;
boolean includeSynonyms = types == null || Arrays.asList(types).contains("SYNONYM"); boolean includeSynonyms = hasSynonyms() && (types == null || Arrays.asList(types).contains("SYNONYM"));
// (1024 - 16) is enough for the most cases // (1024 - 16) is enough for the most cases
StringBuilder select = new StringBuilder(1008); StringBuilder select = new StringBuilder(1008);
...@@ -279,7 +316,72 @@ public class JdbcDatabaseMetaData extends TraceObject implements ...@@ -279,7 +316,72 @@ public class JdbcDatabaseMetaData extends TraceObject implements
+quote(columnNamePattern)+");"); +quote(columnNamePattern)+");");
} }
checkClosed(); checkClosed();
String tableSql = "SELECT " boolean includeSynonyms = hasSynonyms();
StringBuilder select = new StringBuilder(2432);
if (includeSynonyms) {
select.append("SELECT "
+ "TABLE_CAT, "
+ "TABLE_SCHEM, "
+ "TABLE_NAME, "
+ "COLUMN_NAME, "
+ "DATA_TYPE, "
+ "TYPE_NAME, "
+ "COLUMN_SIZE, "
+ "BUFFER_LENGTH, "
+ "DECIMAL_DIGITS, "
+ "NUM_PREC_RADIX, "
+ "NULLABLE, "
+ "REMARKS, "
+ "COLUMN_DEF, "
+ "SQL_DATA_TYPE, "
+ "SQL_DATETIME_SUB, "
+ "CHAR_OCTET_LENGTH, "
+ "ORDINAL_POSITION, "
+ "IS_NULLABLE, "
+ "SCOPE_CATALOG, "
+ "SCOPE_SCHEMA, "
+ "SCOPE_TABLE, "
+ "SOURCE_DATA_TYPE, "
+ "IS_AUTOINCREMENT, "
+ "SCOPE_CATLOG "
+ "FROM ("
+ "SELECT "
+ "s.SYNONYM_CATALOG TABLE_CAT, "
+ "s.SYNONYM_SCHEMA TABLE_SCHEM, "
+ "s.SYNONYM_NAME TABLE_NAME, "
+ "c.COLUMN_NAME, "
+ "c.DATA_TYPE, "
+ "c.TYPE_NAME, "
+ "c.CHARACTER_MAXIMUM_LENGTH COLUMN_SIZE, "
+ "c.CHARACTER_MAXIMUM_LENGTH BUFFER_LENGTH, "
+ "c.NUMERIC_SCALE DECIMAL_DIGITS, "
+ "c.NUMERIC_PRECISION_RADIX NUM_PREC_RADIX, "
+ "c.NULLABLE, "
+ "c.REMARKS, "
+ "c.COLUMN_DEFAULT COLUMN_DEF, "
+ "c.DATA_TYPE SQL_DATA_TYPE, "
+ "ZERO() SQL_DATETIME_SUB, "
+ "c.CHARACTER_OCTET_LENGTH CHAR_OCTET_LENGTH, "
+ "c.ORDINAL_POSITION, "
+ "c.IS_NULLABLE IS_NULLABLE, "
+ "CAST(c.SOURCE_DATA_TYPE AS VARCHAR) SCOPE_CATALOG, "
+ "CAST(c.SOURCE_DATA_TYPE AS VARCHAR) SCOPE_SCHEMA, "
+ "CAST(c.SOURCE_DATA_TYPE AS VARCHAR) SCOPE_TABLE, "
+ "c.SOURCE_DATA_TYPE, "
+ "CASE WHEN c.SEQUENCE_NAME IS NULL THEN "
+ "CAST(?1 AS VARCHAR) ELSE CAST(?2 AS VARCHAR) END IS_AUTOINCREMENT, "
+ "CAST(c.SOURCE_DATA_TYPE AS VARCHAR) SCOPE_CATLOG "
+ "FROM INFORMATION_SCHEMA.COLUMNS c JOIN INFORMATION_SCHEMA.SYNONYMS s ON "
+ "s.SYNONYM_FOR = c.TABLE_NAME "
+ "AND s.SYNONYM_FOR_SCHEMA = c.TABLE_SCHEMA "
+ "WHERE s.SYNONYM_CATALOG LIKE ?3 ESCAPE ?7 "
+ "AND s.SYNONYM_SCHEMA LIKE ?4 ESCAPE ?7 "
+ "AND s.SYNONYM_NAME LIKE ?5 ESCAPE ?7 "
+ "AND c.COLUMN_NAME LIKE ?6 ESCAPE ?7 "
+ "UNION ");
}
select.append("SELECT "
+ "TABLE_CATALOG TABLE_CAT, " + "TABLE_CATALOG TABLE_CAT, "
+ "TABLE_SCHEMA TABLE_SCHEM, " + "TABLE_SCHEMA TABLE_SCHEM, "
+ "TABLE_NAME, " + "TABLE_NAME, "
...@@ -303,94 +405,25 @@ public class JdbcDatabaseMetaData extends TraceObject implements ...@@ -303,94 +405,25 @@ public class JdbcDatabaseMetaData extends TraceObject implements
+ "CAST(SOURCE_DATA_TYPE AS VARCHAR) SCOPE_TABLE, " + "CAST(SOURCE_DATA_TYPE AS VARCHAR) SCOPE_TABLE, "
+ "SOURCE_DATA_TYPE, " + "SOURCE_DATA_TYPE, "
+ "CASE WHEN SEQUENCE_NAME IS NULL THEN " + "CASE WHEN SEQUENCE_NAME IS NULL THEN "
+ "CAST(? AS VARCHAR) ELSE CAST(? AS VARCHAR) END IS_AUTOINCREMENT, " + "CAST(?1 AS VARCHAR) ELSE CAST(?2 AS VARCHAR) END IS_AUTOINCREMENT, "
+ "CAST(SOURCE_DATA_TYPE AS VARCHAR) SCOPE_CATLOG " + "CAST(SOURCE_DATA_TYPE AS VARCHAR) SCOPE_CATLOG "
+ "FROM INFORMATION_SCHEMA.COLUMNS " + "FROM INFORMATION_SCHEMA.COLUMNS "
+ "WHERE TABLE_CATALOG LIKE ? ESCAPE ? " + "WHERE TABLE_CATALOG LIKE ?3 ESCAPE ?7 "
+ "AND TABLE_SCHEMA LIKE ? ESCAPE ? " + "AND TABLE_SCHEMA LIKE ?4 ESCAPE ?7 "
+ "AND TABLE_NAME LIKE ? ESCAPE ? " + "AND TABLE_NAME LIKE ?5 ESCAPE ?7 "
+ "AND COLUMN_NAME LIKE ? ESCAPE ? " + "AND COLUMN_NAME LIKE ?6 ESCAPE ?7");
+ "ORDER BY TABLE_SCHEM, TABLE_NAME, ORDINAL_POSITION"; if (includeSynonyms) {
String synonymSql = "SELECT " select.append(')');
+ "s.SYNONYM_CATALOG TABLE_CAT, " }
+ "s.SYNONYM_SCHEMA TABLE_SCHEM, " PreparedStatement prep = conn.prepareAutoCloseStatement(
+ "s.SYNONYM_NAME TABLE_NAME, " select.append(" ORDER BY TABLE_SCHEM, TABLE_NAME, ORDINAL_POSITION").toString());
+ "c.COLUMN_NAME, "
+ "c.DATA_TYPE, "
+ "c.TYPE_NAME, "
+ "c.CHARACTER_MAXIMUM_LENGTH COLUMN_SIZE, "
+ "c.CHARACTER_MAXIMUM_LENGTH BUFFER_LENGTH, "
+ "c.NUMERIC_SCALE DECIMAL_DIGITS, "
+ "c.NUMERIC_PRECISION_RADIX NUM_PREC_RADIX, "
+ "c.NULLABLE, "
+ "c.REMARKS, "
+ "c.COLUMN_DEFAULT COLUMN_DEF, "
+ "c.DATA_TYPE SQL_DATA_TYPE, "
+ "ZERO() SQL_DATETIME_SUB, "
+ "c.CHARACTER_OCTET_LENGTH CHAR_OCTET_LENGTH, "
+ "c.ORDINAL_POSITION, "
+ "c.IS_NULLABLE IS_NULLABLE, "
+ "CAST(c.SOURCE_DATA_TYPE AS VARCHAR) SCOPE_CATALOG, "
+ "CAST(c.SOURCE_DATA_TYPE AS VARCHAR) SCOPE_SCHEMA, "
+ "CAST(c.SOURCE_DATA_TYPE AS VARCHAR) SCOPE_TABLE, "
+ "c.SOURCE_DATA_TYPE, "
+ "CASE WHEN c.SEQUENCE_NAME IS NULL THEN "
+ "CAST(? AS VARCHAR) ELSE CAST(? AS VARCHAR) END IS_AUTOINCREMENT, "
+ "CAST(c.SOURCE_DATA_TYPE AS VARCHAR) SCOPE_CATLOG "
+ "FROM INFORMATION_SCHEMA.COLUMNS c JOIN INFORMATION_SCHEMA.SYNONYMS s ON "
+ "s.SYNONYM_FOR = c.TABLE_NAME "
+ "AND s.SYNONYM_FOR_SCHEMA = c.TABLE_SCHEMA "
+ "WHERE s.SYNONYM_CATALOG LIKE ? ESCAPE ? "
+ "AND s.SYNONYM_SCHEMA LIKE ? ESCAPE ? "
+ "AND s.SYNONYM_NAME LIKE ? ESCAPE ? "
+ "AND c.COLUMN_NAME LIKE ? ESCAPE ? ";
PreparedStatement prep = conn.prepareAutoCloseStatement("SELECT "
+ "TABLE_CAT, "
+ "TABLE_SCHEM, "
+ "TABLE_NAME, "
+ "COLUMN_NAME, "
+ "DATA_TYPE, "
+ "TYPE_NAME, "
+ "COLUMN_SIZE, "
+ "BUFFER_LENGTH, "
+ "DECIMAL_DIGITS, "
+ "NUM_PREC_RADIX, "
+ "NULLABLE, "
+ "REMARKS, "
+ "COLUMN_DEF, "
+ "SQL_DATA_TYPE, "
+ "SQL_DATETIME_SUB, "
+ "CHAR_OCTET_LENGTH, "
+ "ORDINAL_POSITION, "
+ "IS_NULLABLE, "
+ "SCOPE_CATALOG, "
+ "SCOPE_SCHEMA, "
+ "SCOPE_TABLE, "
+ "SOURCE_DATA_TYPE, "
+ "IS_AUTOINCREMENT, "
+ "SCOPE_CATLOG "
+ "FROM ((" + tableSql + ") UNION (" + synonymSql
+ ")) ORDER BY TABLE_SCHEM, TABLE_NAME, ORDINAL_POSITION");
prep.setString(1, "NO"); prep.setString(1, "NO");
prep.setString(2, "YES"); prep.setString(2, "YES");
prep.setString(3, getCatalogPattern(catalogPattern)); prep.setString(3, getCatalogPattern(catalogPattern));
prep.setString(4, "\\"); prep.setString(4, getSchemaPattern(schemaPattern));
prep.setString(5, getSchemaPattern(schemaPattern)); prep.setString(5, getPattern(tableNamePattern));
prep.setString(6, "\\"); prep.setString(6, getPattern(columnNamePattern));
prep.setString(7, getPattern(tableNamePattern)); prep.setString(7, "\\");
prep.setString(8, "\\");
prep.setString(9, getPattern(columnNamePattern));
prep.setString(10, "\\");
prep.setString(11, "NO");
prep.setString(12, "YES");
prep.setString(13, getCatalogPattern(catalogPattern));
prep.setString(14, "\\");
prep.setString(15, getSchemaPattern(schemaPattern));
prep.setString(16, "\\");
prep.setString(17, getPattern(tableNamePattern));
prep.setString(18, "\\");
prep.setString(19, getPattern(columnNamePattern));
prep.setString(20, "\\");
return prep.executeQuery(); return prep.executeQuery();
} catch (Exception e) { } catch (Exception e) {
throw logAndConvert(e); throw logAndConvert(e);
......
...@@ -323,7 +323,7 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex { ...@@ -323,7 +323,7 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex {
int idx = c.getColumnId(); int idx = c.getColumnId();
Value v = r.getValue(idx); Value v = r.getValue(idx);
if (v != null) { if (v != null) {
array[i] = v.convertTo(c.getType()); array[i] = v.convertTo(c.getType(), -1, null, null, c.getEnumerators());
} }
} }
array[keyColumns - 1] = ValueLong.get(r.getKey()); array[keyColumns - 1] = ValueLong.get(r.getKey());
......
...@@ -334,11 +334,12 @@ public class Column { ...@@ -334,11 +334,12 @@ public class Column {
synchronized (this) { synchronized (this) {
localDefaultExpression = defaultExpression; localDefaultExpression = defaultExpression;
} }
Mode mode = session.getDatabase().getMode();
if (value == null) { if (value == null) {
if (localDefaultExpression == null) { if (localDefaultExpression == null) {
value = ValueNull.INSTANCE; value = ValueNull.INSTANCE;
} else { } else {
value = localDefaultExpression.getValue(session).convertTo(type); value = convert(localDefaultExpression.getValue(session), mode);
if (!localDefaultExpression.isConstant()) { if (!localDefaultExpression.isConstant()) {
session.getGeneratedKeys().add(this); session.getGeneratedKeys().add(this);
} }
...@@ -347,10 +348,9 @@ public class Column { ...@@ -347,10 +348,9 @@ public class Column {
} }
} }
} }
Mode mode = session.getDatabase().getMode();
if (value == ValueNull.INSTANCE) { if (value == ValueNull.INSTANCE) {
if (convertNullToDefault) { if (convertNullToDefault) {
value = localDefaultExpression.getValue(session).convertTo(type); value = convert(localDefaultExpression.getValue(session), mode);
if (!localDefaultExpression.isConstant()) { if (!localDefaultExpression.isConstant()) {
session.getGeneratedKeys().add(this); session.getGeneratedKeys().add(this);
} }
......
...@@ -1688,7 +1688,8 @@ public class MetaTable extends Table { ...@@ -1688,7 +1688,8 @@ public class MetaTable extends Table {
// CONSTRAINT_NAME // CONSTRAINT_NAME
identifier(constraint.getName()), identifier(constraint.getName()),
// CONSTRAINT_TYPE // CONSTRAINT_TYPE
constraintType.toString(), constraintType == Constraint.Type.PRIMARY_KEY ?
constraintType.getSqlName() : constraintType.name(),
// TABLE_CATALOG // TABLE_CATALOG
catalog, catalog,
// TABLE_SCHEMA // TABLE_SCHEMA
......
...@@ -193,7 +193,13 @@ public class TableView extends Table { ...@@ -193,7 +193,13 @@ public class TableView extends Table {
long precision = expr.getPrecision(); long precision = expr.getPrecision();
int scale = expr.getScale(); int scale = expr.getScale();
int displaySize = expr.getDisplaySize(); int displaySize = expr.getDisplaySize();
Column col = new Column(name, type, precision, scale, displaySize); String[] enumerators = null;
if (type == Value.ENUM) {
if (expr instanceof ExpressionColumn) {
enumerators = ((ExpressionColumn) expr).getColumn().getEnumerators();
}
}
Column col = new Column(name, type, precision, scale, displaySize, enumerators);
col.setTable(this, i); col.setTable(this, i);
// Fetch check constraint from view column source // Fetch check constraint from view column source
ExpressionColumn fromColumn = null; ExpressionColumn fromColumn = null;
......
...@@ -38,6 +38,7 @@ import org.h2.util.Bits; ...@@ -38,6 +38,7 @@ import org.h2.util.Bits;
import org.h2.util.JdbcUtils; import org.h2.util.JdbcUtils;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
import org.h2.util.New; import org.h2.util.New;
import org.h2.util.SimpleColumnInfo;
import org.h2.util.Utils; import org.h2.util.Utils;
import org.h2.value.DataType; import org.h2.value.DataType;
...@@ -67,7 +68,7 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData, ...@@ -67,7 +68,7 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData,
private int rowId = -1; private int rowId = -1;
private boolean wasNull; private boolean wasNull;
private SimpleRowSource source; private SimpleRowSource source;
private ArrayList<Column> columns = New.arrayList(); private ArrayList<SimpleColumnInfo> columns = New.arrayList();
private boolean autoClose = true; private boolean autoClose = true;
/** /**
...@@ -123,13 +124,7 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData, ...@@ -123,13 +124,7 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData,
if (name == null) { if (name == null) {
name = "C" + (columns.size() + 1); name = "C" + (columns.size() + 1);
} }
Column column = new Column(); columns.add(new SimpleColumnInfo(name, sqlType, sqlTypeName, precision, scale));
column.name = name;
column.sqlType = sqlType;
column.precision = precision;
column.scale = scale;
column.sqlTypeName = sqlTypeName;
columns.add(column);
} }
/** /**
...@@ -1012,7 +1007,7 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData, ...@@ -1012,7 +1007,7 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData,
if (o == null) { if (o == null) {
return null; return null;
} }
switch (columns.get(columnIndex - 1).sqlType) { switch (columns.get(columnIndex - 1).type) {
case Types.CLOB: case Types.CLOB:
Clob c = (Clob) o; Clob c = (Clob) o;
return c.getSubString(1, MathUtils.convertLongToInt(c.length())); return c.getSubString(1, MathUtils.convertLongToInt(c.length()));
...@@ -1868,7 +1863,7 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData, ...@@ -1868,7 +1863,7 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData,
*/ */
@Override @Override
public int getColumnType(int columnIndex) throws SQLException { public int getColumnType(int columnIndex) throws SQLException {
return getColumn(columnIndex - 1).sqlType; return getColumn(columnIndex - 1).type;
} }
/** /**
...@@ -2045,7 +2040,7 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData, ...@@ -2045,7 +2040,7 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData,
*/ */
@Override @Override
public String getColumnTypeName(int columnIndex) throws SQLException { public String getColumnTypeName(int columnIndex) throws SQLException {
return getColumn(columnIndex - 1).sqlTypeName; return getColumn(columnIndex - 1).typeName;
} }
/** /**
...@@ -2295,7 +2290,7 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData, ...@@ -2295,7 +2290,7 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData,
return o; return o;
} }
private Column getColumn(int i) throws SQLException { private SimpleColumnInfo getColumn(int i) throws SQLException {
checkColumnIndex(i + 1); checkColumnIndex(i + 1);
return columns.get(i); return columns.get(i);
} }
...@@ -2355,37 +2350,6 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData, ...@@ -2355,37 +2350,6 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData,
return autoClose; return autoClose;
} }
/**
* This class holds the data of a result column.
*/
static class Column {
/**
* The column label.
*/
String name;
/**
* The column type Name
*/
String sqlTypeName;
/**
* The SQL type.
*/
int sqlType;
/**
* The precision.
*/
int precision;
/**
* The scale.
*/
int scale;
}
/** /**
* A simple array implementation, * A simple array implementation,
* backed by an object array * backed by an object array
......
...@@ -24,59 +24,9 @@ import org.h2.tools.SimpleResultSet; ...@@ -24,59 +24,9 @@ import org.h2.tools.SimpleResultSet;
* that have {@code NAME} column should also define it with the same type. * that have {@code NAME} column should also define it with the same type.
*/ */
public final class MergedResultSet { public final class MergedResultSet {
/** private final ArrayList<Map<SimpleColumnInfo, Object>> data = New.arrayList();
* Metadata of a column.
*/
private static final class ColumnInfo {
final String name;
final int type;
final int precision;
final int scale;
/**
* Creates metadata.
*
* @param name
* name of the column
* @param type
* type of the column, see {@link java.sql.Types}
* @param precision
* precision of the column
* @param scale
* scale of the column
*/
ColumnInfo(String name, int type, int precision, int scale) {
this.name = name;
this.type = type;
this.precision = precision;
this.scale = scale;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
ColumnInfo other = (ColumnInfo) obj;
return name.equals(other.name);
}
@Override
public int hashCode() {
return name.hashCode();
}
}
private final ArrayList<Map<ColumnInfo, Object>> data = New.arrayList();
private final ArrayList<ColumnInfo> columns = New.arrayList(); private final ArrayList<SimpleColumnInfo> columns = New.arrayList();
/** /**
* Appends a result set. * Appends a result set.
...@@ -92,10 +42,10 @@ public final class MergedResultSet { ...@@ -92,10 +42,10 @@ public final class MergedResultSet {
if (cols == 0) { if (cols == 0) {
return; return;
} }
ColumnInfo[] info = new ColumnInfo[cols]; SimpleColumnInfo[] info = new SimpleColumnInfo[cols];
for (int i = 1; i <= cols; i++) { for (int i = 1; i <= cols; i++) {
ColumnInfo ci = new ColumnInfo(meta.getColumnName(i), meta.getColumnType(i), meta.getPrecision(i), SimpleColumnInfo ci = new SimpleColumnInfo(meta.getColumnName(i), meta.getColumnType(i),
meta.getScale(i)); meta.getColumnTypeName(i), meta.getPrecision(i), meta.getScale(i));
info[i - 1] = ci; info[i - 1] = ci;
if (!columns.contains(ci)) { if (!columns.contains(ci)) {
columns.add(ci); columns.add(ci);
...@@ -105,9 +55,9 @@ public final class MergedResultSet { ...@@ -105,9 +55,9 @@ public final class MergedResultSet {
if (cols == 1) { if (cols == 1) {
data.add(Collections.singletonMap(info[0], rs.getObject(1))); data.add(Collections.singletonMap(info[0], rs.getObject(1)));
} else { } else {
HashMap<ColumnInfo, Object> map = new HashMap<>(); HashMap<SimpleColumnInfo, Object> map = new HashMap<>();
for (int i = 1; i <= cols; i++) { for (int i = 1; i <= cols; i++) {
ColumnInfo ci = info[i - 1]; SimpleColumnInfo ci = info[i - 1];
map.put(ci, rs.getObject(i)); map.put(ci, rs.getObject(i));
} }
data.add(map); data.add(map);
...@@ -122,12 +72,12 @@ public final class MergedResultSet { ...@@ -122,12 +72,12 @@ public final class MergedResultSet {
*/ */
public SimpleResultSet getResult() { public SimpleResultSet getResult() {
SimpleResultSet rs = new SimpleResultSet(); SimpleResultSet rs = new SimpleResultSet();
for (ColumnInfo ci : columns) { for (SimpleColumnInfo ci : columns) {
rs.addColumn(ci.name, ci.type, ci.precision, ci.scale); rs.addColumn(ci.name, ci.type, ci.typeName, ci.precision, ci.scale);
} }
for (Map<ColumnInfo, Object> map : data) { for (Map<SimpleColumnInfo, Object> map : data) {
Object[] row = new Object[columns.size()]; Object[] row = new Object[columns.size()];
for (Map.Entry<ColumnInfo, Object> entry : map.entrySet()) { for (Map.Entry<SimpleColumnInfo, Object> entry : map.entrySet()) {
row[columns.indexOf(entry.getKey())] = entry.getValue(); row[columns.indexOf(entry.getKey())] = entry.getValue();
} }
rs.addRow(row); rs.addRow(row);
......
/*
* Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
/**
* Metadata of a column.
*
* <p>
* Notice: {@linkplain #equals(Object)} and {@linkplain #hashCode()} use only
* {@linkplain #name} field.
* </p>
*/
public final class SimpleColumnInfo {
/**
* Name of the column.
*/
public final String name;
/**
* Type of the column, see {@link java.sql.Types}.
*/
public final int type;
/**
* Type name of the column.
*/
public final String typeName;
/**
* Precision of the column
*/
public final int precision;
/**
* Scale of the column.
*/
public final int scale;
/**
* Creates metadata.
*
* @param name
* name of the column
* @param type
* type of the column, see {@link java.sql.Types}
* @param typeName
* type name of the column
* @param precision
* precision of the column
* @param scale
* scale of the column
*/
public SimpleColumnInfo(String name, int type, String typeName, int precision, int scale) {
this.name = name;
this.type = type;
this.typeName = typeName;
this.precision = precision;
this.scale = scale;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
SimpleColumnInfo other = (SimpleColumnInfo) obj;
return name.equals(other.name);
}
@Override
public int hashCode() {
return name.hashCode();
}
}
...@@ -856,6 +856,11 @@ public class DataType { ...@@ -856,6 +856,11 @@ public class DataType {
*/ */
public static int convertSQLTypeToValueType(int sqlType, String sqlTypeName) { public static int convertSQLTypeToValueType(int sqlType, String sqlTypeName) {
switch (sqlType) { switch (sqlType) {
case Types.BINARY:
if (sqlTypeName.equalsIgnoreCase("UUID")) {
return Value.UUID;
}
break;
case Types.OTHER: case Types.OTHER:
case Types.JAVA_OBJECT: case Types.JAVA_OBJECT:
if (sqlTypeName.equalsIgnoreCase("geometry")) { if (sqlTypeName.equalsIgnoreCase("geometry")) {
......
...@@ -982,7 +982,16 @@ public abstract class Value { ...@@ -982,7 +982,16 @@ public abstract class Value {
case STRING_IGNORECASE: case STRING_IGNORECASE:
case STRING_FIXED: case STRING_FIXED:
return ValueEnum.get(enumerators, getString()); return ValueEnum.get(enumerators, getString());
default: case JAVA_OBJECT:
Object object = JdbcUtils.deserialize(getBytesNoCopy(),
getDataHandler());
if (object instanceof String) {
return ValueEnum.get(enumerators, (String) object);
} else if (object instanceof Integer) {
return ValueEnum.get(enumerators, (int) object);
}
//$FALL-THROUGH$
default:
throw DbException.get( throw DbException.get(
ErrorCode.DATA_CONVERSION_ERROR_1, getString()); ErrorCode.DATA_CONVERSION_ERROR_1, getString());
} }
......
...@@ -77,7 +77,7 @@ public class ValueEnumBase extends Value { ...@@ -77,7 +77,7 @@ public class ValueEnumBase extends Value {
@Override @Override
public Object getObject() { public Object getObject() {
return ordinal; return label;
} }
@Override @Override
......
...@@ -11,6 +11,7 @@ import java.util.Arrays; ...@@ -11,6 +11,7 @@ import java.util.Arrays;
import org.h2.engine.Mode; import org.h2.engine.Mode;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.util.Utils;
import org.locationtech.jts.geom.CoordinateSequence; import org.locationtech.jts.geom.CoordinateSequence;
import org.locationtech.jts.geom.CoordinateSequenceFilter; import org.locationtech.jts.geom.CoordinateSequenceFilter;
import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Envelope;
...@@ -140,7 +141,20 @@ public class ValueGeometry extends Value { ...@@ -140,7 +141,20 @@ public class ValueGeometry extends Value {
* @return a copy of the geometry object * @return a copy of the geometry object
*/ */
public Geometry getGeometry() { public Geometry getGeometry() {
return getGeometryNoCopy().copy(); Geometry geometry = getGeometryNoCopy();
Geometry copy = geometry.copy();
/*
* Geometry factory is not preserved in WKB format, but SRID is preserved.
*
* We use a new factory to read geometries from WKB with default SRID value of
* 0.
*
* Geometry.copy() copies the geometry factory and copied value has SRID form
* the factory instead of original SRID value. So we need to copy SRID here with
* non-recommended (but not deprecated) setSRID() method.
*/
copy.setSRID(geometry.getSRID());
return copy;
} }
public Geometry getGeometryNoCopy() { public Geometry getGeometryNoCopy() {
...@@ -221,7 +235,7 @@ public class ValueGeometry extends Value { ...@@ -221,7 +235,7 @@ public class ValueGeometry extends Value {
@Override @Override
public byte[] getBytes() { public byte[] getBytes() {
return getWKB(); return Utils.cloneByteArray(getWKB());
} }
@Override @Override
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
package org.h2.test.db; package org.h2.test.db;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
...@@ -99,6 +100,9 @@ public class TestIndex extends TestBase { ...@@ -99,6 +100,9 @@ public class TestIndex extends TestBase {
conn.close(); conn.close();
deleteDb("index"); deleteDb("index");
// This test uses own connection
testEnumIndex();
} }
private void testOrderIndex() throws SQLException { private void testOrderIndex() throws SQLException {
...@@ -750,4 +754,27 @@ public class TestIndex extends TestBase { ...@@ -750,4 +754,27 @@ public class TestIndex extends TestBase {
assertEquals(1, testFunctionIndexCounter); assertEquals(1, testFunctionIndexCounter);
} }
private void testEnumIndex() throws SQLException {
if (config.memory || config.networked) {
return;
}
deleteDb("index");
String url = "jdbc:h2:" + getBaseDir() + "/index;DB_CLOSE_DELAY=0";
Connection conn = DriverManager.getConnection(url);
Statement stat = conn.createStatement();
stat.execute("CREATE TABLE TEST(ID INT, V ENUM('A', 'B'), CONSTRAINT PK PRIMARY KEY(ID, V))");
stat.execute("INSERT INTO TEST VALUES (1, 'A'), (2, 'B')");
conn.close();
conn = DriverManager.getConnection(url);
stat = conn.createStatement();
stat.execute("DELETE FROM TEST WHERE V = 'A'");
stat.execute("DROP TABLE TEST");
conn.close();
deleteDb("index");
}
} }
...@@ -648,13 +648,14 @@ public class TestSpatial extends TestBase { ...@@ -648,13 +648,14 @@ public class TestSpatial extends TestBase {
GeometryFactory geometryFactory = new GeometryFactory(); GeometryFactory geometryFactory = new GeometryFactory();
Geometry geometry = geometryFactory.createPoint(new Coordinate(0, 0)); Geometry geometry = geometryFactory.createPoint(new Coordinate(0, 0));
geometry.setSRID(27572); geometry.setSRID(27572);
ValueGeometry valueGeometry = ValueGeometry valueGeometry = ValueGeometry.getFromGeometry(geometry);
ValueGeometry.getFromGeometry(geometry);
Geometry geometry2 = geometryFactory.createPoint(new Coordinate(0, 0)); Geometry geometry2 = geometryFactory.createPoint(new Coordinate(0, 0));
geometry2.setSRID(5326); geometry2.setSRID(5326);
ValueGeometry valueGeometry2 = ValueGeometry valueGeometry2 = ValueGeometry.getFromGeometry(geometry2);
ValueGeometry.getFromGeometry(geometry2);
assertFalse(valueGeometry.equals(valueGeometry2)); assertFalse(valueGeometry.equals(valueGeometry2));
ValueGeometry valueGeometry3 = ValueGeometry.getFromGeometry(geometry);
assertEquals(valueGeometry, valueGeometry3);
assertEquals(geometry.getSRID(), valueGeometry3.getGeometry().getSRID());
// Check illegal geometry (no WKB representation) // Check illegal geometry (no WKB representation)
try { try {
ValueGeometry.get("POINT EMPTY"); ValueGeometry.get("POINT EMPTY");
......
...@@ -8,6 +8,7 @@ package org.h2.test.jdbc; ...@@ -8,6 +8,7 @@ package org.h2.test.jdbc;
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.ResultSetMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.UUID; import java.util.UUID;
...@@ -127,6 +128,9 @@ public class TestGetGeneratedKeys extends TestBase { ...@@ -127,6 +128,9 @@ public class TestGetGeneratedKeys extends TestBase {
prep.addBatch(); prep.addBatch();
prep.executeBatch(); prep.executeBatch();
ResultSet rs = prep.getGeneratedKeys(); ResultSet rs = prep.getGeneratedKeys();
ResultSetMetaData meta = rs.getMetaData();
assertEquals("BIGINT", meta.getColumnTypeName(1));
assertEquals("UUID", meta.getColumnTypeName(2));
rs.next(); rs.next();
assertEquals(1L, rs.getLong(1)); assertEquals(1L, rs.getLong(1));
UUID u1 = (UUID) rs.getObject(2); UUID u1 = (UUID) rs.getObject(2);
......
...@@ -8,11 +8,14 @@ package org.h2.test.jdbc; ...@@ -8,11 +8,14 @@ package org.h2.test.jdbc;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DatabaseMetaData; import java.sql.DatabaseMetaData;
import java.sql.Driver; import java.sql.Driver;
import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.ResultSetMetaData; import java.sql.ResultSetMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.sql.Types; import java.sql.Types;
import java.util.UUID;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.engine.SysProperties; import org.h2.engine.SysProperties;
...@@ -135,6 +138,13 @@ public class TestMetaData extends TestBase { ...@@ -135,6 +138,13 @@ public class TestMetaData extends TestBase {
assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, conn.getHoldability()); assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, conn.getHoldability());
assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, rs.getHoldability()); assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, rs.getHoldability());
stat.executeUpdate("drop table test"); stat.executeUpdate("drop table test");
PreparedStatement prep = conn.prepareStatement("SELECT X FROM TABLE (X UUID = ?)");
prep.setObject(1, UUID.randomUUID());
rs = prep.executeQuery();
rsMeta = rs.getMetaData();
assertEquals("UUID", rsMeta.getColumnTypeName(1));
conn.close(); conn.close();
} }
......
...@@ -476,7 +476,7 @@ public class TestPreparedStatement extends TestBase { ...@@ -476,7 +476,7 @@ public class TestPreparedStatement extends TestBase {
assertEquals(goodSizes[i], rs.getString(1)); assertEquals(goodSizes[i], rs.getString(1));
assertEquals(i, rs.getInt(1)); assertEquals(i, rs.getInt(1));
Object o = rs.getObject(1); Object o = rs.getObject(1);
assertEquals(Integer.class, o.getClass()); assertEquals(String.class, o.getClass());
} }
for (int i = 0; i < goodSizes.length; i++) { for (int i = 0; i < goodSizes.length; i++) {
......
...@@ -87,6 +87,7 @@ public class TestResultSet extends TestBase { ...@@ -87,6 +87,7 @@ public class TestResultSet extends TestBase {
testFindColumn(); testFindColumn();
testColumnLength(); testColumnLength();
testArray(); testArray();
testEnum();
testLimitMaxRows(); testLimitMaxRows();
trace("max rows=" + stat.getMaxRows()); trace("max rows=" + stat.getMaxRows());
...@@ -1798,6 +1799,56 @@ public class TestResultSet extends TestBase { ...@@ -1798,6 +1799,56 @@ public class TestResultSet extends TestBase {
stat.execute("DROP TABLE TEST"); stat.execute("DROP TABLE TEST");
} }
private void testEnum() throws SQLException {
trace("Test ENUM");
stat.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, VALUE ENUM('A', 'B', 'C', 'D', 'E', 'F', 'G'))");
PreparedStatement prep = conn.prepareStatement("INSERT INTO TEST VALUES(?, ?)");
prep.setInt(1, 1);
prep.setString(2, "A");
prep.executeUpdate();
prep.setInt(1, 2);
prep.setObject(2, "B");
prep.executeUpdate();
prep.setInt(1, 3);
prep.setInt(2, 2);
prep.executeUpdate();
prep.setInt(1, 4);
prep.setObject(2, "D", Types.VARCHAR);
prep.executeUpdate();
prep.setInt(1, 5);
prep.setObject(2, "E", Types.OTHER);
prep.executeUpdate();
prep.setInt(1, 6);
prep.setObject(2, 5, Types.OTHER);
prep.executeUpdate();
prep.setInt(1, 7);
prep.setObject(2, 6, Types.INTEGER);
prep.executeUpdate();
ResultSet rs = stat.executeQuery("SELECT * FROM TEST ORDER BY ID");
testEnumResult(rs, 1, "A", 0);
testEnumResult(rs, 2, "B", 1);
testEnumResult(rs, 3, "C", 2);
testEnumResult(rs, 4, "D", 3);
testEnumResult(rs, 5, "E", 4);
testEnumResult(rs, 6, "F", 5);
testEnumResult(rs, 7, "G", 6);
assertFalse(rs.next());
stat.execute("DROP TABLE TEST");
}
private void testEnumResult(ResultSet rs, int id, String name, int ordinal) throws SQLException {
assertTrue(rs.next());
assertEquals(id, rs.getInt(1));
assertEquals(name, rs.getString(2));
assertEquals(name, rs.getObject(2));
assertEquals(name, rs.getObject(2, String.class));
assertEquals(ordinal, rs.getInt(2));
assertEquals((Integer) ordinal, rs.getObject(2, Integer.class));
}
private byte[] readAllBytes(InputStream in) { private byte[] readAllBytes(InputStream in) {
if (in == null) { if (in == null) {
return null; return null;
......
...@@ -162,3 +162,93 @@ drop table card; ...@@ -162,3 +162,93 @@ drop table card;
drop type CARD_SUIT; drop type CARD_SUIT;
> ok > ok
CREATE TABLE TEST(ID INT, E1 ENUM('A', 'B') DEFAULT 'A', E2 ENUM('C', 'D') DEFAULT 'C' ON UPDATE 'D');
> ok
INSERT INTO TEST(ID) VALUES (1);
> update count: 1
SELECT * FROM TEST;
> ID E1 E2
> -- -- --
> 1 A C
> rows: 1
UPDATE TEST SET E1 = 'B';
> update count: 1
SELECT * FROM TEST;
> ID E1 E2
> -- -- --
> 1 B D
> rows: 1
DROP TABLE TEST;
> ok
CREATE TABLE TEST(E ENUM('A', 'B'));
> ok
INSERT INTO TEST VALUES ('B');
> update count: 1
CREATE VIEW V AS SELECT * FROM TEST;
> ok
SELECT * FROM V;
> E
> -
> B
> rows: 1
CREATE VIEW V1 AS SELECT E + 2 AS E FROM TEST;
> ok
SELECT * FROM V1;
> E
> -
> 3
> rows: 1
CREATE VIEW V2 AS SELECT E + E AS E FROM TEST;
> ok
SELECT * FROM V2;
> E
> -
> 2
> rows: 1
CREATE VIEW V3 AS SELECT -E AS E FROM TEST;
> ok
SELECT * FROM V3;
> E
> --
> -1
> rows: 1
SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME = 'E' ORDER BY TABLE_NAME;
> TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION COLUMN_DEFAULT IS_NULLABLE DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_PRECISION_RADIX NUMERIC_SCALE CHARACTER_SET_NAME COLLATION_NAME TYPE_NAME NULLABLE IS_COMPUTED SELECTIVITY CHECK_CONSTRAINT SEQUENCE_NAME REMARKS SOURCE_DATA_TYPE COLUMN_TYPE COLUMN_ON_UPDATE
> ------------- ------------ ---------- ----------- ---------------- -------------- ----------- --------- ------------------------ ---------------------- ----------------- ----------------------- ------------- ------------------ -------------- --------- -------- ----------- ----------- ---------------- ------------- ------- ---------------- ------------- ----------------
> SCRIPT PUBLIC TEST E 1 null YES 1111 2147483647 2147483647 2147483647 10 0 Unicode OFF ENUM 1 FALSE 50 null null ENUM('A','B') null
> SCRIPT PUBLIC V E 1 null YES 1111 2147483647 2147483647 2147483647 10 0 Unicode OFF ENUM 1 FALSE 50 null null ENUM('A','B') null
> SCRIPT PUBLIC V1 E 1 null YES 4 2147483647 2147483647 2147483647 10 0 Unicode OFF INTEGER 1 FALSE 50 null null INTEGER null
> SCRIPT PUBLIC V2 E 1 null YES 4 2147483647 2147483647 2147483647 10 0 Unicode OFF INTEGER 1 FALSE 50 null null INTEGER null
> SCRIPT PUBLIC V3 E 1 null YES 4 2147483647 2147483647 2147483647 10 0 Unicode OFF INTEGER 1 FALSE 50 null null INTEGER null
> rows (ordered): 5
DROP VIEW V;
> ok
DROP VIEW V1;
> ok
DROP VIEW V2;
> ok
DROP VIEW V3;
> ok
DROP TABLE TEST;
> ok
...@@ -30,4 +30,45 @@ EXPLAIN PLAN ...@@ -30,4 +30,45 @@ EXPLAIN PLAN
INSERT (ID, NAME) VALUES (S.ID, S.NAME); INSERT (ID, NAME) VALUES (S.ID, S.NAME);
> PLAN > PLAN
> --------------------------------------------------------------------------------------------------------------------------------- > ---------------------------------------------------------------------------------------------------------------------------------
> MERGE INTO PUBLIC.PARENT(ID, NAME) KEY(ID) SELECT X AS ID, ('Coco' || X) AS NAME FROM SYSTEM_RANGE(1, 2) /* PUBLIC.RANGE_INDEX */ > MERGE INTO PUBLIC.PARENT(ID, NAME) KEY(ID) SELECT X AS ID, ('Coco' || X) AS NAME FROM SYSTEM_RANGE(1, 2) /* PUBLIC.RANGE_INDEX */
\ No newline at end of file
DROP TABLE PARENT;
> ok
CREATE SCHEMA SOURCESCHEMA;
> ok
CREATE TABLE SOURCESCHEMA.SOURCE(ID INT PRIMARY KEY, VALUE INT);
> ok
INSERT INTO SOURCESCHEMA.SOURCE VALUES (1, 10), (3, 30), (5, 50);
> update count: 3
CREATE SCHEMA DESTSCHEMA;
> ok
CREATE TABLE DESTSCHEMA.DESTINATION(ID INT PRIMARY KEY, VALUE INT);
> ok
INSERT INTO DESTSCHEMA.DESTINATION VALUES (3, 300), (6, 600);
> update count: 2
MERGE INTO DESTSCHEMA.DESTINATION USING SOURCESCHEMA.SOURCE ON (DESTSCHEMA.DESTINATION.ID = SOURCESCHEMA.SOURCE.ID)
WHEN MATCHED THEN UPDATE SET VALUE = SOURCESCHEMA.SOURCE.VALUE
WHEN NOT MATCHED THEN INSERT (ID, VALUE) VALUES (SOURCESCHEMA.SOURCE.ID, SOURCESCHEMA.SOURCE.VALUE);
> update count: 3
SELECT * FROM DESTSCHEMA.DESTINATION;
> ID VALUE
> -- -----
> 1 10
> 3 30
> 5 50
> 6 600
> rows: 4
DROP SCHEMA SOURCESCHEMA CASCADE;
> ok
DROP SCHEMA DESTSCHEMA CASCADE;
> ok
...@@ -14,10 +14,9 @@ insert into test values ('1'), ('2'), ('3'), ('4'), ('5'), ('6'), ('7'), ('8'), ...@@ -14,10 +14,9 @@ insert into test values ('1'), ('2'), ('3'), ('4'), ('5'), ('6'), ('7'), ('8'),
select array_agg(v order by v asc), select array_agg(v order by v asc),
array_agg(v order by v desc) filter (where v >= '4') array_agg(v order by v desc) filter (where v >= '4')
from test where v >= '2'; from test where v >= '2';
> ARRAY_AGG(V ORDER BY V) ARRAY_AGG(V ORDER BY V DESC) FILTER (WHERE (V >= '4')) > ARRAY_AGG(V ORDER BY V) ARRAY_AGG(V ORDER BY V DESC) FILTER (WHERE (V >= '4'))
> ---------------------------------------------------------------- ------------------------------------------------------ > ------------------------ ------------------------------------------------------
------------------------------ > (2, 3, 4, 5, 6, 7, 8, 9) (9, 8, 7, 6, 5, 4)
> (2, 3, 4, 5, 6, 7, 8, 9) (9, 8, 7, 6, 5, 4)
> rows (ordered): 1 > rows (ordered): 1
create index test_idx on test(v); create index test_idx on test(v);
...@@ -25,21 +24,45 @@ create index test_idx on test(v); ...@@ -25,21 +24,45 @@ create index test_idx on test(v);
select ARRAY_AGG(v order by v asc), select ARRAY_AGG(v order by v asc),
ARRAY_AGG(v order by v desc) filter (where v >= '4') ARRAY_AGG(v order by v desc) filter (where v >= '4')
from test where v >= '2'; from test where v >= '2';
> ARRAY_AGG(V ORDER BY V) ARRAY_AGG(V ORDER BY V DESC) FILTER (WHERE (V >= '4')) > ARRAY_AGG(V ORDER BY V) ARRAY_AGG(V ORDER BY V DESC) FILTER (WHERE (V >= '4'))
> ---------------------------------------------------------------- ------------------------------------------------------ > ------------------------ ------------------------------------------------------
------------------------------ > (2, 3, 4, 5, 6, 7, 8, 9) (9, 8, 7, 6, 5, 4)
> (2, 3, 4, 5, 6, 7, 8, 9) (9, 8, 7, 6, 5, 4)
> rows (ordered): 1 > rows (ordered): 1
select ARRAY_AGG(v order by v asc), select ARRAY_AGG(v order by v asc),
ARRAY_AGG(v order by v desc) filter (where v >= '4') ARRAY_AGG(v order by v desc) filter (where v >= '4')
from test; from test;
> ARRAY_AGG(V ORDER BY V) ARRAY_AGG(V ORDER BY V DESC) FILTER (WHERE (V >= '4')) > ARRAY_AGG(V ORDER BY V) ARRAY_AGG(V ORDER BY V DESC) FILTER (WHERE (V >= '4'))
> ------------------------------------------------------------------------ ------------------------------------------------------ > --------------------------- ------------------------------------------------------
------------------------------ > (1, 2, 3, 4, 5, 6, 7, 8, 9) (9, 8, 7, 6, 5, 4)
> (1, 2, 3, 4, 5, 6, 7, 8, 9) (9, 8, 7, 6, 5, 4)
> rows (ordered): 1 > rows (ordered): 1
drop table test;
> ok
create table test (id int auto_increment primary key, v int);
> ok
insert into test(v) values (7), (2), (8), (3), (7), (3), (9), (-1);
> update count: 8
select array_agg(v) from test;
> ARRAY_AGG(V)
> -------------------------
> (7, 2, 8, 3, 7, 3, 9, -1)
> rows: 1
select array_agg(distinct v) from test;
> ARRAY_AGG(DISTINCT V)
> ---------------------
> (-1, 2, 3, 7, 8, 9)
> rows: 1
select array_agg(distinct v order by v desc) from test;
> ARRAY_AGG(DISTINCT V ORDER BY V DESC)
> -------------------------------------
> (9, 8, 7, 3, 2, -1)
> rows (ordered): 1
drop table test; drop table test;
> ok > ok
...@@ -37,6 +37,32 @@ select group_concat(v order by v asc separator '-'), ...@@ -37,6 +37,32 @@ select group_concat(v order by v asc separator '-'),
> 1-2-3-4-5-6-7-8-9 9-8-7-6-5-4 > 1-2-3-4-5-6-7-8-9 9-8-7-6-5-4
> rows (ordered): 1 > rows (ordered): 1
drop table test;
> ok
create table test (id int auto_increment primary key, v int);
> ok
insert into test(v) values (7), (2), (8), (3), (7), (3), (9), (-1);
> update count: 8
select group_concat(v) from test;
> GROUP_CONCAT(V)
> ----------------
> 7,2,8,3,7,3,9,-1
> rows: 1
select group_concat(distinct v) from test;
> GROUP_CONCAT(DISTINCT V)
> ------------------------
> -1,2,3,7,8,9
> rows: 1
select group_concat(distinct v order by v desc) from test;
> GROUP_CONCAT(DISTINCT V ORDER BY V DESC)
> ----------------------------------------
> 9,8,7,3,2,-1
> rows (ordered): 1
drop table test; drop table test;
> ok > ok
...@@ -41,6 +41,18 @@ SELECT CONSTRAINT_NAME, CONSTRAINT_TYPE, TABLE_NAME, IS_DEFERRABLE, INITIALLY_DE ...@@ -41,6 +41,18 @@ SELECT CONSTRAINT_NAME, CONSTRAINT_TYPE, TABLE_NAME, IS_DEFERRABLE, INITIALLY_DE
> FK_2 FOREIGN KEY T2 NO NO > FK_2 FOREIGN KEY T2 NO NO
> rows (ordered): 5 > rows (ordered): 5
SELECT CONSTRAINT_NAME, CONSTRAINT_TYPE, TABLE_NAME, COLUMN_LIST FROM INFORMATION_SCHEMA.CONSTRAINTS
WHERE CONSTRAINT_CATALOG = DATABASE() AND CONSTRAINT_SCHEMA = SCHEMA() AND TABLE_CATALOG = DATABASE() AND TABLE_SCHEMA = SCHEMA()
ORDER BY TABLE_NAME, CONSTRAINT_NAME;
> CONSTRAINT_NAME CONSTRAINT_TYPE TABLE_NAME COLUMN_LIST
> --------------- --------------- ---------- -----------
> PK_1 PRIMARY KEY T1 C1,C2
> U_1 UNIQUE T1 C3,C4
> CH_1 CHECK T2 null
> FK_1 REFERENTIAL T2 C3,C4
> FK_2 REFERENTIAL T2 C3,C4
> rows (ordered): 5
SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE LIMIT 0; SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE LIMIT 0;
> CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION POSITION_IN_UNIQUE_CONSTRAINT > CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION POSITION_IN_UNIQUE_CONSTRAINT
> ------------------ ----------------- --------------- ------------- ------------ ---------- ----------- ---------------- ----------------------------- > ------------------ ----------------- --------------- ------------- ------------ ---------- ----------- ---------------- -----------------------------
......
...@@ -770,5 +770,5 @@ openoffice organize libre systemtables gmane sea borders announced millennium al ...@@ -770,5 +770,5 @@ openoffice organize libre systemtables gmane sea borders announced millennium al
opti excessively opti excessively
iterators tech enums incompatibilities loses reimplement readme reorganize milli subdirectory iterators tech enums incompatibilities loses reimplement readme reorganize milli subdirectory linkplain inspections
geometries sourceschema destschema
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论