提交 a9030639 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Fix CHAR data type in PostgreSQL mode

上级 32e51b75
...@@ -18,6 +18,7 @@ import org.h2.message.DbException; ...@@ -18,6 +18,7 @@ import org.h2.message.DbException;
import org.h2.message.Trace; import org.h2.message.Trace;
import org.h2.result.ResultInterface; import org.h2.result.ResultInterface;
import org.h2.result.ResultWithGeneratedKeys; import org.h2.result.ResultWithGeneratedKeys;
import org.h2.result.ResultWithPaddedStrings;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
/** /**
...@@ -200,6 +201,9 @@ public abstract class Command implements CommandInterface { ...@@ -200,6 +201,9 @@ public abstract class Command implements CommandInterface {
try { try {
ResultInterface result = query(maxrows); ResultInterface result = query(maxrows);
callStop = !result.isLazy(); callStop = !result.isLazy();
if (database.getMode().padFixedLengthStrings) {
return ResultWithPaddedStrings.get(result);
}
return result; return result;
} catch (DbException e) { } catch (DbException e) {
start = filterConcurrentUpdate(e, start); start = filterConcurrentUpdate(e, start);
......
/*
* 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.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;
}
}
...@@ -622,7 +622,7 @@ public class Transfer { ...@@ -622,7 +622,7 @@ public class Transfer {
case Value.STRING_IGNORECASE: case Value.STRING_IGNORECASE:
return ValueStringIgnoreCase.get(readString()); return ValueStringIgnoreCase.get(readString());
case Value.STRING_FIXED: case Value.STRING_FIXED:
return ValueStringFixed.get(readString(), ValueStringFixed.PRECISION_DO_NOT_TRIM, null); return ValueStringFixed.get(readString());
case Value.BLOB: { case Value.BLOB: {
long length = readLong(); long length = readLong();
if (version >= Constants.TCP_PROTOCOL_VERSION_11) { if (version >= Constants.TCP_PROTOCOL_VERSION_11) {
......
...@@ -788,7 +788,7 @@ public abstract class Value extends VersionedValue { ...@@ -788,7 +788,7 @@ public abstract class Value extends VersionedValue {
case STRING_IGNORECASE: case STRING_IGNORECASE:
return convertToStringIgnoreCase(mode); return convertToStringIgnoreCase(mode);
case STRING_FIXED: case STRING_FIXED:
return convertToStringFixed(precision, mode); return convertToStringFixed(mode);
case JAVA_OBJECT: case JAVA_OBJECT:
return convertToJavaObject(); return convertToJavaObject();
case ENUM: case ENUM:
...@@ -1177,14 +1177,14 @@ public abstract class Value extends VersionedValue { ...@@ -1177,14 +1177,14 @@ public abstract class Value extends VersionedValue {
return ValueStringIgnoreCase.get(s); return ValueStringIgnoreCase.get(s);
} }
private ValueString convertToStringFixed(int precision, Mode mode) { private ValueString convertToStringFixed(Mode mode) {
String s; String s;
if (getValueType() == BYTES && mode != null && mode.charToBinaryInUtf8) { if (getValueType() == BYTES && mode != null && mode.charToBinaryInUtf8) {
s = new String(getBytesNoCopy(), StandardCharsets.UTF_8); s = new String(getBytesNoCopy(), StandardCharsets.UTF_8);
} else { } else {
s = getString(); s = getString();
} }
return ValueStringFixed.get(s, precision, mode); return ValueStringFixed.get(s);
} }
private ValueJavaObject convertToJavaObject() { private ValueJavaObject convertToJavaObject() {
......
...@@ -5,8 +5,6 @@ ...@@ -5,8 +5,6 @@
*/ */
package org.h2.value; package org.h2.value;
import java.util.Arrays;
import org.h2.engine.Mode;
import org.h2.engine.SysProperties; import org.h2.engine.SysProperties;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
...@@ -15,18 +13,6 @@ import org.h2.util.StringUtils; ...@@ -15,18 +13,6 @@ import org.h2.util.StringUtils;
*/ */
public class ValueStringFixed extends ValueString { 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(""); private static final ValueStringFixed EMPTY = new ValueStringFixed("");
protected ValueStringFixed(String value) { protected ValueStringFixed(String value) {
...@@ -36,6 +22,7 @@ public class ValueStringFixed extends ValueString { ...@@ -36,6 +22,7 @@ public class ValueStringFixed extends ValueString {
private static String trimRight(String s) { private static String trimRight(String s) {
return trimRight(s, 0); return trimRight(s, 0);
} }
private static String trimRight(String s, int minLength) { private static String trimRight(String s, int minLength) {
int endIndex = s.length() - 1; int endIndex = s.length() - 1;
int i = endIndex; int i = endIndex;
...@@ -46,17 +33,6 @@ public class ValueStringFixed extends ValueString { ...@@ -46,17 +33,6 @@ public class ValueStringFixed extends ValueString {
return s; 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 @Override
public int getValueType() { public int getValueType() {
return STRING_FIXED; return STRING_FIXED;
...@@ -70,45 +46,7 @@ public class ValueStringFixed extends ValueString { ...@@ -70,45 +46,7 @@ public class ValueStringFixed extends ValueString {
* @return the value * @return the value
*/ */
public static ValueStringFixed get(String s) { 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(); int length = s.length();
if (length == 0) { if (length == 0) {
return EMPTY; return EMPTY;
......
...@@ -17,3 +17,59 @@ SELECT COLUMN_NAME, DATA_TYPE, TYPE_NAME, COLUMN_TYPE FROM INFORMATION_SCHEMA.CO ...@@ -17,3 +17,59 @@ SELECT COLUMN_NAME, DATA_TYPE, TYPE_NAME, COLUMN_TYPE FROM INFORMATION_SCHEMA.CO
DROP TABLE TEST; DROP TABLE TEST;
> ok > 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
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论