提交 5ae94953 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Separate array and row value expressions

上级 291b541c
......@@ -2023,23 +2023,12 @@ ID=1 AND NAME='Hi'
"Other Grammar","Array","
ARRAY '[' [ expression, [,...] ] ']'
| ( [ expression, [ expression [,...] ] ] )
","
An array of values.
The array can be declared with standard ARRAY[] syntax or with H2 syntax.
With standard syntax trailing comma is not allowed.
With H2 syntax an empty array is '()'. Trailing comma is ignored.
An array with one element must contain a trailing comma to be parsed as an array.
","
ARRAY[1, 2]
ARRAY[1]
ARRAY[]
(1, 2)
(1, )
()
"
"Other Grammar","Boolean","
......@@ -2520,6 +2509,16 @@ LZF is faster but uses more space.
COMPRESSION LZF
"
"Other Grammar","Row value expression","
ROW (expression, [,...])
| ( [ expression, expression [,...] ] )
","
A row value expression.
","
ROW (1)
(1, 2)
"
"Other Grammar","Select Expression","
wildcardExpression | expression [ [ AS ] columnAlias ]
","
......
......@@ -478,7 +478,7 @@ unless they are quoted (surrounded with double quotes). The list is currently:
ALL, CHECK, CONSTRAINT, CROSS, CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, DISTINCT, EXCEPT,
EXISTS, FALSE, FETCH, FOR, FOREIGN, FROM, FULL, GROUP, HAVING, INNER, INTERSECT, INTERSECTS,
IS, JOIN, LIKE, LIMIT, LOCALTIME, LOCALTIMESTAMP, MINUS, NATURAL, NOT, NULL, OFFSET, ON, ORDER,
PRIMARY, ROWNUM, SELECT, SYSDATE, SYSTIME, SYSTIMESTAMP, TODAY, TOP, TRUE, UNION, UNIQUE, WHERE,
PRIMARY, ROW, ROWNUM, SELECT, SYSDATE, SYSTIME, SYSTIMESTAMP, TODAY, TOP, TRUE, UNION, UNIQUE, WHERE,
WINDOW, WITH
</code>
</p><p>
......
......@@ -43,6 +43,7 @@ import static org.h2.util.ParserUtil.OFFSET;
import static org.h2.util.ParserUtil.ON;
import static org.h2.util.ParserUtil.ORDER;
import static org.h2.util.ParserUtil.PRIMARY;
import static org.h2.util.ParserUtil.ROW;
import static org.h2.util.ParserUtil.ROWNUM;
import static org.h2.util.ParserUtil.SELECT;
import static org.h2.util.ParserUtil.TRUE;
......@@ -482,6 +483,8 @@ public class Parser {
"ORDER",
// PRIMARY
"PRIMARY",
// ROW
"ROW",
// ROWNUM
"ROWNUM",
// SELECT
......@@ -2400,7 +2403,7 @@ public class Parser {
if (readIf(OFFSET)) {
hasOffsetOrFetch = true;
command.setOffset(readExpression().optimize(session));
if (!readIf("ROW")) {
if (!readIf(ROW)) {
readIf("ROWS");
}
}
......@@ -2409,7 +2412,7 @@ public class Parser {
if (!readIf("FIRST")) {
read("NEXT");
}
if (readIf("ROW") || readIf("ROWS")) {
if (readIf(ROW) || readIf("ROWS")) {
command.setLimit(ValueExpression.get(ValueInt.get(1)));
} else {
Expression limit = readExpression().optimize(session);
......@@ -2417,7 +2420,7 @@ public class Parser {
if (readIf("PERCENT")) {
command.setFetchPercent(true);
}
if (!readIf("ROW")) {
if (!readIf(ROW)) {
read("ROWS");
}
}
......@@ -3198,7 +3201,7 @@ public class Parser {
WindowFrameExclusion exclusion = WindowFrameExclusion.EXCLUDE_NO_OTHERS;
if (readIf("EXCLUDE")) {
if (readIf("CURRENT")) {
read("ROW");
read(ROW);
exclusion = WindowFrameExclusion.EXCLUDE_CURRENT_ROW;
} else if (readIf(GROUP)) {
exclusion = WindowFrameExclusion.EXCLUDE_GROUP;
......@@ -3222,7 +3225,7 @@ public class Parser {
return new WindowFrameBound(WindowFrameBoundType.UNBOUNDED_PRECEDING, null);
}
if (readIf("CURRENT")) {
read("ROW");
read(ROW);
return new WindowFrameBound(WindowFrameBoundType.CURRENT_ROW, null);
}
Expression value = readValueOrParameter();
......@@ -3239,7 +3242,7 @@ public class Parser {
return new WindowFrameBound(WindowFrameBoundType.UNBOUNDED_FOLLOWING, null);
}
if (readIf("CURRENT")) {
read("ROW");
read(ROW);
return new WindowFrameBound(WindowFrameBoundType.CURRENT_ROW, null);
}
Expression value = readValueOrParameter();
......@@ -3790,7 +3793,7 @@ public class Parser {
case OPEN_PAREN:
read();
if (readIf(CLOSE_PAREN)) {
r = new ExpressionList(new Expression[0]);
r = new ExpressionList(new Expression[0], false);
} else {
r = readExpression();
if (readIfMore(true)) {
......@@ -3801,10 +3804,24 @@ public class Parser {
list.add(readExpression());
} while (readIfMore(false));
}
r = new ExpressionList(list.toArray(new Expression[0]));
r = new ExpressionList(list.toArray(new Expression[0]), false);
}
}
break;
case ROW: {
read();
read(OPEN_PAREN);
if (readIf(CLOSE_PAREN)) {
r = new ExpressionList(new Expression[0], false);
} else {
ArrayList<Expression> list = Utils.newSmallArrayList();
do {
list.add(readExpression());
} while (readIfMore(true));
r = new ExpressionList(list.toArray(new Expression[0]), false);
}
break;
}
case TRUE:
read();
r = ValueExpression.get(ValueBoolean.TRUE);
......@@ -3909,7 +3926,7 @@ public class Parser {
}
read(CLOSE_BRACKET);
}
return new ExpressionList(list.toArray(new Expression[0]));
return new ExpressionList(list.toArray(new Expression[0]), true);
}
break;
case 'C':
......@@ -5790,7 +5807,7 @@ public class Parser {
for (int j = 0; j < rowCount; j++) {
array[j] = rows.get(j).get(i);
}
ExpressionList list = new ExpressionList(array);
ExpressionList list = new ExpressionList(array, false);
tf.setParameter(i, list);
}
tf.setColumns(columns);
......@@ -5964,7 +5981,7 @@ public class Parser {
command.setTableName(tableName);
if (readIf(FOR)) {
read("EACH");
read("ROW");
read(ROW);
command.setRowBased(true);
} else {
command.setRowBased(false);
......
......@@ -11,17 +11,20 @@ import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.value.Value;
import org.h2.value.ValueArray;
import org.h2.value.ValueRow;
/**
* A list of expressions, as in (ID, NAME).
* The result of this expression is an array.
* The result of this expression is a row or an array.
*/
public class ExpressionList extends Expression {
private final Expression[] list;
private final boolean isArray;
public ExpressionList(Expression[] list) {
public ExpressionList(Expression[] list, boolean isArray) {
this.list = list;
this.isArray = isArray;
}
@Override
......@@ -30,12 +33,12 @@ public class ExpressionList extends Expression {
for (int i = 0; i < list.length; i++) {
v[i] = list[i].getValue(session);
}
return ValueArray.get(v);
return isArray ? ValueArray.get(v) : ValueRow.get(v);
}
@Override
public int getType() {
return Value.ARRAY;
return isArray ? Value.ARRAY : Value.ROW;
}
@Override
......@@ -85,12 +88,9 @@ public class ExpressionList extends Expression {
@Override
public StringBuilder getSQL(StringBuilder builder) {
builder.append('(');
builder.append(isArray ? "ARRAY [" : "ROW (");
writeExpressions(builder, list);
if (list.length == 1) {
builder.append(',');
}
return builder.append(')');
return builder.append(isArray ? ']' : ')');
}
@Override
......
......@@ -14,8 +14,8 @@ import org.h2.result.ResultInterface;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.value.Value;
import org.h2.value.ValueArray;
import org.h2.value.ValueNull;
import org.h2.value.ValueRow;
/**
* A query returning a single value.
......@@ -42,7 +42,7 @@ public class Subquery extends Expression {
if (result.getVisibleColumnCount() == 1) {
v = values[0];
} else {
v = ValueArray.get(values);
v = ValueRow.get(values);
}
if (result.hasNext()) {
throw DbException.get(ErrorCode.SCALAR_SUBQUERY_CONTAINS_MORE_THAN_ONE_ROW);
......@@ -109,7 +109,7 @@ public class Subquery extends Expression {
for (int i = 0; i < columnCount; i++) {
list[i] = expressions.get(i);
}
expression = new ExpressionList(list);
expression = new ExpressionList(list, false);
}
}
return expression;
......
......@@ -205,6 +205,7 @@ public class Comparison extends Condition {
left = left.optimize(session);
if (right != null) {
right = right.optimize(session);
// TODO check row values too
if (right.getType() == Value.ARRAY && left.getType() != Value.ARRAY) {
throw DbException.get(ErrorCode.COMPARING_ARRAY_TO_SCALAR);
}
......
......@@ -70,6 +70,7 @@ import org.h2.value.ValueInt;
import org.h2.value.ValueLong;
import org.h2.value.ValueNull;
import org.h2.value.ValueResultSet;
import org.h2.value.ValueRow;
import org.h2.value.ValueString;
import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp;
......@@ -1038,10 +1039,10 @@ public class Function extends Expression implements FunctionCall {
break;
}
case ARRAY_GET: {
if (v0.getType() == Value.ARRAY) {
Value[] list = getArray(v0);
if (list != null) {
Value v1 = getNullOrValue(session, args, values, 1);
int element = v1.getInt();
Value[] list = ((ValueArray) v0).getList();
if (element < 1 || element > list.length) {
result = ValueNull.INSTANCE;
} else {
......@@ -1053,8 +1054,8 @@ public class Function extends Expression implements FunctionCall {
break;
}
case ARRAY_LENGTH: {
if (v0.getType() == Value.ARRAY) {
Value[] list = ((ValueArray) v0).getList();
Value[] list = getArray(v0);
if (list != null) {
result = ValueInt.get(list.length);
} else {
result = ValueNull.INSTANCE;
......@@ -1063,9 +1064,9 @@ public class Function extends Expression implements FunctionCall {
}
case ARRAY_CONTAINS: {
result = ValueBoolean.FALSE;
if (v0.getType() == Value.ARRAY) {
Value[] list = getArray(v0);
if (list != null) {
Value v1 = getNullOrValue(session, args, values, 1);
Value[] list = ((ValueArray) v0).getList();
for (Value v : list) {
if (database.areEqual(v, v1)) {
result = ValueBoolean.TRUE;
......@@ -1091,6 +1092,19 @@ public class Function extends Expression implements FunctionCall {
return result;
}
private Value[] getArray(Value v0) {
int t = v0.getType();
Value[] list;
if (t == Value.ARRAY) {
list = ((ValueArray) v0).getList();
} else if (t == Value.ROW) {
list = ((ValueRow) v0).getList();
} else {
list = null;
}
return list;
}
private static boolean cancelStatement(Session session, int targetSessionId) {
session.getUser().checkAdmin();
Session[] sessions = session.getDatabase().getSessions(false);
......
......@@ -1550,7 +1550,7 @@ public class JdbcDatabaseMetaData extends TraceObject implements
* ALL, CHECK, CONSTRAINT, CROSS, CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP,
* DISTINCT, EXCEPT, EXISTS, FALSE, FETCH, FOR, FOREIGN, FROM, FULL, GROUP,
* HAVING, INNER, INTERSECT, INTERSECTS, IS, JOIN, LIKE, LIMIT, LOCALTIME,
* LOCALTIMESTAMP, MINUS, NATURAL, NOT, NULL, OFFSET, ON, ORDER, PRIMARY, ROWNUM,
* LOCALTIMESTAMP, MINUS, NATURAL, NOT, NULL, OFFSET, ON, ORDER, PRIMARY, ROW, ROWNUM,
* SELECT, SYSDATE, SYSTIME, SYSTIMESTAMP, TODAY, TOP, TRUE, UNION, UNIQUE, WHERE,
* WINDOW, WITH
* </pre>
......
......@@ -187,10 +187,15 @@ public class ParserUtil {
*/
public static final int PRIMARY = ORDER + 1;
/**
* The token "ROW".
*/
public static final int ROW = PRIMARY + 1;
/**
* The token "ROWNUM".
*/
public static final int ROWNUM = PRIMARY + 1;
public static final int ROWNUM = ROW + 1;
/**
* The token "SELECT".
......@@ -425,7 +430,9 @@ public class ParserUtil {
}
return IDENTIFIER;
case 'R':
if (eq("ROWNUM", s, ignoreCase, start, end)) {
if (eq("ROW", s, ignoreCase, start, end)) {
return ROW;
} else if (eq("ROWNUM", s, ignoreCase, start, end)) {
return ROWNUM;
}
return IDENTIFIER;
......
......@@ -773,6 +773,19 @@ public class DataType {
ValueInt.get(value);
break;
}
case Value.ROW: {
Object[] list = (Object[]) rs.getObject(columnIndex);
if (list == null) {
return ValueNull.INSTANCE;
}
int len = list.length;
Value[] values = new Value[len];
for (int i = 0; i < len; i++) {
values[i] = DataType.convertToValue(session, list[i], Value.NULL);
}
v = ValueRow.get(values);
break;
}
case Value.RESULT_SET: {
ResultSet x = (ResultSet) rs.getObject(columnIndex);
if (x == null) {
......
......@@ -471,6 +471,16 @@ public class Transfer {
}
break;
}
case Value.ROW: {
ValueRow va = (ValueRow) v;
Value[] list = va.getList();
int len = list.length;
writeInt(len);
for (Value value : list) {
writeValue(value);
}
break;
}
case Value.ENUM: {
writeInt(v.getInt());
writeString(v.getString());
......@@ -682,6 +692,14 @@ public class Transfer {
}
return ValueArray.get(componentType, list);
}
case Value.ROW: {
int len = readInt();
Value[] list = new Value[len];
for (int i = 0; i < len; i++) {
list[i] = readValue();
}
return ValueRow.get(list);
}
case Value.RESULT_SET: {
SimpleResult rs = new SimpleResult();
int columns = readInt();
......
......@@ -241,10 +241,15 @@ public abstract class Value {
*/
public static final int INTERVAL_MINUTE_TO_SECOND = 38;
/**
* The value type for ROW values.
*/
public static final int ROW = 39;
/**
* The number of value types.
*/
public static final int TYPE_COUNT = INTERVAL_MINUTE_TO_SECOND + 1;
public static final int TYPE_COUNT = ROW + 1;
private static SoftReference<Value[]> softCache;
......@@ -439,6 +444,8 @@ public abstract class Value {
return 44_000;
case ARRAY:
return 50_000;
case ROW:
return 50_500;
case RESULT_SET:
return 51_000;
case ENUM:
......@@ -790,6 +797,8 @@ public abstract class Value {
return convertToIntervalDayTime(targetType);
case ARRAY:
return convertToArray();
case ROW:
return convertToRow();
case RESULT_SET:
return convertToResultSet();
default:
......@@ -1298,7 +1307,37 @@ public abstract class Value {
}
private ValueArray convertToArray() {
return ValueArray.get(new Value[] { ValueString.get(getString()) });
Value[] a;
switch (getType()) {
case ROW:
a = ((ValueRow) this).getList();
break;
case BLOB:
case CLOB:
case RESULT_SET:
a = new Value[] { ValueString.get(getString()) };
break;
default:
a = new Value[] { this };
}
return ValueArray.get(a);
}
private ValueRow convertToRow() {
Value[] a;
switch (getType()) {
case ARRAY:
a = ((ValueArray) this).getList();
break;
case BLOB:
case CLOB:
case RESULT_SET:
a = new Value[] { ValueString.get(getString()) };
break;
default:
a = new Value[] { this };
}
return ValueRow.get(a);
}
private ValueResultSet convertToResultSet() {
......
......@@ -13,7 +13,6 @@ import java.util.Arrays;
import org.h2.engine.Constants;
import org.h2.engine.SysProperties;
import org.h2.util.MathUtils;
import org.h2.util.StatementBuilder;
/**
* Implementation of the ARRAY data type.
......@@ -103,12 +102,14 @@ public class ValueArray extends Value {
@Override
public String getString() {
StatementBuilder buff = new StatementBuilder("(");
for (Value v : values) {
buff.appendExceptFirst(", ");
buff.append(v.getString());
StringBuilder builder = new StringBuilder().append('[');
for (int i = 0; i < values.length; i++) {
if (i > 0) {
builder.append(", ");
}
builder.append(values[i].getString());
}
return buff.append(')').toString();
return builder.append(']').toString();
}
@Override
......@@ -156,7 +157,7 @@ public class ValueArray extends Value {
@Override
public StringBuilder getSQL(StringBuilder builder) {
builder.append('(');
builder.append("ARRAY [");
int length = values.length;
for (int i = 0; i < length; i++) {
if (i > 0) {
......@@ -164,20 +165,20 @@ public class ValueArray extends Value {
}
values[i].getSQL(builder);
}
if (length == 1) {
builder.append(',');
}
return builder.append(')');
return builder.append(']');
}
@Override
public String getTraceSQL() {
StatementBuilder buff = new StatementBuilder("(");
for (Value v : values) {
buff.appendExceptFirst(", ");
buff.append(v == null ? "null" : v.getTraceSQL());
StringBuilder builder = new StringBuilder("[");
for (int i = 0; i < values.length; i++) {
if (i > 0) {
builder.append(", ");
}
Value v = values[i];
builder.append(v == null ? "null" : v.getTraceSQL());
}
return buff.append(')').toString();
return builder.append(']').toString();
}
@Override
......
/*
* 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.value;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Arrays;
import org.h2.api.ErrorCode;
import org.h2.engine.Constants;
import org.h2.engine.SysProperties;
import org.h2.message.DbException;
import org.h2.util.MathUtils;
/**
* Row value.
*/
public class ValueRow extends Value {
/**
* Empty row.
*/
private static final Object EMPTY = get(new Value[0]);
private final Value[] values;
private int hash;
private ValueRow(Value[] list) {
this.values = list;
}
/**
* Get or create a row value for the given value array.
* Do not clone the data.
*
* @param list the value array
* @return the value
*/
public static ValueRow get(Value[] list) {
return new ValueRow(list);
}
/**
* Returns empty row.
*
* @return empty row
*/
public static ValueRow getEmpty() {
return (ValueRow) EMPTY;
}
@Override
public int hashCode() {
if (hash != 0) {
return hash;
}
int h = 1;
for (Value v : values) {
h = h * 31 + v.hashCode();
}
hash = h;
return h;
}
public Value[] getList() {
return values;
}
@Override
public int getType() {
return Value.ROW;
}
@Override
public long getPrecision() {
long p = 0;
for (Value v : values) {
p += v.getPrecision();
}
return p;
}
@Override
public String getString() {
StringBuilder builder = new StringBuilder("ROW (");
for (int i = 0; i < values.length; i++) {
if (i > 0) {
builder.append(", ");
}
builder.append(values[i].getString());
}
return builder.append(')').toString();
}
@Override
public int compareTypeSafe(Value o, CompareMode mode) {
ValueRow v = (ValueRow) o;
if (values == v.values) {
return 0;
}
int len = values.length;
if (len != v.values.length) {
throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH);
}
for (int i = 0; i < len; i++) {
Value v1 = values[i];
Value v2 = v.values[i];
int comp = v1.compareTo(v2, /* TODO */ null, mode);
if (comp != 0) {
return comp;
}
}
return 0;
}
@Override
public Object getObject() {
int len = values.length;
Object[] list = new Object[len];
for (int i = 0; i < len; i++) {
final Value value = values[i];
if (!SysProperties.OLD_RESULT_SET_GET_OBJECT) {
final int type = value.getType();
if (type == Value.BYTE || type == Value.SHORT) {
list[i] = value.getInt();
continue;
}
}
list[i] = value.getObject();
}
return list;
}
@Override
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
throw getUnsupportedExceptionForOperation("PreparedStatement.set");
}
@Override
public StringBuilder getSQL(StringBuilder builder) {
builder.append("ROW (");
int length = values.length;
for (int i = 0; i < length; i++) {
if (i > 0) {
builder.append(", ");
}
values[i].getSQL(builder);
}
return builder.append(')');
}
@Override
public String getTraceSQL() {
StringBuilder builder = new StringBuilder("ROW (");
for (int i = 0; i < values.length; i++) {
if (i > 0) {
builder.append(", ");
}
Value v = values[i];
builder.append(v == null ? "null" : v.getTraceSQL());
}
return builder.append(')').toString();
}
@Override
public int getDisplaySize() {
long size = 0;
for (Value v : values) {
size += v.getDisplaySize();
}
return MathUtils.convertLongToInt(size);
}
@Override
public boolean equals(Object other) {
if (!(other instanceof ValueRow)) {
return false;
}
ValueRow v = (ValueRow) other;
if (values == v.values) {
return true;
}
int len = values.length;
if (len != v.values.length) {
return false;
}
for (int i = 0; i < len; i++) {
if (!values[i].equals(v.values[i])) {
return false;
}
}
return true;
}
@Override
public int getMemory() {
int memory = 32;
for (Value v : values) {
memory += v.getMemory() + Constants.MEMORY_POINTER;
}
return memory;
}
@Override
public Value convertPrecision(long precision, boolean force) {
if (!force) {
return this;
}
int length = values.length;
Value[] newValues = new Value[length];
int i = 0;
boolean modified = false;
for (; i < length; i++) {
Value old = values[i];
Value v = old.convertPrecision(precision, true);
if (v != old) {
modified = true;
}
// empty byte arrays or strings have precision 0
// they count as precision 1 here
precision -= Math.max(1, v.getPrecision());
if (precision < 0) {
break;
}
newValues[i] = v;
}
if (i < length) {
return get(Arrays.copyOf(newValues, i));
}
return modified ? get(newValues) : this;
}
}
......@@ -157,8 +157,8 @@ public class TestFullText extends TestDb {
assertEquals("KEYS", rs.getMetaData().getColumnLabel(4));
assertEquals("PUBLIC", rs.getString(1));
assertEquals("TEST", rs.getString(2));
assertEquals("(ID)", rs.getString(3));
assertEquals("(1)", rs.getString(4));
assertEquals("[ID]", rs.getString(3));
assertEquals("[1]", rs.getString(4));
rs = stat.executeQuery("SELECT * FROM FT_SEARCH('this', 0, 0)");
assertFalse(rs.next());
......
......@@ -1860,7 +1860,7 @@ public class TestResultSet extends TestDb {
assertEquals(Types.NULL, array.getBaseType());
assertEquals("NULL", array.getBaseTypeName());
assertTrue(array.toString().endsWith(": (11, 12)"));
assertTrue(array.toString().endsWith(": [11, 12]"));
// free
array.free();
......
......@@ -144,7 +144,7 @@ public class TestScript extends TestDb {
for (String s : new String[] { "array", "bigint", "binary", "blob",
"boolean", "char", "clob", "date", "decimal", decimal2, "double", "enum",
"geometry", "identity", "int", "interval", "other", "real", "smallint",
"geometry", "identity", "int", "interval", "other", "real", "row", "smallint",
"time", "timestamp-with-timezone", "timestamp", "tinyint",
"uuid", "varchar", "varchar-ignorecase" }) {
testScript("datatypes/" + s + ".sql");
......
......@@ -16,10 +16,10 @@ SELECT (10, 20, 30)[4];
>> null
SELECT ARRAY[];
>> ()
>> []
SELECT ARRAY[10];
>> (10)
>> [10]
SELECT ARRAY[10, 20, 30];
>> (10, 20, 30)
>> [10, 20, 30]
-- 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
--
SELECT ROW (10);
>> ROW (10)
SELECT (10, 20, 30);
>> ROW (10, 20, 30)
......@@ -6,9 +6,15 @@
CREATE TABLE test (id INT NOT NULL, name VARCHAR);
> ok
select * from test where id = (1, 2);
select * from test where id = ARRAY [1, 2];
> exception COMPARING_ARRAY_TO_SCALAR
insert into test values (1, 't');
> update count: 1
select * from test where id = (1, 2);
> exception COLUMN_COUNT_DOES_NOT_MATCH
drop table test;
> ok
......
......@@ -6,18 +6,18 @@
SELECT HISTOGRAM(X), HISTOGRAM(DISTINCT X) FROM VALUES (1), (2), (3), (1), (2), (NULL), (5) T(X);
> HISTOGRAM(C1) HISTOGRAM(DISTINCT C1)
> ------------------------------------------- -------------------------------------------
> ((null, 1), (1, 2), (2, 2), (3, 1), (5, 1)) ((null, 1), (1, 1), (2, 1), (3, 1), (5, 1))
> [[null, 1], [1, 2], [2, 2], [3, 1], [5, 1]] [[null, 1], [1, 1], [2, 1], [3, 1], [5, 1]]
> rows: 1
SELECT HISTOGRAM(X) FILTER (WHERE X > 1), HISTOGRAM(DISTINCT X) FILTER (WHERE X > 1)
FROM VALUES (1), (2), (3), (1), (2), (NULL), (5) T(X);
> HISTOGRAM(C1) FILTER (WHERE (C1 > 1)) HISTOGRAM(DISTINCT C1) FILTER (WHERE (C1 > 1))
> ------------------------------------- ----------------------------------------------
> ((2, 2), (3, 1), (5, 1)) ((2, 1), (3, 1), (5, 1))
> [[2, 2], [3, 1], [5, 1]] [[2, 1], [3, 1], [5, 1]]
> rows: 1
SELECT HISTOGRAM(X) FILTER (WHERE X > 0), HISTOGRAM(DISTINCT X) FILTER (WHERE X > 0) FROM VALUES (0) T(X);
> HISTOGRAM(C1) FILTER (WHERE (C1 > 0)) HISTOGRAM(DISTINCT C1) FILTER (WHERE (C1 > 0))
> ------------------------------------- ----------------------------------------------
> () ()
> [] []
> rows: 1
......@@ -32,7 +32,7 @@ drop table test;
> ok
explain select * from table(id int = (1, 2), name varchar=('Hello', 'World'));
>> SELECT TABLE.ID, TABLE.NAME FROM TABLE(ID INT=(1, 2), NAME VARCHAR=('Hello', 'World')) /* function */
>> SELECT TABLE.ID, TABLE.NAME FROM TABLE(ID INT=ROW (1, 2), NAME VARCHAR=ROW ('Hello', 'World')) /* function */
select * from table(id int=(1, 2), name varchar=('Hello', 'World')) x order by id;
> ID NAME
......
......@@ -36,10 +36,10 @@ SELECT * FROM UNNEST(ARRAY[1], ARRAY[2, 3, 4], ARRAY[5, 6]) WITH ORDINALITY;
> rows: 3
EXPLAIN SELECT * FROM UNNEST(ARRAY[1]);
>> SELECT UNNEST.C1 FROM UNNEST((1,)) /* function */
>> SELECT UNNEST.C1 FROM UNNEST(ARRAY [1]) /* function */
EXPLAIN SELECT * FROM UNNEST(ARRAY[1]) WITH ORDINALITY;
>> SELECT UNNEST.C1, UNNEST.NORD FROM UNNEST((1,)) WITH ORDINALITY /* function */
>> SELECT UNNEST.C1, UNNEST.NORD FROM UNNEST(ARRAY [1]) WITH ORDINALITY /* function */
SELECT 1 IN(UNNEST(ARRAY[1, 2, 3]));
>> TRUE
......@@ -80,8 +80,8 @@ INSERT INTO TEST VALUES (2, ARRAY[2, 4]), (3, ARRAY[2, 5]);
SELECT A, B, A IN(UNNEST(B)) FROM TEST;
> A B A IN(UNNEST(B))
> - ------ ---------------
> 2 (2, 4) TRUE
> 3 (2, 5) FALSE
> 2 [2, 4] TRUE
> 3 [2, 5] FALSE
> rows: 2
DROP TABLE TEST;
......
......@@ -366,14 +366,14 @@ explain select -cast(0 as real), -cast(0 as double);
select () empty;
> EMPTY
> -----
> ()
> ------
> ROW ()
> rows: 1
select (1,) one_element;
> ONE_ELEMENT
> -----------
> (1)
> ROW (1)
> rows: 1
select (1) one;
......@@ -1401,9 +1401,9 @@ insert into test values(1, (1, 1)), (2, (1, 2)), (3, (1, 1, 1));
select * from test order by data;
> ID DATA
> -- ---------
> 1 (1, 1)
> 3 (1, 1, 1)
> 2 (1, 2)
> 1 [1, 1]
> 3 [1, 1, 1]
> 2 [1, 2]
> rows (ordered): 3
drop table test;
......@@ -2003,9 +2003,9 @@ drop table people, cars;
> ok
select (1, 2);
> 1, 2
> ------
> (1, 2)
> ROW (1, 2)
> ----------
> ROW (1, 2)
> rows: 1
create table array_test(x array);
......@@ -2017,7 +2017,7 @@ insert into array_test values((1, 2, 3)), ((2, 3, 4));
select * from array_test where x = (1, 2, 3);
> X
> ---------
> (1, 2, 3)
> [1, 2, 3]
> rows: 1
drop table array_test;
......@@ -3177,7 +3177,7 @@ drop table test;
call select 1.0/3.0*3.0, 100.0/2.0, -25.0/100.0, 0.0/3.0, 6.9/2.0, 0.72179425150347250912311550800000 / 5314251955.21;
> SELECT 0.999999999999999999999999990, 50, -0.25, 0, 3.45, 1.35822361752313607260107721120531135706133161972E-10 FROM SYSTEM_RANGE(1, 1) /* PUBLIC.RANGE_INDEX */ /* scanCount: 2 */
> -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> (0.999999999999999999999999990, 50, -0.25, 0, 3.45, 1.35822361752313607260107721120531135706133161972E-10)
> ROW (0.999999999999999999999999990, 50, -0.25, 0, 3.45, 1.35822361752313607260107721120531135706133161972E-10)
> rows: 1
call (select x from dual where x is null);
......@@ -3254,9 +3254,12 @@ select count(*) from test where id in ((select id from test));
select count(*) from test where id = ((select id from test));
> exception SCALAR_SUBQUERY_CONTAINS_MORE_THAN_ONE_ROW
select count(*) from test where id = ((select id from test), 1);
select count(*) from test where id = ARRAY [(select id from test), 1];
> exception COMPARING_ARRAY_TO_SCALAR
select count(*) from test where id = ((select id from test fetch first row only), 1);
> exception COLUMN_COUNT_DOES_NOT_MATCH
select (select id from test where 1=0) from test;
> SELECT ID FROM PUBLIC.TEST /* PUBLIC.TEST.tableScan: FALSE */ WHERE FALSE
> -------------------------------------------------------------------------
......@@ -4315,8 +4318,8 @@ update test set (id, name)=(select id+1, name || 'Ho' from test t1 where test.id
> update count: 2
explain update test set (id, name)=(id+1, name || 'Hi');
#+mvStore#>> UPDATE PUBLIC.TEST /* PUBLIC.TEST.tableScan */ SET ID = ARRAY_GET(((ID + 1), (NAME || 'Hi')), 1), NAME = ARRAY_GET(((ID + 1), (NAME || 'Hi')), 2)
#-mvStore#>> UPDATE PUBLIC.TEST /* PUBLIC.PRIMARY_KEY_2 */ SET ID = ARRAY_GET(((ID + 1), (NAME || 'Hi')), 1), NAME = ARRAY_GET(((ID + 1), (NAME || 'Hi')), 2)
#+mvStore#>> UPDATE PUBLIC.TEST /* PUBLIC.TEST.tableScan */ SET ID = ARRAY_GET(ROW ((ID + 1), (NAME || 'Hi')), 1), NAME = ARRAY_GET(ROW ((ID + 1), (NAME || 'Hi')), 2)
#-mvStore#>> UPDATE PUBLIC.TEST /* PUBLIC.PRIMARY_KEY_2 */ SET ID = ARRAY_GET(ROW ((ID + 1), (NAME || 'Hi')), 1), NAME = ARRAY_GET(ROW ((ID + 1), (NAME || 'Hi')), 2)
explain update test set (id, name)=(select id+1, name || 'Ho' from test t1 where test.id=t1.id);
#+mvStore#>> UPDATE PUBLIC.TEST /* PUBLIC.TEST.tableScan */ SET ID = ARRAY_GET((SELECT (ID + 1), (NAME || 'Ho') FROM PUBLIC.TEST T1 /* PUBLIC.PRIMARY_KEY_2: ID = TEST.ID */ WHERE TEST.ID = T1.ID), 1), NAME = ARRAY_GET((SELECT (ID + 1), (NAME || 'Ho') FROM PUBLIC.TEST T1 /* PUBLIC.PRIMARY_KEY_2: ID = TEST.ID */ WHERE TEST.ID = T1.ID), 2)
......
......@@ -170,7 +170,7 @@ public class TestValue extends TestDb {
assertEquals(5, v.convertPrecision(5, true).getPrecision());
v = ValueArray.get(new Value[]{ValueString.get(""), ValueString.get("")});
assertEquals(0, v.getPrecision());
assertEquals("('')", v.convertPrecision(1, true).toString());
assertEquals("['']", v.convertPrecision(1, true).toString());
v = ValueBytes.get(spaces.getBytes());
assertEquals(100, v.getPrecision());
......
......@@ -43,6 +43,7 @@ import org.h2.value.ValueJavaObject;
import org.h2.value.ValueLong;
import org.h2.value.ValueNull;
import org.h2.value.ValueResultSet;
import org.h2.value.ValueRow;
import org.h2.value.ValueShort;
import org.h2.value.ValueString;
import org.h2.value.ValueStringFixed;
......@@ -208,14 +209,10 @@ public class TestValueMemory extends TestBase implements DataHandler {
String s = randomString(len);
return getLobStorage().createClob(new StringReader(s), len);
}
case Value.ARRAY: {
int len = random.nextInt(20);
Value[] list = new Value[len];
for (int i = 0; i < list.length; i++) {
list[i] = create(Value.STRING);
}
return ValueArray.get(list);
}
case Value.ARRAY:
return ValueArray.get(createArray());
case Value.ROW:
return ValueRow.get(createArray());
case Value.RESULT_SET:
return ValueResultSet.get(new SimpleResult());
case Value.JAVA_OBJECT:
......@@ -254,6 +251,15 @@ public class TestValueMemory extends TestBase implements DataHandler {
}
}
private Value[] createArray() throws SQLException {
int len = random.nextInt(20);
Value[] list = new Value[len];
for (int i = 0; i < list.length; i++) {
list[i] = create(Value.STRING);
}
return list;
}
private byte[] randomBytes(int len) {
byte[] data = new byte[len];
if (random.nextBoolean()) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论