Unverified 提交 fad4a812 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov 提交者: GitHub

Merge pull request #1685 from katzyn/value

Fix CHAR in PostgreSQL mode and refactor some code
......@@ -21,6 +21,8 @@ Change Log
<h2>Next Version (unreleased)</h2>
<ul>
<li>PR #1685: Fix CHAR in PostgreSQL mode and refactor some code
</li>
<li>Issue #1681: IN () doesn't work with row values when data types are not exactly the same
</li>
<li>Issue #1320: OOME / GC overhead in IndexCursor.nextCursor()
......
......@@ -18,6 +18,7 @@ import org.h2.message.DbException;
import org.h2.message.Trace;
import org.h2.result.ResultInterface;
import org.h2.result.ResultWithGeneratedKeys;
import org.h2.result.ResultWithPaddedStrings;
import org.h2.util.MathUtils;
/**
......@@ -200,6 +201,9 @@ public abstract class Command implements CommandInterface {
try {
ResultInterface result = query(maxrows);
callStop = !result.isLazy();
if (database.getMode().padFixedLengthStrings) {
return ResultWithPaddedStrings.get(result);
}
return result;
} catch (DbException e) {
start = filterConcurrentUpdate(e, start);
......
......@@ -216,7 +216,6 @@ import org.h2.table.TableFilter;
import org.h2.table.TableFilter.TableFilterVisitor;
import org.h2.table.TableView;
import org.h2.util.IntervalUtils;
import org.h2.util.MathUtils;
import org.h2.util.ParserUtil;
import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils;
......@@ -5351,7 +5350,6 @@ public class Parser {
regular = true;
}
long precision = -1;
int displaySize = -1;
ExtTypeInfo extTypeInfo = null;
int scale = -1;
String comment = null;
......@@ -5368,7 +5366,6 @@ public class Parser {
comment = templateColumn.getComment();
original = forTable ? domain.getSQL() : templateColumn.getOriginalSQL();
precision = type.getPrecision();
displaySize = type.getDisplaySize();
scale = type.getScale();
extTypeInfo = type.getExtTypeInfo();
} else {
......@@ -5388,8 +5385,6 @@ public class Parser {
read();
}
precision = precision == -1 ? dataType.defaultPrecision : precision;
displaySize = displaySize == -1 ? dataType.defaultDisplaySize
: displaySize;
scale = scale == -1 ? dataType.defaultScale : scale;
if (dataType.supportsPrecision || dataType.supportsScale) {
int t = dataType.type;
......@@ -5403,7 +5398,6 @@ public class Parser {
} else {
original = original + '(' + originalScale + ')';
}
precision = displaySize = ValueTime.getDisplaySize(originalScale);
break;
case Value.TIMESTAMP:
if (original.equals("TIMESTAMP WITHOUT TIME ZONE")) {
......@@ -5411,11 +5405,9 @@ public class Parser {
} else {
original = original + '(' + originalScale + ')';
}
precision = displaySize = ValueTimestamp.getDisplaySize(originalScale);
break;
case Value.TIMESTAMP_TZ:
original = "TIMESTAMP(" + originalScale + ") WITH TIME ZONE";
precision = displaySize = ValueTimestampTimeZone.getDisplaySize(originalScale);
break;
}
} else if (original.equals("DATETIME") || original.equals("DATETIME2")) {
......@@ -5428,11 +5420,9 @@ public class Parser {
read(CLOSE_PAREN);
scale = originalScale;
original = original + '(' + originalScale + ')';
precision = displaySize = ValueTimestamp.getDisplaySize(originalScale);
}
} else if (original.equals("SMALLDATETIME")) {
scale = 0;
precision = displaySize = ValueTimestamp.getDisplaySize(0);
}
} else if (DataType.isIntervalType(t)) {
if (originalPrecision >= 0 || originalScale >= 0) {
......@@ -5466,7 +5456,6 @@ public class Parser {
}
}
precision = p;
displaySize = MathUtils.convertLongToInt(precision);
original += ")";
}
read(CLOSE_PAREN);
......@@ -5547,13 +5536,13 @@ public class Parser {
// MySQL compatibility
readIf("UNSIGNED");
int type = dataType.type;
if (scale > precision && !DataType.isIntervalType(type)) {
if (scale > precision && dataType.supportsPrecision && dataType.supportsScale
&& !DataType.isIntervalType(type)) {
throw DbException.get(ErrorCode.INVALID_VALUE_SCALE_PRECISION,
Integer.toString(scale), Long.toString(precision));
}
Column column = new Column(columnName, type, precision, scale,
displaySize, extTypeInfo);
Column column = new Column(columnName, TypeInfo.getTypeInfo(type, precision, scale, extTypeInfo));
if (templateColumn != null) {
column.setNullable(templateColumn.isNullable());
column.setDefaultExpression(session,
......@@ -5883,7 +5872,7 @@ public class Parser {
for (int i = 0; i < columnCount; i++) {
Column c = columns.get(i);
if (c.getType().getValueType() == Value.UNKNOWN) {
c = new Column(c.getName(), Value.STRING, 0, 0, 0);
c = new Column(c.getName(), Value.STRING);
columns.set(i, c);
}
Expression[] array = new Expression[rowCount];
......
......@@ -15,17 +15,12 @@ import org.h2.engine.Database;
import org.h2.engine.DbObject;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.message.DbException;
import org.h2.schema.Schema;
import org.h2.schema.Sequence;
import org.h2.table.Column;
import org.h2.table.Table;
import org.h2.util.ColumnNamer;
import org.h2.value.DataType;
import org.h2.value.ExtTypeInfo;
import org.h2.value.TypeInfo;
import org.h2.value.Value;
/**
* This class represents the statement
......@@ -181,39 +176,8 @@ public class CreateTable extends CommandWithColumns {
ColumnNamer columnNamer= new ColumnNamer(session);
for (int i = 0; i < columnCount; i++) {
Expression expr = expressions.get(i);
TypeInfo type = expr.getType();
int valueType = type.getValueType();
String name = columnNamer.getColumnName(expr,i,expr.getAlias());
long precision = type.getPrecision();
int displaySize = type.getDisplaySize();
DataType dt = DataType.getDataType(valueType);
if (precision > 0 && (dt.defaultPrecision == 0 ||
(dt.defaultPrecision > precision && dt.defaultPrecision < Byte.MAX_VALUE))) {
// dont' set precision to MAX_VALUE if this is the default
precision = dt.defaultPrecision;
}
int scale = type.getScale();
if (scale > 0 && (dt.defaultScale == 0 ||
(dt.defaultScale > scale && dt.defaultScale < precision))) {
scale = dt.defaultScale;
}
if (scale > precision) {
precision = scale;
}
ExtTypeInfo extTypeInfo = null;
int t = dt.type;
if (DataType.isExtInfoType(t)) {
if (expr instanceof ExpressionColumn) {
extTypeInfo = ((ExpressionColumn) expr).getColumn().getType().getExtTypeInfo();
} else if (t == Value.ENUM) {
/*
* Only columns of tables may be enumerated.
*/
throw DbException.get(ErrorCode.GENERAL_ERROR_1,
"Unable to resolve enumerators of expression");
}
}
Column col = new Column(name, valueType, precision, scale, displaySize, extTypeInfo);
String name = columnNamer.getColumnName(expr, i, expr.getAlias());
Column col = new Column(name, expr.getType());
addColumn(col);
}
}
......
......@@ -18,6 +18,7 @@ import org.h2.table.Column;
import org.h2.table.Table;
import org.h2.table.TableType;
import org.h2.table.TableView;
import org.h2.value.TypeInfo;
import org.h2.value.Value;
/**
......@@ -110,7 +111,7 @@ public class CreateView extends SchemaCommand {
columnTemplatesAsStrings = new Column[columnNames.length];
for (int i = 0; i < columnNames.length; ++i) {
// non table expressions are fine to use unknown column type
columnTemplatesAsUnknowns[i] = new Column(columnNames[i], Value.UNKNOWN);
columnTemplatesAsUnknowns[i] = new Column(columnNames[i], TypeInfo.TYPE_UNKNOWN);
// table expressions can't have unknown types - so we use string instead
columnTemplatesAsStrings[i] = new Column(columnNames[i], Value.STRING);
}
......
......@@ -45,12 +45,6 @@ public class Constants {
*/
public static final String BUILD_VENDOR_AND_VERSION = null;
/**
* The TCP protocol version number 8.
* @since 1.2.143 (2010-09-18)
*/
public static final int TCP_PROTOCOL_VERSION_8 = 8;
/**
* The TCP protocol version number 9.
* @since 1.3.158 (2011-07-17)
......@@ -114,7 +108,7 @@ public class Constants {
/**
* Minimum supported version of TCP protocol.
*/
public static final int TCP_PROTOCOL_VERSION_MIN_SUPPORTED = TCP_PROTOCOL_VERSION_8;
public static final int TCP_PROTOCOL_VERSION_MIN_SUPPORTED = TCP_PROTOCOL_VERSION_9;
/**
* Maximum supported version of TCP protocol.
......
......@@ -99,11 +99,10 @@ public class BinaryOperation extends Expression {
@Override
public Value getValue(Session session) {
Mode mode = session.getDatabase().getMode();
int dataType = type.getValueType();
Value l = left.getValue(session).convertTo(dataType, mode);
Value l = left.getValue(session).convertTo(type, mode, null);
Value r = right.getValue(session);
if (convertRight) {
r = r.convertTo(dataType, mode);
r = r.convertTo(type, mode, null);
}
switch (opType) {
case CONCAT: {
......
......@@ -21,11 +21,9 @@ import org.h2.table.Column;
import org.h2.table.ColumnResolver;
import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.value.ExtTypeInfo;
import org.h2.value.TypeInfo;
import org.h2.value.Value;
import org.h2.value.ValueBoolean;
import org.h2.value.ValueNull;
/**
* A expression that represents a column of a table or view.
......@@ -216,12 +214,6 @@ public class ExpressionColumn extends Expression {
throw DbException.get(ErrorCode.MUST_GROUP_BY_COLUMN_1, getSQL());
}
}
if (value != ValueNull.INSTANCE) {
ExtTypeInfo extTypeInfo = column.getType().getExtTypeInfo();
if (extTypeInfo != null) {
return extTypeInfo.cast(value);
}
}
return value;
}
......
......@@ -34,7 +34,7 @@ public class UnaryOperation extends Expression {
@Override
public Value getValue(Session session) {
Value a = arg.getValue(session).convertTo(type.getValueType(), session.getDatabase().getMode());
Value a = arg.getValue(session).convertTo(type, session.getDatabase().getMode(), null);
return a == ValueNull.INSTANCE ? a : a.negate();
}
......
......@@ -21,7 +21,7 @@ import org.h2.message.DbException;
import org.h2.table.Column;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.util.MathUtils;
import org.h2.value.TypeInfo;
import org.h2.value.Value;
import org.h2.value.ValueBoolean;
import org.h2.value.ValueGeometry;
......@@ -225,17 +225,17 @@ public class Comparison extends Condition {
return ValueExpression.getNull();
}
}
int colType = left.getType().getValueType();
int constType = r.getValueType();
int resType = Value.getHigherOrder(colType, constType);
// If not, the column values will need to be promoted
// to constant type, but vise versa, then let's do this here
// once.
if (constType != resType) {
Column column = ((ExpressionColumn) left).getColumn();
right = ValueExpression.get(r.convertTo(resType,
MathUtils.convertLongToInt(left.getType().getPrecision()),
session.getDatabase().getMode(), column, column.getType().getExtTypeInfo()));
TypeInfo colType = left.getType(), constType = r.getType();
int constValueType = constType.getValueType();
if (constValueType != colType.getValueType()) {
TypeInfo resType = Value.getHigherType(colType, constType);
// If not, the column values will need to be promoted
// to constant type, but vise versa, then let's do this here
// once.
if (constValueType != resType.getValueType()) {
Column column = ((ExpressionColumn) left).getColumn();
right = ValueExpression.get(r.convertTo(resType, session.getDatabase().getMode(), column));
}
}
} else if (right instanceof Parameter) {
((Parameter) right).setColumn(
......
......@@ -19,6 +19,7 @@ import org.h2.result.ResultInterface;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.util.StringUtils;
import org.h2.value.TypeInfo;
import org.h2.value.Value;
import org.h2.value.ValueBoolean;
import org.h2.value.ValueNull;
......@@ -72,8 +73,8 @@ public class ConditionInSelect extends Condition {
return ValueBoolean.TRUE;
}
} else {
int dataType = rows.getColumnType(0).getValueType();
if (dataType == Value.NULL) {
TypeInfo colType = rows.getColumnType(0);
if (colType.getValueType() == Value.NULL) {
return ValueBoolean.FALSE;
}
if (l.getValueType() == Value.ROW) {
......@@ -83,7 +84,7 @@ public class ConditionInSelect extends Condition {
}
l = leftList[0];
}
l = l.convertTo(dataType, database.getMode());
l = l.convertTo(colType, database.getMode(), null);
if (rows.containsDistinct(new Value[] { l })) {
return ValueBoolean.TRUE;
}
......
......@@ -7,11 +7,10 @@ package org.h2.expression.function;
import static java.lang.String.format;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.TimeZone;
import org.h2.engine.Session;
import org.h2.util.DateTimeUtils;
import org.h2.value.ValueTimestamp;
import org.h2.value.ValueTimestampTimeZone;
......@@ -21,6 +20,8 @@ import org.h2.value.ValueTimestampTimeZone;
* This class holds and handles the input data form the TO_DATE-method
*/
public class ToDateParser {
private final Session session;
private final String unmodifiedInputStr;
private final String unmodifiedFormatStr;
private final ConfigParam functionName;
......@@ -52,12 +53,14 @@ public class ToDateParser {
private int currentYear, currentMonth;
/**
* @param input the input date with the date-time info
* @param format the format of date-time info
* @param session the database session
* @param functionName one of [TO_DATE, TO_TIMESTAMP] (both share the same
* code)
* @param input the input date with the date-time info
* @param format the format of date-time info
*/
private ToDateParser(ConfigParam functionName, String input, String format) {
private ToDateParser(Session session, ConfigParam functionName, String input, String format) {
this.session = session;
this.functionName = functionName;
inputStr = input.trim();
// Keep a copy
......@@ -72,8 +75,8 @@ public class ToDateParser {
unmodifiedFormatStr = formatStr;
}
private static ToDateParser getTimestampParser(ConfigParam param, String input, String format) {
ToDateParser result = new ToDateParser(param, input, format);
private static ToDateParser getTimestampParser(Session session, ConfigParam param, String input, String format) {
ToDateParser result = new ToDateParser(session, param, input, format);
parse(result);
return result;
}
......@@ -145,10 +148,10 @@ public class ToDateParser {
}
private void queryCurrentYearAndMonth() {
GregorianCalendar gc = DateTimeUtils.getCalendar();
gc.setTimeInMillis(System.currentTimeMillis());
currentYear = gc.get(Calendar.YEAR);
currentMonth = gc.get(Calendar.MONTH) + 1;
long dateValue = (session.getDatabase().getMode().dateTimeValueWithinTransaction
? session.getTransactionStart() : session.getCurrentCommandStart()).getDateValue();
currentYear = DateTimeUtils.yearFromDateValue(dateValue);
currentMonth = DateTimeUtils.monthFromDateValue(dateValue);
}
int getCurrentYear() {
......@@ -319,36 +322,39 @@ public class ToDateParser {
/**
* Parse a string as a timestamp with the given format.
*
* @param session the database session
* @param input the input
* @param format the format
* @return the timestamp
*/
public static ValueTimestamp toTimestamp(String input, String format) {
ToDateParser parser = getTimestampParser(ConfigParam.TO_TIMESTAMP, input, format);
public static ValueTimestamp toTimestamp(Session session, String input, String format) {
ToDateParser parser = getTimestampParser(session, ConfigParam.TO_TIMESTAMP, input, format);
return parser.getResultingValue();
}
/**
* Parse a string as a timestamp with the given format.
*
* @param session the database session
* @param input the input
* @param format the format
* @return the timestamp
*/
public static ValueTimestampTimeZone toTimestampTz(String input, String format) {
ToDateParser parser = getTimestampParser(ConfigParam.TO_TIMESTAMP_TZ, input, format);
public static ValueTimestampTimeZone toTimestampTz(Session session, String input, String format) {
ToDateParser parser = getTimestampParser(session, ConfigParam.TO_TIMESTAMP_TZ, input, format);
return parser.getResultingValueWithTimeZone();
}
/**
* Parse a string as a date with the given format.
*
* @param session the database session
* @param input the input
* @param format the format
* @return the date as a timestamp
*/
public static ValueTimestamp toDate(String input, String format) {
ToDateParser parser = getTimestampParser(ConfigParam.TO_DATE, input, format);
public static ValueTimestamp toDate(Session session, String input, String format) {
ToDateParser parser = getTimestampParser(session, ConfigParam.TO_DATE, input, format);
return parser.getResultingValue();
}
......
......@@ -97,10 +97,10 @@ public class HashIndex extends BaseIndex {
/*
* Sometimes the incoming search is a similar, but not the same type
* e.g. the search value is INT, but the index column is LONG. In which
* case we need to convert, otherwise the ValueHashMap will not find the
* case we need to convert, otherwise the HashMap will not find the
* result.
*/
v = v.convertTo(tableData.getColumn(indexColumn).getType().getValueType(), database.getMode());
v = v.convertTo(tableData.getColumn(indexColumn).getType(), database.getMode(), null);
Row result;
Long pos = rows.get(v);
if (pos == null) {
......
......@@ -105,10 +105,10 @@ public class NonUniqueHashIndex extends BaseIndex {
/*
* Sometimes the incoming search is a similar, but not the same type
* e.g. the search value is INT, but the index column is LONG. In which
* case we need to convert, otherwise the ValueHashMap will not find the
* case we need to convert, otherwise the HashMap will not find the
* result.
*/
v = v.convertTo(tableData.getColumn(indexColumn).getType().getValueType(), database.getMode());
v = v.convertTo(tableData.getColumn(indexColumn).getType(), database.getMode(), null);
ArrayList<Long> positions = rows.get(v);
return new NonUniqueHashCursor(session, tableData, positions);
}
......
......@@ -1087,7 +1087,7 @@ public class JdbcResultSet extends TraceObject implements ResultSet, JdbcResultS
public byte[] getBytes(String columnLabel) throws SQLException {
try {
debugCodeCall("getBytes", columnLabel);
return get(columnLabel).getBytes();
return get(columnLabel).convertTo(Value.BYTES, conn.getMode()).getBytes();
} catch (Exception e) {
throw logAndConvert(e);
}
......
......@@ -202,8 +202,7 @@ public class FunctionsMySQL extends FunctionsBase {
if (allConst) {
return ValueExpression.get(getValue(session));
}
dataType = info.returnDataType;
type = TypeInfo.getTypeInfo(dataType);
type = TypeInfo.getTypeInfo(info.returnDataType);
return this;
}
......
......@@ -30,7 +30,6 @@ import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.TableFilter;
import org.h2.value.CompareMode;
import org.h2.value.TypeInfo;
import org.h2.value.Value;
import org.h2.value.ValueArray;
import org.h2.value.ValueLong;
......@@ -289,8 +288,7 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex {
int idx = c.getColumnId();
Value v = r.getValue(idx);
if (v != null) {
TypeInfo type = c.getType();
array[i] = v.convertTo(type.getValueType(), -1, database.getMode(), null, type.getExtTypeInfo());
array[i] = v.convertTo(c.getType(), database.getMode(), null);
}
}
array[keyColumns - 1] = key != null ? key : ValueLong.get(r.getKey());
......
/*
* Copyright 2004-2019 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.result;
import java.util.Arrays;
import org.h2.engine.SessionInterface;
import org.h2.util.MathUtils;
import org.h2.value.TypeInfo;
import org.h2.value.Value;
import org.h2.value.ValueString;
/**
* Result with padded fixed length strings.
*/
public class ResultWithPaddedStrings implements ResultInterface {
private final ResultInterface source;
/**
* Returns wrapped result if necessary, or original result if it does not
* contain visible CHAR columns.
*
* @param source
* source result
* @return wrapped result or original result
*/
public static ResultInterface get(ResultInterface source) {
int count = source.getVisibleColumnCount();
for (int i = 0; i < count; i++) {
if (source.getColumnType(i).getValueType() == Value.STRING_FIXED) {
return new ResultWithPaddedStrings(source);
}
}
return source;
}
/**
* Creates new instance of result.
*
* @param source
* the source result
*/
private ResultWithPaddedStrings(ResultInterface source) {
this.source = source;
}
@Override
public void reset() {
source.reset();
}
@Override
public Value[] currentRow() {
int count = source.getVisibleColumnCount();
Value[] row = Arrays.copyOf(source.currentRow(), count);
for (int i = 0; i < count; i++) {
TypeInfo type = source.getColumnType(i);
if (type.getValueType() == Value.STRING_FIXED) {
long precision = type.getPrecision();
if (precision == Integer.MAX_VALUE) {
// CHAR is CHAR(1)
precision = 1;
}
String s = row[i].getString();
if (s != null && s.length() < precision) {
/*
* Use ValueString to avoid truncation of spaces. There is
* no difference between ValueStringFixed and ValueString
* for JDBC layer anyway.
*/
row[i] = ValueString.get(rightPadWithSpaces(s, MathUtils.convertLongToInt(precision)));
}
}
}
return row;
}
private static String rightPadWithSpaces(String s, int length) {
int used = s.length();
if (length <= used) {
return s;
}
char[] res = new char[length];
s.getChars(0, used, res, 0);
Arrays.fill(res, used, length, ' ');
return new String(res);
}
@Override
public boolean next() {
return source.next();
}
@Override
public int getRowId() {
return source.getRowId();
}
@Override
public boolean isAfterLast() {
return source.isAfterLast();
}
@Override
public int getVisibleColumnCount() {
return source.getVisibleColumnCount();
}
@Override
public int getRowCount() {
return source.getRowCount();
}
@Override
public boolean hasNext() {
return source.hasNext();
}
@Override
public boolean needToClose() {
return source.needToClose();
}
@Override
public void close() {
source.close();
}
@Override
public String getAlias(int i) {
return source.getAlias(i);
}
@Override
public String getSchemaName(int i) {
return source.getSchemaName(i);
}
@Override
public String getTableName(int i) {
return source.getTableName(i);
}
@Override
public String getColumnName(int i) {
return source.getColumnName(i);
}
@Override
public TypeInfo getColumnType(int i) {
return source.getColumnType(i);
}
@Override
public boolean isAutoIncrement(int i) {
return source.isAutoIncrement(i);
}
@Override
public int getNullable(int i) {
return source.getNullable(i);
}
@Override
public void setFetchSize(int fetchSize) {
source.setFetchSize(fetchSize);
}
@Override
public int getFetchSize() {
return source.getFetchSize();
}
@Override
public boolean isLazy() {
return source.isLazy();
}
@Override
public boolean isClosed() {
return source.isClosed();
}
@Override
public ResultInterface createShallowCopy(SessionInterface targetSession) {
ResultInterface copy = source.createShallowCopy(targetSession);
return copy != null ? new ResultWithPaddedStrings(copy) : null;
}
}
......@@ -27,7 +27,6 @@ import org.h2.schema.Sequence;
import org.h2.util.MathUtils;
import org.h2.util.StringUtils;
import org.h2.value.DataType;
import org.h2.value.ExtTypeInfo;
import org.h2.value.TypeInfo;
import org.h2.value.Value;
import org.h2.value.ValueInt;
......@@ -88,22 +87,7 @@ public class Column {
private Domain domain;
public Column(String name, int valueType) {
this(name, valueType, -1, -1, -1, null);
}
public Column(String name, int valueType, long precision, int scale, int displaySize) {
this(name, valueType, precision, scale, displaySize, null);
}
public Column(String name, int valueType, long precision, int scale, int displaySize, ExtTypeInfo extTypeInfo) {
this.name = name;
if (precision == -1 && scale == -1 && displaySize == -1 && valueType != Value.UNKNOWN) {
DataType dt = DataType.getDataType(valueType);
precision = dt.defaultPrecision;
scale = dt.defaultScale;
displaySize = dt.defaultDisplaySize;
}
this.type = new TypeInfo(valueType, precision, scale, displaySize, extTypeInfo);
this(name, TypeInfo.getTypeInfo(valueType));
}
public Column(String name, TypeInfo type) {
......@@ -164,8 +148,7 @@ public class Column {
*/
public Value convert(Value v, Mode mode) {
try {
return v.convertTo(type.getValueType(), MathUtils.convertLongToInt(type.getPrecision()), mode, this,
type.getExtTypeInfo());
return v.convertTo(type, mode, this);
} catch (DbException e) {
if (e.getErrorCode() == ErrorCode.DATA_CONVERSION_ERROR_1) {
String target = (table == null ? "" : table.getName() + ": ") +
......
......@@ -28,11 +28,11 @@ import org.h2.message.DbException;
import org.h2.result.Row;
import org.h2.result.RowList;
import org.h2.schema.Schema;
import org.h2.util.MathUtils;
import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils;
import org.h2.util.Utils;
import org.h2.value.DataType;
import org.h2.value.TypeInfo;
import org.h2.value.Value;
import org.h2.value.ValueDate;
import org.h2.value.ValueTime;
......@@ -156,9 +156,8 @@ public class TableLink extends Table {
precision = convertPrecision(sqlType, precision);
int scale = rs.getInt("DECIMAL_DIGITS");
scale = convertScale(sqlType, scale);
int displaySize = MathUtils.convertLongToInt(precision);
int type = DataType.convertSQLTypeToValueType(sqlType, sqlTypeName);
Column col = new Column(n, type, precision, scale, displaySize);
Column col = new Column(n, TypeInfo.getTypeInfo(type, precision, scale, null));
col.setTable(this, i++);
columnList.add(col);
columnMap.put(n, col);
......@@ -185,9 +184,8 @@ public class TableLink extends Table {
precision = convertPrecision(sqlType, precision);
int scale = rsMeta.getScale(i + 1);
scale = convertScale(sqlType, scale);
int displaySize = rsMeta.getColumnDisplaySize(i + 1);
int type = DataType.getValueTypeFromResultSet(rsMeta, i + 1);
Column col = new Column(n, type, precision, scale, displaySize);
Column col = new Column(n, TypeInfo.getTypeInfo(type, precision, scale, null));
col.setTable(this, i++);
columnList.add(col);
columnMap.put(n, col);
......
......@@ -38,8 +38,6 @@ import org.h2.util.ColumnNamer;
import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils;
import org.h2.util.Utils;
import org.h2.value.DataType;
import org.h2.value.ExtTypeInfo;
import org.h2.value.TypeInfo;
import org.h2.value.Value;
......@@ -182,29 +180,19 @@ public class TableView extends Table {
for (int i = 0; i < count; i++) {
Expression expr = expressions.get(i);
String name = null;
int valueType = Value.UNKNOWN;
TypeInfo type = TypeInfo.TYPE_UNKNOWN;
if (columnTemplates != null && columnTemplates.length > i) {
name = columnTemplates[i].getName();
valueType = columnTemplates[i].getType().getValueType();
type = columnTemplates[i].getType();
}
if (name == null) {
name = expr.getAlias();
}
name = columnNamer.getColumnName(expr, i, name);
if (valueType == Value.UNKNOWN) {
valueType = expr.getType().getValueType();
if (type.getValueType() == Value.UNKNOWN) {
type = expr.getType();
}
TypeInfo type = expr.getType();
long precision = type.getPrecision();
int scale = type.getScale();
int displaySize = type.getDisplaySize();
ExtTypeInfo extTypeInfo = null;
if (DataType.isExtInfoType(valueType)) {
if (expr instanceof ExpressionColumn) {
extTypeInfo = ((ExpressionColumn) expr).getColumn().getType().getExtTypeInfo();
}
}
Column col = new Column(name, valueType, precision, scale, displaySize, extTypeInfo);
Column col = new Column(name, type);
col.setTable(this, i);
// Fetch check constraint from view column source
ExpressionColumn fromColumn = null;
......
......@@ -109,12 +109,12 @@ public class DateTimeUtils {
private static volatile TimeZone timeZone;
/**
* Observed JVM behaviour is that if the timezone of the host computer is
* changed while the JVM is running, the zone offset does not change but
* keeps the initial value. So it is correct to measure this once and use
* this value throughout the JVM's lifecycle. In any case, it is safer to
* use a fixed value throughout the duration of the JVM's life, rather than
* have this offset change, possibly midway through a long-running query.
* Raw offset doesn't change during DST transitions, but changes during
* other transitions that some time zones have. H2 1.4.193 and later
* versions use zone offset that is valid for startup time for performance
* reasons. This code is now used only by old PageStore engine and its
* datetime storage code has issues with all time zone transitions, so this
* buggy logic is preserved as is too.
*/
private static int zoneOffsetMillis = createGregorianCalendar().get(Calendar.ZONE_OFFSET);
......
......@@ -14,7 +14,6 @@ import java.io.Reader;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.Socket;
import java.sql.Timestamp;
import org.h2.api.ErrorCode;
import org.h2.api.IntervalQualifier;
import org.h2.engine.Constants;
......@@ -26,7 +25,6 @@ import org.h2.security.SHA256;
import org.h2.store.Data;
import org.h2.store.DataReader;
import org.h2.util.Bits;
import org.h2.util.DateTimeUtils;
import org.h2.util.IOUtils;
import org.h2.util.JdbcUtils;
import org.h2.util.MathUtils;
......@@ -348,31 +346,17 @@ public class Transfer {
break;
case Value.TIME:
writeInt(Value.TIME);
if (version >= Constants.TCP_PROTOCOL_VERSION_9) {
writeLong(((ValueTime) v).getNanos());
} else {
writeLong(DateTimeUtils.getTimeLocalWithoutDst(v.getTime()));
}
writeLong(((ValueTime) v).getNanos());
break;
case Value.DATE:
writeInt(Value.DATE);
if (version >= Constants.TCP_PROTOCOL_VERSION_9) {
writeLong(((ValueDate) v).getDateValue());
} else {
writeLong(DateTimeUtils.getTimeLocalWithoutDst(v.getDate()));
}
writeLong(((ValueDate) v).getDateValue());
break;
case Value.TIMESTAMP: {
writeInt(Value.TIMESTAMP);
if (version >= Constants.TCP_PROTOCOL_VERSION_9) {
ValueTimestamp ts = (ValueTimestamp) v;
writeLong(ts.getDateValue());
writeLong(ts.getTimeNanos());
} else {
Timestamp ts = v.getTimestamp();
writeLong(DateTimeUtils.getTimeLocalWithoutDst(ts));
writeInt(ts.getNanos() % 1_000_000);
}
ValueTimestamp ts = (ValueTimestamp) v;
writeLong(ts.getDateValue());
writeLong(ts.getTimeNanos());
break;
}
case Value.TIMESTAMP_TZ: {
......@@ -608,30 +592,13 @@ public class Transfer {
case Value.BYTE:
return ValueByte.get(readByte());
case Value.DATE:
if (version >= Constants.TCP_PROTOCOL_VERSION_9) {
return ValueDate.fromDateValue(readLong());
} else {
return ValueDate.fromMillis(DateTimeUtils.getTimeUTCWithoutDst(readLong()));
}
return ValueDate.fromDateValue(readLong());
case Value.TIME:
if (version >= Constants.TCP_PROTOCOL_VERSION_9) {
return ValueTime.fromNanos(readLong());
} else {
return ValueTime.fromMillis(DateTimeUtils.getTimeUTCWithoutDst(readLong()));
}
case Value.TIMESTAMP: {
if (version >= Constants.TCP_PROTOCOL_VERSION_9) {
return ValueTimestamp.fromDateValueAndNanos(
readLong(), readLong());
} else {
return ValueTimestamp.fromMillisNanos(
DateTimeUtils.getTimeUTCWithoutDst(readLong()),
readInt() % 1_000_000);
}
}
return ValueTime.fromNanos(readLong());
case Value.TIMESTAMP:
return ValueTimestamp.fromDateValueAndNanos(readLong(), readLong());
case Value.TIMESTAMP_TZ: {
return ValueTimestampTimeZone.fromDateValueAndNanos(readLong(),
readLong(), (short) readInt());
return ValueTimestampTimeZone.fromDateValueAndNanos(readLong(), readLong(), (short) readInt());
}
case Value.DECIMAL:
return ValueDecimal.get(new BigDecimal(readString()));
......@@ -655,7 +622,7 @@ public class Transfer {
case Value.STRING_IGNORECASE:
return ValueStringIgnoreCase.get(readString());
case Value.STRING_FIXED:
return ValueStringFixed.get(readString(), ValueStringFixed.PRECISION_DO_NOT_TRIM, null);
return ValueStringFixed.get(readString());
case Value.BLOB: {
long length = readLong();
if (version >= Constants.TCP_PROTOCOL_VERSION_11) {
......
......@@ -257,29 +257,36 @@ public class TypeInfo {
if (scale < 0) {
scale = ValueDecimal.DEFAULT_SCALE;
}
if (precision < scale) {
precision = scale;
}
return new TypeInfo(Value.DECIMAL, precision, scale, MathUtils.convertLongToInt(precision + 2), null);
case Value.TIME:
case Value.TIME: {
if (scale < 0 || scale >= ValueTime.MAXIMUM_SCALE) {
return TYPE_TIME;
}
return new TypeInfo(Value.TIME, ValueTime.MAXIMUM_PRECISION, scale, ValueTime.DEFAULT_PRECISION, null);
case Value.TIMESTAMP:
int d = scale == 0 ? 8 : 9 + scale;
return new TypeInfo(Value.TIME, d, scale, d, null);
}
case Value.TIMESTAMP: {
if (scale < 0 || scale >= ValueTimestamp.MAXIMUM_SCALE) {
return TYPE_TIMESTAMP;
}
return new TypeInfo(Value.TIMESTAMP, ValueTimestamp.MAXIMUM_PRECISION, scale,
ValueTimestamp.MAXIMUM_PRECISION, null);
case Value.TIMESTAMP_TZ:
int d = scale == 0 ? 19 : 20 + scale;
return new TypeInfo(Value.TIMESTAMP, d, scale, d, null);
}
case Value.TIMESTAMP_TZ: {
if (scale < 0 || scale >= ValueTimestampTimeZone.MAXIMUM_SCALE) {
return TYPE_TIMESTAMP_TZ;
}
return new TypeInfo(Value.TIMESTAMP_TZ, ValueTimestampTimeZone.MAXIMUM_PRECISION, scale,
ValueTimestampTimeZone.MAXIMUM_PRECISION, null);
int d = scale == 0 ? 25 : 26 + scale;
return new TypeInfo(Value.TIMESTAMP_TZ, d, scale, d, null);
}
case Value.BYTES:
if (precision < 0) {
precision = Integer.MAX_VALUE;
}
return new TypeInfo(Value.BYTES, precision, scale, MathUtils.convertLongToInt(precision) * 2, null);
return new TypeInfo(Value.BYTES, precision, 0, MathUtils.convertLongToInt(precision) * 2, null);
case Value.STRING:
if (precision < 0) {
return TYPE_STRING_DEFAULT;
......@@ -287,19 +294,28 @@ public class TypeInfo {
//$FALL-THROUGH$
case Value.STRING_FIXED:
case Value.STRING_IGNORECASE:
if (precision < 0) {
precision = Integer.MAX_VALUE;
}
return new TypeInfo(type, precision, 0, MathUtils.convertLongToInt(precision), null);
case Value.BLOB:
case Value.CLOB:
if (precision < 0) {
precision = Long.MAX_VALUE;
}
return new TypeInfo(type, precision, 0, MathUtils.convertLongToInt(precision), null);
case Value.GEOMETRY:
if (extTypeInfo == null) {
if (extTypeInfo instanceof ExtTypeInfoGeometry) {
return new TypeInfo(Value.GEOMETRY, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, extTypeInfo);
} else {
return TYPE_GEOMETRY;
}
return new TypeInfo(Value.GEOMETRY, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, extTypeInfo);
case Value.ENUM:
if (extTypeInfo == null) {
if (extTypeInfo instanceof ExtTypeInfoEnum) {
return new TypeInfo(Value.ENUM, ValueEnum.PRECISION, 0, ValueEnum.DISPLAY_SIZE, extTypeInfo);
} else {
return TYPE_ENUM_UNDEFINED;
}
return new TypeInfo(Value.ENUM, ValueEnum.PRECISION, 0, ValueEnum.DISPLAY_SIZE, extTypeInfo);
case Value.INTERVAL_YEAR:
case Value.INTERVAL_MONTH:
case Value.INTERVAL_DAY:
......
......@@ -696,9 +696,7 @@ public abstract class Value extends VersionedValue {
* @return the converted value
*/
public final Value convertTo(int targetType) {
// Use -1 to indicate "default behaviour" where value conversion should not
// depend on any datatype precision.
return convertTo(targetType, null);
return convertTo(targetType, null, null, null);
}
/**
......@@ -706,10 +704,8 @@ public abstract class Value extends VersionedValue {
* @param enumerators the extended type information for the ENUM data type
* @return value represented as ENUM
*/
public final Value convertToEnum(ExtTypeInfo enumerators) {
// Use -1 to indicate "default behaviour" where value conversion should not
// depend on any datatype precision.
return convertTo(ENUM, -1, null, null, enumerators);
private Value convertToEnum(ExtTypeInfo enumerators) {
return convertTo(ENUM, null, null, enumerators);
}
/**
......@@ -720,7 +716,7 @@ public abstract class Value extends VersionedValue {
* @return the converted value
*/
public final Value convertTo(int targetType, Mode mode) {
return convertTo(targetType, -1, mode, null, null);
return convertTo(targetType, mode, null, null);
}
/**
......@@ -731,23 +727,20 @@ public abstract class Value extends VersionedValue {
* @param column the column (if any), used for to improve the error message if conversion fails
* @return the converted value
*/
public Value convertTo(TypeInfo targetType, Mode mode, Object column) {
return convertTo(targetType.getValueType(), -1, mode, column, targetType.getExtTypeInfo());
public final Value convertTo(TypeInfo targetType, Mode mode, Object column) {
return convertTo(targetType.getValueType(), mode, column, targetType.getExtTypeInfo());
}
/**
* Convert a value to the specified type.
*
* @param targetType the type of the returned value
* @param precision the precision of the column to convert this value to.
* The special constant <code>-1</code> is used to indicate that
* the precision plays no role when converting the value
* @param mode the conversion mode
* @param column the column (if any), used for to improve the error message if conversion fails
* @param extTypeInfo the extended data type information, or null
* @return the converted value
*/
public Value convertTo(int targetType, int precision, Mode mode, Object column, ExtTypeInfo extTypeInfo) {
protected Value convertTo(int targetType, Mode mode, Object column, ExtTypeInfo extTypeInfo) {
// converting NULL is done in ValueNull
// converting BLOB to CLOB and vice versa is done in ValueLob
if (getValueType() == targetType) {
......@@ -788,7 +781,7 @@ public abstract class Value extends VersionedValue {
case STRING_IGNORECASE:
return convertToStringIgnoreCase(mode);
case STRING_FIXED:
return convertToStringFixed(precision, mode);
return convertToStringFixed(mode);
case JAVA_OBJECT:
return convertToJavaObject();
case ENUM:
......@@ -1177,14 +1170,14 @@ public abstract class Value extends VersionedValue {
return ValueStringIgnoreCase.get(s);
}
private ValueString convertToStringFixed(int precision, Mode mode) {
private ValueString convertToStringFixed(Mode mode) {
String s;
if (getValueType() == BYTES && mode != null && mode.charToBinaryInUtf8) {
s = new String(getBytesNoCopy(), StandardCharsets.UTF_8);
} else {
s = getString();
}
return ValueStringFixed.get(s, precision, mode);
return ValueStringFixed.get(s);
}
private ValueJavaObject convertToJavaObject() {
......
......@@ -137,11 +137,11 @@ public class ValueEnumBase extends Value {
}
@Override
public Value convertTo(int targetType, int precision, Mode mode, Object column, ExtTypeInfo extTypeInfo) {
protected Value convertTo(int targetType, Mode mode, Object column, ExtTypeInfo extTypeInfo) {
if (targetType == Value.ENUM) {
return extTypeInfo.cast(this);
}
return super.convertTo(targetType, precision, mode, column, extTypeInfo);
return super.convertTo(targetType, mode, column, extTypeInfo);
}
}
......@@ -346,13 +346,13 @@ public class ValueGeometry extends Value {
}
@Override
public Value convertTo(int targetType, int precision, Mode mode, Object column, ExtTypeInfo extTypeInfo) {
protected Value convertTo(int targetType, Mode mode, Object column, ExtTypeInfo extTypeInfo) {
if (targetType == Value.GEOMETRY) {
return extTypeInfo != null ? extTypeInfo.cast(this) : this;
} else if (targetType == Value.JAVA_OBJECT) {
return this;
}
return super.convertTo(targetType, precision, mode, column, null);
return super.convertTo(targetType, mode, column, null);
}
}
......@@ -330,7 +330,7 @@ public class ValueInterval extends Value {
if (leading == 0L && remaining == 0L) {
return this;
}
return from(getQualifier(), !negative, leading, remaining);
return Value.cache(new ValueInterval(valueType, !negative, leading, remaining));
}
}
......@@ -367,16 +367,13 @@ public class ValueLob extends Value {
* except when converting to BLOB or CLOB.
*
* @param t the new type
* @param precision the precision of the column to convert this value to.
* The special constant <code>-1</code> is used to indicate that
* the precision plays no role when converting the value
* @param mode the database mode
* @param column the column (if any), used for to improve the error message if conversion fails
* @param extTypeInfo the extended data type information, or null
* @return the converted value
*/
@Override
public Value convertTo(int t, int precision, Mode mode, Object column, ExtTypeInfo extTypeInfo) {
protected Value convertTo(int t, Mode mode, Object column, ExtTypeInfo extTypeInfo) {
if (t == valueType) {
return this;
} else if (t == Value.CLOB) {
......@@ -384,7 +381,7 @@ public class ValueLob extends Value {
} else if (t == Value.BLOB) {
return ValueLobDb.createTempBlob(getInputStream(), -1, handler);
}
return super.convertTo(t, precision, mode, column, null);
return super.convertTo(t, mode, column, null);
}
@Override
......
......@@ -205,14 +205,13 @@ public class ValueLobDb extends Value {
* except when converting to BLOB or CLOB.
*
* @param t the new type
* @param precision the precision
* @param mode the mode
* @param column the column (if any), used for to improve the error message if conversion fails
* @param extTypeInfo the extended data type information, or null
* @return the converted value
*/
@Override
public Value convertTo(int t, int precision, Mode mode, Object column, ExtTypeInfo extTypeInfo) {
protected Value convertTo(int t, Mode mode, Object column, ExtTypeInfo extTypeInfo) {
if (t == valueType) {
return this;
} else if (t == Value.CLOB) {
......@@ -230,7 +229,7 @@ public class ValueLobDb extends Value {
return ValueLobDb.createSmallLob(t, small);
}
}
return super.convertTo(t, precision, mode, column, null);
return super.convertTo(t, mode, column, null);
}
@Override
......
......@@ -139,7 +139,7 @@ public class ValueNull extends Value {
}
@Override
public Value convertTo(int type, int precision, Mode mode, Object column, ExtTypeInfo extTypeInfo) {
protected Value convertTo(int type, Mode mode, Object column, ExtTypeInfo extTypeInfo) {
return this;
}
......
......@@ -5,8 +5,6 @@
*/
package org.h2.value;
import java.util.Arrays;
import org.h2.engine.Mode;
import org.h2.engine.SysProperties;
import org.h2.util.StringUtils;
......@@ -15,18 +13,6 @@ import org.h2.util.StringUtils;
*/
public class ValueStringFixed extends ValueString {
/**
* Special value for the precision in {@link #get(String, int, Mode)} to indicate that the value
* should <i>not</i> be trimmed.
*/
public static final int PRECISION_DO_NOT_TRIM = Integer.MIN_VALUE;
/**
* Special value for the precision in {@link #get(String, int, Mode)} to indicate
* that the default behaviour should of trimming the value should apply.
*/
public static final int PRECISION_TRIM = -1;
private static final ValueStringFixed EMPTY = new ValueStringFixed("");
protected ValueStringFixed(String value) {
......@@ -36,6 +22,7 @@ public class ValueStringFixed extends ValueString {
private static String trimRight(String s) {
return trimRight(s, 0);
}
private static String trimRight(String s, int minLength) {
int endIndex = s.length() - 1;
int i = endIndex;
......@@ -46,17 +33,6 @@ public class ValueStringFixed extends ValueString {
return s;
}
private static String rightPadWithSpaces(String s, int length) {
int used = s.length();
if (length <= used) {
return s;
}
char[] res = new char[length];
s.getChars(0, used, res, 0);
Arrays.fill(res, used, length, ' ');
return new String(res);
}
@Override
public int getValueType() {
return STRING_FIXED;
......@@ -70,45 +46,7 @@ public class ValueStringFixed extends ValueString {
* @return the value
*/
public static ValueStringFixed get(String s) {
// Use the special precision constant PRECISION_TRIM to indicate
// default H2 behaviour of trimming the value.
return get(s, PRECISION_TRIM, null);
}
/**
* Get or create a fixed length string value for the given string.
* <p>
* This method will use a {@link Mode}-specific conversion when <code>mode</code> is not
* <code>null</code>.
* Otherwise it will use the default H2 behaviour of trimming the given string if
* <code>precision</code> is not {@link #PRECISION_DO_NOT_TRIM}.
*
* @param s the string
* @param precision if the {@link Mode#padFixedLengthStrings} indicates that strings should
* be padded, this defines the overall length of the (potentially padded) string.
* If the special constant {@link #PRECISION_DO_NOT_TRIM} is used the value will
* not be trimmed.
* @param mode the database mode
* @return the value
*/
public static ValueStringFixed get(String s, int precision, Mode mode) {
// Should fixed strings be padded?
if (mode != null && mode.padFixedLengthStrings) {
if (precision == Integer.MAX_VALUE) {
// CHAR without a length specification is identical to CHAR(1)
precision = 1;
}
if (s.length() < precision) {
// We have to pad
s = rightPadWithSpaces(s, precision);
} else {
// We should trim, because inserting 'A ' into a CHAR(1) is possible!
s = trimRight(s, precision);
}
} else if (precision != PRECISION_DO_NOT_TRIM) {
// Default behaviour of H2
s = trimRight(s);
}
s = trimRight(s);
int length = s.length();
if (length == 0) {
return EMPTY;
......
......@@ -39,16 +39,6 @@ public class ValueTime extends Value {
*/
public static final int MAXIMUM_SCALE = 9;
/**
* Get display size for the specified scale.
*
* @param scale scale
* @return display size
*/
public static int getDisplaySize(int scale) {
return scale == 0 ? 8 : 9 + scale;
}
/**
* Nanoseconds since midnight
*/
......
......@@ -40,16 +40,6 @@ public class ValueTimestamp extends Value {
*/
public static final int MAXIMUM_SCALE = 9;
/**
* Get display size for the specified scale.
*
* @param scale scale
* @return display size
*/
public static int getDisplaySize(int scale) {
return scale == 0 ? 19 : 20 + scale;
}
/**
* A bit field with bits for the year, month, and day (see DateTimeUtils for
* encoding)
......
......@@ -45,16 +45,6 @@ public class ValueTimestampTimeZone extends Value {
*/
static final int MAXIMUM_SCALE = ValueTimestamp.MAXIMUM_SCALE;
/**
* Get display size for the specified scale.
*
* @param scale scale
* @return display size
*/
public static int getDisplaySize(int scale) {
return scale == 0 ? 25 : 26 + scale;
}
/**
* A bit field with bits for the year, month, and day (see DateTimeUtils for
* encoding)
......
......@@ -356,7 +356,7 @@ public class TestCases extends TestDb {
Statement stat = conn.createStatement();
stat.execute("create table test as select cast(0 as dec(10, 2)) x");
ResultSetMetaData meta = stat.executeQuery("select * from test").getMetaData();
assertEquals(2, meta.getPrecision(1));
assertEquals(10, meta.getPrecision(1));
assertEquals(2, meta.getScale(1));
stat.execute("alter table test add column y int");
stat.execute("drop table test");
......
......@@ -337,6 +337,7 @@ public class TestCompatibility extends TestDb {
ResultSet rs = stat.executeQuery("SELECT B FROM TEST2");
assertTrue(rs.next());
assertEquals(bytes, rs.getBytes(1));
assertEquals(bytes, rs.getBytes("B"));
assertEquals(1, stat.executeUpdate("UPDATE TEST2 SET C = B"));
testMySQLBytesCheck(stat, string, bytes);
PreparedStatement prep = conn.prepareStatement("UPDATE TEST2 SET C = ?");
......@@ -452,6 +453,7 @@ public class TestCompatibility extends TestDb {
assertTrue(rs.next());
assertEquals(string, rs.getString(1));
assertEquals(bytes, rs.getBytes(1));
assertEquals(bytes, rs.getBytes("C"));
}
private void testSybaseAndMSSQLServer() throws SQLException {
......
......@@ -391,7 +391,7 @@ public class TestCustomDataTypesHandler extends TestDb {
}
@Override
public Value convertTo(int targetType, int precision, Mode mode, Object column, ExtTypeInfo extTypeInfo) {
protected Value convertTo(int targetType, Mode mode, Object column, ExtTypeInfo extTypeInfo) {
if (getValueType() == targetType) {
return this;
}
......
......@@ -17,3 +17,59 @@ SELECT COLUMN_NAME, DATA_TYPE, TYPE_NAME, COLUMN_TYPE FROM INFORMATION_SCHEMA.CO
DROP TABLE TEST;
> ok
CREATE TABLE TEST(C CHAR(2));
> ok
INSERT INTO TEST VALUES 'aa', 'b';
> update count: 2
SELECT * FROM TEST WHERE C = 'b';
>> b
SELECT * FROM TEST WHERE C = 'b ';
>> b
SELECT * FROM TEST WHERE C = 'b ';
>> b
SELECT C || 'x' V FROM TEST;
> V
> ---
> aax
> bx
> rows: 2
DROP TABLE TEST;
> ok
SET MODE PostgreSQL;
> ok
CREATE TABLE TEST(C CHAR(2));
> ok
INSERT INTO TEST VALUES 'aa', 'b';
> update count: 2
SELECT * FROM TEST WHERE C = 'b';
>> b
SELECT * FROM TEST WHERE C = 'b ';
>> b
SELECT * FROM TEST WHERE C = 'b ';
>> b
SELECT C || 'x' V FROM TEST;
> V
> ---
> aax
> bx
> rows: 2
DROP TABLE TEST;
> ok
SET MODE Regular;
> ok
......@@ -253,8 +253,8 @@ SELECT * FROM V3;
SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME = 'E' ORDER BY TABLE_NAME;
> TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION DOMAIN_CATALOG DOMAIN_SCHEMA DOMAIN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_PRECISION_RADIX NUMERIC_SCALE DATETIME_PRECISION INTERVAL_TYPE INTERVAL_PRECISION CHARACTER_SET_NAME COLLATION_NAME TYPE_NAME NULLABLE IS_COMPUTED SELECTIVITY CHECK_CONSTRAINT SEQUENCE_NAME REMARKS SOURCE_DATA_TYPE COLUMN_TYPE COLUMN_ON_UPDATE IS_VISIBLE
> ------------- ------------ ---------- ----------- ---------------- -------------- ------------- ----------- -------------- ----------- --------- ------------------------ ---------------------- ----------------- ----------------------- ------------- ------------------ ------------- ------------------ ------------------ -------------- --------- -------- ----------- ----------- ---------------- ------------- ------- ---------------- -------------- ---------------- ----------
> SCRIPT PUBLIC TEST E 1 null null null null YES 1111 2147483647 2147483647 2147483647 10 0 null null null Unicode OFF ENUM 1 FALSE 50 null null ENUM('A', 'B') null TRUE
> SCRIPT PUBLIC V E 1 null null null null YES 1111 2147483647 2147483647 2147483647 10 0 null null null Unicode OFF ENUM 1 FALSE 50 null null ENUM('A', 'B') null TRUE
> SCRIPT PUBLIC TEST E 1 null null null null YES 1111 10 10 10 10 0 null null null Unicode OFF ENUM 1 FALSE 50 null null ENUM('A', 'B') null TRUE
> SCRIPT PUBLIC V E 1 null null null null YES 1111 10 10 10 10 0 null null null Unicode OFF ENUM 1 FALSE 50 null null ENUM('A', 'B') null TRUE
> SCRIPT PUBLIC V1 E 1 null null null null YES 4 10 10 10 10 0 null null null Unicode OFF INTEGER 1 FALSE 50 null null INTEGER null TRUE
> SCRIPT PUBLIC V2 E 1 null null null null YES 4 10 10 10 10 0 null null null Unicode OFF INTEGER 1 FALSE 50 null null INTEGER null TRUE
> SCRIPT PUBLIC V3 E 1 null null null null YES 4 10 10 10 10 0 null null null Unicode OFF INTEGER 1 FALSE 50 null null INTEGER null TRUE
......
......@@ -6084,18 +6084,18 @@ SELECT ID FROM TEST WHERE XVI LIKE 'abc%';
> 3
> rows: 1
SELECT 'abc', 'Papa Joe''s', CAST(-1 AS SMALLINT), CAST(2 AS BIGINT), CAST(0 AS DOUBLE), CAST('0a0f' AS BINARY), CAST(125 AS TINYINT), TRUE, FALSE FROM TEST WHERE ID=1;
> 'abc' 'Papa Joe''s' -1 2 0.0 X'0a0f' 125 TRUE FALSE
> ----- ------------- -- - --- ------- --- ---- -----
> abc Papa Joe's -1 2 0.0 0a0f 125 TRUE FALSE
SELECT 'abc', 'Papa Joe''s', CAST(-1 AS SMALLINT), CAST(2 AS BIGINT), CAST(0 AS DOUBLE), CAST('0a0f' AS BINARY) B, CAST(125 AS TINYINT), TRUE, FALSE FROM TEST WHERE ID=1;
> 'abc' 'Papa Joe''s' -1 2 0.0 B 125 TRUE FALSE
> ----- ------------- -- - --- ---- --- ---- -----
> abc Papa Joe's -1 2 0.0 0a0f 125 TRUE FALSE
> rows: 1
-- ' This apostrophe is here to fix syntax highlighting in the text editors.
SELECT CAST('abcd' AS VARCHAR(255)), CAST('ef_gh' AS VARCHAR(3));
> 'abcd' 'ef_'
> ------ -----
> abcd ef_
SELECT CAST('abcd' AS VARCHAR(255)) C1, CAST('ef_gh' AS VARCHAR(3)) C2;
> C1 C2
> ---- ---
> abcd ef_
> rows: 1
DROP TABLE TEST;
......
......@@ -414,7 +414,11 @@ public class TestValue extends TestDb {
ValueJavaObject voString = ValueJavaObject.getNoCopy(
new String("This is not a ValueUuid object"), null, null);
assertThrows(DbException.class, voString).convertTo(Value.UUID);
try {
voString.convertTo(Value.UUID);
fail();
} catch (DbException expected) {
}
}
private void testModulusDouble() {
......
......@@ -806,4 +806,4 @@ econd irst bcef ordinality nord unnest
analyst occupation distributive josaph aor engineer sajeewa isuru randil kevin doctor businessman artist ashan
corrupts splitted disruption unintentional octets preconditions predicates subq objectweb insn opcodes
preserves masking holder unboxing avert iae transformed subtle reevaluate exclusions subclause ftbl rgr
presorted inclusion contexts
presorted inclusion contexts aax
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论