From c5baae7e114087cb594ac573d9009198bc4b1c9c Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Thu, 30 Sep 2021 19:41:12 +0200 Subject: [PATCH] Improve SQL rendering performance by avoiding intermediate String objects --- .../community/dialect/CUBRIDDialect.java | 9 +- .../community/dialect/CacheDialect.java | 5 +- .../community/dialect/FirebirdDialect.java | 107 ++++- .../community/dialect/InformixDialect.java | 9 +- .../community/dialect/IngresDialect.java | 16 +- .../community/dialect/MimerSQLDialect.java | 3 +- .../community/dialect/RDMSOS2200Dialect.java | 9 +- .../community/dialect/SQLiteDialect.java | 87 +++- .../dialect/AbstractHANADialect.java | 16 +- .../dialect/AbstractTransactSQLDialect.java | 7 +- .../hibernate/dialect/CockroachDialect.java | 92 +++- .../org/hibernate/dialect/DB2Dialect.java | 18 +- .../dialect/DB2SqlAstTranslator.java | 2 +- .../org/hibernate/dialect/DerbyDialect.java | 14 +- .../dialect/DerbySqlAstTranslator.java | 2 +- .../java/org/hibernate/dialect/Dialect.java | 132 +++--- .../java/org/hibernate/dialect/H2Dialect.java | 25 +- .../org/hibernate/dialect/HSQLDialect.java | 13 +- .../dialect/MariaDBSqlAstTranslator.java | 2 +- .../org/hibernate/dialect/MySQLDialect.java | 22 +- .../dialect/MySQLSqlAstTranslator.java | 2 +- .../org/hibernate/dialect/OracleDialect.java | 12 +- .../dialect/OracleSqlAstTranslator.java | 2 +- .../hibernate/dialect/PostgreSQLDialect.java | 100 ++++- .../dialect/PostgreSQLSqlAstTranslator.java | 2 +- .../hibernate/dialect/SQLServerDialect.java | 110 ++++- .../dialect/SQLServerSqlAstTranslator.java | 43 +- .../org/hibernate/dialect/SpannerDialect.java | 9 +- .../org/hibernate/dialect/SybaseDialect.java | 3 +- .../dialect/function/CountFunction.java | 4 +- .../dialect/function/CurrentFunction.java | 2 +- .../function/SQLServerFormatEmulation.java | 2 +- .../sql/ast/spi/AbstractSqlAstTranslator.java | 397 +++--------------- .../hibernate/sql/ast/spi/SqlAppender.java | 42 +- .../ast/tree/expression/Summarization.java | 14 +- .../type/descriptor/DateTimeUtils.java | 148 +++---- .../PrimitiveByteArrayTypeDescriptor.java | 11 +- .../descriptor/jdbc/JdbcLiteralFormatter.java | 9 +- .../descriptor/jdbc/JdbcTypeDescriptor.java | 2 +- .../internal/JdbcLiteralFormatterBinary.java | 7 +- .../internal/JdbcLiteralFormatterBoolean.java | 7 +- .../JdbcLiteralFormatterCharacterData.java | 16 +- .../JdbcLiteralFormatterNumericData.java | 7 +- .../JdbcLiteralFormatterTemporal.java | 62 +-- .../hibernate/type/spi/TypeConfiguration.java | 2 +- .../postgis/PGGeometryTypeDescriptor.java | 12 +- .../orm/junit/DialectFeatureChecks.java | 4 +- 47 files changed, 914 insertions(+), 707 deletions(-) diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CUBRIDDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CUBRIDDialect.java index d8d7c11fe4..f4181e8fc2 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CUBRIDDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CUBRIDDialect.java @@ -24,6 +24,7 @@ import org.hibernate.query.TemporalUnit; import org.hibernate.query.spi.QueryEngine; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.exec.spi.JdbcOperation; @@ -335,17 +336,19 @@ public class CUBRIDDialect extends Dialect { } @Override - public String translateDatetimeFormat(String format) { + public void appendDatetimeFormat(SqlAppender appender, String format) { //I do not know if CUBRID supports FM, but it //seems that it does pad by default, so it needs it! - return OracleDialect.datetimeFormat( format, true, false ) + appender.appendSql( + OracleDialect.datetimeFormat( format, true, false ) .replace("SSSSSS", "FF") .replace("SSSSS", "FF") .replace("SSSS", "FF") .replace("SSS", "FF") .replace("SS", "FF") .replace("S", "FF") - .result(); + .result() + ); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CacheDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CacheDialect.java index 47106689c4..f8a5318493 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CacheDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CacheDialect.java @@ -31,6 +31,7 @@ import org.hibernate.query.spi.QueryEngine; import org.hibernate.sql.JoinFragment; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.exec.spi.JdbcOperation; @@ -416,9 +417,9 @@ public class CacheDialect extends Dialect { } @Override - public String translateDatetimeFormat(String format) { + public void appendDatetimeFormat(SqlAppender appender, String format) { //I don't think Cache needs FM - return OracleDialect.datetimeFormat( format, false, false ).result(); + appender.appendSql( OracleDialect.datetimeFormat( format, false, false ).result() ); } } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/FirebirdDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/FirebirdDialect.java index 9dfdb6a710..0ccdca948b 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/FirebirdDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/FirebirdDialect.java @@ -45,6 +45,7 @@ import org.hibernate.query.sqm.mutation.internal.idtable.TempIdTableExporter; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.exec.spi.JdbcOperation; @@ -68,7 +69,13 @@ import java.util.regex.Pattern; import jakarta.persistence.TemporalType; -import static org.hibernate.type.descriptor.DateTimeUtils.formatAsTimestampWithMillis; +import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_END; +import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_START_DATE; +import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_START_TIME; +import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_START_TIMESTAMP; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis; /** * An SQL dialect for Firebird 2.0 and above. @@ -567,11 +574,14 @@ public class FirebirdDialect extends Dialect { } @Override - public String toBooleanValueString(boolean bool) { + public void appendBooleanValueString(SqlAppender appender, boolean bool) { //'boolean' type introduced in 3.0 - return getVersion() < 300 - ? super.toBooleanValueString( bool ) - : bool ? "true" : "false"; + if ( getVersion() < 300 ) { + appender.appendSql( bool ? '1' : '0' ); + } + else { + appender.appendSql( bool ); + } } @Override @@ -704,23 +714,82 @@ public class FirebirdDialect extends Dialect { } } - @Override - protected String formatAsTimestamp(Date date, TimeZone jdbcTimeZone) { - return formatAsTimestampWithMillis( date, jdbcTimeZone ); + public void appendDateTimeLiteral( + SqlAppender appender, + TemporalAccessor temporalAccessor, + TemporalType precision, + TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( JDBC_ESCAPE_START_DATE ); + appendAsDate( appender, temporalAccessor ); + appender.appendSql( JDBC_ESCAPE_END ); + break; + case TIME: + appender.appendSql( JDBC_ESCAPE_START_TIME ); + appendAsTime( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); + appender.appendSql( JDBC_ESCAPE_END ); + break; + case TIMESTAMP: + appender.appendSql( JDBC_ESCAPE_START_TIMESTAMP ); + appendAsTimestampWithMillis( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); + appender.appendSql( JDBC_ESCAPE_END ); + break; + default: + throw new IllegalArgumentException(); + } + } + + public void appendDateTimeLiteral(SqlAppender appender, Date date, TemporalType precision, TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( JDBC_ESCAPE_START_DATE ); + appendAsDate( appender, date ); + appender.appendSql( JDBC_ESCAPE_END ); + break; + case TIME: + appender.appendSql( JDBC_ESCAPE_START_TIME ); + appendAsTime( appender, date ); + appender.appendSql( JDBC_ESCAPE_END ); + break; + case TIMESTAMP: + appender.appendSql( JDBC_ESCAPE_START_TIMESTAMP ); + appendAsTimestampWithMillis( appender, date, jdbcTimeZone ); + appender.appendSql( JDBC_ESCAPE_END ); + break; + default: + throw new IllegalArgumentException(); + } + } + + public void appendDateTimeLiteral( + SqlAppender appender, + Calendar calendar, + TemporalType precision, + TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( JDBC_ESCAPE_START_DATE ); + appendAsDate( appender, calendar ); + appender.appendSql( JDBC_ESCAPE_END ); + break; + case TIME: + appender.appendSql( JDBC_ESCAPE_START_TIME ); + appendAsTime( appender, calendar ); + appender.appendSql( JDBC_ESCAPE_END ); + break; + case TIMESTAMP: + appender.appendSql( JDBC_ESCAPE_START_TIMESTAMP ); + appendAsTimestampWithMillis( appender, calendar, jdbcTimeZone ); + appender.appendSql( JDBC_ESCAPE_END ); + break; + default: + throw new IllegalArgumentException(); + } } @Override - protected String formatAsTimestamp(Calendar calendar, TimeZone jdbcTimeZone) { - return formatAsTimestampWithMillis( calendar, jdbcTimeZone ); - } - - @Override - protected String formatAsTimestamp(TemporalAccessor temporalAccessor, TimeZone jdbcTimeZone) { - return formatAsTimestampWithMillis( temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); - } - - @Override - public String translateDatetimeFormat(String format) { + public void appendDatetimeFormat(SqlAppender appender, String format) { throw new NotYetImplementedFor6Exception( "format() function not supported on Firebird" ); } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java index ce95a8aa07..7aa71fac27 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java @@ -45,6 +45,7 @@ import org.hibernate.query.sqm.sql.StandardSqmTranslatorFactory; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; @@ -432,8 +433,8 @@ public class InformixDialect extends Dialect { } @Override - public String toBooleanValueString(boolean bool) { - return bool ? "'t'" : "'f'"; + public void appendBooleanValueString(SqlAppender appender, boolean bool) { + appender.appendSql( bool ? "'t'" : "'f'" ); } @Override @@ -447,9 +448,9 @@ public class InformixDialect extends Dialect { } @Override - public String translateDatetimeFormat(String format) { + public void appendDatetimeFormat(SqlAppender appender, String format) { //Informix' own variation of MySQL - return datetimeFormat( format ).result(); + appender.appendSql( datetimeFormat( format ).result() ); } public static Replacer datetimeFormat(String format) { diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/IngresDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/IngresDialect.java index 6da15696cb..65cb9929c2 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/IngresDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/IngresDialect.java @@ -41,6 +41,7 @@ import org.hibernate.query.sqm.sql.StandardSqmTranslatorFactory; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; @@ -197,10 +198,13 @@ public class IngresDialect extends Dialect { } @Override - public String toBooleanValueString(boolean bool) { - return getVersion() < 1000 - ? super.toBooleanValueString( bool ) - : String.valueOf( bool ); + public void appendBooleanValueString(SqlAppender appender, boolean bool) { + if ( getVersion() < 1000 ) { + appender.appendSql( bool ? '1' : '0' ); + } + else { + appender.appendSql( bool ); + } } @@ -496,8 +500,8 @@ public class IngresDialect extends Dialect { } @Override - public String translateDatetimeFormat(String format) { - return MySQLDialect.datetimeFormat( format ).result(); + public void appendDatetimeFormat(SqlAppender appender, String format) { + appender.appendSql( MySQLDialect.datetimeFormat( format ).result() ); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MimerSQLDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MimerSQLDialect.java index 1be895dac1..00f2954b52 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MimerSQLDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MimerSQLDialect.java @@ -25,6 +25,7 @@ import org.hibernate.query.TemporalUnit; import org.hibernate.query.spi.QueryEngine; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.exec.spi.JdbcOperation; @@ -271,7 +272,7 @@ public class MimerSQLDialect extends Dialect { } @Override - public String translateDatetimeFormat(String format) { + public void appendDatetimeFormat(SqlAppender appender, String format) { throw new NotYetImplementedFor6Exception("format() function not supported on Mimer SQL"); } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/RDMSOS2200Dialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/RDMSOS2200Dialect.java index 4d262cfb0a..a15ca04191 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/RDMSOS2200Dialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/RDMSOS2200Dialect.java @@ -26,6 +26,7 @@ import org.hibernate.sql.CaseFragment; import org.hibernate.sql.DecodeCaseFragment; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.exec.spi.JdbcOperation; @@ -370,15 +371,17 @@ public class RDMSOS2200Dialect extends Dialect { } @Override - public String translateDatetimeFormat(String format) { - return OracleDialect.datetimeFormat( format, true, false ) //Does it really support FM? + public void appendDatetimeFormat(SqlAppender appender, String format) { + appender.appendSql( + OracleDialect.datetimeFormat( format, true, false ) //Does it really support FM? .replace("SSSSSS", "MLS") .replace("SSSSS", "MLS") .replace("SSSS", "MLS") .replace("SSS", "MLS") .replace("SS", "MLS") .replace("S", "MLS") - .result(); + .result() + ); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLiteDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLiteDialect.java index 47586dc9b7..8df97640a8 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLiteDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLiteDialect.java @@ -7,6 +7,11 @@ package org.hibernate.community.dialect; import java.sql.Types; +import java.time.temporal.TemporalAccessor; +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + import jakarta.persistence.TemporalType; import org.hibernate.ScrollMode; @@ -39,6 +44,7 @@ import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolv import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.exec.spi.JdbcOperation; @@ -53,6 +59,9 @@ import static org.hibernate.query.TemporalUnit.EPOCH; import static org.hibernate.query.TemporalUnit.MONTH; import static org.hibernate.query.TemporalUnit.QUARTER; import static org.hibernate.query.TemporalUnit.YEAR; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros; /** * An SQL dialect for SQLite. @@ -536,8 +545,8 @@ public class SQLiteDialect extends Dialect { } @Override - public String translateDatetimeFormat(String format) { - return datetimeFormat( format ).result(); + public void appendDatetimeFormat(SqlAppender appender, String format) { + appender.appendSql( datetimeFormat( format ).result() ); } public static Replacer datetimeFormat(String format) { @@ -600,18 +609,80 @@ public class SQLiteDialect extends Dialect { } @Override - protected String wrapDateLiteral(String date) { - return "date(" + date + ")"; + public void appendDateTimeLiteral( + SqlAppender appender, + TemporalAccessor temporalAccessor, + TemporalType precision, + TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( "date(" ); + appendAsDate( appender, temporalAccessor ); + appender.appendSql( ')' ); + break; + case TIME: + appender.appendSql( "time(" ); + appendAsTime( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); + appender.appendSql( ')' ); + break; + case TIMESTAMP: + appender.appendSql( "datetime(" ); + appendAsTimestampWithMicros( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); + appender.appendSql( ')' ); + break; + default: + throw new IllegalArgumentException(); + } } @Override - protected String wrapTimeLiteral(String time) { - return "time(" + time + ")"; + public void appendDateTimeLiteral(SqlAppender appender, Date date, TemporalType precision, TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( "date(" ); + appendAsDate( appender, date ); + appender.appendSql( ')' ); + break; + case TIME: + appender.appendSql( "time(" ); + appendAsTime( appender, date ); + appender.appendSql( ')' ); + break; + case TIMESTAMP: + appender.appendSql( "datetime(" ); + appendAsTimestampWithMicros( appender, date, jdbcTimeZone ); + appender.appendSql( ')' ); + break; + default: + throw new IllegalArgumentException(); + } } @Override - protected String wrapTimestampLiteral(String timestamp) { - return "datetime(" + timestamp + ")"; + public void appendDateTimeLiteral( + SqlAppender appender, + Calendar calendar, + TemporalType precision, + TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( "date(" ); + appendAsDate( appender, calendar ); + appender.appendSql( ')' ); + break; + case TIME: + appender.appendSql( "time(" ); + appendAsTime( appender, calendar ); + appender.appendSql( ')' ); + break; + case TIMESTAMP: + appender.appendSql( "datetime(" ); + appendAsTimestampWithMicros( appender, calendar, jdbcTimeZone ); + appender.appendSql( ')' ); + break; + default: + throw new IllegalArgumentException(); + } } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractHANADialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractHANADialect.java index feb646d27b..d7115adbf4 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractHANADialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractHANADialect.java @@ -45,6 +45,7 @@ import org.hibernate.service.ServiceRegistry; import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorHANADatabaseImpl; @@ -1551,10 +1552,13 @@ public abstract class AbstractHANADialect extends Dialect { } @Override - public String toBooleanValueString(boolean bool) { - return this.useLegacyBooleanType - ? super.toBooleanValueString( bool ) - : String.valueOf( bool ); + public void appendBooleanValueString(SqlAppender appender, boolean bool) { + if ( this.useLegacyBooleanType ) { + appender.appendSql( bool ? '1' : '0' ); + } + else { + appender.appendSql( bool ); + } } @Override @@ -1630,9 +1634,9 @@ public abstract class AbstractHANADialect extends Dialect { } @Override - public String translateDatetimeFormat(String format) { + public void appendDatetimeFormat(SqlAppender appender, String format) { //I don't think HANA needs FM - return OracleDialect.datetimeFormat( format, false, false ).result(); + appender.appendSql( OracleDialect.datetimeFormat( format, false, false ).result() ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java index 2dbc4e8727..3f1eee8f2b 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java @@ -25,7 +25,9 @@ import org.hibernate.query.sqm.mutation.internal.idtable.IdTable; import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy; import org.hibernate.query.sqm.mutation.internal.idtable.TempIdTableExporter; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.type.StandardBasicTypes; +import org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor; import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptor; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeDescriptorRegistry; @@ -326,7 +328,8 @@ public abstract class AbstractTransactSQLDialect extends Dialect { } @Override - public String formatBinaryLiteral(byte[] bytes) { - return "0x" + StandardBasicTypes.BINARY.toString( bytes ); + public void appendBinaryLiteral(SqlAppender appender, byte[] bytes) { + appender.appendSql( "0x" ); + PrimitiveByteArrayTypeDescriptor.INSTANCE.appendString( appender, bytes ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java index 9324bf0773..238d12e8ba 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java @@ -24,6 +24,7 @@ import org.hibernate.query.TemporalUnit; import org.hibernate.query.spi.QueryEngine; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.exec.spi.JdbcOperation; @@ -32,15 +33,20 @@ import org.hibernate.type.StandardBasicTypes; import java.sql.DatabaseMetaData; import java.sql.SQLException; import java.sql.Types; +import java.time.temporal.TemporalAccessor; +import java.util.Calendar; +import java.util.Date; import java.util.Iterator; import java.util.Map; +import java.util.TimeZone; import jakarta.persistence.TemporalType; import static org.hibernate.query.TemporalUnit.DAY; import static org.hibernate.query.TemporalUnit.NATIVE; -import static org.hibernate.type.descriptor.DateTimeUtils.wrapAsAnsiDateLiteral; -import static org.hibernate.type.descriptor.DateTimeUtils.wrapAsAnsiTimeLiteral; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros; /** * A dialect for CockroachDB. @@ -135,8 +141,8 @@ public class CockroachDialect extends Dialect { } @Override - public String toBooleanValueString(boolean bool) { - return String.valueOf( bool ); + public void appendBooleanValueString(SqlAppender appender, boolean bool) { + appender.appendSql( bool ); } @Override @@ -258,18 +264,80 @@ public class CockroachDialect extends Dialect { } @Override - protected String wrapDateLiteral(String date) { - return wrapAsAnsiDateLiteral(date); + public void appendDateTimeLiteral( + SqlAppender appender, + TemporalAccessor temporalAccessor, + TemporalType precision, + TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( "date '" ); + appendAsDate( appender, temporalAccessor ); + appender.appendSql( '\'' ); + break; + case TIME: + appender.appendSql( "time '" ); + appendAsTime( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); + appender.appendSql( '\'' ); + break; + case TIMESTAMP: + appender.appendSql( "timestamp with time zone '" ); + appendAsTimestampWithMicros( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); + appender.appendSql( '\'' ); + break; + default: + throw new IllegalArgumentException(); + } } @Override - protected String wrapTimeLiteral(String time) { - return wrapAsAnsiTimeLiteral(time); + public void appendDateTimeLiteral(SqlAppender appender, Date date, TemporalType precision, TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( "date '" ); + appendAsDate( appender, date ); + appender.appendSql( '\'' ); + break; + case TIME: + appender.appendSql( "time '" ); + appendAsTime( appender, date ); + appender.appendSql( '\'' ); + break; + case TIMESTAMP: + appender.appendSql( "timestamp with time zone '" ); + appendAsTimestampWithMicros( appender,date, jdbcTimeZone ); + appender.appendSql( '\'' ); + break; + default: + throw new IllegalArgumentException(); + } } @Override - protected String wrapTimestampLiteral(String timestamp) { - return "timestamp with time zone '" + timestamp + "'"; + public void appendDateTimeLiteral( + SqlAppender appender, + Calendar calendar, + TemporalType precision, + TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( "date '" ); + appendAsDate( appender, calendar ); + appender.appendSql( '\'' ); + break; + case TIME: + appender.appendSql( "time '" ); + appendAsTime( appender, calendar ); + appender.appendSql( '\'' ); + break; + case TIMESTAMP: + appender.appendSql( "timestamp with time zone '" ); + appendAsTimestampWithMicros( appender, calendar, jdbcTimeZone ); + appender.appendSql( '\'' ); + break; + default: + throw new IllegalArgumentException(); + } } /** @@ -363,8 +431,8 @@ public class CockroachDialect extends Dialect { } @Override - public String translateDatetimeFormat(String format) { - return SpannerDialect.datetimeFormat( format ).result(); + public void appendDatetimeFormat(SqlAppender appender, String format) { + appender.appendSql( SpannerDialect.datetimeFormat( format ).result() ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java index 84ebad8cca..30fc1c427a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java @@ -36,6 +36,7 @@ import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.service.ServiceRegistry; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.exec.spi.JdbcOperation; @@ -44,6 +45,7 @@ import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNo import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor; import org.hibernate.type.JavaObjectType; import org.hibernate.type.StandardBasicTypes; +import org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor; import org.hibernate.type.descriptor.jdbc.*; import java.sql.CallableStatement; @@ -593,8 +595,10 @@ public class DB2Dialect extends Dialect { } @Override - public String formatBinaryLiteral(byte[] bytes) { - return "BX'" + StandardBasicTypes.BINARY.toString( bytes ) + "'"; + public void appendBinaryLiteral(SqlAppender appender, byte[] bytes) { + appender.appendSql( "BX'" ); + PrimitiveByteArrayTypeDescriptor.INSTANCE.appendString( appender, bytes ); + appender.appendSql( '\'' ); } @Override @@ -735,9 +739,9 @@ public class DB2Dialect extends Dialect { } @Override - public String translateDatetimeFormat(String format) { + public void appendDatetimeFormat(SqlAppender appender, String format) { //DB2 does not need nor support FM - return OracleDialect.datetimeFormat( format, false, false ).result(); + appender.appendSql( OracleDialect.datetimeFormat( format, false, false ).result() ); } @Override @@ -752,12 +756,12 @@ public class DB2Dialect extends Dialect { } @Override - public String toBooleanValueString(boolean bool) { + public void appendBooleanValueString(SqlAppender appender, boolean bool) { if ( getVersion() < 1100 ) { - return bool ? "1" : "0"; + appender.appendSql( bool ? '1' : '0' ); } else { - return bool ? "true" : "false"; + appender.appendSql( bool ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2SqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2SqlAstTranslator.java index 9f9f97388d..fd17a71a28 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2SqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2SqlAstTranslator.java @@ -257,7 +257,7 @@ public class DB2SqlAstTranslator extends AbstractSqlAst } else if ( expression instanceof Summarization ) { Summarization summarization = (Summarization) expression; - appendSql( summarization.getKind().name().toLowerCase() ); + appendSql( summarization.getKind().sqlText() ); appendSql( OPEN_PARENTHESIS ); renderCommaSeparated( summarization.getGroupings() ); appendSql( CLOSE_PARENTHESIS ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java index 6a5debdeba..d1bd47b4e8 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java @@ -48,6 +48,7 @@ import org.hibernate.sql.DerbyCaseFragment; import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.exec.spi.JdbcOperation; @@ -355,10 +356,13 @@ public class DerbyDialect extends Dialect { } @Override - public String toBooleanValueString(boolean bool) { - return getVersion() < 1070 - ? super.toBooleanValueString( bool ) - : String.valueOf( bool ); + public void appendBooleanValueString(SqlAppender appender, boolean bool) { + if ( getVersion() < 1070 ) { + appender.appendSql( bool ? '1' : '0' ); + } + else { + appender.appendSql( bool ); + } } @Override @@ -608,7 +612,7 @@ public class DerbyDialect extends Dialect { } @Override - public String translateDatetimeFormat(String format) { + public void appendDatetimeFormat(SqlAppender appender, String format) { throw new NotYetImplementedFor6Exception("format() function not supported on Derby"); } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DerbySqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/DerbySqlAstTranslator.java index db0956c1b3..bceeb7283a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DerbySqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DerbySqlAstTranslator.java @@ -191,7 +191,7 @@ public class DerbySqlAstTranslator extends AbstractSqlA } else if ( expression instanceof Summarization ) { Summarization summarization = (Summarization) expression; - appendSql( summarization.getKind().name().toLowerCase() ); + appendSql( summarization.getKind().sqlText() ); appendSql( OPEN_PARENTHESIS ); renderCommaSeparated( summarization.getGroupings() ); appendSql( CLOSE_PARENTHESIS ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index 4a286e14db..1b569cad1d 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -73,6 +73,7 @@ import org.hibernate.sql.*; import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstTranslatorFactory; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl; import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl; @@ -82,6 +83,7 @@ import org.hibernate.tool.schema.spi.Exporter; import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.Type; import org.hibernate.type.descriptor.java.JavaTypeDescriptor; +import org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor; import org.hibernate.type.descriptor.jdbc.ClobTypeDescriptor; import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptor; import org.hibernate.type.descriptor.jdbc.LongNVarcharTypeDescriptor; @@ -97,7 +99,6 @@ import java.sql.*; import java.time.temporal.TemporalAccessor; import java.util.Date; import java.util.*; -import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.hibernate.type.descriptor.DateTimeUtils.*; @@ -137,16 +138,10 @@ public abstract class Dialect implements ConversionContext { * Characters used as closing for quoting SQL identifiers */ public static final String CLOSED_QUOTE = "`\"]"; - private static final Pattern SINGLE_QUOTE_PATTERN = Pattern.compile( - "'", - Pattern.LITERAL - ); private static final Pattern ESCAPE_CLOSING_COMMENT_PATTERN = Pattern.compile( "\\*/" ); private static final Pattern ESCAPE_OPENING_COMMENT_PATTERN = Pattern.compile( "/\\*" ); - public static final String TWO_SINGLE_QUOTES_REPLACEMENT = Matcher.quoteReplacement( "''" ); - private final TypeNames typeNames = new TypeNames(); private final TypeNames hibernateTypeNames = new TypeNames(); @@ -2399,7 +2394,13 @@ public abstract class Dialect implements ConversionContext { * @return The appropriate SQL literal. */ public String toBooleanValueString(boolean bool) { - return bool ? "1" : "0"; + final StringBuilder sb = new StringBuilder(); + appendBooleanValueString( sb::append, bool ); + return sb.toString(); + } + + public void appendBooleanValueString(SqlAppender appender, boolean bool) { + appender.appendSql( bool ? '1' : '0' ); } @@ -3736,7 +3737,21 @@ public abstract class Dialect implements ConversionContext { * @return escaped String */ public String inlineLiteral(String literal) { - return String.format( "\'%s\'", escapeLiteral( literal ) ); + final StringBuilder sb = new StringBuilder( literal.length() + 2 ); + appendLiteral( sb::append, literal ); + return sb.toString(); + } + + public void appendLiteral(SqlAppender appender, String literal) { + appender.appendSql( '\'' ); + for ( int i = 0; i < literal.length(); i++ ) { + final char c = literal.charAt( i ); + if ( c == '\'' ) { + appender.appendSql( '\'' ); + } + appender.appendSql( c ); + } + appender.appendSql( '\'' ); } /** @@ -3751,15 +3766,6 @@ public abstract class Dialect implements ConversionContext { return true; } - /** - * Escape String literal. - * - * @return escaped String - */ - protected String escapeLiteral(String literal) { - return SINGLE_QUOTE_PATTERN.matcher( literal ).replaceAll( TWO_SINGLE_QUOTES_REPLACEMENT ); - } - /** * Modify the SQL, adding hints or comments, if necessary */ @@ -3943,8 +3949,10 @@ public abstract class Dialect implements ConversionContext { return true; } - public String formatBinaryLiteral(byte[] bytes) { - return "X'" + StandardBasicTypes.BINARY.toString( bytes ) + "'"; + public void appendBinaryLiteral(SqlAppender appender, byte[] bytes) { + appender.appendSql( "X'" ); + PrimitiveByteArrayTypeDescriptor.INSTANCE.appendString( appender, bytes ); + appender.appendSql( '\'' ); } public RowLockStrategy getLockRowIdentifier(LockMode lockMode) { @@ -4109,11 +4117,11 @@ public abstract class Dialect implements ConversionContext { * @return a pattern accepted by the function that * formats dates and times in this dialect */ - public String translateDatetimeFormat(String format) { + public void appendDatetimeFormat(SqlAppender appender, String format) { //most databases support a datetime format //copied from Oracle's to_char() function, //with some minor variation - return OracleDialect.datetimeFormat( format, true, false ).result(); + appender.appendSql( OracleDialect.datetimeFormat( format, true, false ).result() ); } /** @@ -4196,72 +4204,80 @@ public abstract class Dialect implements ConversionContext { } } - protected String wrapTimestampLiteral(String timestamp) { - return wrapAsJdbcTimestampLiteral( timestamp ); - } - - protected String wrapDateLiteral(String date) { - return wrapAsJdbcDateLiteral( date ); - } - - protected String wrapTimeLiteral(String time) { - return wrapAsJdbcTimeLiteral( time ); - } - - public String formatDateTimeLiteral( + public void appendDateTimeLiteral( + SqlAppender appender, TemporalAccessor temporalAccessor, TemporalType precision, TimeZone jdbcTimeZone) { switch ( precision ) { case DATE: - return wrapDateLiteral( formatAsDate( temporalAccessor ) ); + appender.appendSql( JDBC_ESCAPE_START_DATE ); + appendAsDate( appender, temporalAccessor ); + appender.appendSql( JDBC_ESCAPE_END ); + break; case TIME: - return wrapTimeLiteral( formatAsTime( temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ) ); + appender.appendSql( JDBC_ESCAPE_START_TIME ); + appendAsTime( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); + appender.appendSql( JDBC_ESCAPE_END ); + break; case TIMESTAMP: - return wrapTimestampLiteral( formatAsTimestamp( temporalAccessor, jdbcTimeZone ) ); + appender.appendSql( JDBC_ESCAPE_START_TIMESTAMP ); + appendAsTimestampWithMicros( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); + appender.appendSql( JDBC_ESCAPE_END ); + break; default: throw new IllegalArgumentException(); } } - protected String formatAsTimestamp(TemporalAccessor temporalAccessor, TimeZone jdbcTimeZone) { - return formatAsTimestampWithMicros( temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); - } - - public String formatDateTimeLiteral(Date date, TemporalType precision, TimeZone jdbcTimeZone) { + public void appendDateTimeLiteral(SqlAppender appender, Date date, TemporalType precision, TimeZone jdbcTimeZone) { switch ( precision ) { case DATE: - return wrapDateLiteral( formatAsDate( date ) ); + appender.appendSql( JDBC_ESCAPE_START_DATE ); + appendAsDate( appender, date ); + appender.appendSql( JDBC_ESCAPE_END ); + break; case TIME: - return wrapTimeLiteral( formatAsTime( date ) ); + appender.appendSql( JDBC_ESCAPE_START_TIME ); + appendAsTime( appender, date ); + appender.appendSql( JDBC_ESCAPE_END ); + break; case TIMESTAMP: - return wrapTimestampLiteral( formatAsTimestamp( date, jdbcTimeZone) ); + appender.appendSql( JDBC_ESCAPE_START_TIMESTAMP ); + appendAsTimestampWithMicros( appender, date, jdbcTimeZone ); + appender.appendSql( JDBC_ESCAPE_END ); + break; default: throw new IllegalArgumentException(); } } - protected String formatAsTimestamp(Date date, TimeZone jdbcTimeZone) { - return formatAsTimestampWithMicros( date, jdbcTimeZone ); - } - - public String formatDateTimeLiteral(Calendar calendar, TemporalType precision, TimeZone jdbcTimeZone) { + public void appendDateTimeLiteral( + SqlAppender appender, + Calendar calendar, + TemporalType precision, + TimeZone jdbcTimeZone) { switch ( precision ) { case DATE: - return wrapDateLiteral( formatAsDate( calendar ) ); + appender.appendSql( JDBC_ESCAPE_START_DATE ); + appendAsDate( appender, calendar ); + appender.appendSql( JDBC_ESCAPE_END ); + break; case TIME: - return wrapTimeLiteral( formatAsTime( calendar ) ); + appender.appendSql( JDBC_ESCAPE_START_TIME ); + appendAsTime( appender, calendar ); + appender.appendSql( JDBC_ESCAPE_END ); + break; case TIMESTAMP: - return wrapTimestampLiteral( formatAsTimestamp( calendar, jdbcTimeZone ) ); + appender.appendSql( JDBC_ESCAPE_START_TIMESTAMP ); + appendAsTimestampWithMicros( appender, calendar, jdbcTimeZone ); + appender.appendSql( JDBC_ESCAPE_END ); + break; default: throw new IllegalArgumentException(); } } - protected String formatAsTimestamp(Calendar calendar, TimeZone jdbcTimeZone) { - return formatAsTimestampWithMicros( calendar, jdbcTimeZone ); - } - /** * Whether the Dialect supports timezone offset in temporal literals. */ diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java index aaae5d312c..f1cbbca06a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java @@ -44,6 +44,7 @@ import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.exec.spi.JdbcOperation; @@ -269,8 +270,8 @@ public class H2Dialect extends Dialect { } @Override - public String toBooleanValueString(boolean bool) { - return String.valueOf( bool ); + public void appendBooleanValueString(SqlAppender appender, boolean bool) { + appender.appendSql( bool ); } @Override @@ -462,17 +463,21 @@ public class H2Dialect extends Dialect { } @Override - public String translateDatetimeFormat(String format) { + public void appendDatetimeFormat(SqlAppender appender, String format) { if ( version == 104200 ) { // See https://github.com/h2database/h2database/issues/2518 - return OracleDialect.datetimeFormat( format, true, true ).result(); + appender.appendSql( OracleDialect.datetimeFormat( format, true, true ).result() ); + } + else { + appender.appendSql( + new Replacer( format, "'", "''" ) + .replace("e", "u") + .replace( "xxx", "XXX" ) + .replace( "xx", "XX" ) + .replace( "x", "X" ) + .result() + ); } - return new Replacer( format, "'", "''" ) - .replace("e", "u") - .replace( "xxx", "XXX" ) - .replace( "xx", "XX" ) - .replace( "x", "X" ) - .result(); } public String translateExtractField(TemporalUnit unit) { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java index ae5bb28b41..0e9c5e70c4 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java @@ -56,6 +56,7 @@ import org.hibernate.query.sqm.mutation.internal.idtable.TempIdTableExporter; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.exec.spi.JdbcOperation; @@ -646,8 +647,8 @@ public class HSQLDialect extends Dialect { } @Override - public String toBooleanValueString(boolean bool) { - return String.valueOf( bool ); + public void appendBooleanValueString(SqlAppender appender, boolean bool) { + appender.appendSql( bool ); } @Override @@ -698,8 +699,9 @@ public class HSQLDialect extends Dialect { } @Override - public String translateDatetimeFormat(String format) { - return OracleDialect.datetimeFormat( format, false, false ) + public void appendDatetimeFormat(SqlAppender appender, String format) { + appender.appendSql( + OracleDialect.datetimeFormat( format, false, false ) // HSQL is case sensitive i.e. requires MONTH and DAY instead of Month and Day .replace("MMMM", "MONTH") .replace("EEEE", "DAY") @@ -709,7 +711,8 @@ public class HSQLDialect extends Dialect { .replace("SSS", "FF") .replace("SS", "FF") .replace("S", "FF") - .result(); + .result() + ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java index 79d052f8d0..250157500c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java @@ -102,7 +102,7 @@ public class MariaDBSqlAstTranslator extends AbstractSq Summarization summarization = (Summarization) expression; renderCommaSeparated( summarization.getGroupings() ); appendSql( " with " ); - appendSql( summarization.getKind().name().toLowerCase() ); + appendSql( summarization.getKind().sqlText() ); } else { expression.accept( this ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java index b063c3fe7c..d975aece82 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java @@ -51,6 +51,7 @@ import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.service.ServiceRegistry; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.exec.spi.JdbcOperation; @@ -940,13 +941,26 @@ public class MySQLDialect extends Dialect { } @Override - protected String escapeLiteral(String literal) { - return super.escapeLiteral( literal ).replace("\\", "\\\\"); + public void appendLiteral(SqlAppender appender, String literal) { + appender.appendSql( '\'' ); + for ( int i = 0; i < literal.length(); i++ ) { + final char c = literal.charAt( i ); + switch ( c ) { + case '\'': + appender.appendSql( '\'' ); + break; + case '\\': + appender.appendSql( '\\' ); + break; + } + appender.appendSql( c ); + } + appender.appendSql( '\'' ); } @Override - public String translateDatetimeFormat(String format) { - return datetimeFormat( format ).result(); + public void appendDatetimeFormat(SqlAppender appender, String format) { + appender.appendSql( datetimeFormat( format ).result() ); } public static Replacer datetimeFormat(String format) { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java index a35b529c73..3b02879f84 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java @@ -103,7 +103,7 @@ public class MySQLSqlAstTranslator extends AbstractSqlA Summarization summarization = (Summarization) expression; renderCommaSeparated( summarization.getGroupings() ); appendSql( " with " ); - appendSql( summarization.getKind().name().toLowerCase() ); + appendSql( summarization.getKind().sqlText() ); } else { expression.accept( this ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java index 6a5a30657e..249cf79903 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -51,6 +51,7 @@ import org.hibernate.sql.*; import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.exec.spi.JdbcOperation; @@ -59,6 +60,7 @@ import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor; import org.hibernate.type.JavaObjectType; import org.hibernate.type.NullType; import org.hibernate.type.StandardBasicTypes; +import org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor; import org.hibernate.type.descriptor.jdbc.BlobTypeDescriptor; import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptor; import org.hibernate.type.descriptor.jdbc.NullJdbcTypeDescriptor; @@ -1185,10 +1187,10 @@ public class OracleDialect extends Dialect { } @Override - public String translateDatetimeFormat(String format) { + public void appendDatetimeFormat(SqlAppender appender, String format) { // Unlike other databases, Oracle requires an explicit reset for the fm modifier, // otherwise all following pattern variables trim zeros - return datetimeFormat( format, true, true ).result(); + appender.appendSql( datetimeFormat( format, true, true ).result() ); } public static Replacer datetimeFormat(String format, boolean useFm, boolean resetFm) { @@ -1277,8 +1279,10 @@ public class OracleDialect extends Dialect { } @Override - public String formatBinaryLiteral(byte[] bytes) { - return "hextoraw('" + StandardBasicTypes.BINARY.toString( bytes ) + "')"; + public void appendBinaryLiteral(SqlAppender appender, byte[] bytes) { + appender.appendSql( "hextoraw('" ); + PrimitiveByteArrayTypeDescriptor.INSTANCE.appendString( appender, bytes ); + appender.appendSql( "')" ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java index fa27f37636..0eed9bed59 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java @@ -318,7 +318,7 @@ public class OracleSqlAstTranslator extends AbstractSql } else if ( expression instanceof Summarization ) { Summarization summarization = (Summarization) expression; - appendSql( summarization.getKind().name().toLowerCase() ); + appendSql( summarization.getKind().sqlText() ); appendSql( OPEN_PARENTHESIS ); renderCommaSeparated( summarization.getGroupings() ); appendSql( CLOSE_PARENTHESIS ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java index b6adc4fb36..f344812984 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -11,9 +11,14 @@ import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; +import java.time.temporal.TemporalAccessor; +import java.util.Calendar; +import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.TimeZone; + import jakarta.persistence.TemporalType; import org.hibernate.LockMode; @@ -54,12 +59,14 @@ import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.service.ServiceRegistry; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.type.JavaObjectType; import org.hibernate.type.PostgresUUIDType; import org.hibernate.type.StandardBasicTypes; +import org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor; import org.hibernate.type.descriptor.jdbc.BlobTypeDescriptor; import org.hibernate.type.descriptor.jdbc.ClobTypeDescriptor; import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptor; @@ -67,8 +74,9 @@ import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcTypeDescript import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; import static org.hibernate.query.TemporalUnit.*; -import static org.hibernate.type.descriptor.DateTimeUtils.wrapAsAnsiDateLiteral; -import static org.hibernate.type.descriptor.DateTimeUtils.wrapAsAnsiTimeLiteral; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros; /** * An SQL dialect for Postgres 8 and above. @@ -544,8 +552,8 @@ public class PostgreSQLDialect extends Dialect { } @Override - public String toBooleanValueString(boolean bool) { - return String.valueOf( bool ); + public void appendBooleanValueString(SqlAppender appender, boolean bool) { + appender.appendSql( bool ); } @Override @@ -714,8 +722,8 @@ public class PostgreSQLDialect extends Dialect { } @Override - public String translateDatetimeFormat(String format) { - return datetimeFormat( format ).result(); + public void appendDatetimeFormat(SqlAppender appender, String format) { + appender.appendSql( datetimeFormat( format ).result() ); } public Replacer datetimeFormat(String format) { @@ -750,23 +758,87 @@ public class PostgreSQLDialect extends Dialect { } @Override - public String formatBinaryLiteral(byte[] bytes) { - return "bytea '\\x" + StandardBasicTypes.BINARY.toString( bytes ) + "'"; + public void appendBinaryLiteral(SqlAppender appender, byte[] bytes) { + appender.appendSql( "bytea '\\x" ); + PrimitiveByteArrayTypeDescriptor.INSTANCE.appendString( appender, bytes ); + appender.appendSql( '\'' ); } @Override - protected String wrapDateLiteral(String date) { - return wrapAsAnsiDateLiteral(date); + public void appendDateTimeLiteral( + SqlAppender appender, + TemporalAccessor temporalAccessor, + TemporalType precision, + TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( "date '" ); + appendAsDate( appender, temporalAccessor ); + appender.appendSql( '\'' ); + break; + case TIME: + appender.appendSql( "time '" ); + appendAsTime( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); + appender.appendSql( '\'' ); + break; + case TIMESTAMP: + appender.appendSql( "timestamp with time zone '" ); + appendAsTimestampWithMicros( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); + appender.appendSql( '\'' ); + break; + default: + throw new IllegalArgumentException(); + } } @Override - protected String wrapTimeLiteral(String time) { - return wrapAsAnsiTimeLiteral(time); + public void appendDateTimeLiteral(SqlAppender appender, Date date, TemporalType precision, TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( "date '" ); + appendAsDate( appender, date ); + appender.appendSql( '\'' ); + break; + case TIME: + appender.appendSql( "time '" ); + appendAsTime( appender, date ); + appender.appendSql( '\'' ); + break; + case TIMESTAMP: + appender.appendSql( "timestamp with time zone '" ); + appendAsTimestampWithMicros( appender, date, jdbcTimeZone ); + appender.appendSql( '\'' ); + break; + default: + throw new IllegalArgumentException(); + } } @Override - protected String wrapTimestampLiteral(String timestamp) { - return "timestamp with time zone '" + timestamp + "'"; + public void appendDateTimeLiteral( + SqlAppender appender, + Calendar calendar, + TemporalType precision, + TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( "date '" ); + appendAsDate( appender, calendar ); + appender.appendSql( '\'' ); + break; + case TIME: + appender.appendSql( "time '" ); + appendAsTime( appender, calendar ); + appender.appendSql( '\'' ); + break; + case TIMESTAMP: + appender.appendSql( "timestamp with time zone '" ); + appendAsTimestampWithMicros( appender, calendar, jdbcTimeZone ); + appender.appendSql( '\'' ); + break; + default: + throw new IllegalArgumentException(); + } } private String withTimeout(String lockString, int timeout) { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLSqlAstTranslator.java index d164cc9e84..70d69a6d48 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLSqlAstTranslator.java @@ -129,7 +129,7 @@ public class PostgreSQLSqlAstTranslator extends Abstrac else if ( expression instanceof Summarization ) { Summarization summarization = (Summarization) expression; if ( getDialect().getVersion() >= 950 ) { - appendSql( summarization.getKind().name().toLowerCase() ); + appendSql( summarization.getKind().sqlText() ); appendSql( OPEN_PARENTHESIS ); renderCommaSeparated( summarization.getGroupings() ); appendSql( CLOSE_PARENTHESIS ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java index 362b5a284c..65465b07e6 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java @@ -39,24 +39,33 @@ import org.hibernate.query.spi.QueryEngine; import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.tool.schema.internal.StandardSequenceExporter; import org.hibernate.tool.schema.spi.Exporter; import org.hibernate.type.StandardBasicTypes; +import org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor; import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptor; import org.hibernate.type.descriptor.jdbc.SmallIntTypeDescriptor; import java.sql.DatabaseMetaData; import java.sql.SQLException; import java.sql.Types; -import java.util.regex.Pattern; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAccessor; +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; import jakarta.persistence.TemporalType; import static java.util.regex.Pattern.compile; import static org.hibernate.query.TemporalUnit.NANOSECOND; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros; /** * A dialect for Microsoft SQL Server 2000 and above @@ -674,8 +683,8 @@ public class SQLServerDialect extends AbstractTransactSQLDialect { } @Override - public String translateDatetimeFormat(String format) { - return datetimeFormat(format).result(); + public void appendDatetimeFormat(SqlAppender appender, String format) { + appender.appendSql( datetimeFormat(format).result() ); } public static Replacer datetimeFormat(String format) { @@ -717,31 +726,96 @@ public class SQLServerDialect extends AbstractTransactSQLDialect { .replace("x", "zz"); } - private static final Pattern OFFSET_PATTERN = compile(".*[-+]\\d{2}(:\\d{2})?$"); - @Override - public String formatBinaryLiteral(byte[] bytes) { - return "0x" + StandardBasicTypes.BINARY.toString( bytes ); + public void appendBinaryLiteral(SqlAppender appender, byte[] bytes) { + appender.appendSql( "0x" ); + PrimitiveByteArrayTypeDescriptor.INSTANCE.appendString( appender, bytes ); } @Override - protected String wrapTimestampLiteral(String timestamp) { - //needed because the {ts ... } JDBC escape chokes on microseconds - return OFFSET_PATTERN.matcher( timestamp ).matches() - ? "cast('" + timestamp + "' as datetimeoffset)" - : "cast('" + timestamp + "' as datetime2)"; + public void appendDateTimeLiteral( + SqlAppender appender, + TemporalAccessor temporalAccessor, + TemporalType precision, + TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( "cast('" ); + appendAsDate( appender, temporalAccessor ); + appender.appendSql( "' as date)" ); + break; + case TIME: + //needed because the {t ... } JDBC is just buggy + appender.appendSql( "cast('" ); + appendAsTime( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); + appender.appendSql( "' as time)" ); + break; + case TIMESTAMP: + appender.appendSql( "cast('" ); + appendAsTimestampWithMicros( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); + //needed because the {ts ... } JDBC escape chokes on microseconds + if ( supportsTemporalLiteralOffset() && temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) ) { + appender.appendSql( "' as datetimeoffset)" ); + } + else { + appender.appendSql( "' as datetime2)" ); + } + break; + default: + throw new IllegalArgumentException(); + } } @Override - protected String wrapTimeLiteral(String time) { - //needed because the {t ... } JDBC is just buggy - return "cast('" + time + "' as time)"; + public void appendDateTimeLiteral(SqlAppender appender, Date date, TemporalType precision, TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( "cast('" ); + appendAsDate( appender, date ); + appender.appendSql( "' as date)" ); + break; + case TIME: + //needed because the {t ... } JDBC is just buggy + appender.appendSql( "cast('" ); + appendAsTime( appender, date ); + appender.appendSql( "' as time)" ); + break; + case TIMESTAMP: + appender.appendSql( "cast('" ); + appendAsTimestampWithMicros( appender, date, jdbcTimeZone ); + appender.appendSql( "' as datetimeoffset)" ); + break; + default: + throw new IllegalArgumentException(); + } } @Override - protected String wrapDateLiteral(String date) { - //possibly not needed - return "cast('" + date + "' as date)"; + public void appendDateTimeLiteral( + SqlAppender appender, + Calendar calendar, + TemporalType precision, + TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( "cast('" ); + appendAsDate( appender, calendar ); + appender.appendSql( "' as date)" ); + break; + case TIME: + //needed because the {t ... } JDBC is just buggy + appender.appendSql( "cast('" ); + appendAsTime( appender, calendar ); + appender.appendSql( "' as time)" ); + break; + case TIMESTAMP: + appender.appendSql( "cast('" ); + appendAsTimestampWithMicros( appender, calendar, jdbcTimeZone ); + appender.appendSql( "' as datetime2)" ); + break; + default: + throw new IllegalArgumentException(); + } } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerSqlAstTranslator.java index 3756e16fc9..e5d7be40f9 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerSqlAstTranslator.java @@ -52,12 +52,12 @@ public class SQLServerSqlAstTranslator extends Abstract int searchIndex = 0; int unionIndex; while ( ( unionIndex = tableExpression.indexOf( UNION_ALL, searchIndex ) ) != -1 ) { - appendSql( tableExpression.substring( searchIndex, unionIndex ) ); + append( tableExpression, searchIndex, unionIndex ); renderLockHint( lockMode ); appendSql( UNION_ALL ); searchIndex = unionIndex + UNION_ALL.length(); } - appendSql( tableExpression.substring( searchIndex, tableExpression.length() - 2 ) ); + append( tableExpression, searchIndex, tableExpression.length() - 2 ); renderLockHint( lockMode ); appendSql( " )" ); @@ -82,24 +82,43 @@ public class SQLServerSqlAstTranslator extends Abstract private void renderLockHint(LockMode lockMode) { if ( getDialect().getVersion() >= 9 ) { final int effectiveLockTimeout = getEffectiveLockTimeout( lockMode ); - final String writeLockStr = effectiveLockTimeout == LockOptions.SKIP_LOCKED ? "updlock" : "updlock,holdlock"; - final String readLockStr = effectiveLockTimeout == LockOptions.SKIP_LOCKED ? "updlock" : "holdlock"; - - final String noWaitStr = effectiveLockTimeout == LockOptions.NO_WAIT ? ",nowait" : ""; - final String skipLockStr = effectiveLockTimeout == LockOptions.SKIP_LOCKED ? ",readpast" : ""; - switch ( lockMode ) { //noinspection deprecation case UPGRADE: case PESSIMISTIC_WRITE: case WRITE: - appendSql( " with (" + writeLockStr + ",rowlock" + noWaitStr + skipLockStr + ")" ); + switch ( effectiveLockTimeout ) { + case LockOptions.SKIP_LOCKED: + appendSql( " with (updlock,rowlock,readpast)" ); + break; + case LockOptions.NO_WAIT: + appendSql( " with (updlock,holdlock,rowlock,nowait)" ); + break; + default: + appendSql( " with (updlock,holdlock,rowlock)" ); + break; + } break; case PESSIMISTIC_READ: - appendSql( " with (" + readLockStr + ",rowlock" + noWaitStr + skipLockStr + ")" ); + switch ( effectiveLockTimeout ) { + case LockOptions.SKIP_LOCKED: + appendSql( " with (updlock,rowlock,readpast)" ); + break; + case LockOptions.NO_WAIT: + appendSql( " with (holdlock,rowlock,nowait)"); + break; + default: + appendSql( " with (holdlock,rowlock)"); + break; + } break; case UPGRADE_SKIPLOCKED: - appendSql( " with (updlock,rowlock,readpast" + noWaitStr + ")" ); + if ( effectiveLockTimeout == LockOptions.NO_WAIT ) { + appendSql( " with (updlock,rowlock,readpast,nowait)" ); + } + else { + appendSql( " with (updlock,rowlock,readpast)" ); + } break; case UPGRADE_NOWAIT: appendSql( " with (updlock,holdlock,rowlock,nowait)" ); @@ -322,7 +341,7 @@ public class SQLServerSqlAstTranslator extends Abstract Summarization summarization = (Summarization) expression; renderCommaSeparated( summarization.getGroupings() ); appendSql( " with " ); - appendSql( summarization.getKind().name().toLowerCase() ); + appendSql( summarization.getKind().sqlText() ); } else { expression.accept( this ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SpannerDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SpannerDialect.java index c47e25fb44..7136f1b0f1 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SpannerDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SpannerDialect.java @@ -34,6 +34,7 @@ import org.hibernate.query.TemporalUnit; import org.hibernate.query.spi.QueryEngine; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.exec.spi.JdbcOperation; @@ -416,8 +417,8 @@ public class SpannerDialect extends Dialect { } @Override - public String toBooleanValueString(boolean bool) { - return String.valueOf( bool ); + public void appendBooleanValueString(SqlAppender appender, boolean bool) { + appender.appendSql( bool ); } @Override @@ -489,8 +490,8 @@ public class SpannerDialect extends Dialect { } @Override - public String translateDatetimeFormat(String format) { - return datetimeFormat( format ).result(); + public void appendDatetimeFormat(SqlAppender appender, String format) { + appender.appendSql( datetimeFormat( format ).result() ); } public static Replacer datetimeFormat(String format) { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java index 5819913897..a21cd9c4a0 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java @@ -32,6 +32,7 @@ import org.hibernate.service.ServiceRegistry; import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; @@ -317,7 +318,7 @@ public class SybaseDialect extends AbstractTransactSQLDialect { } @Override - public String translateDatetimeFormat(String format) { + public void appendDatetimeFormat(SqlAppender appender, String format) { throw new NotYetImplementedFor6Exception( "format() function not supported on Sybase"); } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/CountFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/CountFunction.java index 11773fe1e8..909b966c47 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/CountFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/CountFunction.java @@ -105,7 +105,7 @@ public class CountFunction extends AbstractSqmSelfRenderingFunctionDescriptor { sqlAppender.appendSql( concatOperator ); sqlAppender.appendSql( "''" ); sqlAppender.appendSql( ",'\\0'),''),'\\0" ); - sqlAppender.appendSql( Integer.toString( argumentNumber ) ); + sqlAppender.appendSql( argumentNumber ); sqlAppender.appendSql( "')" ); sqlAppender.appendSql( concatOperator ); sqlAppender.appendSql( "'\\0'" ); @@ -117,7 +117,7 @@ public class CountFunction extends AbstractSqmSelfRenderingFunctionDescriptor { sqlAppender.appendSql( concatOperator ); sqlAppender.appendSql( "''" ); sqlAppender.appendSql( ",'\\0'),''),'\\0" ); - sqlAppender.appendSql( Integer.toString( argumentNumber ) ); + sqlAppender.appendSql( argumentNumber ); sqlAppender.appendSql( "')" ); if ( caseWrapper ) { sqlAppender.appendSql( " else null end" ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/CurrentFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/CurrentFunction.java index a1459d81ab..516810bf5d 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/CurrentFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/CurrentFunction.java @@ -38,7 +38,7 @@ public class CurrentFunction SqlAppender sqlAppender, List arguments, SqlAstTranslator walker) { - sqlAppender.appendSql(sql); + sqlAppender.appendSql( sql ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/SQLServerFormatEmulation.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/SQLServerFormatEmulation.java index 912e386e1e..3fd3e25817 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/SQLServerFormatEmulation.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/SQLServerFormatEmulation.java @@ -58,7 +58,7 @@ public class SQLServerFormatEmulation extends AbstractSqmSelfRenderingFunctionDe datetime.accept( walker ); } sqlAppender.appendSql(",'"); - sqlAppender.appendSql( dialect.translateDatetimeFormat( format.getFormat() ) ); + dialect.appendDatetimeFormat( sqlAppender, format.getFormat() ); sqlAppender.appendSql("')"); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java index f4106a3499..2078409793 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java @@ -16,7 +16,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.TimeZone; @@ -362,6 +361,34 @@ public abstract class AbstractSqlAstTranslator implemen sqlBuffer.append( fragment ); } + @Override + public void appendSql(int value) { + sqlBuffer.append( value ); + } + + @Override + public void appendSql(boolean value) { + sqlBuffer.append( value ); + } + + @Override + public Appendable append(CharSequence csq) { + sqlBuffer.append( csq ); + return this; + } + + @Override + public Appendable append(CharSequence csq, int start, int end) { + sqlBuffer.append( csq, start, end ); + return this; + } + + @Override + public Appendable append(char c) { + sqlBuffer.append( c ); + return this; + } + protected JdbcServices getJdbcServices() { return getSessionFactory().getJdbcServices(); } @@ -964,7 +991,7 @@ public abstract class AbstractSqlAstTranslator implemen } else { forUpdate.merge( getLockOptions() ); - forUpdate.applyAliases( dialect.getWriteRowLockStrategy(), querySpec ); + forUpdate.applyAliases( getDialect().getWriteRowLockStrategy(), querySpec ); if ( LockMode.READ.lessThan( forUpdate.getLockMode() ) ) { final LockStrategy lockStrategy = determineLockingStrategy( querySpec, @@ -994,7 +1021,7 @@ public abstract class AbstractSqlAstTranslator implemen else if ( lockOptions.getLockMode() != LockMode.NONE ) { final ForUpdateClause forUpdateClause = new ForUpdateClause(); forUpdateClause.merge( getLockOptions() ); - forUpdateClause.applyAliases( dialect.getWriteRowLockStrategy(), querySpec ); + forUpdateClause.applyAliases( getDialect().getWriteRowLockStrategy(), querySpec ); if ( LockMode.READ.lessThan( forUpdateClause.getLockMode() ) ) { final LockStrategy lockStrategy = determineLockingStrategy( querySpec, @@ -1021,7 +1048,7 @@ public abstract class AbstractSqlAstTranslator implemen } else if ( forUpdate != null ) { forUpdate.merge( getLockOptions() ); - forUpdate.applyAliases( dialect.getWriteRowLockStrategy(), querySpec ); + forUpdate.applyAliases( getDialect().getWriteRowLockStrategy(), querySpec ); if ( LockMode.READ.lessThan( forUpdate.getLockMode() ) ) { final LockStrategy lockStrategy = determineLockingStrategy( querySpec, forUpdate, null ); switch ( lockStrategy ) { @@ -1095,7 +1122,7 @@ public abstract class AbstractSqlAstTranslator implemen default: if ( getDialect().supportsWait() ) { appendSql( " wait " ); - appendSql( Integer.toString( Math.round( timeoutMillis / 1e3f ) ) ); + appendSql( Math.round( timeoutMillis / 1e3f ) ); } break; } @@ -1484,7 +1511,7 @@ public abstract class AbstractSqlAstTranslator implemen visitSelectClause( querySpec.getSelectClause() ); visitFromClause( querySpec.getFromClause() ); visitWhereClause( querySpec ); - visitGroupByClause( querySpec, dialect.getGroupBySelectItemReferenceStrategy() ); + visitGroupByClause( querySpec, getDialect().getGroupBySelectItemReferenceStrategy() ); visitHavingClause( querySpec ); visitOrderBy( querySpec.getSortSpecifications() ); visitOffsetFetchClause( querySpec ); @@ -1681,7 +1708,7 @@ public abstract class AbstractSqlAstTranslator implemen } else if ( expression instanceof Summarization ) { Summarization summarization = (Summarization) expression; - appendSql( summarization.getKind().name().toLowerCase() ); + appendSql( summarization.getKind().sqlText() ); appendSql( OPEN_PARENTHESIS ); renderCommaSeparated( summarization.getGroupings() ); appendSql( CLOSE_PARENTHESIS ); @@ -2093,8 +2120,8 @@ public abstract class AbstractSqlAstTranslator implemen nullPrecedence = sessionFactory.getSessionFactoryOptions().getDefaultNullPrecedence(); } final boolean renderNullPrecedence = nullPrecedence != null && - !nullPrecedence.isDefaultOrdering( sortOrder, dialect.getNullOrdering() ); - if ( renderNullPrecedence && !dialect.supportsNullPrecedence() ) { + !nullPrecedence.isDefaultOrdering( sortOrder, getDialect().getNullOrdering() ); + if ( renderNullPrecedence && !getDialect().supportsNullPrecedence() ) { emulateSortSpecificationNullPrecedence( sortExpression, nullPrecedence ); } @@ -2112,9 +2139,9 @@ public abstract class AbstractSqlAstTranslator implemen appendSql( " desc" ); } - if ( renderNullPrecedence && dialect.supportsNullPrecedence() ) { + if ( renderNullPrecedence && getDialect().supportsNullPrecedence() ) { appendSql( " nulls " ); - appendSql( nullPrecedence.name().toLowerCase( Locale.ROOT ) ); + appendSql( nullPrecedence == NullPrecedence.LAST ? "last" : "first" ); } } @@ -2397,7 +2424,7 @@ public abstract class AbstractSqlAstTranslator implemen renderOffsetExpression( offsetClauseExpression ); if ( offset != 0 ) { appendSql( '+' ); - appendSql( Integer.toString( offset ) ); + appendSql( offset ); } } @@ -2407,7 +2434,7 @@ public abstract class AbstractSqlAstTranslator implemen int offset) { final Number offsetCount = interpretExpression( offsetClauseExpression, jdbcParameterBindings ); final Number fetchCount = interpretExpression( fetchClauseExpression, jdbcParameterBindings ); - appendSql( Integer.toString( fetchCount.intValue() + offsetCount.intValue() + offset ) ); + appendSql( fetchCount.intValue() + offsetCount.intValue() + offset ); } protected void renderFetchPlusOffsetExpressionAsSingleParameter( @@ -2418,7 +2445,7 @@ public abstract class AbstractSqlAstTranslator implemen final Number fetchCount = (Number) ( (Literal) fetchClauseExpression ).getLiteralValue(); if ( offsetClauseExpression instanceof Literal ) { final Number offsetCount = (Number) ( (Literal) offsetClauseExpression ).getLiteralValue(); - appendSql( Integer.toString( fetchCount.intValue() + offsetCount.intValue() + offset ) ); + appendSql( fetchCount.intValue() + offsetCount.intValue() + offset ); } else { appendSql( PARAM_MARKER ); @@ -2653,7 +2680,7 @@ public abstract class AbstractSqlAstTranslator implemen } } else { - appendSql( Integer.toString( Integer.MAX_VALUE ) ); + appendSql( Integer.MAX_VALUE ); } } else if ( fetchExpression != null ) { @@ -2693,7 +2720,7 @@ public abstract class AbstractSqlAstTranslator implemen } else if ( offsetExpression != null ) { appendSql( " limit " ); - appendSql( Integer.toString( Integer.MAX_VALUE ) ); + appendSql( Integer.MAX_VALUE ); } if ( offsetExpression != null ) { final Stack clauseStack = getClauseStack(); @@ -2784,7 +2811,7 @@ public abstract class AbstractSqlAstTranslator implemen appendSql( separator ); appendSql( alias ); appendSql( ".c" ); - appendSql( Integer.toString( i ) ); + appendSql( i ); separator = COMA_SEPARATOR; } } @@ -2944,7 +2971,7 @@ public abstract class AbstractSqlAstTranslator implemen protected void visitSqlSelections(SelectClause selectClause) { final List sqlSelections = selectClause.getSqlSelections(); final int size = sqlSelections.size(); - final SelectItemReferenceStrategy referenceStrategy = dialect.getGroupBySelectItemReferenceStrategy(); + final SelectItemReferenceStrategy referenceStrategy = getDialect().getGroupBySelectItemReferenceStrategy(); // When the dialect needs to render the aliased expression and there are aliased group by items, // we need to inline parameters as the database would otherwise not be able to match the group by item // to the select item, ultimately leading to a query error @@ -2967,7 +2994,7 @@ public abstract class AbstractSqlAstTranslator implemen visitSqlSelection( sqlSelection ); parameterRenderingMode = original; appendSql( " c" ); - appendSql( Integer.toString( i ) ); + appendSql( i ); separator = COMA_SEPARATOR; } if ( queryPartForRowNumbering != null ) { @@ -3218,9 +3245,10 @@ public abstract class AbstractSqlAstTranslator implemen appendSql( "case when " ); expression.accept( this ); appendSql( " then " ); - appendSql( getDialect().toBooleanValueString( true ) ); + final Dialect dialect = getDialect(); + dialect.appendBooleanValueString( this, true ); appendSql( " else " ); - appendSql( getDialect().toBooleanValueString( false ) ); + dialect.appendBooleanValueString( this, false ); appendSql( " end" ); } else { @@ -3290,12 +3318,11 @@ public abstract class AbstractSqlAstTranslator implemen } } else { - appendSql( - literalFormatter.toJdbcLiteral( - literal.getLiteralValue(), - dialect, - getWrapperOptions() - ) + literalFormatter.appendJdbcLiteral( + this, + literal.getLiteralValue(), + dialect, + getWrapperOptions() ); } } @@ -3557,9 +3584,8 @@ public abstract class AbstractSqlAstTranslator implemen @Override public void visitFormat(Format format) { - final String dialectFormat = getDialect().translateDatetimeFormat( format.getFormat() ); appendSql( '\'' ); - appendSql( dialectFormat ); + getDialect().appendDatetimeFormat( this, format.getFormat() ); appendSql( '\'' ); } @@ -3680,297 +3706,16 @@ public abstract class AbstractSqlAstTranslator implemen @Override public void visitSqlSelectionExpression(SqlSelectionExpression expression) { - final boolean useSelectionPosition = dialect.supportsOrdinalSelectItemReference(); + final boolean useSelectionPosition = getDialect().supportsOrdinalSelectItemReference(); if ( useSelectionPosition ) { - appendSql( Integer.toString( expression.getSelection().getJdbcResultSetIndex() ) ); + appendSql( expression.getSelection().getJdbcResultSetIndex() ); } else { expression.getSelection().getExpression().accept( this ); } } - -// // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// // Expression : Function : Non-Standard -// -// @Override -// @SuppressWarnings("unchecked") -// public void visitNonStandardFunctionExpression(NonStandardFunction function) { -// appendSql( function.getFunctionName() ); -// if ( !function.getArguments().isEmpty() ) { -// appendSql( OPEN_PARENTHESIS ); -// String separator = NO_SEPARATOR; -// for ( Expression argumentExpression : function.getArguments() ) { -// appendSql( separator ); -// argumentExpression.accept( this ); -// separator = COMA_SEPARATOR; -// } -// appendSql( CLOSE_PARENTHESIS ); -// } -// } -// -// -// // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// // Expression : Function : Standard -// -// @Override -// @SuppressWarnings("unchecked") -// public void visitAbsFunction(AbsFunction function) { -// appendSql( "abs(" ); -// function.getArgument().accept( this ); -// appendSql( CLOSE_PARENTHESIS ); -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public void visitAvgFunction(AvgFunction function) { -// appendSql( "avg(" ); -// function.getArgument().accept( this ); -// appendSql( CLOSE_PARENTHESIS ); -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public void visitBitLengthFunction(BitLengthFunction function) { -// appendSql( "bit_length(" ); -// function.getArgument().accept( this ); -// appendSql( CLOSE_PARENTHESIS ); -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public void visitCastFunction(CastFunction function) { -// sqlAppender.appendSql( "cast(" ); -// function.getExpressionToCast().accept( this ); -// sqlAppender.appendSql( AS_KEYWORD ); -// sqlAppender.appendSql( determineCastTargetTypeSqlExpression( function ) ); -// sqlAppender.appendSql( CLOSE_PARENTHESIS ); -// } -// -// private String determineCastTargetTypeSqlExpression(CastFunction castFunction) { -// if ( castFunction.getExplicitCastTargetTypeSqlExpression() != null ) { -// return castFunction.getExplicitCastTargetTypeSqlExpression(); -// } -// -// final SqlExpressableType castResultType = castFunction.getCastResultType(); -// -// if ( castResultType == null ) { -// throw new SqlTreeException( -// "CastFunction did not define an explicit cast target SQL expression and its return type was null" -// ); -// } -// -// final BasicJavaDescriptor javaTypeDescriptor = castResultType.getJavaTypeDescriptor(); -// return getJdbcServices() -// .getDialect() -// .getCastTypeName( javaTypeDescriptor.getJdbcRecommendedSqlType( this ).getJdbcTypeCode() ); -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public void visitConcatFunction(ConcatFunction function) { -// appendSql( "concat(" ); -// -// boolean firstPass = true; -// for ( Expression expression : function.getExpressions() ) { -// if ( ! firstPass ) { -// appendSql( COMA_SEPARATOR ); -// } -// expression.accept( this ); -// firstPass = false; -// } -// -// appendSql( CLOSE_PARENTHESIS ); -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public void visitSubstrFunction(SubstrFunction function) { -// appendSql( "substr(" ); -// -// boolean firstPass = true; -// for ( Expression expression : function.getExpressions() ) { -// if ( ! firstPass ) { -// appendSql( COMA_SEPARATOR ); -// } -// expression.accept( this ); -// firstPass = false; -// } -// -// appendSql( CLOSE_PARENTHESIS ); -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public void visitCountFunction(CountFunction function) { -// appendSql( "count(" ); -// if ( function.isDistinct() ) { -// appendSql( DISTINCT_KEYWORD ); -// } -// function.getArgument().accept( this ); -// appendSql( CLOSE_PARENTHESIS ); -// } -// -// @Override -// public void visitCountStarFunction(CountStarFunction function) { -// appendSql( "count(" ); -// if ( function.isDistinct() ) { -// appendSql( DISTINCT_KEYWORD ); -// } -// appendSql( "*)" ); -// } -// -// @Override -// public void visitCurrentDateFunction(CurrentDateFunction function) { -// appendSql( "current_date" ); -// } -// -// @Override -// public void visitCurrentTimeFunction(CurrentTimeFunction function) { -// appendSql( "current_time" ); -// } -// -// @Override -// public void visitCurrentTimestampFunction(CurrentTimestampFunction function) { -// appendSql( "current_timestamp" ); -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public void visitExtractFunction(ExtractFunction extractFunction) { -// appendSql( "extract(" ); -// extractFunction.getUnitToExtract().accept( this ); -// appendSql( FROM_KEYWORD ); -// extractFunction.getExtractionSource().accept( this ); -// appendSql( CLOSE_PARENTHESIS ); -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public void visitLengthFunction(LengthFunction function) { -// sqlAppender.appendSql( "length(" ); -// function.getArgument().accept( this ); -// sqlAppender.appendSql( CLOSE_PARENTHESIS ); -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public void visitLocateFunction(LocateFunction function) { -// appendSql( "locate(" ); -// function.getPatternString().accept( this ); -// appendSql( COMA_SEPARATOR ); -// function.getStringToSearch().accept( this ); -// if ( function.getStartPosition() != null ) { -// appendSql( COMA_SEPARATOR ); -// function.getStartPosition().accept( this ); -// } -// appendSql( CLOSE_PARENTHESIS ); -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public void visitLowerFunction(LowerFunction function) { -// appendSql( "lower(" ); -// function.getArgument().accept( this ); -// appendSql( CLOSE_PARENTHESIS ); -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public void visitMaxFunction(MaxFunction function) { -// appendSql( "max(" ); -// if ( function.isDistinct() ) { -// appendSql( DISTINCT_KEYWORD ); -// } -// function.getArgument().accept( this ); -// appendSql( CLOSE_PARENTHESIS ); -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public void visitMinFunction(MinFunction function) { -// appendSql( "min(" ); -// if ( function.isDistinct() ) { -// appendSql( DISTINCT_KEYWORD ); -// } -// function.getArgument().accept( this ); -// appendSql( CLOSE_PARENTHESIS ); -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public void visitModFunction(ModFunction function) { -// sqlAppender.appendSql( "mod(" ); -// function.getDividend().accept( this ); -// sqlAppender.appendSql( COMA_SEPARATOR ); -// function.getDivisor().accept( this ); -// sqlAppender.appendSql( CLOSE_PARENTHESIS ); -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public void visitSqrtFunction(SqrtFunction function) { -// appendSql( "sqrt(" ); -// function.getArgument().accept( this ); -// appendSql( CLOSE_PARENTHESIS ); -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public void visitSumFunction(SumFunction function) { -// appendSql( "sum(" ); -// if ( function.isDistinct() ) { -// appendSql( DISTINCT_KEYWORD ); -// } -// function.getArgument().accept( this ); -// appendSql( CLOSE_PARENTHESIS ); -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public void visitTrimFunction(TrimFunction function) { -// sqlAppender.appendSql( "trim(" ); -// sqlAppender.appendSql( function.getSpecification().toSqlText() ); -// sqlAppender.appendSql( EMPTY_STRING_SEPARATOR ); -// function.getTrimCharacter().accept( this ); -// sqlAppender.appendSql( FROM_KEYWORD ); -// function.getSource().accept( this ); -// sqlAppender.appendSql( CLOSE_PARENTHESIS ); -// -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public void visitUpperFunction(UpperFunction function) { -// appendSql( "upper(" ); -// function.getArgument().accept( this ); -// appendSql( CLOSE_PARENTHESIS ); -// } -// -// @Override -// public void visitCoalesceFunction(CoalesceFunction coalesceExpression) { -// appendSql( "coalesce(" ); -// String separator = NO_SEPARATOR; -// for ( Expression expression : coalesceExpression.getValues() ) { -// appendSql( separator ); -// expression.accept( this ); -// separator = COMA_SEPARATOR; -// } -// -// appendSql( CLOSE_PARENTHESIS ); -// } -// -// @Override -// public void visitNullifFunction(NullifFunction function) { -// appendSql( "nullif(" ); -// function.getFirstArgument().accept( this ); -// appendSql( COMA_SEPARATOR ); -// function.getSecondArgument().accept( this ); -// appendSql( CLOSE_PARENTHESIS ); -// } - - @Override public void visitEntityTypeLiteral(EntityTypeLiteral expression) { final EntityPersister entityTypeDescriptor = expression.getEntityTypeDescriptor(); @@ -4179,7 +3924,6 @@ public abstract class AbstractSqlAstTranslator implemen private void visitLiteral(Literal literal) { if ( literal.getLiteralValue() == null ) { - // todo : not sure we allow this "higher up" appendSql( SqlAppender.NULL_KEYWORD ); } else { @@ -4199,12 +3943,11 @@ public abstract class AbstractSqlAstTranslator implemen throw new IllegalArgumentException( "Can't render parameter as literal, no literal formatter found" ); } else { - appendSql( - literalFormatter.toJdbcLiteral( - literalValue, - dialect, - getWrapperOptions() - ) + literalFormatter.appendJdbcLiteral( + this, + literalValue, + dialect, + getWrapperOptions() ); } } @@ -4248,7 +3991,7 @@ public abstract class AbstractSqlAstTranslator implemen // Most databases do not support boolean expressions in a predicate context, so we render `expr=true` booleanExpressionPredicate.getExpression().accept( this ); appendSql( '=' ); - appendSql( getDialect().toBooleanValueString( true ) ); + getDialect().appendBooleanValueString( this, true ); } @Override @@ -4314,7 +4057,7 @@ public abstract class AbstractSqlAstTranslator implemen ComparisonOperator.NOT_EQUAL : ComparisonOperator.EQUAL; // Some DBs like Oracle support tuples only for the IN subquery predicate - if ( supportsRowValueConstructorSyntaxInInSubQuery() && dialect.supportsUnionAll() ) { + if ( supportsRowValueConstructorSyntaxInInSubQuery() && getDialect().supportsUnionAll() ) { inListPredicate.getTestExpression().accept( this ); if ( inListPredicate.isNegated() ) { appendSql( " not" ); @@ -4527,7 +4270,7 @@ public abstract class AbstractSqlAstTranslator implemen visitSelectClause( subQuery.getSelectClause() ); visitFromClause( subQuery.getFromClause() ); visitWhereClause( subQuery ); - visitGroupByClause( subQuery, dialect.getGroupBySelectItemReferenceStrategy() ); + visitGroupByClause( subQuery, getDialect().getGroupBySelectItemReferenceStrategy() ); visitHavingClause( subQuery ); appendSql( " order by " ); @@ -4544,7 +4287,7 @@ public abstract class AbstractSqlAstTranslator implemen appendSql( order ); for ( int i = 1; i < sqlSelections.size(); i++ ) { appendSql( COMA_SEPARATOR_CHAR ); - appendSql( Integer.toString( i + 1 ) ); + appendSql( i + 1 ); appendSql( order ); } renderFetch( ONE_LITERAL, null, FetchClauseType.ROWS_ONLY ); @@ -4613,13 +4356,13 @@ public abstract class AbstractSqlAstTranslator implemen } } else { - if (dialect.supportsCaseInsensitiveLike()) { + if (getDialect().supportsCaseInsensitiveLike()) { likePredicate.getMatchExpression().accept( this ); if ( likePredicate.isNegated() ) { appendSql( " not" ); } appendSql( WHITESPACE ); - appendSql( dialect.getCaseInsensitiveLike() ); + appendSql( getDialect().getCaseInsensitiveLike() ); appendSql( WHITESPACE ); likePredicate.getPattern().accept( this ); if ( likePredicate.getEscapeCharacter() != null ) { @@ -4637,7 +4380,7 @@ public abstract class AbstractSqlAstTranslator implemen protected void renderCaseInsensitiveLikeEmulation(Expression lhs, Expression rhs, Expression escapeCharacter, boolean negated) { //LOWER(lhs) operator LOWER(rhs) - appendSql( dialect.getLowercaseFunction() ); + appendSql( getDialect().getLowercaseFunction() ); appendSql( OPEN_PARENTHESIS ); lhs.accept( this ); appendSql( CLOSE_PARENTHESIS ); @@ -4645,7 +4388,7 @@ public abstract class AbstractSqlAstTranslator implemen appendSql( " not" ); } appendSql( " like " ); - appendSql( dialect.getLowercaseFunction() ); + appendSql( getDialect().getLowercaseFunction() ); appendSql( OPEN_PARENTHESIS ); rhs.accept( this ); appendSql( CLOSE_PARENTHESIS ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAppender.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAppender.java index 3b4ef757d0..1354ece2f6 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAppender.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAppender.java @@ -6,12 +6,14 @@ */ package org.hibernate.sql.ast.spi; +import java.io.IOException; + /** * Access to appending SQL fragments to an in-flight buffer * * @author Steve Ebersole */ -public interface SqlAppender { +public interface SqlAppender extends Appendable { String NO_SEPARATOR = ""; String COMA_SEPARATOR = ","; char COMA_SEPARATOR_CHAR = ','; @@ -29,17 +31,31 @@ public interface SqlAppender { */ void appendSql(String fragment); - void appendSql(char fragment); - - default void appendQuoted(String value, char quoteChar) { - appendSql( quoteChar ); - for ( int i = 0; i < value.length(); i++ ) { - final char c = value.charAt( i ); - if ( c == quoteChar ) { - appendSql( quoteChar ); - } - appendSql( c ); - } - appendSql( quoteChar ); + default void appendSql(char fragment) { + appendSql( Character.toString( fragment ) ); } + + default void appendSql(int value) { + appendSql( Integer.toString( value ) ); + } + + default void appendSql(boolean value) { + appendSql( String.valueOf( value ) ); + } + + default Appendable append(CharSequence csq) { + appendSql( csq.toString() ); + return this; + } + + default Appendable append(CharSequence csq, int start, int end) { + appendSql( csq.toString().substring( start, end ) ); + return this; + } + + default Appendable append(char c) { + appendSql( Character.toString( c ) ); + return this; + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/Summarization.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/Summarization.java index a55da76394..10d3a75841 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/Summarization.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/Summarization.java @@ -43,7 +43,17 @@ public class Summarization implements Expression { } public enum Kind { - ROLLUP, - CUBE + ROLLUP( "rollup" ), + CUBE( "cube" ); + + private final String sqlText; + + Kind(String sqlText) { + this.sqlText = sqlText; + } + + public String sqlText() { + return sqlText; + } } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/DateTimeUtils.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/DateTimeUtils.java index 4eee54aac8..30efbac94a 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/DateTimeUtils.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/DateTimeUtils.java @@ -13,9 +13,13 @@ import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; +import java.util.Calendar; +import java.util.Date; import java.util.Locale; import java.util.TimeZone; +import org.hibernate.sql.ast.spi.SqlAppender; + import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE; import static java.time.format.DateTimeFormatter.ISO_LOCAL_TIME; @@ -71,8 +75,8 @@ public final class DateTimeUtils { .optionalStart().appendZoneOrOffsetId().optionalEnd() .toFormatter(); - private static final ThreadLocal LOCAL_DATE_FORMAT = ThreadLocal.withInitial( DateTimeUtils::simpleDateFormatDate ); - private static final ThreadLocal LOCAL_TIME_FORMAT = ThreadLocal.withInitial( DateTimeUtils::simpleDateFormatTime ); + private static final ThreadLocal LOCAL_DATE_FORMAT = ThreadLocal.withInitial( () -> new SimpleDateFormat( FORMAT_STRING_DATE, Locale.ENGLISH ) ); + private static final ThreadLocal LOCAL_TIME_FORMAT = ThreadLocal.withInitial( () -> new SimpleDateFormat( FORMAT_STRING_TIME, Locale.ENGLISH ) ); private static final ThreadLocal TIMESTAMP_WITH_MILLIS_FORMAT = ThreadLocal.withInitial( () -> new SimpleDateFormat( FORMAT_STRING_TIMESTAMP_WITH_MILLIS, @@ -103,201 +107,163 @@ public final class DateTimeUtils { .appendOffset("+HH:mm", "+00") .toFormatter(); - public static String formatAsTimestampWithMicros(TemporalAccessor temporalAccessor, boolean supportsOffset, TimeZone jdbcTimeZone) { + public static void appendAsTimestampWithMicros( + SqlAppender appender, + TemporalAccessor temporalAccessor, + boolean supportsOffset, + TimeZone jdbcTimeZone) { if ( temporalAccessor.isSupported(ChronoField.OFFSET_SECONDS) ) { if ( supportsOffset ) { - return DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS_AND_OFFSET.format( temporalAccessor ); + DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS_AND_OFFSET.formatTo( temporalAccessor, appender ); } else { - return DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS.format( + DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS.formatTo( LocalDateTime.ofInstant( Instant.from( temporalAccessor ), jdbcTimeZone.toZoneId() - ) + ), + appender ); } } else { - return DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS.format( temporalAccessor ); + DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS.formatTo( temporalAccessor, appender ); } } - public static String formatAsTimestampWithMillis(TemporalAccessor temporalAccessor, boolean supportsOffset, TimeZone jdbcTimeZone) { + public static void appendAsTimestampWithMillis( + SqlAppender appender, + TemporalAccessor temporalAccessor, + boolean supportsOffset, + TimeZone jdbcTimeZone) { if ( temporalAccessor.isSupported(ChronoField.OFFSET_SECONDS) ) { if ( supportsOffset ) { - return DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS_AND_OFFSET.format( temporalAccessor ); + DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS_AND_OFFSET.formatTo( temporalAccessor, appender ); } else { - return DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS.format( + DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS.formatTo( LocalDateTime.ofInstant( Instant.from( temporalAccessor ), jdbcTimeZone.toZoneId() - ) + ), + appender ); } } else { - return DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS.format( temporalAccessor ); + DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS.formatTo( temporalAccessor, appender ); } } - public static String formatAsDate(TemporalAccessor temporalAccessor) { - return DATE_TIME_FORMATTER_DATE.format( temporalAccessor ); + public static void appendAsDate(SqlAppender appender, TemporalAccessor temporalAccessor) { + DATE_TIME_FORMATTER_DATE.formatTo( temporalAccessor, appender ); } - public static String formatAsTime(TemporalAccessor temporalAccessor, boolean supportsOffset, TimeZone jdbcTimeZone) { + public static void appendAsTime( + SqlAppender appender, + TemporalAccessor temporalAccessor, + boolean supportsOffset, + TimeZone jdbcTimeZone) { if ( temporalAccessor.isSupported(ChronoField.OFFSET_SECONDS) ) { if ( supportsOffset ) { - return DATE_TIME_FORMATTER_TIME_WITH_OFFSET.format( temporalAccessor ); + DATE_TIME_FORMATTER_TIME_WITH_OFFSET.formatTo( temporalAccessor, appender ); } else { - return DATE_TIME_FORMATTER_TIME.format( + DATE_TIME_FORMATTER_TIME.formatTo( LocalDateTime.ofInstant( Instant.from( temporalAccessor ), jdbcTimeZone.toZoneId() - ) + ), + appender ); } } else { - return DATE_TIME_FORMATTER_TIME.format( temporalAccessor ); + DATE_TIME_FORMATTER_TIME.formatTo( temporalAccessor, appender ); } } - public static String formatAsTimestampWithMillis(java.util.Date date, TimeZone jdbcTimeZone) { + public static void appendAsTimestampWithMillis(SqlAppender appender, java.util.Date date, TimeZone jdbcTimeZone) { final SimpleDateFormat simpleDateFormat = TIMESTAMP_WITH_MILLIS_FORMAT.get(); final TimeZone originalTimeZone = simpleDateFormat.getTimeZone(); try { simpleDateFormat.setTimeZone( jdbcTimeZone ); - return simpleDateFormat.format( date ); + appender.appendSql( simpleDateFormat.format( date ) ); } finally { simpleDateFormat.setTimeZone( originalTimeZone ); } } - public static String formatAsTimestampWithMicros(java.util.Date date, TimeZone jdbcTimeZone) { + public static void appendAsTimestampWithMicros(SqlAppender appender, Date date, TimeZone jdbcTimeZone) { final SimpleDateFormat simpleDateFormat = TIMESTAMP_WITH_MICROS_FORMAT.get(); final TimeZone originalTimeZone = simpleDateFormat.getTimeZone(); try { simpleDateFormat.setTimeZone( jdbcTimeZone ); - return simpleDateFormat.format( date ); + appender.appendSql( simpleDateFormat.format( date ) ); } finally { simpleDateFormat.setTimeZone( originalTimeZone ); } } - public static String wrapAsJdbcDateLiteral(String literal) { - return JDBC_ESCAPE_START_DATE + literal + JDBC_ESCAPE_END; + public static void appendAsDate(SqlAppender appender, Date date) { + appender.appendSql( LOCAL_DATE_FORMAT.get().format( date ) ); } - public static String wrapAsJdbcTimeLiteral(String literal) { - return JDBC_ESCAPE_START_TIME + literal + JDBC_ESCAPE_END; + public static void appendAsTime(SqlAppender appender, java.util.Date date) { + appender.appendSql( LOCAL_TIME_FORMAT.get().format( date ) ); } - public static String wrapAsJdbcTimestampLiteral(String literal) { - return JDBC_ESCAPE_START_TIMESTAMP + literal + JDBC_ESCAPE_END; - } - - public static String wrapAsAnsiDateLiteral(String literal) { - return "date '" + literal + "'"; - } - - public static String wrapAsAnsiTimeLiteral(String literal) { - return "time '" + literal + "'"; - } - - public static String wrapAsAnsiTimestampLiteral(String literal) { - return "timestamp '" + literal + "'"; - } - - public static String formatAsDate(java.util.Date date) { - return LOCAL_DATE_FORMAT.get().format( date ); - } - - public static SimpleDateFormat simpleDateFormatDate() { - return new SimpleDateFormat( FORMAT_STRING_DATE, Locale.ENGLISH ); - } - - public static String formatAsTime(java.util.Date date) { - return LOCAL_TIME_FORMAT.get().format( date ); - } - - public static SimpleDateFormat simpleDateFormatTime() { - return new SimpleDateFormat( FORMAT_STRING_TIME, Locale.ENGLISH ); - } - - public static String formatAsTimestampWithMillis(java.util.Calendar calendar, TimeZone jdbcTimeZone) { + public static void appendAsTimestampWithMillis( + SqlAppender appender, + java.util.Calendar calendar, + TimeZone jdbcTimeZone) { final SimpleDateFormat simpleDateFormat = TIMESTAMP_WITH_MILLIS_FORMAT.get(); final TimeZone originalTimeZone = simpleDateFormat.getTimeZone(); try { simpleDateFormat.setTimeZone( jdbcTimeZone ); - return simpleDateFormat.format( calendar.getTime() ); + appender.appendSql( simpleDateFormat.format( calendar.getTime() ) ); } finally { simpleDateFormat.setTimeZone( originalTimeZone ); } } - public static SimpleDateFormat simpleDateFormatTimestampWithMillis(TimeZone timeZone) { - final SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_STRING_TIMESTAMP_WITH_MILLIS, Locale.ENGLISH ); - formatter.setTimeZone( timeZone ); - return formatter; - } - - public static String formatAsTimestampWithMicros(java.util.Calendar calendar, TimeZone jdbcTimeZone) { + public static void appendAsTimestampWithMicros(SqlAppender appender, Calendar calendar, TimeZone jdbcTimeZone) { final SimpleDateFormat simpleDateFormat = TIMESTAMP_WITH_MICROS_FORMAT.get(); final TimeZone originalTimeZone = simpleDateFormat.getTimeZone(); try { simpleDateFormat.setTimeZone( jdbcTimeZone ); - return simpleDateFormat.format( calendar.getTime() ); + appender.appendSql( simpleDateFormat.format( calendar.getTime() ) ); } finally { simpleDateFormat.setTimeZone( originalTimeZone ); } } - public static SimpleDateFormat simpleDateFormatTimestampWithMicros(TimeZone timeZone) { - final SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_STRING_TIMESTAMP_WITH_MICROS, Locale.ENGLISH ); - formatter.setTimeZone( timeZone ); - return formatter; - } - - public static String formatAsDate(java.util.Calendar calendar) { + public static void appendAsDate(SqlAppender appender, java.util.Calendar calendar) { final SimpleDateFormat simpleDateFormat = LOCAL_DATE_FORMAT.get(); final TimeZone originalTimeZone = simpleDateFormat.getTimeZone(); try { simpleDateFormat.setTimeZone( calendar.getTimeZone() ); - return simpleDateFormat.format( calendar.getTime() ); + appender.appendSql( simpleDateFormat.format( calendar.getTime() ) ); } finally { simpleDateFormat.setTimeZone( originalTimeZone ); } } - public static SimpleDateFormat simpleDateFormatDate(TimeZone timeZone) { - final SimpleDateFormat formatter = new SimpleDateFormat( FORMAT_STRING_DATE, Locale.ENGLISH ); - formatter.setTimeZone( timeZone ); - return formatter; - } - - public static String formatAsTime(java.util.Calendar calendar) { + public static void appendAsTime(SqlAppender appender, java.util.Calendar calendar) { final SimpleDateFormat simpleDateFormat = LOCAL_TIME_FORMAT.get(); final TimeZone originalTimeZone = simpleDateFormat.getTimeZone(); try { simpleDateFormat.setTimeZone( calendar.getTimeZone() ); - return simpleDateFormat.format( calendar.getTime() ); + appender.appendSql( simpleDateFormat.format( calendar.getTime() ) ); } finally { simpleDateFormat.setTimeZone( originalTimeZone ); } } - public static SimpleDateFormat simpleDateFormatTime(TimeZone timeZone) { - final SimpleDateFormat formatter = new SimpleDateFormat( FORMAT_STRING_TIME, Locale.ENGLISH ); - formatter.setTimeZone( timeZone ); - return formatter; - } - } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveByteArrayTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveByteArrayTypeDescriptor.java index 3112b8b3fd..c433626f16 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveByteArrayTypeDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveByteArrayTypeDescriptor.java @@ -16,6 +16,7 @@ import java.util.Comparator; import org.hibernate.HibernateException; import org.hibernate.engine.jdbc.BinaryStream; import org.hibernate.engine.jdbc.internal.BinaryStreamImpl; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.type.descriptor.WrapperOptions; /** @@ -48,14 +49,18 @@ public class PrimitiveByteArrayTypeDescriptor extends AbstractClassTypeDescripto public String toString(byte[] bytes) { final StringBuilder buf = new StringBuilder( bytes.length * 2 ); + appendString( buf::append, bytes ); + return buf.toString(); + } + + public void appendString(SqlAppender appender, byte[] bytes) { for ( byte aByte : bytes ) { final String hexStr = Integer.toHexString( Byte.toUnsignedInt(aByte) ); if ( hexStr.length() == 1 ) { - buf.append( '0' ); + appender.appendSql( '0' ); } - buf.append( hexStr ); + appender.appendSql( hexStr ); } - return buf.toString(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcLiteralFormatter.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcLiteralFormatter.java index de9dc79ee0..983ac97731 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcLiteralFormatter.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcLiteralFormatter.java @@ -7,6 +7,7 @@ package org.hibernate.type.descriptor.jdbc; import org.hibernate.dialect.Dialect; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.type.descriptor.WrapperOptions; /** @@ -24,5 +25,11 @@ import org.hibernate.type.descriptor.WrapperOptions; public interface JdbcLiteralFormatter { String NULL = "null"; - String toJdbcLiteral(T value, Dialect dialect, WrapperOptions wrapperOptions); + default String toJdbcLiteral(T value, Dialect dialect, WrapperOptions wrapperOptions) { + final StringBuilder sb = new StringBuilder(); + appendJdbcLiteral( sb::append, value, dialect, wrapperOptions ); + return sb.toString(); + } + + void appendJdbcLiteral(SqlAppender appender, T value, Dialect dialect, WrapperOptions wrapperOptions); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcTypeDescriptor.java index 6bedb5991b..fc2ac36d7d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcTypeDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcTypeDescriptor.java @@ -79,7 +79,7 @@ public interface JdbcTypeDescriptor extends Serializable { * todo (6.0) : move to {@link org.hibernate.metamodel.mapping.JdbcMapping}? */ default JdbcLiteralFormatter getJdbcLiteralFormatter(JavaTypeDescriptor javaTypeDescriptor) { - return (value, dialect, wrapperOptions) -> value.toString(); + return (appender, value, dialect, wrapperOptions) -> appender.appendSql( value.toString() ); } /** diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcLiteralFormatterBinary.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcLiteralFormatterBinary.java index acdb578adc..261c1545fc 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcLiteralFormatterBinary.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcLiteralFormatterBinary.java @@ -7,6 +7,7 @@ package org.hibernate.type.descriptor.jdbc.internal; import org.hibernate.dialect.Dialect; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.jdbc.spi.BasicJdbcLiteralFormatter; @@ -17,12 +18,12 @@ import org.hibernate.type.descriptor.jdbc.spi.BasicJdbcLiteralFormatter; * @author Gavin King */ public class JdbcLiteralFormatterBinary extends BasicJdbcLiteralFormatter { - public JdbcLiteralFormatterBinary(JavaTypeDescriptor javaTypeDescriptor) { + public JdbcLiteralFormatterBinary(JavaTypeDescriptor javaTypeDescriptor) { super( javaTypeDescriptor ); } @Override - public String toJdbcLiteral(Object value, Dialect dialect, WrapperOptions wrapperOptions) { - return dialect.formatBinaryLiteral( unwrap( value, byte[].class, wrapperOptions ) ); + public void appendJdbcLiteral(SqlAppender appender, Object value, Dialect dialect, WrapperOptions wrapperOptions) { + dialect.appendBinaryLiteral( appender, unwrap( value, byte[].class, wrapperOptions ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcLiteralFormatterBoolean.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcLiteralFormatterBoolean.java index 8a4321f8bf..87713b4c31 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcLiteralFormatterBoolean.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcLiteralFormatterBoolean.java @@ -7,6 +7,7 @@ package org.hibernate.type.descriptor.jdbc.internal; import org.hibernate.dialect.Dialect; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.jdbc.spi.BasicJdbcLiteralFormatter; @@ -17,12 +18,12 @@ import org.hibernate.type.descriptor.jdbc.spi.BasicJdbcLiteralFormatter; * @author Steve Ebersole */ public class JdbcLiteralFormatterBoolean extends BasicJdbcLiteralFormatter { - public JdbcLiteralFormatterBoolean(JavaTypeDescriptor javaTypeDescriptor) { + public JdbcLiteralFormatterBoolean(JavaTypeDescriptor javaTypeDescriptor) { super( javaTypeDescriptor ); } @Override - public String toJdbcLiteral(Object value, Dialect dialect, WrapperOptions wrapperOptions) { - return dialect.toBooleanValueString( unwrap( value, Boolean.class, wrapperOptions ) ); + public void appendJdbcLiteral(SqlAppender appender, Object value, Dialect dialect, WrapperOptions wrapperOptions) { + dialect.appendBooleanValueString( appender, unwrap( value, Boolean.class, wrapperOptions ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcLiteralFormatterCharacterData.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcLiteralFormatterCharacterData.java index bc0aa0be14..2c25e8503f 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcLiteralFormatterCharacterData.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcLiteralFormatterCharacterData.java @@ -7,6 +7,7 @@ package org.hibernate.type.descriptor.jdbc.internal; import org.hibernate.dialect.Dialect; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.jdbc.spi.BasicJdbcLiteralFormatter; @@ -21,26 +22,21 @@ public class JdbcLiteralFormatterCharacterData extends BasicJdbcLiteralFormatter private final boolean isNationalized; - public JdbcLiteralFormatterCharacterData(JavaTypeDescriptor javaTypeDescriptor) { + public JdbcLiteralFormatterCharacterData(JavaTypeDescriptor javaTypeDescriptor) { this( javaTypeDescriptor, false ); } - public JdbcLiteralFormatterCharacterData(JavaTypeDescriptor javaTypeDescriptor, boolean isNationalized) { + public JdbcLiteralFormatterCharacterData(JavaTypeDescriptor javaTypeDescriptor, boolean isNationalized) { super( javaTypeDescriptor ); this.isNationalized = isNationalized; } @Override - public String toJdbcLiteral(Object value, Dialect dialect, WrapperOptions wrapperOptions) { + public void appendJdbcLiteral(SqlAppender appender, Object value, Dialect dialect, WrapperOptions wrapperOptions) { final String literalValue = unwrap( value, String.class, wrapperOptions ); - - final String inlineLiteral = dialect.inlineLiteral( literalValue ); - if ( isNationalized ) { - // is there a standardized form for n-string literals? This is the SQL Server syntax for sure - return NATIONALIZED_PREFIX.concat( inlineLiteral ); + appender.appendSql( NATIONALIZED_PREFIX ); } - - return inlineLiteral; + dialect.appendLiteral( appender, literalValue ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcLiteralFormatterNumericData.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcLiteralFormatterNumericData.java index 8773e9b8a5..99d5ed4fd0 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcLiteralFormatterNumericData.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcLiteralFormatterNumericData.java @@ -7,6 +7,7 @@ package org.hibernate.type.descriptor.jdbc.internal; import org.hibernate.dialect.Dialect; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.jdbc.spi.BasicJdbcLiteralFormatter; @@ -18,14 +19,14 @@ public class JdbcLiteralFormatterNumericData extends BasicJdbcLiteralFormatter { private final Class unwrapJavaType; public JdbcLiteralFormatterNumericData( - JavaTypeDescriptor javaTypeDescriptor, + JavaTypeDescriptor javaTypeDescriptor, Class unwrapJavaType) { super( javaTypeDescriptor ); this.unwrapJavaType = unwrapJavaType; } @Override - public String toJdbcLiteral(Object value, Dialect dialect, WrapperOptions wrapperOptions) { - return unwrap( value, unwrapJavaType, wrapperOptions ).toString(); + public void appendJdbcLiteral(SqlAppender appender, Object value, Dialect dialect, WrapperOptions wrapperOptions) { + appender.appendSql( unwrap( value, unwrapJavaType, wrapperOptions ).toString() ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcLiteralFormatterTemporal.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcLiteralFormatterTemporal.java index 67b3ad6c76..fe88ccaecb 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcLiteralFormatterTemporal.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcLiteralFormatterTemporal.java @@ -11,6 +11,7 @@ import java.util.TimeZone; import jakarta.persistence.TemporalType; import org.hibernate.dialect.Dialect; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.jdbc.spi.BasicJdbcLiteralFormatter; @@ -21,13 +22,13 @@ import org.hibernate.type.descriptor.jdbc.spi.BasicJdbcLiteralFormatter; public class JdbcLiteralFormatterTemporal extends BasicJdbcLiteralFormatter { private final TemporalType precision; - public JdbcLiteralFormatterTemporal(JavaTypeDescriptor javaTypeDescriptor, TemporalType precision) { + public JdbcLiteralFormatterTemporal(JavaTypeDescriptor javaTypeDescriptor, TemporalType precision) { super( javaTypeDescriptor ); this.precision = precision; } @Override - public String toJdbcLiteral(Object value, Dialect dialect, WrapperOptions wrapperOptions) { + public void appendJdbcLiteral(SqlAppender appender, Object value, Dialect dialect, WrapperOptions wrapperOptions) { final TimeZone jdbcTimeZone; if ( wrapperOptions == null || wrapperOptions.getJdbcTimeZone() == null ) { jdbcTimeZone = TimeZone.getDefault(); @@ -37,48 +38,55 @@ public class JdbcLiteralFormatterTemporal extends BasicJdbcLiteralFormatter { } // for performance reasons, avoid conversions if we can if ( value instanceof java.util.Date ) { - return dialect.formatDateTimeLiteral( + dialect.appendDateTimeLiteral( + appender, (java.util.Date) value, precision, jdbcTimeZone ); } else if ( value instanceof java.util.Calendar ) { - return dialect.formatDateTimeLiteral( + dialect.appendDateTimeLiteral( + appender, (java.util.Calendar) value, precision, jdbcTimeZone ); } else if ( value instanceof TemporalAccessor ) { - return dialect.formatDateTimeLiteral( + dialect.appendDateTimeLiteral( + appender, (TemporalAccessor) value, precision, jdbcTimeZone ); } - - switch ( precision) { - case DATE: { - return dialect.formatDateTimeLiteral( - unwrap( value, java.sql.Date.class, wrapperOptions ), - precision, - jdbcTimeZone - ); - } - case TIME: { - return dialect.formatDateTimeLiteral( - unwrap( value, java.sql.Time.class, wrapperOptions ), - precision, - jdbcTimeZone - ); - } - default: { - return dialect.formatDateTimeLiteral( - unwrap( value, java.util.Date.class, wrapperOptions ), - precision, - jdbcTimeZone - ); + else { + switch ( precision ) { + case DATE: + dialect.appendDateTimeLiteral( + appender, + unwrap( value, java.sql.Date.class, wrapperOptions ), + precision, + jdbcTimeZone + ); + break; + case TIME: + dialect.appendDateTimeLiteral( + appender, + unwrap( value, java.sql.Time.class, wrapperOptions ), + precision, + jdbcTimeZone + ); + break; + default: + dialect.appendDateTimeLiteral( + appender, + unwrap( value, java.util.Date.class, wrapperOptions ), + precision, + jdbcTimeZone + ); + break; } } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/spi/TypeConfiguration.java b/hibernate-core/src/main/java/org/hibernate/type/spi/TypeConfiguration.java index a358f276cb..d8ebc8b32f 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/spi/TypeConfiguration.java +++ b/hibernate-core/src/main/java/org/hibernate/type/spi/TypeConfiguration.java @@ -354,7 +354,7 @@ public class TypeConfiguration implements SessionFactoryObserver, Serializable { private String sessionFactoryName; private String sessionFactoryUuid; - private transient JdbcTypeDescriptorIndicators currentSqlTypeIndicators = new JdbcTypeDescriptorIndicators() { + private final transient JdbcTypeDescriptorIndicators currentSqlTypeIndicators = new JdbcTypeDescriptorIndicators() { @Override public TypeConfiguration getTypeConfiguration() { return typeConfiguration; diff --git a/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/postgis/PGGeometryTypeDescriptor.java b/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/postgis/PGGeometryTypeDescriptor.java index 4429be9370..296b63904c 100644 --- a/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/postgis/PGGeometryTypeDescriptor.java +++ b/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/postgis/PGGeometryTypeDescriptor.java @@ -52,9 +52,17 @@ public class PGGeometryTypeDescriptor implements JdbcTypeDescriptor { @Override public JdbcLiteralFormatter getJdbcLiteralFormatter(JavaTypeDescriptor javaTypeDescriptor) { if ( javaTypeDescriptor instanceof GeolatteGeometryJavaTypeDescriptor ) { - return (value, dialect, wrapperOptions) -> "ST_GeomFromEWKT('" + value + "')"; + return (appender, value, dialect, wrapperOptions) -> { + appender.appendSql( "ST_GeomFromEWKT('" ); + appender.appendSql( value.toString() ); + appender.appendSql( "')" ); + }; } - return (value, dialect, wrapperOptions) -> "ST_GeomFromEWKT('" + jts2Gl( value ) + "')"; + return (appender, value, dialect, wrapperOptions) -> { + appender.appendSql( "ST_GeomFromEWKT('" ); + appender.appendSql( jts2Gl( value ).toString() ); + appender.appendSql( "')" ); + }; } private Geometry jts2Gl(T value) { diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java index 1c262b9073..ef53b5fdc0 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java @@ -334,7 +334,7 @@ abstract public class DialectFeatureChecks { public static class SupportsFormat implements DialectFeatureCheck { public boolean apply(Dialect dialect) { try { - dialect.translateDatetimeFormat( "" ); + dialect.appendDatetimeFormat( new StringBuilder()::append, "" ); return true; } catch (Exception ex) { @@ -346,7 +346,7 @@ abstract public class DialectFeatureChecks { public static class SupportsTruncateThroughCast implements DialectFeatureCheck { public boolean apply(Dialect dialect) { try { - dialect.translateDatetimeFormat( "" ); + dialect.appendDatetimeFormat( new StringBuilder()::append, "" ); return true; } catch (Exception ex) {