Add hexadecimal binary literals with standard SQL syntax
And fix up the parsing and formatting in VarbinaryTypeDescriptor which was inconsistent with how the JDBC drivers handle the conversion to signed bytes.
This commit is contained in:
parent
eb43734658
commit
ddaff28838
|
@ -93,6 +93,10 @@ UNICODE_ESCAPE
|
|||
: 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
|
||||
;
|
||||
|
||||
BINARY_LITERAL
|
||||
: [xX] SINGLE_QUOTE (HEX_DIGIT HEX_DIGIT)* SINGLE_QUOTE
|
||||
;
|
||||
|
||||
// ESCAPE start tokens
|
||||
TIMESTAMP_ESCAPE_START : '{ts';
|
||||
DATE_ESCAPE_START : '{d';
|
||||
|
|
|
@ -501,6 +501,7 @@ nullifFunction
|
|||
|
||||
literal
|
||||
: STRING_LITERAL
|
||||
| BINARY_LITERAL
|
||||
| INTEGER_LITERAL
|
||||
| LONG_LITERAL
|
||||
| BIG_INTEGER_LITERAL
|
||||
|
|
|
@ -3285,6 +3285,10 @@ public abstract class Dialect implements ConversionContext {
|
|||
return true;
|
||||
}
|
||||
|
||||
public String formatBinaryliteral(byte[] bytes) {
|
||||
return "X'" + StandardBasicTypes.BINARY.toString( bytes ) + "'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Pluggable strategy for determining the Size to use for columns of
|
||||
* a given SQL type when no explicit Size has been given.
|
||||
|
|
|
@ -1085,6 +1085,11 @@ public class OracleDialect extends Dialect {
|
|||
.replace("x", "TZH"); //note special case
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatBinaryliteral(byte[] bytes) {
|
||||
return "hextoraw('" + StandardBasicTypes.BINARY.toString( bytes ) + "')";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultSet getResultSet(CallableStatement statement, int position) throws SQLException {
|
||||
return (ResultSet) statement.getObject( position );
|
||||
|
|
|
@ -691,6 +691,11 @@ public class PostgreSQLDialect extends Dialect {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatBinaryliteral(byte[] bytes) {
|
||||
return "bytea '\\x" + StandardBasicTypes.BINARY.toString( bytes ) + "'";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String wrapDateLiteral(String date) {
|
||||
return wrapAsAnsiDateLiteral(date);
|
||||
|
|
|
@ -584,6 +584,11 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
|
||||
private static final Pattern OFFSET_PATTERN = compile(".*[-+]\\d{2}(:\\d{2})?$");
|
||||
|
||||
@Override
|
||||
public String formatBinaryliteral(byte[] bytes) {
|
||||
return "0x" + StandardBasicTypes.BINARY.toString( bytes );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String wrapTimestampLiteral(String timestamp) {
|
||||
//needed because the {ts ... } JDBC escape chokes on microseconds
|
||||
|
|
|
@ -1746,6 +1746,9 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
|
|||
if ( ctx.literal().STRING_LITERAL() != null ) {
|
||||
return stringLiteral( ctx.literal().STRING_LITERAL().getText() );
|
||||
}
|
||||
else if ( ctx.literal().BINARY_LITERAL() != null ) {
|
||||
return binaryLiteral( ctx.literal().BINARY_LITERAL().getText() );
|
||||
}
|
||||
else if ( ctx.literal().INTEGER_LITERAL() != null ) {
|
||||
return integerLiteral( ctx.literal().INTEGER_LITERAL().getText() );
|
||||
}
|
||||
|
@ -2034,6 +2037,14 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
|
|||
);
|
||||
}
|
||||
|
||||
private SqmLiteral<byte[]> binaryLiteral(String text) {
|
||||
return new SqmLiteral(
|
||||
StandardBasicTypes.BINARY.fromStringValue( text.substring( 2, text.length()-1 ) ),
|
||||
resolveExpressableTypeBasic( byte[].class ),
|
||||
creationContext.getNodeBuilder()
|
||||
);
|
||||
}
|
||||
|
||||
private SqmLiteral<Integer> integerLiteral(String text) {
|
||||
try {
|
||||
final Integer value = Integer.valueOf( text );
|
||||
|
|
|
@ -48,7 +48,7 @@ public class ByteArrayTypeDescriptor extends AbstractTypeDescriptor<Byte[]> {
|
|||
public String toString(Byte[] bytes) {
|
||||
final StringBuilder buf = new StringBuilder();
|
||||
for ( Byte aByte : bytes ) {
|
||||
final String hexStr = Integer.toHexString( aByte - Byte.MIN_VALUE );
|
||||
final String hexStr = Integer.toHexString( Byte.toUnsignedInt(aByte) );
|
||||
if ( hexStr.length() == 1 ) {
|
||||
buf.append( '0' );
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ public class ByteArrayTypeDescriptor extends AbstractTypeDescriptor<Byte[]> {
|
|||
Byte[] bytes = new Byte[string.length() / 2];
|
||||
for ( int i = 0; i < bytes.length; i++ ) {
|
||||
final String hexStr = string.substring( i * 2, (i + 1) * 2 );
|
||||
bytes[i] = (byte) ( Integer.parseInt( hexStr, 16 ) + Byte.MIN_VALUE );
|
||||
bytes[i] = (byte) Integer.parseInt( hexStr, 16 );
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ public class PrimitiveByteArrayTypeDescriptor extends AbstractTypeDescriptor<byt
|
|||
public String toString(byte[] bytes) {
|
||||
final StringBuilder buf = new StringBuilder( bytes.length * 2 );
|
||||
for ( byte aByte : bytes ) {
|
||||
final String hexStr = Integer.toHexString( aByte - Byte.MIN_VALUE );
|
||||
final String hexStr = Integer.toHexString( Byte.toUnsignedInt(aByte) );
|
||||
if ( hexStr.length() == 1 ) {
|
||||
buf.append( '0' );
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ public class PrimitiveByteArrayTypeDescriptor extends AbstractTypeDescriptor<byt
|
|||
|
||||
@Override
|
||||
public String extractLoggableRepresentation(byte[] value) {
|
||||
return (value == null) ? super.extractLoggableRepresentation( null ) : Arrays.toString( value );
|
||||
return value == null ? super.extractLoggableRepresentation( null ) : Arrays.toString( value );
|
||||
}
|
||||
|
||||
public byte[] fromString(String string) {
|
||||
|
@ -73,7 +73,7 @@ public class PrimitiveByteArrayTypeDescriptor extends AbstractTypeDescriptor<byt
|
|||
byte[] bytes = new byte[string.length() / 2];
|
||||
for ( int i = 0; i < bytes.length; i++ ) {
|
||||
final String hexStr = string.substring( i * 2, (i + 1) * 2 );
|
||||
bytes[i] = (byte) (Integer.parseInt(hexStr, 16) + Byte.MIN_VALUE);
|
||||
bytes[i] = (byte) Integer.parseInt( hexStr, 16 );
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
|
|||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface JdbcLiteralFormatter<T> {
|
||||
String NULL = "null";
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.hibernate.type.descriptor.ValueExtractor;
|
|||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
import org.hibernate.type.descriptor.sql.internal.JdbcLiteralFormatterBinary;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
|
@ -44,6 +45,12 @@ public class VarbinaryTypeDescriptor implements SqlTypeDescriptor {
|
|||
return (BasicJavaDescriptor<T>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( byte[].class );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaTypeDescriptor<T> javaTypeDescriptor) {
|
||||
//noinspection unchecked
|
||||
return new JdbcLiteralFormatterBinary( javaTypeDescriptor );
|
||||
}
|
||||
|
||||
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
|
||||
return new BasicBinder<X>( javaTypeDescriptor, this ) {
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.type.descriptor.sql.internal;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
import org.hibernate.type.descriptor.sql.spi.BasicJdbcLiteralFormatter;
|
||||
|
||||
/**
|
||||
* JdbcLiteralFormatter implementation for handling binary literals
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class JdbcLiteralFormatterBinary extends BasicJdbcLiteralFormatter {
|
||||
public JdbcLiteralFormatterBinary(JavaTypeDescriptor javaTypeDescriptor) {
|
||||
super( javaTypeDescriptor );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toJdbcLiteral(Object value, Dialect dialect, SharedSessionContractImplementor session) {
|
||||
return dialect.formatBinaryliteral( unwrap( value, byte[].class, session ) );
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ import java.math.BigInteger;
|
|||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hibernate.type.StandardBasicTypes.BINARY;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
|
@ -28,6 +29,18 @@ import static org.hamcrest.Matchers.is;
|
|||
@SessionFactory
|
||||
public class LiteralTests {
|
||||
|
||||
@Test
|
||||
public void testBinaryLiteral(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
byte[] bytes1 = (byte[]) session.createQuery( "select X'DEADBEEF'" ).getSingleResult();
|
||||
assertThat( BINARY.toString(bytes1), is("deadbeef") );
|
||||
byte[] bytes2 = (byte[]) session.createQuery( "select X'deadbeef'" ).getSingleResult();
|
||||
assertThat( BINARY.toString(bytes2), is("deadbeef") );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJdbcTimeLiteral(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
|
|
Loading…
Reference in New Issue