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

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

......@@ -21,6 +21,32 @@ Change Log
<h2>Next Version (unreleased)</h2>
<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>
</ul>
......@@ -389,7 +415,7 @@ Change Log
</li>
<li>PR #743: Change REGEXP_REPLACE mode for MariaDB and PostgreSQL
</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>PR #738: Copy javadoc to *BackwardsCompat to fix building of documentation
</li>
......@@ -696,7 +722,7 @@ changed from Types.OTHER (1111) to Types.TIMESTAMP_WITH_TIMEZONE (2014)
</li>
<li>Issue #266: Spatial index not updating, fixed by merging PR #267
</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>Issue #299: Nested derived tables did not always work as expected.
</li>
......
......@@ -1150,11 +1150,8 @@ public class Parser {
TableFilter sourceTableFilter = readSimpleTableFilter(0, excludeIdentifiers);
command.setSourceTableFilter(sourceTableFilter);
StringBuilder buff = new StringBuilder("SELECT * FROM ")
.append(sourceTableFilter.getTable().getName());
if (sourceTableFilter.getTableAlias() != null) {
buff.append(" AS ").append(sourceTableFilter.getTableAlias());
}
StringBuilder buff = new StringBuilder("SELECT * FROM ");
appendTableWithSchemaAndAlias(buff, sourceTableFilter.getTable(), sourceTableFilter.getTableAlias());
Prepared preparedQuery = prepare(session, buff.toString(), null/*paramValues*/);
command.setQuery((Select) preparedQuery);
......@@ -1197,11 +1194,9 @@ public class Parser {
// build and prepare the targetMatchQuery ready to test each rows
// existence in the target table (using source row to match)
StringBuilder targetMatchQuerySQL = new StringBuilder(
"SELECT _ROWID_ FROM " + command.getTargetTable().getName());
if (command.getTargetTableFilter().getTableAlias() != null) {
targetMatchQuerySQL.append(" AS ").append(command.getTargetTableFilter().getTableAlias());
}
StringBuilder targetMatchQuerySQL = new StringBuilder("SELECT _ROWID_ FROM ");
appendTableWithSchemaAndAlias(targetMatchQuerySQL, command.getTargetTable(),
command.getTargetTableFilter().getTableAlias());
targetMatchQuerySQL
.append(" WHERE ").append(command.getOnCondition().getSQL());
command.setTargetMatchQuery(
......@@ -1210,6 +1205,14 @@ public class Parser {
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() {
Insert command = new Insert(session);
currentPrepared = command;
......@@ -2628,7 +2631,7 @@ public class Parser {
readExpression(), currentSelect, distinct);
if (readIf("ORDER")) {
read("BY");
r.setGroupConcatOrder(parseSimpleOrderList());
r.setOrderByList(parseSimpleOrderList());
}
if (readIf("SEPARATOR")) {
......@@ -2642,7 +2645,7 @@ public class Parser {
r.setGroupConcatSeparator(readExpression());
if (readIf("ORDER")) {
read("BY");
r.setGroupConcatOrder(parseSimpleOrderList());
r.setOrderByList(parseSimpleOrderList());
}
} else {
r = null;
......@@ -2654,7 +2657,7 @@ public class Parser {
readExpression(), currentSelect, distinct);
if (readIf("ORDER")) {
read("BY");
r.setArrayAggOrder(parseSimpleOrderList());
r.setOrderByList(parseSimpleOrderList());
}
} else {
boolean distinct = readIf("DISTINCT");
......@@ -3027,7 +3030,7 @@ public class Parser {
int index = currentValue.getInt() - 1;
if (index < 0 || index >= Constants.MAX_PARAMETER_INDEX) {
throw DbException.getInvalidValueException(
"parameter index", index);
"parameter index", index + 1);
}
if (indexedParameterList.size() <= index) {
indexedParameterList.ensureCapacity(index + 1);
......
......@@ -6,12 +6,13 @@
package org.h2.expression;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import org.h2.api.ErrorCode;
import org.h2.command.dml.Select;
import org.h2.command.dml.SelectOrderBy;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.index.Cursor;
import org.h2.index.Index;
......@@ -143,10 +144,8 @@ public class Aggregate extends Expression {
private Expression on;
private Expression groupConcatSeparator;
private ArrayList<SelectOrderBy> groupConcatOrderList;
private ArrayList<SelectOrderBy> arrayAggOrderList;
private SortOrder groupConcatSort;
private SortOrder arrayOrderSort;
private ArrayList<SelectOrderBy> orderByList;
private SortOrder orderBySort;
private int dataType, scale;
private long precision;
private int displaySize;
......@@ -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) {
this.groupConcatOrderList = orderBy;
}
/**
* Set the order for ARRAY_AGG() aggregate.
*
* @param orderBy the order by list
*/
public void setArrayAggOrder(ArrayList<SelectOrderBy> orderBy) {
this.arrayAggOrderList = orderBy;
public void setOrderByList(ArrayList<SelectOrderBy> orderByList) {
this.orderByList = orderByList;
}
/**
......@@ -255,12 +245,12 @@ public class Aggregate extends Expression {
this.filterCondition = filterCondition;
}
private SortOrder initOrder(ArrayList<SelectOrderBy> orderList, Session session) {
int size = orderList.size();
private SortOrder initOrder(Session session) {
int size = orderByList.size();
int[] index = new int[size];
int[] sortType = new int[size];
for (int i = 0; i < size; i++) {
SelectOrderBy o = orderList.get(i);
SelectOrderBy o = orderByList.get(i);
index[i] = i + 1;
int order = o.descending ? SortOrder.DESCENDING : SortOrder.ASCENDING;
sortType[i] = order;
......@@ -268,6 +258,26 @@ public class Aggregate extends Expression {
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
public void updateAggregate(Session session) {
// TODO aggregates: check nested MIN(MAX(ID)) and so on
......@@ -296,12 +306,12 @@ public class Aggregate extends Expression {
if (type == AggregateType.GROUP_CONCAT) {
if (v != ValueNull.INSTANCE) {
v = v.convertTo(Value.STRING);
if (groupConcatOrderList != null) {
int size = groupConcatOrderList.size();
if (orderByList != null) {
int size = orderByList.size();
Value[] array = new Value[1 + size];
array[0] = v;
for (int i = 0; i < size; i++) {
SelectOrderBy o = groupConcatOrderList.get(i);
SelectOrderBy o = orderByList.get(i);
array[i + 1] = o.expression.getValue(session);
}
v = ValueArray.get(array);
......@@ -310,12 +320,12 @@ public class Aggregate extends Expression {
}
if (type == AggregateType.ARRAY_AGG) {
if (v != ValueNull.INSTANCE) {
if (arrayAggOrderList != null) {
int size = arrayAggOrderList.size();
if (orderByList != null) {
int size = orderByList.size();
Value[] array = new Value[1 + size];
array[0] = v;
for (int i = 0; i < size; i++) {
SelectOrderBy o = arrayAggOrderList.get(i);
SelectOrderBy o = orderByList.get(i);
array[i + 1] = o.expression.getValue(session);
}
v = ValueArray.get(array);
......@@ -371,27 +381,18 @@ public class Aggregate extends Expression {
if (data == null) {
data = AggregateData.create(type);
}
Value v = data.getValue(session.getDatabase(), dataType, distinct);
if (type == AggregateType.GROUP_CONCAT) {
ArrayList<Value> list = ((AggregateDataArrayCollecting) data).getList();
if (list == null || list.isEmpty()) {
Value[] array = ((AggregateDataCollecting) data).getArray();
if (array == null) {
return ValueNull.INSTANCE;
}
if (groupConcatOrderList != null) {
final SortOrder sortOrder = groupConcatSort;
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);
}
});
if (orderByList != null || distinct) {
sortWithOrderBy(array);
}
StatementBuilder buff = new StatementBuilder();
String sep = groupConcatSeparator == null ?
"," : groupConcatSeparator.getValue(session).getString();
for (Value val : list) {
for (Value val : array) {
String s;
if (val.getType() == Value.ARRAY) {
s = ((ValueArray) val).getList()[0].getString();
......@@ -406,26 +407,23 @@ public class Aggregate extends Expression {
}
buff.append(s);
}
v = ValueString.get(buff.toString());
return ValueString.get(buff.toString());
} else if (type == AggregateType.ARRAY_AGG) {
ArrayList<Value> list = ((AggregateDataArrayCollecting) data).getList();
if (list == null || list.isEmpty()) {
Value[] array = ((AggregateDataCollecting) data).getArray();
if (array == null) {
return ValueNull.INSTANCE;
}
if (arrayAggOrderList != null) {
final SortOrder sortOrder = arrayOrderSort;
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);
}
});
if (orderByList != null || distinct) {
sortWithOrderBy(array);
}
if (orderByList != null) {
for (int i = 0; i < array.length; i++) {
array[i] = ((ValueArray) array[i]).getList()[0];
}
}
v = ValueArray.get(list.toArray(new Value[list.size()]));
return ValueArray.get(array);
}
return v;
return data.getValue(session.getDatabase(), dataType, distinct);
}
@Override
......@@ -438,13 +436,8 @@ public class Aggregate extends Expression {
if (on != null) {
on.mapColumns(resolver, level);
}
if (groupConcatOrderList != null) {
for (SelectOrderBy o : groupConcatOrderList) {
o.expression.mapColumns(resolver, level);
}
}
if (arrayAggOrderList != null) {
for (SelectOrderBy o : arrayAggOrderList) {
if (orderByList != null) {
for (SelectOrderBy o : orderByList) {
o.expression.mapColumns(resolver, level);
}
}
......@@ -465,17 +458,11 @@ public class Aggregate extends Expression {
precision = on.getPrecision();
displaySize = on.getDisplaySize();
}
if (groupConcatOrderList != null) {
for (SelectOrderBy o : groupConcatOrderList) {
if (orderByList != null) {
for (SelectOrderBy o : orderByList) {
o.expression = o.expression.optimize(session);
}
groupConcatSort = initOrder(groupConcatOrderList, session);
}
if (arrayAggOrderList != null) {
for (SelectOrderBy o : arrayAggOrderList) {
o.expression = o.expression.optimize(session);
}
arrayOrderSort = initOrder(arrayAggOrderList, session);
orderBySort = initOrder(session);
}
if (groupConcatSeparator != null) {
groupConcatSeparator = groupConcatSeparator.optimize(session);
......@@ -564,13 +551,8 @@ public class Aggregate extends Expression {
if (on != null) {
on.setEvaluatable(tableFilter, b);
}
if (groupConcatOrderList != null) {
for (SelectOrderBy o : groupConcatOrderList) {
o.expression.setEvaluatable(tableFilter, b);
}
}
if (arrayAggOrderList != null) {
for (SelectOrderBy o : arrayAggOrderList) {
if (orderByList != null) {
for (SelectOrderBy o : orderByList) {
o.expression.setEvaluatable(tableFilter, b);
}
}
......@@ -603,9 +585,9 @@ public class Aggregate extends Expression {
buff.append("DISTINCT ");
}
buff.append(on.getSQL());
if (groupConcatOrderList != null) {
if (orderByList != null) {
buff.append(" ORDER BY ");
for (SelectOrderBy o : groupConcatOrderList) {
for (SelectOrderBy o : orderByList) {
buff.appendExceptFirst(", ");
buff.append(o.expression.getSQL());
if (o.descending) {
......@@ -629,9 +611,9 @@ public class Aggregate extends Expression {
buff.append("DISTINCT ");
}
buff.append(on.getSQL());
if (arrayAggOrderList != null) {
if (orderByList != null) {
buff.append(" ORDER BY ");
for (SelectOrderBy o : arrayAggOrderList) {
for (SelectOrderBy o : orderByList) {
buff.appendExceptFirst(", ");
buff.append(o.expression.getSQL());
if (o.descending) {
......@@ -765,15 +747,8 @@ public class Aggregate extends Expression {
!groupConcatSeparator.isEverything(visitor)) {
return false;
}
if (groupConcatOrderList != null) {
for (SelectOrderBy o : groupConcatOrderList) {
if (!o.expression.isEverything(visitor)) {
return false;
}
}
}
if (arrayAggOrderList != null) {
for (SelectOrderBy o : arrayAggOrderList) {
if (orderByList != null) {
for (SelectOrderBy o : orderByList) {
if (!o.expression.isEverything(visitor)) {
return false;
}
......
......@@ -21,21 +21,21 @@ abstract class AggregateData {
* @return the aggregate data object of the specified type
*/
static AggregateData create(AggregateType aggregateType) {
if (aggregateType == AggregateType.SELECTIVITY) {
switch (aggregateType) {
case SELECTIVITY:
return new AggregateDataSelectivity();
} else if (aggregateType == AggregateType.GROUP_CONCAT) {
return new AggregateDataArrayCollecting();
} else if (aggregateType == AggregateType.ARRAY_AGG) {
return new AggregateDataArrayCollecting();
} else if (aggregateType == AggregateType.COUNT_ALL) {
case GROUP_CONCAT:
case ARRAY_AGG:
return new AggregateDataCollecting();
case COUNT_ALL:
return new AggregateDataCountAll();
} else if (aggregateType == AggregateType.COUNT) {
case COUNT:
return new AggregateDataCount();
} else if (aggregateType == AggregateType.HISTOGRAM) {
case HISTOGRAM:
return new AggregateDataHistogram();
} else if (aggregateType == AggregateType.MEDIAN) {
case MEDIAN:
return new AggregateDataMedian();
} else {
default:
return new AggregateDataDefault(aggregateType);
}
}
......
......@@ -6,55 +6,54 @@
package org.h2.expression;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import org.h2.engine.Database;
import org.h2.util.New;
import org.h2.util.ValueHashMap;
import org.h2.value.Value;
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 {
private ArrayList<Value> list;
private ValueHashMap<AggregateDataArrayCollecting> distinctValues;
class AggregateDataCollecting extends AggregateData {
Collection<Value> values;
@Override
void add(Database database, int dataType, boolean distinct, Value v) {
if (v == ValueNull.INSTANCE) {
return;
}
if (distinct) {
if (distinctValues == null) {
distinctValues = ValueHashMap.newInstance();
}
distinctValues.put(v, this);
return;
}
if (list == null) {
list = New.arrayList();
Collection<Value> c = values;
if (c == null) {
values = c = distinct ? new HashSet<Value>() : new ArrayList<Value>();
}
list.add(v);
c.add(v);
}
@Override
Value getValue(Database database, int dataType, boolean distinct) {
if (distinct) {
distinct(database, dataType);
}
return null;
}
ArrayList<Value> getList() {
return list;
}
private void distinct(Database database, int dataType) {
if (distinctValues == null) {
return;
}
for (Value v : distinctValues.keys()) {
add(database, dataType, false, v);
/**
* Returns array with values or {@code null}.
*
* @return array with values or {@code null}
*/
Value[] getArray() {
Collection<Value> values = this.values;
if (values == null) {
return null;
}
return values.toArray(new Value[0]);
}
}
......@@ -8,9 +8,7 @@ package org.h2.expression;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import org.h2.engine.Database;
import org.h2.engine.Session;
......@@ -39,9 +37,7 @@ import org.h2.value.ValueTimestampTimeZone;
/**
* Data stored while calculating a MEDIAN aggregate.
*/
class AggregateDataMedian extends AggregateData {
private Collection<Value> values;
class AggregateDataMedian extends AggregateDataCollecting {
private static boolean isNullsLast(Index index) {
IndexColumn ic = index.getIndexColumns()[0];
int sortType = ic.sortType;
......@@ -168,29 +164,12 @@ class AggregateDataMedian extends AggregateData {
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
Value getValue(Database database, int dataType, boolean distinct) {
Collection<Value> c = values;
// Non-null collection cannot be empty here
if (c == null) {
Value[] a = getArray();
if (a == null) {
return ValueNull.INSTANCE;
}
if (distinct && c instanceof ArrayList) {
c = new HashSet<>(c);
}
Value[] a = c.toArray(new Value[0]);
final CompareMode mode = database.getCompareMode();
Arrays.sort(a, new Comparator<Value>() {
@Override
......
......@@ -186,6 +186,8 @@ public class Operation extends Expression {
dataType = left.getType();
if (dataType == Value.UNKNOWN) {
dataType = Value.DECIMAL;
} else if (dataType == Value.ENUM) {
dataType = Value.INT;
}
break;
case CONCAT:
......@@ -315,7 +317,9 @@ public class Operation extends Expression {
DataType.getDataType(r).name);
} else {
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) {
opType = OpType.CONCAT;
}
......
......@@ -135,14 +135,12 @@ public class TableFunction extends Function {
simple.setAutoClose(false);
for (int i = 0; i < columnCount; i++) {
String name = rs.getColumnName(i);
/*
* TODO Some types, such as Value.BYTES and Value.UUID are mapped to the same
* SQL type and we can lose real type here.
*/
int sqlType = DataType.convertTypeToSQLType(rs.getColumnType(i));
DataType dataType = DataType.getDataType(rs.getColumnType(i));
int sqlType = dataType.sqlType;
String sqlTypeName = dataType.name;
int precision = MathUtils.convertLongToInt(rs.getColumnPrecision(i));
int scale = rs.getColumnScale(i);
simple.addColumn(name, sqlType, precision, scale);
simple.addColumn(name, sqlType, sqlTypeName, precision, scale);
}
rs.reset();
for (int i = 0; i < maxrows && rs.next(); i++) {
......
......@@ -14,11 +14,16 @@ import java.sql.SQLException;
import java.sql.Types;
import java.util.Arrays;
import java.util.Properties;
import org.h2.command.CommandInterface;
import org.h2.engine.Constants;
import org.h2.engine.SessionInterface;
import org.h2.engine.SessionRemote;
import org.h2.engine.SysProperties;
import org.h2.message.DbException;
import org.h2.message.Trace;
import org.h2.message.TraceObject;
import org.h2.result.ResultInterface;
import org.h2.tools.SimpleResultSet;
import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils;
......@@ -31,6 +36,11 @@ public class JdbcDatabaseMetaData extends TraceObject implements
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) {
setTrace(trace, TraceObject.DATABASE_META_DATA, id);
this.conn = conn;
......@@ -105,6 +115,33 @@ public class JdbcDatabaseMetaData extends TraceObject implements
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
* TABLE_TYPE, TABLE_SCHEM, and TABLE_NAME.
......@@ -144,7 +181,7 @@ public class JdbcDatabaseMetaData extends TraceObject implements
}
checkClosed();
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
StringBuilder select = new StringBuilder(1008);
......@@ -279,7 +316,72 @@ public class JdbcDatabaseMetaData extends TraceObject implements
+quote(columnNamePattern)+");");
}
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_SCHEMA TABLE_SCHEM, "
+ "TABLE_NAME, "
......@@ -303,94 +405,25 @@ public class JdbcDatabaseMetaData extends TraceObject implements
+ "CAST(SOURCE_DATA_TYPE AS VARCHAR) SCOPE_TABLE, "
+ "SOURCE_DATA_TYPE, "
+ "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 "
+ "FROM INFORMATION_SCHEMA.COLUMNS "
+ "WHERE TABLE_CATALOG LIKE ? ESCAPE ? "
+ "AND TABLE_SCHEMA LIKE ? ESCAPE ? "
+ "AND TABLE_NAME LIKE ? ESCAPE ? "
+ "AND COLUMN_NAME LIKE ? ESCAPE ? "
+ "ORDER BY TABLE_SCHEM, TABLE_NAME, ORDINAL_POSITION";
String synonymSql = "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(? 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");
+ "WHERE TABLE_CATALOG LIKE ?3 ESCAPE ?7 "
+ "AND TABLE_SCHEMA LIKE ?4 ESCAPE ?7 "
+ "AND TABLE_NAME LIKE ?5 ESCAPE ?7 "
+ "AND COLUMN_NAME LIKE ?6 ESCAPE ?7");
if (includeSynonyms) {
select.append(')');
}
PreparedStatement prep = conn.prepareAutoCloseStatement(
select.append(" ORDER BY TABLE_SCHEM, TABLE_NAME, ORDINAL_POSITION").toString());
prep.setString(1, "NO");
prep.setString(2, "YES");
prep.setString(3, getCatalogPattern(catalogPattern));
prep.setString(4, "\\");
prep.setString(5, getSchemaPattern(schemaPattern));
prep.setString(6, "\\");
prep.setString(7, getPattern(tableNamePattern));
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, "\\");
prep.setString(4, getSchemaPattern(schemaPattern));
prep.setString(5, getPattern(tableNamePattern));
prep.setString(6, getPattern(columnNamePattern));
prep.setString(7, "\\");
return prep.executeQuery();
} catch (Exception e) {
throw logAndConvert(e);
......
......@@ -323,7 +323,7 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex {
int idx = c.getColumnId();
Value v = r.getValue(idx);
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());
......
......@@ -334,11 +334,12 @@ public class Column {
synchronized (this) {
localDefaultExpression = defaultExpression;
}
Mode mode = session.getDatabase().getMode();
if (value == null) {
if (localDefaultExpression == null) {
value = ValueNull.INSTANCE;
} else {
value = localDefaultExpression.getValue(session).convertTo(type);
value = convert(localDefaultExpression.getValue(session), mode);
if (!localDefaultExpression.isConstant()) {
session.getGeneratedKeys().add(this);
}
......@@ -347,10 +348,9 @@ public class Column {
}
}
}
Mode mode = session.getDatabase().getMode();
if (value == ValueNull.INSTANCE) {
if (convertNullToDefault) {
value = localDefaultExpression.getValue(session).convertTo(type);
value = convert(localDefaultExpression.getValue(session), mode);
if (!localDefaultExpression.isConstant()) {
session.getGeneratedKeys().add(this);
}
......
......@@ -1688,7 +1688,8 @@ public class MetaTable extends Table {
// CONSTRAINT_NAME
identifier(constraint.getName()),
// CONSTRAINT_TYPE
constraintType.toString(),
constraintType == Constraint.Type.PRIMARY_KEY ?
constraintType.getSqlName() : constraintType.name(),
// TABLE_CATALOG
catalog,
// TABLE_SCHEMA
......
......@@ -193,7 +193,13 @@ public class TableView extends Table {
long precision = expr.getPrecision();
int scale = expr.getScale();
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);
// Fetch check constraint from view column source
ExpressionColumn fromColumn = null;
......
......@@ -38,6 +38,7 @@ import org.h2.util.Bits;
import org.h2.util.JdbcUtils;
import org.h2.util.MathUtils;
import org.h2.util.New;
import org.h2.util.SimpleColumnInfo;
import org.h2.util.Utils;
import org.h2.value.DataType;
......@@ -67,7 +68,7 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData,
private int rowId = -1;
private boolean wasNull;
private SimpleRowSource source;
private ArrayList<Column> columns = New.arrayList();
private ArrayList<SimpleColumnInfo> columns = New.arrayList();
private boolean autoClose = true;
/**
......@@ -123,13 +124,7 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData,
if (name == null) {
name = "C" + (columns.size() + 1);
}
Column column = new Column();
column.name = name;
column.sqlType = sqlType;
column.precision = precision;
column.scale = scale;
column.sqlTypeName = sqlTypeName;
columns.add(column);
columns.add(new SimpleColumnInfo(name, sqlType, sqlTypeName, precision, scale));
}
/**
......@@ -1012,7 +1007,7 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData,
if (o == null) {
return null;
}
switch (columns.get(columnIndex - 1).sqlType) {
switch (columns.get(columnIndex - 1).type) {
case Types.CLOB:
Clob c = (Clob) o;
return c.getSubString(1, MathUtils.convertLongToInt(c.length()));
......@@ -1868,7 +1863,7 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData,
*/
@Override
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,
*/
@Override
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,
return o;
}
private Column getColumn(int i) throws SQLException {
private SimpleColumnInfo getColumn(int i) throws SQLException {
checkColumnIndex(i + 1);
return columns.get(i);
}
......@@ -2355,37 +2350,6 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData,
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,
* backed by an object array
......
......@@ -24,59 +24,9 @@ import org.h2.tools.SimpleResultSet;
* that have {@code NAME} column should also define it with the same type.
*/
public final class MergedResultSet {
/**
* 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<Map<SimpleColumnInfo, Object>> data = New.arrayList();
private final ArrayList<ColumnInfo> columns = New.arrayList();
private final ArrayList<SimpleColumnInfo> columns = New.arrayList();
/**
* Appends a result set.
......@@ -92,10 +42,10 @@ public final class MergedResultSet {
if (cols == 0) {
return;
}
ColumnInfo[] info = new ColumnInfo[cols];
SimpleColumnInfo[] info = new SimpleColumnInfo[cols];
for (int i = 1; i <= cols; i++) {
ColumnInfo ci = new ColumnInfo(meta.getColumnName(i), meta.getColumnType(i), meta.getPrecision(i),
meta.getScale(i));
SimpleColumnInfo ci = new SimpleColumnInfo(meta.getColumnName(i), meta.getColumnType(i),
meta.getColumnTypeName(i), meta.getPrecision(i), meta.getScale(i));
info[i - 1] = ci;
if (!columns.contains(ci)) {
columns.add(ci);
......@@ -105,9 +55,9 @@ public final class MergedResultSet {
if (cols == 1) {
data.add(Collections.singletonMap(info[0], rs.getObject(1)));
} else {
HashMap<ColumnInfo, Object> map = new HashMap<>();
HashMap<SimpleColumnInfo, Object> map = new HashMap<>();
for (int i = 1; i <= cols; i++) {
ColumnInfo ci = info[i - 1];
SimpleColumnInfo ci = info[i - 1];
map.put(ci, rs.getObject(i));
}
data.add(map);
......@@ -122,12 +72,12 @@ public final class MergedResultSet {
*/
public SimpleResultSet getResult() {
SimpleResultSet rs = new SimpleResultSet();
for (ColumnInfo ci : columns) {
rs.addColumn(ci.name, ci.type, ci.precision, ci.scale);
for (SimpleColumnInfo ci : columns) {
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()];
for (Map.Entry<ColumnInfo, Object> entry : map.entrySet()) {
for (Map.Entry<SimpleColumnInfo, Object> entry : map.entrySet()) {
row[columns.indexOf(entry.getKey())] = entry.getValue();
}
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 {
*/
public static int convertSQLTypeToValueType(int sqlType, String sqlTypeName) {
switch (sqlType) {
case Types.BINARY:
if (sqlTypeName.equalsIgnoreCase("UUID")) {
return Value.UUID;
}
break;
case Types.OTHER:
case Types.JAVA_OBJECT:
if (sqlTypeName.equalsIgnoreCase("geometry")) {
......
......@@ -982,7 +982,16 @@ public abstract class Value {
case STRING_IGNORECASE:
case STRING_FIXED:
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(
ErrorCode.DATA_CONVERSION_ERROR_1, getString());
}
......
......@@ -77,7 +77,7 @@ public class ValueEnumBase extends Value {
@Override
public Object getObject() {
return ordinal;
return label;
}
@Override
......
......@@ -11,6 +11,7 @@ import java.util.Arrays;
import org.h2.engine.Mode;
import org.h2.message.DbException;
import org.h2.util.StringUtils;
import org.h2.util.Utils;
import org.locationtech.jts.geom.CoordinateSequence;
import org.locationtech.jts.geom.CoordinateSequenceFilter;
import org.locationtech.jts.geom.Envelope;
......@@ -140,7 +141,20 @@ public class ValueGeometry extends Value {
* @return a copy of the geometry object
*/
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() {
......@@ -221,7 +235,7 @@ public class ValueGeometry extends Value {
@Override
public byte[] getBytes() {
return getWKB();
return Utils.cloneByteArray(getWKB());
}
@Override
......
......@@ -6,6 +6,7 @@
package org.h2.test.db;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
......@@ -99,6 +100,9 @@ public class TestIndex extends TestBase {
conn.close();
deleteDb("index");
// This test uses own connection
testEnumIndex();
}
private void testOrderIndex() throws SQLException {
......@@ -750,4 +754,27 @@ public class TestIndex extends TestBase {
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 {
GeometryFactory geometryFactory = new GeometryFactory();
Geometry geometry = geometryFactory.createPoint(new Coordinate(0, 0));
geometry.setSRID(27572);
ValueGeometry valueGeometry =
ValueGeometry.getFromGeometry(geometry);
ValueGeometry valueGeometry = ValueGeometry.getFromGeometry(geometry);
Geometry geometry2 = geometryFactory.createPoint(new Coordinate(0, 0));
geometry2.setSRID(5326);
ValueGeometry valueGeometry2 =
ValueGeometry.getFromGeometry(geometry2);
ValueGeometry valueGeometry2 = ValueGeometry.getFromGeometry(geometry2);
assertFalse(valueGeometry.equals(valueGeometry2));
ValueGeometry valueGeometry3 = ValueGeometry.getFromGeometry(geometry);
assertEquals(valueGeometry, valueGeometry3);
assertEquals(geometry.getSRID(), valueGeometry3.getGeometry().getSRID());
// Check illegal geometry (no WKB representation)
try {
ValueGeometry.get("POINT EMPTY");
......
......@@ -8,6 +8,7 @@ package org.h2.test.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.UUID;
......@@ -127,6 +128,9 @@ public class TestGetGeneratedKeys extends TestBase {
prep.addBatch();
prep.executeBatch();
ResultSet rs = prep.getGeneratedKeys();
ResultSetMetaData meta = rs.getMetaData();
assertEquals("BIGINT", meta.getColumnTypeName(1));
assertEquals("UUID", meta.getColumnTypeName(2));
rs.next();
assertEquals(1L, rs.getLong(1));
UUID u1 = (UUID) rs.getObject(2);
......
......@@ -8,11 +8,14 @@ package org.h2.test.jdbc;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Driver;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.UUID;
import org.h2.api.ErrorCode;
import org.h2.engine.Constants;
import org.h2.engine.SysProperties;
......@@ -135,6 +138,13 @@ public class TestMetaData extends TestBase {
assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, conn.getHoldability());
assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, rs.getHoldability());
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();
}
......
......@@ -476,7 +476,7 @@ public class TestPreparedStatement extends TestBase {
assertEquals(goodSizes[i], rs.getString(1));
assertEquals(i, rs.getInt(1));
Object o = rs.getObject(1);
assertEquals(Integer.class, o.getClass());
assertEquals(String.class, o.getClass());
}
for (int i = 0; i < goodSizes.length; i++) {
......
......@@ -87,6 +87,7 @@ public class TestResultSet extends TestBase {
testFindColumn();
testColumnLength();
testArray();
testEnum();
testLimitMaxRows();
trace("max rows=" + stat.getMaxRows());
......@@ -1798,6 +1799,56 @@ public class TestResultSet extends TestBase {
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) {
if (in == null) {
return null;
......
......@@ -162,3 +162,93 @@ drop table card;
drop type CARD_SUIT;
> 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
INSERT (ID, NAME) VALUES (S.ID, S.NAME);
> 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 */
\ No newline at end of file
> MERGE INTO PUBLIC.PARENT(ID, NAME) KEY(ID) SELECT X AS ID, ('Coco' || X) AS NAME FROM SYSTEM_RANGE(1, 2) /* PUBLIC.RANGE_INDEX */
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'),
select array_agg(v order by v asc),
array_agg(v order by v desc) filter (where v >= '4')
from test where v >= '2';
> 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)
> 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)
> rows (ordered): 1
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),
ARRAY_AGG(v order by v desc) filter (where v >= '4')
from test where v >= '2';
> 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)
> 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)
> rows (ordered): 1
select ARRAY_AGG(v order by v asc),
ARRAY_AGG(v order by v desc) filter (where v >= '4')
from test;
> 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)
> 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)
> 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;
> ok
......@@ -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
> 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;
> ok
......@@ -41,6 +41,18 @@ SELECT CONSTRAINT_NAME, CONSTRAINT_TYPE, TABLE_NAME, IS_DEFERRABLE, INITIALLY_DE
> FK_2 FOREIGN KEY T2 NO NO
> 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;
> 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
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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论