Skip to content

Commit

Permalink
#3121 BindMaxLength validation
Browse files Browse the repository at this point in the history
At deploy time derive a BindMaxLength property to use
per BeanProperty
  • Loading branch information
rbygrave committed Jun 26, 2023
1 parent 4683877 commit df7b048
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
*
* @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) {
Expand Down
26 changes: 13 additions & 13 deletions ebean-core/src/main/java/io/ebeaninternal/server/bind/DataBind.java
Original file line number Diff line number Diff line change
Expand Up @@ -132,37 +132,37 @@ public void setString(String value) throws SQLException {
@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
Expand All @@ -173,7 +173,7 @@ public final void setDate(java.sql.Date value) throws SQLException {
} else {
pstmt.setDate(++pos, value);
}
lastObject = value;
lastObject = null;
}

@Override
Expand All @@ -184,7 +184,7 @@ public final void setTimestamp(Timestamp value) throws SQLException {
} else {
pstmt.setTimestamp(++pos, value);
}
lastObject = value;
lastObject = null;
}

@Override
Expand All @@ -195,13 +195,13 @@ public final void setTime(Time value) throws SQLException {
} 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
Expand All @@ -213,13 +213,13 @@ public void setBytes(byte[] value) throws SQLException {
@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
Expand Down Expand Up @@ -252,7 +252,7 @@ public void setClob(String content) throws SQLException {
@Override
public void setArray(String arrayType, Object[] elements) throws SQLException {
pstmt.setArray(++pos, connection.createArrayOf(arrayType, elements));
lastObject = elements;
lastObject = null;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
/**
Expand Down Expand Up @@ -159,6 +160,19 @@ public BeanDescriptorManager(InternalConfiguration config) {
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
Expand Down Expand Up @@ -1502,6 +1516,10 @@ public List<MetaQueryPlan> queryPlanInit(QueryPlanInit request) {
return list;
}

public BindMaxLength bindMaxLength() {
return bindMaxLength;
}

/**
* Comparator to sort the BeanDescriptors by name.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -263,6 +264,7 @@ public BeanProperty(BeanDescriptor<?> descriptor, DeployBeanProperty deploy) {
}
this.jsonSerialize = deploy.isJsonSerialize();
this.jsonDeserialize = deploy.isJsonDeserialize();
this.bindMaxLength = deploy.bindMaxLength();
}

private String tableAliasIntern(BeanDescriptor<?> descriptor, String s, boolean dbEncrypted, String dbColumn) {
Expand Down Expand Up @@ -350,6 +352,7 @@ protected BeanProperty(BeanProperty source, BeanPropertyOverride override) {
this.elPlaceHolderEncrypted = override.replace(source.elPlaceHolderEncrypted, source.dbColumn);
this.jsonSerialize = source.jsonSerialize;
this.jsonDeserialize = source.jsonDeserialize;
this.bindMaxLength = source.bindMaxLength;
}

/**
Expand Down Expand Up @@ -559,20 +562,16 @@ public Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException {
@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 + ")");
}
}
}
Expand All @@ -586,43 +585,6 @@ public Object readData(DataInput dataInput) throws IOException {
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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ public DeployBeanDescriptor(BeanDescriptorManager manager, Class<T> beanType, Da
this.beanType = beanType;
}

public BindMaxLength bindMaxLength() {
return manager.bindMaxLength();
}

/**
* Set the IdClass to use.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -1133,4 +1136,20 @@ boolean isJsonMapper() {
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;
}

}
}

0 comments on commit df7b048

Please sign in to comment.