提交 2eb2a31b authored 作者: httpdigest's avatar httpdigest

Implement right-padding of CHAR(N) datatype in PostgreSQL mode

When using CHAR(N) in PostgreSQL any inserted value smaller than N
characters will be right-padded with spaces to fill all N characters.
See: https://www.postgresql.org/docs/9.1/static/datatype-character.html

This change adds an additional Mode.padFixedStrings and set it to true
in the PostgreSQL Mode. Any value converted to a ValueStringFixed at
INSERT will be right-padded with spaces.

For additional infos see the H2 Google Groups post:
https://groups.google.com/forum/#!topic/h2-database/Luaxdrl137Y
上级 0fb1e332
...@@ -21,7 +21,9 @@ Change Log ...@@ -21,7 +21,9 @@ Change Log
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul> <ul>
<li>- <li>Add padding for CHAR(N) values in PostgreSQL mode
</li>
<li>
</li> </li>
</ul> </ul>
......
...@@ -140,7 +140,7 @@ public class Insert extends Prepared implements ResultTarget { ...@@ -140,7 +140,7 @@ public class Insert extends Prepared implements ResultTarget {
// e can be null (DEFAULT) // e can be null (DEFAULT)
e = e.optimize(session); e = e.optimize(session);
try { try {
Value v = c.convert(e.getValue(session)); Value v = c.convert(e.getValue(session), session.getDatabase().getMode());
newRow.setValue(index, v); newRow.setValue(index, v);
} catch (DbException ex) { } catch (DbException ex) {
throw setRow(ex, x, getSQL(expr)); throw setRow(ex, x, getSQL(expr));
...@@ -186,7 +186,7 @@ public class Insert extends Prepared implements ResultTarget { ...@@ -186,7 +186,7 @@ public class Insert extends Prepared implements ResultTarget {
Column c = columns[j]; Column c = columns[j];
int index = c.getColumnId(); int index = c.getColumnId();
try { try {
Value v = c.convert(values[j]); Value v = c.convert(values[j], session.getDatabase().getMode());
newRow.setValue(index, v); newRow.setValue(index, v);
} catch (DbException ex) { } catch (DbException ex) {
throw setRow(ex, rowNumber, getSQL(values)); throw setRow(ex, rowNumber, getSQL(values));
......
...@@ -160,6 +160,11 @@ public class Mode { ...@@ -160,6 +160,11 @@ public class Mode {
*/ */
public boolean allowAffinityKey; public boolean allowAffinityKey;
/**
* Whether to right-pad fixed strings with spaces.
*/
public boolean padFixedStrings;
private final String name; private final String name;
static { static {
...@@ -256,6 +261,7 @@ public class Mode { ...@@ -256,6 +261,7 @@ public class Mode {
mode.supportedClientInfoPropertiesRegEx = mode.supportedClientInfoPropertiesRegEx =
Pattern.compile("ApplicationName"); Pattern.compile("ApplicationName");
mode.prohibitEmptyInPredicate = true; mode.prohibitEmptyInPredicate = true;
mode.padFixedStrings = true;
add(mode); add(mode);
mode = new Mode("Ignite"); mode = new Mode("Ignite");
......
...@@ -13,6 +13,7 @@ import org.h2.index.IndexCondition; ...@@ -13,6 +13,7 @@ import org.h2.index.IndexCondition;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.table.ColumnResolver; import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.util.MathUtils;
import org.h2.util.New; import org.h2.util.New;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueBoolean; import org.h2.value.ValueBoolean;
...@@ -201,7 +202,7 @@ public class Comparison extends Condition { ...@@ -201,7 +202,7 @@ public class Comparison extends Condition {
// to constant type, but vise versa, then let's do this here // to constant type, but vise versa, then let's do this here
// once. // once.
if (constType != resType) { if (constType != resType) {
right = ValueExpression.get(r.convertTo(resType)); right = ValueExpression.get(r.convertTo(resType, MathUtils.convertLongToInt(left.getPrecision()), session.getDatabase().getMode()));
} }
} else if (right instanceof Parameter) { } else if (right instanceof Parameter) {
((Parameter) right).setColumn( ((Parameter) right).setColumn(
......
...@@ -163,8 +163,21 @@ public class Column { ...@@ -163,8 +163,21 @@ public class Column {
* @return the value * @return the value
*/ */
public Value convert(Value v) { public Value convert(Value v) {
return convert(v, null);
}
/**
* Convert a value to this column's type using the given {@link Mode}.
* <p>
* Use this method in case the conversion is Mode-dependent.
*
* @param v the value
* @param mode the database {@link Mode} to use
* @return the value
*/
public Value convert(Value v, Mode mode) {
try { try {
return v.convertTo(type); return v.convertTo(type, MathUtils.convertLongToInt(precision), mode);
} catch (DbException e) { } catch (DbException e) {
if (e.getErrorCode() == ErrorCode.DATA_CONVERSION_ERROR_1) { if (e.getErrorCode() == ErrorCode.DATA_CONVERSION_ERROR_1) {
String target = (table == null ? "" : table.getName() + ": ") + String target = (table == null ? "" : table.getName() + ": ") +
......
...@@ -615,7 +615,7 @@ public class Transfer { ...@@ -615,7 +615,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()); return ValueStringFixed.get(readString(), ValueStringFixed.PRECISION_DO_NOT_TRIM, null);
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) {
......
...@@ -20,6 +20,7 @@ import java.sql.Timestamp; ...@@ -20,6 +20,7 @@ import java.sql.Timestamp;
import java.sql.Types; import java.sql.Types;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.engine.Mode;
import org.h2.engine.SysProperties; import org.h2.engine.SysProperties;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.store.DataHandler; import org.h2.store.DataHandler;
...@@ -534,13 +535,17 @@ public abstract class Value { ...@@ -534,13 +535,17 @@ public abstract class Value {
throw throwUnsupportedExceptionForType("%"); throw throwUnsupportedExceptionForType("%");
} }
public Value convertTo(int targetType) {
return convertTo(targetType, -1, null);
}
/** /**
* Compare a value to the specified type. * Compare a value to the specified type.
* *
* @param targetType the type of the returned value * @param targetType the type of the returned value
* @return the converted value * @return the converted value
*/ */
public Value convertTo(int targetType) { public Value convertTo(int targetType, int precision, Mode mode) {
// converting NULL is done in ValueNull // converting NULL is done in ValueNull
// converting BLOB to CLOB and vice versa is done in ValueLob // converting BLOB to CLOB and vice versa is done in ValueLob
if (getType() == targetType) { if (getType() == targetType) {
...@@ -962,7 +967,7 @@ public abstract class Value { ...@@ -962,7 +967,7 @@ public abstract class Value {
case STRING_IGNORECASE: case STRING_IGNORECASE:
return ValueStringIgnoreCase.get(s); return ValueStringIgnoreCase.get(s);
case STRING_FIXED: case STRING_FIXED:
return ValueStringFixed.get(s); return ValueStringFixed.get(s, precision, mode);
case DOUBLE: case DOUBLE:
return ValueDouble.get(Double.parseDouble(s.trim())); return ValueDouble.get(Double.parseDouble(s.trim()));
case FLOAT: case FLOAT:
......
...@@ -12,6 +12,8 @@ import java.util.Arrays; ...@@ -12,6 +12,8 @@ import java.util.Arrays;
import com.vividsolutions.jts.geom.CoordinateSequence; import com.vividsolutions.jts.geom.CoordinateSequence;
import com.vividsolutions.jts.geom.CoordinateSequenceFilter; import com.vividsolutions.jts.geom.CoordinateSequenceFilter;
import com.vividsolutions.jts.geom.PrecisionModel; import com.vividsolutions.jts.geom.PrecisionModel;
import org.h2.engine.Mode;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Envelope;
...@@ -272,11 +274,11 @@ public class ValueGeometry extends Value { ...@@ -272,11 +274,11 @@ public class ValueGeometry extends Value {
} }
@Override @Override
public Value convertTo(int targetType) { public Value convertTo(int targetType, int precision, Mode mode) {
if (targetType == Value.JAVA_OBJECT) { if (targetType == Value.JAVA_OBJECT) {
return this; return this;
} }
return super.convertTo(targetType); return super.convertTo(targetType, precision, mode);
} }
/** /**
......
...@@ -14,6 +14,7 @@ import java.io.Reader; ...@@ -14,6 +14,7 @@ import java.io.Reader;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.engine.Mode;
import org.h2.engine.SysProperties; import org.h2.engine.SysProperties;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.mvstore.DataUtils; import org.h2.mvstore.DataUtils;
...@@ -443,7 +444,7 @@ public class ValueLob extends Value { ...@@ -443,7 +444,7 @@ public class ValueLob extends Value {
* @return the converted value * @return the converted value
*/ */
@Override @Override
public Value convertTo(int t) { public Value convertTo(int t, int precision, Mode mode) {
if (t == type) { if (t == type) {
return this; return this;
} else if (t == Value.CLOB) { } else if (t == Value.CLOB) {
...@@ -453,7 +454,7 @@ public class ValueLob extends Value { ...@@ -453,7 +454,7 @@ public class ValueLob extends Value {
ValueLob copy = ValueLob.createBlob(getInputStream(), -1, handler); ValueLob copy = ValueLob.createBlob(getInputStream(), -1, handler);
return copy; return copy;
} }
return super.convertTo(t); return super.convertTo(t, precision, mode);
} }
@Override @Override
......
...@@ -14,6 +14,7 @@ import java.io.Reader; ...@@ -14,6 +14,7 @@ import java.io.Reader;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.engine.Mode;
import org.h2.engine.SysProperties; import org.h2.engine.SysProperties;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.mvstore.DataUtils; import org.h2.mvstore.DataUtils;
...@@ -184,7 +185,7 @@ public class ValueLobDb extends Value implements Value.ValueClob, ...@@ -184,7 +185,7 @@ public class ValueLobDb extends Value implements Value.ValueClob,
* @return the converted value * @return the converted value
*/ */
@Override @Override
public Value convertTo(int t) { public Value convertTo(int t, int precision, Mode mode) {
if (t == type) { if (t == type) {
return this; return this;
} else if (t == Value.CLOB) { } else if (t == Value.CLOB) {
...@@ -204,7 +205,7 @@ public class ValueLobDb extends Value implements Value.ValueClob, ...@@ -204,7 +205,7 @@ public class ValueLobDb extends Value implements Value.ValueClob,
return ValueLobDb.createSmallLob(t, small); return ValueLobDb.createSmallLob(t, small);
} }
} }
return super.convertTo(t); return super.convertTo(t, precision, mode);
} }
@Override @Override
......
...@@ -14,6 +14,7 @@ import java.sql.SQLException; ...@@ -14,6 +14,7 @@ import java.sql.SQLException;
import java.sql.Time; import java.sql.Time;
import java.sql.Timestamp; import java.sql.Timestamp;
import org.h2.engine.Mode;
import org.h2.message.DbException; import org.h2.message.DbException;
/** /**
...@@ -132,7 +133,7 @@ public class ValueNull extends Value { ...@@ -132,7 +133,7 @@ public class ValueNull extends Value {
} }
@Override @Override
public Value convertTo(int type) { public Value convertTo(int type, int precision, Mode mode) {
return this; return this;
} }
......
...@@ -5,6 +5,9 @@ ...@@ -5,6 +5,9 @@
*/ */
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;
...@@ -13,6 +16,18 @@ import org.h2.util.StringUtils; ...@@ -13,6 +16,18 @@ 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) {
...@@ -29,6 +44,19 @@ public class ValueStringFixed extends ValueString { ...@@ -29,6 +44,19 @@ public class ValueStringFixed extends ValueString {
return s; return s;
} }
private static String rightPadWithSpaces(String s, int length) {
int pad = length - s.length();
if (pad <= 0) {
return s;
}
char[] res = new char[length];
for (int i = 0; i < s.length(); i++) {
res[i] = s.charAt(i);
}
Arrays.fill(res, s.length(), length, ' ');
return new String(res);
}
@Override @Override
public int getType() { public int getType() {
return Value.STRING_FIXED; return Value.STRING_FIXED;
...@@ -42,7 +70,14 @@ public class ValueStringFixed extends ValueString { ...@@ -42,7 +70,14 @@ public class ValueStringFixed extends ValueString {
* @return the value * @return the value
*/ */
public static ValueStringFixed get(String s) { public static ValueStringFixed get(String s) {
s = trimRight(s); return get(s, PRECISION_TRIM, null);
}
public static ValueStringFixed get(String s, int precision, Mode mode) {
if (mode != null && mode.padFixedStrings && precision < Integer.MAX_VALUE) {
s = rightPadWithSpaces(s, precision);
} else if (precision != PRECISION_DO_NOT_TRIM) {
s = trimRight(s);
}
if (s.length() == 0) { if (s.length() == 0) {
return EMPTY; return EMPTY;
} }
......
...@@ -245,6 +245,14 @@ public class TestCompatibility extends TestBase { ...@@ -245,6 +245,14 @@ public class TestCompatibility extends TestBase {
assertResult("ABC", stat, "SELECT SUBSTRING('ABCDEF' FOR 3)"); assertResult("ABC", stat, "SELECT SUBSTRING('ABCDEF' FOR 3)");
assertResult("ABCD", stat, "SELECT SUBSTRING('0ABCDEF' FROM 2 FOR 4)"); assertResult("ABCD", stat, "SELECT SUBSTRING('0ABCDEF' FROM 2 FOR 4)");
/* Test right-padding of CHAR(N) at INSERT */
stat.execute("CREATE TABLE TEST(CH CHAR(10))");
stat.execute("INSERT INTO TEST (CH) VALUES ('Hello')");
assertResult("Hello ", stat, "SELECT CH FROM TEST");
/* Test that WHERE clauses accept unpadded values and will pad before comparison */
assertResult("Hello ", stat, "SELECT CH FROM TEST WHERE CH = 'Hello'");
} }
private void testMySQL() throws SQLException { private void testMySQL() throws SQLException {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论