mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#3121 BindMaxLength validation
At deploy time derive a BindMaxLength property to use per BeanProperty
This commit is contained in:
@@ -7,9 +7,9 @@ import java.io.InputStream;
|
||||
*
|
||||
* @author Roland Praml, FOCONIS AG
|
||||
*/
|
||||
public class InputStreamInfo {
|
||||
private final InputStream stream;
|
||||
public final class InputStreamInfo {
|
||||
|
||||
private final InputStream stream;
|
||||
private final long length;
|
||||
|
||||
public InputStreamInfo(InputStream stream, long length) {
|
||||
|
||||
@@ -132,37 +132,37 @@ public class DataBind implements DataBinder {
|
||||
@Override
|
||||
public final void setInt(int value) throws SQLException {
|
||||
pstmt.setInt(++pos, value);
|
||||
lastObject = value;
|
||||
lastObject = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setLong(long value) throws SQLException {
|
||||
pstmt.setLong(++pos, value);
|
||||
lastObject = value;
|
||||
lastObject = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setShort(short value) throws SQLException {
|
||||
pstmt.setShort(++pos, value);
|
||||
lastObject = value;
|
||||
lastObject = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setFloat(float value) throws SQLException {
|
||||
pstmt.setFloat(++pos, value);
|
||||
lastObject = value;
|
||||
lastObject = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setDouble(double value) throws SQLException {
|
||||
pstmt.setDouble(++pos, value);
|
||||
lastObject = value;
|
||||
lastObject = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setBigDecimal(BigDecimal value) throws SQLException {
|
||||
pstmt.setBigDecimal(++pos, value);
|
||||
lastObject = value;
|
||||
lastObject = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -173,7 +173,7 @@ public class DataBind implements DataBinder {
|
||||
} else {
|
||||
pstmt.setDate(++pos, value);
|
||||
}
|
||||
lastObject = value;
|
||||
lastObject = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -184,7 +184,7 @@ public class DataBind implements DataBinder {
|
||||
} else {
|
||||
pstmt.setTimestamp(++pos, value);
|
||||
}
|
||||
lastObject = value;
|
||||
lastObject = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -195,13 +195,13 @@ public class DataBind implements DataBinder {
|
||||
} else {
|
||||
pstmt.setTime(++pos, value);
|
||||
}
|
||||
lastObject = value;
|
||||
lastObject = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoolean(boolean value) throws SQLException {
|
||||
pstmt.setBoolean(++pos, value);
|
||||
lastObject = value;
|
||||
lastObject = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -213,13 +213,13 @@ public class DataBind implements DataBinder {
|
||||
@Override
|
||||
public void setByte(byte value) throws SQLException {
|
||||
pstmt.setByte(++pos, value);
|
||||
lastObject = value;
|
||||
lastObject = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChar(char value) throws SQLException {
|
||||
pstmt.setString(++pos, String.valueOf(value));
|
||||
lastObject = value;
|
||||
lastObject = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -252,7 +252,7 @@ public class DataBind implements DataBinder {
|
||||
@Override
|
||||
public void setArray(String arrayType, Object[] elements) throws SQLException {
|
||||
pstmt.setArray(++pos, connection.createArrayOf(arrayType, elements));
|
||||
lastObject = elements;
|
||||
lastObject = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -105,6 +105,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
private final String asOfViewSuffix;
|
||||
private final boolean jacksonCorePresent;
|
||||
private final int queryPlanTTLSeconds;
|
||||
private final BindMaxLength bindMaxLength;
|
||||
private int entityBeanCount;
|
||||
private List<BeanDescriptor<?>> immutableDescriptorList;
|
||||
/**
|
||||
@@ -159,6 +160,19 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
this.changeLogListener = config.changeLogListener(bootupClasses.getChangeLogListener());
|
||||
this.changeLogRegister = config.changeLogRegister(bootupClasses.getChangeLogRegister());
|
||||
this.jacksonCorePresent = config.isJacksonCorePresent();
|
||||
this.bindMaxLength = initMaxLength();
|
||||
}
|
||||
|
||||
BindMaxLength initMaxLength() {
|
||||
LengthCheck lengthCheck = this.config.getLengthCheck();
|
||||
switch (lengthCheck) {
|
||||
case OFF:
|
||||
return null;
|
||||
case UTF8:
|
||||
return BindMaxLength.ofUtf8();
|
||||
default:
|
||||
return BindMaxLength.ofStandard();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1502,6 +1516,10 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
return list;
|
||||
}
|
||||
|
||||
public BindMaxLength bindMaxLength() {
|
||||
return bindMaxLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparator to sort the BeanDescriptors by name.
|
||||
*/
|
||||
|
||||
@@ -171,6 +171,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
*/
|
||||
private final String dbComment;
|
||||
private final DbEncryptFunction dbEncryptFunction;
|
||||
private final BindMaxLength bindMaxLength;
|
||||
private int deployOrder;
|
||||
final boolean jsonSerialize;
|
||||
final boolean jsonDeserialize;
|
||||
@@ -263,6 +264,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
}
|
||||
this.jsonSerialize = deploy.isJsonSerialize();
|
||||
this.jsonDeserialize = deploy.isJsonDeserialize();
|
||||
this.bindMaxLength = deploy.bindMaxLength();
|
||||
}
|
||||
|
||||
private String tableAliasIntern(BeanDescriptor<?> descriptor, String s, boolean dbEncrypted, String dbColumn) {
|
||||
@@ -350,6 +352,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
this.elPlaceHolderEncrypted = override.replace(source.elPlaceHolderEncrypted, source.dbColumn);
|
||||
this.jsonSerialize = source.jsonSerialize;
|
||||
this.jsonDeserialize = source.jsonDeserialize;
|
||||
this.bindMaxLength = source.bindMaxLength;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -559,20 +562,16 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void bind(DataBind b, Object value) throws SQLException {
|
||||
scalarType.bind(b, value);
|
||||
|
||||
if (needsLengthCheck()) {
|
||||
LengthCheck lengthCheck = descriptor().config().getLengthCheck();
|
||||
if (lengthCheck != LengthCheck.OFF) {
|
||||
Object obj = b.popLastObject();
|
||||
long l = getLength(obj, lengthCheck == LengthCheck.UTF8);
|
||||
if (l > dbLength) {
|
||||
b.closeInputStreams();
|
||||
String s = String.valueOf(value); // take original bind value here.
|
||||
if (s.length() > 100) {
|
||||
s = s.substring(0, 97) + "...";
|
||||
}
|
||||
throw new DataIntegrityException("Cannot bind value '" + s + "' (effective length=" + l + ") to column '" + dbColumn + "' (length=" + dbLength + ")");
|
||||
if (bindMaxLength != null) {
|
||||
Object obj = b.popLastObject();
|
||||
long length = bindMaxLength.length(dbLength, obj);
|
||||
if (length > dbLength) {
|
||||
b.closeInputStreams();
|
||||
String s = String.valueOf(value); // take original bind value here.
|
||||
if (s.length() > 50) {
|
||||
s = s.substring(0, 47) + "...";
|
||||
}
|
||||
throw new DataIntegrityException("Cannot bind value '" + s + "' (effective length=" + length + ") to column '" + dbColumn + "' (length=" + dbLength + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -586,43 +585,6 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
return scalarType.readData(dataInput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns, if this property needs a length check.
|
||||
*/
|
||||
boolean needsLengthCheck() {
|
||||
if (dbLength == 0) {
|
||||
return false;
|
||||
}
|
||||
switch (dbType) {
|
||||
case Types.VARCHAR:
|
||||
case Types.BLOB:
|
||||
case ExtraDbTypes.JSON:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of <code>obj</code>. Note: for UTF8 strings -1 will be retuned, if the string length is lower than 1/4th of db length
|
||||
*/
|
||||
private long getLength(Object obj, boolean utf8) {
|
||||
if (obj instanceof String) {
|
||||
String s = (String) obj;
|
||||
if (utf8) {
|
||||
return s.length() * 4 <= dbLength ? -1 : s.getBytes(StandardCharsets.UTF_8).length;
|
||||
} else {
|
||||
return s.length();
|
||||
}
|
||||
} else if (obj instanceof byte[]) {
|
||||
return ((byte[]) obj).length;
|
||||
} else if (obj instanceof InputStreamInfo) {
|
||||
return ((InputStreamInfo) obj).length();
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanProperty beanProperty() {
|
||||
return this;
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.core.type.InputStreamInfo;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public interface BindMaxLength {
|
||||
|
||||
/**
|
||||
* Return a UTF8 based implementation.
|
||||
*/
|
||||
static BindMaxLength ofUtf8() {
|
||||
return new UTF8();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a standard implementation.
|
||||
*/
|
||||
static BindMaxLength ofStandard() {
|
||||
return new Standard();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the length of the object.
|
||||
*/
|
||||
long length(int dbLength, Object obj);
|
||||
|
||||
final class UTF8 implements BindMaxLength {
|
||||
|
||||
@Override
|
||||
public long length(int dbLength, Object obj) {
|
||||
if (obj instanceof String) {
|
||||
String s = (String) obj;
|
||||
int stringLength = s.length();
|
||||
if (stringLength > dbLength) {
|
||||
return stringLength;
|
||||
} else if (stringLength * 4 <= dbLength) {
|
||||
return -1;
|
||||
} else {
|
||||
return s.getBytes(StandardCharsets.UTF_8).length;
|
||||
}
|
||||
|
||||
} else if (obj instanceof byte[]) {
|
||||
return ((byte[]) obj).length;
|
||||
} else if (obj instanceof InputStreamInfo) {
|
||||
return ((InputStreamInfo) obj).length();
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class Standard implements BindMaxLength {
|
||||
|
||||
@Override
|
||||
public long length(int dbLength, Object obj) {
|
||||
if (obj instanceof String) {
|
||||
return ((String) obj).length();
|
||||
} else if (obj instanceof byte[]) {
|
||||
return ((byte[]) obj).length;
|
||||
} else if (obj instanceof InputStreamInfo) {
|
||||
return ((InputStreamInfo) obj).length();
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,6 +139,10 @@ public class DeployBeanDescriptor<T> {
|
||||
this.beanType = beanType;
|
||||
}
|
||||
|
||||
public BindMaxLength bindMaxLength() {
|
||||
return manager.bindMaxLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the IdClass to use.
|
||||
*/
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
package io.ebeaninternal.server.deploy.meta;
|
||||
|
||||
import io.avaje.lang.Nullable;
|
||||
import io.ebean.annotation.*;
|
||||
import io.ebean.config.ScalarTypeConverter;
|
||||
import io.ebean.config.dbplatform.DbDefaultValue;
|
||||
import io.ebean.config.dbplatform.DbEncrypt;
|
||||
import io.ebean.config.dbplatform.DbEncryptFunction;
|
||||
import io.ebean.config.dbplatform.ExtraDbTypes;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
import io.ebean.util.AnnotationUtil;
|
||||
import io.ebeaninternal.server.core.InternString;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BindMaxLength;
|
||||
import io.ebeaninternal.server.deploy.DbMigrationInfo;
|
||||
import io.ebeaninternal.server.deploy.DeployDocPropertyOptions;
|
||||
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
|
||||
@@ -1133,4 +1136,20 @@ public class DeployBeanProperty {
|
||||
boolean isJsonType() {
|
||||
return mutationDetection != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BindMaxLength bindMaxLength() {
|
||||
if (dbLength == 0) {
|
||||
return null;
|
||||
}
|
||||
switch (dbType) {
|
||||
case Types.VARCHAR:
|
||||
case Types.BLOB:
|
||||
case ExtraDbTypes.JSON:
|
||||
return desc.bindMaxLength();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user