Skip to content

Commit

Permalink
feat: support Decimal($p, $s) (#162)
Browse files Browse the repository at this point in the history
  • Loading branch information
KirillKurdyukov authored Nov 8, 2024
1 parent 456abee commit e940f1d
Show file tree
Hide file tree
Showing 9 changed files with 432 additions and 20 deletions.
6 changes: 4 additions & 2 deletions hibernate-dialect/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@
<junit5.version>5.9.3</junit5.version>
<log4j2.version>2.17.2</log4j2.version>

<ydb.sdk.version>2.2.6</ydb.sdk.version>
<ydb.jdbc.version>2.2.4</ydb.jdbc.version>
<ydb.sdk.version>2.3.4</ydb.sdk.version>
<ydb.jdbc.version>2.3.3</ydb.jdbc.version>
</properties>

<licenses>
Expand Down Expand Up @@ -145,6 +145,8 @@
<configuration>
<environmentVariables>
<TESTCONTAINERS_REUSE_ENABLE>true</TESTCONTAINERS_REUSE_ENABLE>
<YDB_DOCKER_FEATURE_FLAGS>enable_parameterized_decimal</YDB_DOCKER_FEATURE_FLAGS>
<YDB_DOCKER_IMAGE>cr.yandex/yc/yandex-docker-local-ydb:trunk</YDB_DOCKER_IMAGE>
</environmentVariables>
</configuration>
</plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import org.hibernate.boot.model.FunctionContributions;
import org.hibernate.boot.model.TypeContributions;
import org.hibernate.dialect.Dialect;
Expand Down Expand Up @@ -51,33 +52,40 @@
import static org.hibernate.type.SqlTypes.VARBINARY;
import static org.hibernate.type.SqlTypes.VARCHAR;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
import tech.ydb.hibernate.dialect.code.YdbJdbcCode;
import static tech.ydb.hibernate.dialect.code.YdbJdbcCode.DECIMAL_SHIFT;
import tech.ydb.hibernate.dialect.exporter.EmptyExporter;
import tech.ydb.hibernate.dialect.exporter.YdbIndexExporter;
import tech.ydb.hibernate.dialect.hint.IndexQueryHintHandler;
import tech.ydb.hibernate.dialect.hint.QueryHintHandler;
import tech.ydb.hibernate.dialect.hint.ScanQueryHintHandler;
import tech.ydb.hibernate.dialect.translator.YdbSqlAstTranslatorFactory;
import tech.ydb.hibernate.dialect.types.BigDecimalJavaType;
import tech.ydb.hibernate.dialect.types.DecimalJdbcType;
import tech.ydb.hibernate.dialect.types.InstantJavaType;
import tech.ydb.hibernate.dialect.types.InstantJdbcType;
import tech.ydb.hibernate.dialect.types.LocalDateJavaType;
import tech.ydb.hibernate.dialect.types.LocalDateJdbcType;
import tech.ydb.hibernate.dialect.types.LocalDateTimeJavaType;
import tech.ydb.hibernate.dialect.types.LocalDateTimeJdbcType;
import static tech.ydb.hibernate.dialect.types.LocalDateTimeJdbcType.JDBC_TYPE_DATETIME_CODE;
import tech.ydb.hibernate.dialect.types.Uint8JdbcType;

/**
* @author Kirill Kurdyukov
*/
public class YdbDialect extends Dialect {

private static final Exporter<ForeignKey> FOREIGN_KEY_EMPTY_EXPORTER = new EmptyExporter<>();
private static final Exporter<Constraint> UNIQUE_KEY_EMPTY_EXPORTER = new EmptyExporter<>();
private static final List<QueryHintHandler> QUERY_HINT_HANDLERS = List.of(
IndexQueryHintHandler.INSTANCE,
ScanQueryHintHandler.INSTANCE
);
private static final ConcurrentHashMap<Integer, DecimalJdbcType> DECIMAL_JDBC_TYPE_CACHE = new ConcurrentHashMap<>();

public YdbDialect(DialectResolutionInfo dialectResolutionInfo) {
super(dialectResolutionInfo);
Expand All @@ -93,7 +101,7 @@ protected String columnType(int sqlTypeCode) {
case BIGINT -> "Int64";
case REAL, FLOAT -> "Float";
case DOUBLE -> "Double";
case NUMERIC, DECIMAL -> "Decimal (22,9)"; // Fixed
case NUMERIC, DECIMAL -> "Decimal($p, $s)";
case DATE -> "Date";
case JDBC_TYPE_DATETIME_CODE -> "Datetime";
case TIME_WITH_TIMEZONE -> "TzDateTime";
Expand All @@ -117,6 +125,14 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
typeContributions.contributeJdbcType(LocalDateJdbcType.INSTANCE);
typeContributions.contributeJavaType(InstantJavaType.INSTANCE);
typeContributions.contributeJdbcType(InstantJdbcType.INSTANCE);
typeContributions.contributeJdbcType(new DecimalJdbcType(YdbJdbcCode.DECIMAL_22_9));
typeContributions.contributeJdbcType(new DecimalJdbcType(YdbJdbcCode.DECIMAL_31_9));
typeContributions.contributeJdbcType(new DecimalJdbcType(YdbJdbcCode.DECIMAL_35_0));
typeContributions.contributeJdbcType(new DecimalJdbcType(YdbJdbcCode.DECIMAL_35_9));

// custom jdbc codec
typeContributions.contributeJdbcType(Uint8JdbcType.INSTANCE);
typeContributions.contributeJavaType(BigDecimalJavaType.INSTANCE_22_9);
}

@Override
Expand All @@ -125,7 +141,33 @@ protected void registerColumnTypes(TypeContributions typeContributions, ServiceR

final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry();

ddlTypeRegistry.addDescriptor(new DdlTypeImpl(JDBC_TYPE_DATETIME_CODE, "Datetime", "Datetime", this));
ddlTypeRegistry.addDescriptor(new DdlTypeImpl(YdbJdbcCode.DATETIME, "Datetime", "Datetime", this));
ddlTypeRegistry.addDescriptor(new DdlTypeImpl(YdbJdbcCode.UINT8, "Uint8", "Uint8", this));
ddlTypeRegistry.addDescriptor(new DdlTypeImpl(YdbJdbcCode.DECIMAL_22_9, "Decimal(22, 9)", "Decimal(22, 9)", this));
ddlTypeRegistry.addDescriptor(new DdlTypeImpl(YdbJdbcCode.DECIMAL_31_9, "Decimal(31, 9)", "Decimal(31, 9)", this));
ddlTypeRegistry.addDescriptor(new DdlTypeImpl(YdbJdbcCode.DECIMAL_35_0, "Decimal(35, 0)", "Decimal(35, 0)", this));
ddlTypeRegistry.addDescriptor(new DdlTypeImpl(YdbJdbcCode.DECIMAL_35_9, "Decimal(35, 9)", "Decimal(35, 9)", this));
}

@Override
public JdbcType resolveSqlTypeDescriptor(
String columnTypeName,
int jdbcTypeCode,
int precision,
int scale,
JdbcTypeRegistry jdbcTypeRegistry) {
if ((jdbcTypeCode == NUMERIC || jdbcTypeCode == DECIMAL) && (precision != 0 || scale != 0)) {
int sqlCode = DECIMAL_SHIFT + (precision << 6) + scale;

return DECIMAL_JDBC_TYPE_CACHE.computeIfAbsent(sqlCode, DecimalJdbcType::new);
}

return super.resolveSqlTypeDescriptor(columnTypeName, jdbcTypeCode, precision, scale, jdbcTypeRegistry);
}

@Override
public int getDefaultDecimalPrecision() {
return 22;
}

@Override
Expand All @@ -139,11 +181,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio

functionContributions.getFunctionRegistry().register(
"current_time",
new CurrentFunction(
"current_time",
currentTime(),
localDateTimeType
)
new CurrentFunction("current_time", currentTime(), localDateTimeType)
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package tech.ydb.hibernate.dialect.code;

/**
* @author Kirill Kurdyukov
*/
public final class YdbJdbcCode {

/**
* Boolean value.
*/
public static final int BOOL = 10000;

/**
* A signed integer. Acceptable values: from -2^7 to 2^7–1. Not supported for table columns
*/
public static final int INT8 = 10001;

/**
* An unsigned integer. Acceptable values: from 0 to 2^8–1.
*/
public static final int UINT8 = 10002;

/**
* A signed integer. Acceptable values: from –2^15 to 2^15–1. Not supported for table columns
*/
public static final int INT16 = 10003;

/**
* An unsigned integer. Acceptable values: from 0 to 2^16–1. Not supported for table columns
*/
public static final int UINT16 = 10004;

/**
* A signed integer. Acceptable values: from –2^31 to 2^31–1.
*/
public static final int INT32 = 10005;

/**
* An unsigned integer. Acceptable values: from 0 to 2^32–1.
*/
public static final int UINT32 = 10006;

/**
* A signed integer. Acceptable values: from –2^63 to 2^63–1.
*/
public static final int INT64 = 10007;

/**
* An unsigned integer. Acceptable values: from 0 to 2^64–1.
*/
public static final int UINT64 = 10008;

/**
* A real number with variable precision, 4 bytes in size. Can't be used in the primary key
*/
public static final int FLOAT = 10009;

/**
* A real number with variable precision, 8 bytes in size. Can't be used in the primary key
*/
public static final int DOUBLE = 10010;

/**
* A binary data, synonym for YDB type String
*/
public static final int BYTES = 10011;

/**
* Text encoded in UTF-8, synonym for YDB type Utf8
*/
public static final int TEXT = 10012;

/**
* YSON in a textual or binary representation. Doesn't support matching, can't be used in the primary key
*/
public static final int YSON = 10013;

/**
* JSON represented as text. Doesn't support matching, can't be used in the primary key
*/
public static final int JSON = 10014;

/**
* Universally unique identifier UUID. Not supported for table columns
*/
public static final int UUID = 10015;

/**
* Date, precision to the day
*/
public static final int DATE = 10016;

/**
* Date/time, precision to the second
*/
public static final int DATETIME = 10017;

/**
* Date/time, precision to the microsecond
*/
public static final int TIMESTAMP = 10018;

/**
* Time interval (signed), precision to microseconds
*/
public static final int INTERVAL = 10019;

/**
* Date with time zone label, precision to the day
*/
public static final int TZ_DATE = 10020;

/**
* Date/time with time zone label, precision to the second
*/
public static final int TZ_DATETIME = 10021;

/**
* Date/time with time zone label, precision to the microsecond
*/
public static final int TZ_TIMESTAMP = 10022;

/**
* JSON in an indexed binary representation. Doesn't support matching, can't be used in the primary key
*/
public static final int JSON_DOCUMENT = 10023;

public static final int DECIMAL_SHIFT = (1 << 14);

/**
* <a href="https://github.com/ydb-platform/ydb-jdbc-driver/blob/v2.3.3/jdbc/src/main/java/tech/ydb/jdbc/impl/YdbTypes.java#L37-L66">link</a>
*/
public static final int DECIMAL_22_9 = DECIMAL_SHIFT + (22 << 6) + 9;

public static final int DECIMAL_31_9 = DECIMAL_SHIFT + (31 << 6) + 9;

public static final int DECIMAL_35_0 = DECIMAL_SHIFT + (35 << 6);

public static final int DECIMAL_35_9 = DECIMAL_SHIFT + (35 << 6) + 9;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package tech.ydb.hibernate.dialect.types;

import org.hibernate.dialect.Dialect;
import org.hibernate.type.descriptor.jdbc.JdbcType;

/**
* @author Kirill Kurdyukov
*/
public final class BigDecimalJavaType extends org.hibernate.type.descriptor.java.BigDecimalJavaType {

public static final BigDecimalJavaType INSTANCE_22_9 = new BigDecimalJavaType();

@Override
public int getDefaultSqlScale(Dialect dialect, JdbcType jdbcType) {
return 9;
}

@Override
public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) {
return 22;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package tech.ydb.hibernate.dialect.types;

import java.math.BigDecimal;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaType;

/**
* @author Kirill Kurdyukov
*/
public class DecimalJdbcType extends org.hibernate.type.descriptor.jdbc.DecimalJdbcType {
private final int sqlCode;

public DecimalJdbcType(int sqlCode) {
this.sqlCode = sqlCode;
}

@Override
public int getJdbcTypeCode() {
return sqlCode;
}

@Override
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
return new ValueBinder<>() {
@Override
public void bind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
st.setObject(index, javaType.unwrap(value, BigDecimal.class, options), sqlCode);
}

@Override
public void bind(CallableStatement st, X value, String name, WrapperOptions options) throws SQLException {
st.setObject(name, javaType.unwrap(value, BigDecimal.class, options), sqlCode);
}
};
}
}
Loading

0 comments on commit e940f1d

Please sign in to comment.