From 5a4d7688e2d82011bb1c6cd2525d92aec0ba3386 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Mon, 21 Aug 2023 17:59:26 +0200 Subject: [PATCH 1/4] [#1614] Format existing code --- .../reactive/pool/impl/Parameters.java | 10 +- .../pool/impl/SQLServerParameters.java | 279 +++++++++--------- 2 files changed, 152 insertions(+), 137 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/Parameters.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/Parameters.java index e738a2abd..6362cc42e 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/Parameters.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/Parameters.java @@ -40,15 +40,19 @@ public static Parameters instance(Dialect dialect) { if ( dialect instanceof DialectDelegateWrapper ) { dialect = ( (DialectDelegateWrapper) dialect ).getWrappedDialect(); } - if (dialect instanceof PostgreSQLDialect || dialect instanceof CockroachDialect ) return PostgresParameters.INSTANCE; - if (dialect instanceof SQLServerDialect) return SQLServerParameters.INSTANCE; + if ( dialect instanceof PostgreSQLDialect || dialect instanceof CockroachDialect ) { + return PostgresParameters.INSTANCE; + } + if ( dialect instanceof SQLServerDialect ) { + return SQLServerParameters.INSTANCE; + } return NO_PARSING; } public static boolean isProcessingNotRequired(String sql) { return sql == null // There aren't any parameters - || sql.indexOf('?') == -1; + || sql.indexOf( '?' ) == -1; } public abstract String process(String sql); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SQLServerParameters.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SQLServerParameters.java index c48af1141..48cf3cfb4 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SQLServerParameters.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SQLServerParameters.java @@ -9,139 +9,150 @@ public class SQLServerParameters extends Parameters { - public static final SQLServerParameters INSTANCE = new SQLServerParameters(); - - private SQLServerParameters() { - } - - @Override - public String process(String sql) { - if (isProcessingNotRequired(sql)) { - return sql; - } - return new Parser(sql).result(); - } - - @Override - public String process(String sql, int parameterCount) { - if (isProcessingNotRequired(sql)) { - return sql; - } - return new Parser(sql, parameterCount).result(); - } - - /* Offset and Fetch gets applied just before the execution of the query but because we know - * how the string looks like for Sql Server, it's faster to replace the last bit instead - * of processing the whole query - */ - @Override - public String processLimit(String sql, Object[] parameterArray, boolean hasOffset) { - if ( isProcessingNotRequired( sql ) ) { - return sql; - } - - // Replace 'offset ? fetch next ? rows only' with the @P style parameters for Sql Server - int index = hasOffset ? parameterArray.length - 1 : parameterArray.length; - int pos = sql.indexOf( " offset " ); - if ( pos > -1 ) { - // The dialect doesn't use a parameter if the offset is 0 - String offsetQueryString = sql.contains( " offset 0 " ) - ? " offset 0" - : " offset @P" + index++; - String sqlProcessed = sql.substring( 0, pos ) + offsetQueryString + " rows"; - if ( sql.contains( " fetch next ?" ) ) { - sqlProcessed += " fetch next @P" + index + " rows only "; - } - return sqlProcessed; - } - // Different Hibernate ORM versions may use different lowercase/uppercase letters - if ( sql.toLowerCase( Locale.ROOT ).startsWith( "select top(?)" ) ) { - // 13 is the length of the string "select top(?)" - String sqlProcessed = "select top(@P" + index + ")" + sql.substring( 13 ); - shiftValues( parameterArray ); - return sqlProcessed; - } - - // No order by, no filter - // See SQLServer2005LimitHandler#processSql - int paginationFilterPos = sql.lastIndexOf( "__row__ >= ? and __row__ < ?" ); - if ( paginationFilterPos > -1 ) { - sql = sql.substring( 0, paginationFilterPos ); - sql += "__row__ >= @P" + index++ + " and __row__ < @P" + index++; - } - return sql; - } - - /** - * Left shift all the values in the array (moving the first value to the end) - */ - private void shiftValues(Object[] parameterArray) { - Object temp = parameterArray[0]; - System.arraycopy( parameterArray, 1, parameterArray, 0, parameterArray.length - 1 ); - parameterArray[parameterArray.length - 1] = temp; - } - - private static class Parser { - - private boolean inString; - private boolean inQuoted; - private boolean inSqlComment; - private boolean inCComment; - private boolean escaped; - private int count = 0; - private final StringBuilder result; - private int previous; - - private Parser(String sql) { - this(sql, 10); - } - - private Parser(String sql, int parameterCount) { - result = new StringBuilder(sql.length() + parameterCount); - sql.codePoints().forEach(this::append); - } - - private String result() { - return result.toString(); - } - - private void append(int codePoint) { - if (escaped) { - escaped = false; - } else { - switch (codePoint) { - case '\\': - escaped = true; - break; - case '"': - if (!inString && !inSqlComment && !inCComment) inQuoted = !inQuoted; - break; - case '\'': - if (!inQuoted && !inSqlComment && !inCComment) inString = !inString; - break; - case '-': - if (!inQuoted && !inString && !inCComment && previous == '-') inSqlComment = true; - break; - case '\n': - inSqlComment = false; - break; - case '*': - if (!inQuoted && !inString && !inSqlComment && previous == '/') inCComment = true; - break; - case '/': - if (previous == '*') inCComment = false; - break; - //TODO: $$-quoted strings - case '?': - if (!inQuoted && !inString) { - result.append("@P").append(++count); - previous = '?'; - return; + public static final SQLServerParameters INSTANCE = new SQLServerParameters(); + + private SQLServerParameters() { + } + + @Override + public String process(String sql) { + if ( isProcessingNotRequired( sql ) ) { + return sql; + } + return new Parser( sql ).result(); + } + + @Override + public String process(String sql, int parameterCount) { + if ( isProcessingNotRequired( sql ) ) { + return sql; + } + return new Parser( sql, parameterCount ).result(); + } + + /* Offset and Fetch gets applied just before the execution of the query but because we know + * how the string looks like for Sql Server, it's faster to replace the last bit instead + * of processing the whole query + */ + @Override + public String processLimit(String sql, Object[] parameterArray, boolean hasOffset) { + if ( isProcessingNotRequired( sql ) ) { + return sql; + } + + // Replace 'offset ? fetch next ? rows only' with the @P style parameters for Sql Server + int index = hasOffset ? parameterArray.length - 1 : parameterArray.length; + int pos = sql.indexOf( " offset " ); + if ( pos > -1 ) { + // The dialect doesn't use a parameter if the offset is 0 + String offsetQueryString = sql.contains( " offset 0 " ) + ? " offset 0" + : " offset @P" + index++; + String sqlProcessed = sql.substring( 0, pos ) + offsetQueryString + " rows"; + if ( sql.contains( " fetch next ?" ) ) { + sqlProcessed += " fetch next @P" + index + " rows only "; + } + return sqlProcessed; + } + // Different Hibernate ORM versions may use different lowercase/uppercase letters + if ( sql.toLowerCase( Locale.ROOT ).startsWith( "select top(?)" ) ) { + // 13 is the length of the string "select top(?)" + String sqlProcessed = "select top(@P" + index + ")" + sql.substring( 13 ); + shiftValues( parameterArray ); + return sqlProcessed; + } + + // No order by, no filter + // See SQLServer2005LimitHandler#processSql + int paginationFilterPos = sql.lastIndexOf( "__row__ >= ? and __row__ < ?" ); + if ( paginationFilterPos > -1 ) { + sql = sql.substring( 0, paginationFilterPos ); + sql += "__row__ >= @P" + index++ + " and __row__ < @P" + index++; + } + return sql; + } + + /** + * Left shift all the values in the array (moving the first value to the end) + */ + private void shiftValues(Object[] parameterArray) { + Object temp = parameterArray[0]; + System.arraycopy( parameterArray, 1, parameterArray, 0, parameterArray.length - 1 ); + parameterArray[parameterArray.length - 1] = temp; + } + + private static class Parser { + + private boolean inString; + private boolean inQuoted; + private boolean inSqlComment; + private boolean inCComment; + private boolean escaped; + private int count = 0; + private final StringBuilder result; + private int previous; + + private Parser(String sql) { + this( sql, 10 ); + } + + private Parser(String sql, int parameterCount) { + result = new StringBuilder( sql.length() + parameterCount ); + sql.codePoints().forEach( this::append ); + } + + private String result() { + return result.toString(); + } + + private void append(int codePoint) { + if ( escaped ) { + escaped = false; + } + else { + switch ( codePoint ) { + case '\\': + escaped = true; + break; + case '"': + if ( !inString && !inSqlComment && !inCComment ) { + inQuoted = !inQuoted; } - } - } - previous = codePoint; - result.appendCodePoint(codePoint); - } - } + break; + case '\'': + if ( !inQuoted && !inSqlComment && !inCComment ) { + inString = !inString; + } + break; + case '-': + if ( !inQuoted && !inString && !inCComment && previous == '-' ) { + inSqlComment = true; + } + break; + case '\n': + inSqlComment = false; + break; + case '*': + if ( !inQuoted && !inString && !inSqlComment && previous == '/' ) { + inCComment = true; + } + break; + case '/': + if ( previous == '*' ) { + inCComment = false; + } + break; + //TODO: $$-quoted strings + case '?': + if ( !inQuoted && !inString ) { + result.append( "@P" ).append( ++count ); + previous = '?'; + return; + } + } + } + previous = codePoint; + result.appendCodePoint( codePoint ); + } + } } From c516952ff33d5600eb09e08ad63e3998f4f1c5eb Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 22 Aug 2023 11:23:44 +0200 Subject: [PATCH 2/4] [#1614] Fix escaping when processing SQL Escaping wasn't having any effect because of a missing return. --- .../java/org/hibernate/reactive/pool/impl/OracleParameters.java | 2 +- .../org/hibernate/reactive/pool/impl/PostgresParameters.java | 2 +- .../org/hibernate/reactive/pool/impl/SQLServerParameters.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/OracleParameters.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/OracleParameters.java index 883651ece..6ab57efdd 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/OracleParameters.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/OracleParameters.java @@ -75,7 +75,7 @@ private void append(int codePoint) { switch ( codePoint ) { case '\\': escaped = true; - break; + return; case '"': if ( !inString && !inSqlComment && !inCComment ) { inQuoted = !inQuoted; diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/PostgresParameters.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/PostgresParameters.java index 5e4163411..69e3de5f6 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/PostgresParameters.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/PostgresParameters.java @@ -86,7 +86,7 @@ public void accept(int codePoint) { switch ( codePoint ) { case '\\': escaped = true; - break; + return; case '"': if ( !inString && !inSqlComment && !inCComment ) { inQuoted = !inQuoted; diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SQLServerParameters.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SQLServerParameters.java index 48cf3cfb4..672059046 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SQLServerParameters.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SQLServerParameters.java @@ -113,7 +113,7 @@ private void append(int codePoint) { switch ( codePoint ) { case '\\': escaped = true; - break; + return; case '"': if ( !inString && !inSqlComment && !inCComment ) { inQuoted = !inQuoted; From 1e42cc433db52433654891311a57f12ee64b519b Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Mon, 21 Aug 2023 23:20:42 +0200 Subject: [PATCH 3/4] [#1614] Refactor the code Group together all the duplicated code --- .../reactive/pool/impl/OracleParameters.java | 107 +------------ .../reactive/pool/impl/Parameters.java | 112 ++++++++++++-- .../pool/impl/PostgresParameters.java | 124 +-------------- .../pool/impl/SQLServerParameters.java | 145 +----------------- 4 files changed, 106 insertions(+), 382 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/OracleParameters.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/OracleParameters.java index 6ab57efdd..2f4dd34c1 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/OracleParameters.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/OracleParameters.java @@ -10,111 +10,6 @@ public class OracleParameters extends Parameters { public static final OracleParameters INSTANCE = new OracleParameters(); private OracleParameters() { - } - - public String process(String sql) { - if ( isProcessingNotRequired( sql ) ) { - return sql; - } - return new OracleParameters.Parser( sql ).result(); - } - - /** - * Limit and offset gets applied just before the execution of the query but because we know - * how the string looks like for Oracle, it's faster to replace the last bit instead - * of processing the whole query - */ - public String processLimit(String sql, Object[] parameterArray, boolean hasOffset) { - if ( isProcessingNotRequired( sql ) ) { - return sql; - } - - throw new UnsupportedOperationException(); - } - - /** - * Replace all JDBC-style {@code ?} parameters with Oracle-style - * {@code :n} parameters in the given SQL string. - */ - public String process(String sql, int parameterCount) { - if ( isProcessingNotRequired( sql ) ) { - return sql; - } - return new Parser( sql, parameterCount ).result(); - } - - private static class Parser { - - private boolean inString; - private boolean inQuoted; - private boolean inSqlComment; - private boolean inCComment; - private boolean escaped; - private int count = 0; - private StringBuilder result; - private int previous; - - private Parser(String sql) { - this( sql, 10 ); - } - - private Parser(String sql, int parameterCount) { - result = new StringBuilder( sql.length() + parameterCount ); - sql.codePoints().forEach( this::append ); - } - - private String result() { - return result.toString(); - } - - private void append(int codePoint) { - if ( escaped ) { - escaped = false; - } - else { - switch ( codePoint ) { - case '\\': - escaped = true; - return; - case '"': - if ( !inString && !inSqlComment && !inCComment ) { - inQuoted = !inQuoted; - } - break; - case '\'': - if ( !inQuoted && !inSqlComment && !inCComment ) { - inString = !inString; - } - break; - case '-': - if ( !inQuoted && !inString && !inCComment && previous == '-' ) { - inSqlComment = true; - } - break; - case '\n': - inSqlComment = false; - break; - case '*': - if ( !inQuoted && !inString && !inSqlComment && previous == '/' ) { - inCComment = true; - } - break; - case '/': - if ( previous == '*' ) { - inCComment = false; - } - break; - //TODO: $$-quoted strings - case '?': - if ( !inQuoted && !inString ) { - result.append( ':' ).append( ++count ); - previous = '?'; - return; - } - } - } - previous = codePoint; - result.appendCodePoint( codePoint ); - } + super( ":" ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/Parameters.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/Parameters.java index 6362cc42e..bf9ddfccb 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/Parameters.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/Parameters.java @@ -5,6 +5,8 @@ */ package org.hibernate.reactive.pool.impl; +import java.util.function.IntConsumer; + import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.DialectDelegateWrapper; @@ -19,7 +21,9 @@ */ public abstract class Parameters { - private static final Parameters NO_PARSING = new Parameters() { + private final String paramPrefix; + + private static final Parameters NO_PARSING = new Parameters( null ) { @Override public String process(String sql) { return sql; @@ -29,13 +33,12 @@ public String process(String sql) { public String process(String sql, int parameterCount) { return sql; } - - @Override - public String processLimit(String sql, Object[] parameterArray, boolean hasOffset) { - return sql; - } }; + protected Parameters(String paramPrefix) { + this.paramPrefix = paramPrefix; + } + public static Parameters instance(Dialect dialect) { if ( dialect instanceof DialectDelegateWrapper ) { dialect = ( (DialectDelegateWrapper) dialect ).getWrappedDialect(); @@ -55,9 +58,100 @@ public static boolean isProcessingNotRequired(String sql) { || sql.indexOf( '?' ) == -1; } - public abstract String process(String sql); + public String process(String sql) { + if ( isProcessingNotRequired( sql ) ) { + return sql; + } + return new Parser( sql, paramPrefix ).result(); + } + + /** + * Replace all JDBC-style {@code ?} parameters with Postgres-style + * {@code $n} parameters in the given SQL string. + */ + public String process(String sql, int parameterCount) { + if ( isProcessingNotRequired( sql ) ) { + return sql; + } + return new Parser( sql, parameterCount, paramPrefix ).result(); + } + + private static class Parser { - public abstract String process(String sql, int parameterCount); + private boolean inString; + private boolean inQuoted; + private boolean inSqlComment; + private boolean inCComment; + private boolean escaped; + private int count = 0; + private StringBuilder result; + private int previous; - public abstract String processLimit(String sql, Object[] parameterArray, boolean hasOffset); + private Parser(String sql, String paramPrefix) { + this( sql, 10, paramPrefix ); + } + + private Parser(String sql, int parameterCount, final String paramPrefix) { + result = new StringBuilder( sql.length() + parameterCount ); + // We aren't using lambdas or method reference because of a bug in the JVM: + // https://bugs.openjdk.java.net/browse/JDK-8161588 + // Please, don't change this unless you've tested it with Quarkus + sql.codePoints().forEach( new IntConsumer() { + @Override + public void accept(int codePoint) { + if ( escaped ) { + escaped = false; + } + else { + switch ( codePoint ) { + case '\\': + escaped = true; + return; + case '"': + if ( !inString && !inSqlComment && !inCComment ) { + inQuoted = !inQuoted; + } + break; + case '\'': + if ( !inQuoted && !inSqlComment && !inCComment ) { + inString = !inString; + } + break; + case '-': + if ( !inQuoted && !inString && !inCComment && previous == '-' ) { + inSqlComment = true; + } + break; + case '\n': + inSqlComment = false; + break; + case '*': + if ( !inQuoted && !inString && !inSqlComment && previous == '/' ) { + inCComment = true; + } + break; + case '/': + if ( previous == '*' ) { + inCComment = false; + } + break; + //TODO: $$-quoted strings + case '?': + if ( !inQuoted && !inString ) { + result.append( paramPrefix ).append( ++count ); + previous = '?'; + return; + } + } + } + previous = codePoint; + result.appendCodePoint( codePoint ); + } + } ); + } + + public String result() { + return result.toString(); + } + } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/PostgresParameters.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/PostgresParameters.java index 69e3de5f6..b53e37839 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/PostgresParameters.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/PostgresParameters.java @@ -5,133 +5,11 @@ */ package org.hibernate.reactive.pool.impl; -import java.util.function.IntConsumer; - public class PostgresParameters extends Parameters { public static final PostgresParameters INSTANCE = new PostgresParameters(); private PostgresParameters() { - } - - public String process(String sql) { - if ( isProcessingNotRequired( sql ) ) { - return sql; - } - return new Parser( sql ).result(); - } - - /** - * Limit and offset gets applied just before the execution of the query but because we know - * how the string looks like for Postgres, it's faster to replace the last bit instead - * of processing the whole query - */ - public String processLimit(String sql, Object[] parameterArray, boolean hasOffset) { - if ( isProcessingNotRequired( sql ) ) { - return sql; - } - - // Replace 'limit ? offset ?' with the $ style parameters for PostgreSQL - int index = hasOffset ? parameterArray.length - 1 : parameterArray.length; - int pos = sql.indexOf( " limit ?" ); - if ( pos > -1 ) { - String sqlProcessed = sql.substring( 0, pos ) + " limit $" + index++; - if ( hasOffset ) { - sqlProcessed += " offset $" + index; - } - return sqlProcessed; - } - - return sql; - } - - /** - * Replace all JDBC-style {@code ?} parameters with Postgres-style - * {@code $n} parameters in the given SQL string. - */ - public String process(String sql, int parameterCount) { - if ( isProcessingNotRequired( sql ) ) { - return sql; - } - return new Parser( sql, parameterCount ).result(); - } - - private static class Parser { - - private boolean inString; - private boolean inQuoted; - private boolean inSqlComment; - private boolean inCComment; - private boolean escaped; - private int count = 0; - private StringBuilder result; - private int previous; - - private Parser(String sql) { - this( sql, 10 ); - } - - private Parser(String sql, int parameterCount) { - result = new StringBuilder( sql.length() + parameterCount ); - // We aren't using lambdas or method reference because of a bug in the JVM: - // https://bugs.openjdk.java.net/browse/JDK-8161588 - // Please, don't change this unless you've tested it with Quarkus - sql.codePoints().forEach( new IntConsumer() { - @Override - public void accept(int codePoint) { - if ( escaped ) { - escaped = false; - } - else { - switch ( codePoint ) { - case '\\': - escaped = true; - return; - case '"': - if ( !inString && !inSqlComment && !inCComment ) { - inQuoted = !inQuoted; - } - break; - case '\'': - if ( !inQuoted && !inSqlComment && !inCComment ) { - inString = !inString; - } - break; - case '-': - if ( !inQuoted && !inString && !inCComment && previous == '-' ) { - inSqlComment = true; - } - break; - case '\n': - inSqlComment = false; - break; - case '*': - if ( !inQuoted && !inString && !inSqlComment && previous == '/' ) { - inCComment = true; - } - break; - case '/': - if ( previous == '*' ) { - inCComment = false; - } - break; - //TODO: $$-quoted strings - case '?': - if ( !inQuoted && !inString ) { - result.append( '$' ).append( ++count ); - previous = '?'; - return; - } - } - } - previous = codePoint; - result.appendCodePoint( codePoint ); - } - } ); - } - - private String result() { - return result.toString(); - } + super( "$" ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SQLServerParameters.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SQLServerParameters.java index 672059046..232c001c1 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SQLServerParameters.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SQLServerParameters.java @@ -5,154 +5,11 @@ */ package org.hibernate.reactive.pool.impl; -import java.util.Locale; - public class SQLServerParameters extends Parameters { public static final SQLServerParameters INSTANCE = new SQLServerParameters(); private SQLServerParameters() { - } - - @Override - public String process(String sql) { - if ( isProcessingNotRequired( sql ) ) { - return sql; - } - return new Parser( sql ).result(); - } - - @Override - public String process(String sql, int parameterCount) { - if ( isProcessingNotRequired( sql ) ) { - return sql; - } - return new Parser( sql, parameterCount ).result(); - } - - /* Offset and Fetch gets applied just before the execution of the query but because we know - * how the string looks like for Sql Server, it's faster to replace the last bit instead - * of processing the whole query - */ - @Override - public String processLimit(String sql, Object[] parameterArray, boolean hasOffset) { - if ( isProcessingNotRequired( sql ) ) { - return sql; - } - - // Replace 'offset ? fetch next ? rows only' with the @P style parameters for Sql Server - int index = hasOffset ? parameterArray.length - 1 : parameterArray.length; - int pos = sql.indexOf( " offset " ); - if ( pos > -1 ) { - // The dialect doesn't use a parameter if the offset is 0 - String offsetQueryString = sql.contains( " offset 0 " ) - ? " offset 0" - : " offset @P" + index++; - String sqlProcessed = sql.substring( 0, pos ) + offsetQueryString + " rows"; - if ( sql.contains( " fetch next ?" ) ) { - sqlProcessed += " fetch next @P" + index + " rows only "; - } - return sqlProcessed; - } - // Different Hibernate ORM versions may use different lowercase/uppercase letters - if ( sql.toLowerCase( Locale.ROOT ).startsWith( "select top(?)" ) ) { - // 13 is the length of the string "select top(?)" - String sqlProcessed = "select top(@P" + index + ")" + sql.substring( 13 ); - shiftValues( parameterArray ); - return sqlProcessed; - } - - // No order by, no filter - // See SQLServer2005LimitHandler#processSql - int paginationFilterPos = sql.lastIndexOf( "__row__ >= ? and __row__ < ?" ); - if ( paginationFilterPos > -1 ) { - sql = sql.substring( 0, paginationFilterPos ); - sql += "__row__ >= @P" + index++ + " and __row__ < @P" + index++; - } - return sql; - } - - /** - * Left shift all the values in the array (moving the first value to the end) - */ - private void shiftValues(Object[] parameterArray) { - Object temp = parameterArray[0]; - System.arraycopy( parameterArray, 1, parameterArray, 0, parameterArray.length - 1 ); - parameterArray[parameterArray.length - 1] = temp; - } - - private static class Parser { - - private boolean inString; - private boolean inQuoted; - private boolean inSqlComment; - private boolean inCComment; - private boolean escaped; - private int count = 0; - private final StringBuilder result; - private int previous; - - private Parser(String sql) { - this( sql, 10 ); - } - - private Parser(String sql, int parameterCount) { - result = new StringBuilder( sql.length() + parameterCount ); - sql.codePoints().forEach( this::append ); - } - - private String result() { - return result.toString(); - } - - private void append(int codePoint) { - if ( escaped ) { - escaped = false; - } - else { - switch ( codePoint ) { - case '\\': - escaped = true; - return; - case '"': - if ( !inString && !inSqlComment && !inCComment ) { - inQuoted = !inQuoted; - } - break; - case '\'': - if ( !inQuoted && !inSqlComment && !inCComment ) { - inString = !inString; - } - break; - case '-': - if ( !inQuoted && !inString && !inCComment && previous == '-' ) { - inSqlComment = true; - } - break; - case '\n': - inSqlComment = false; - break; - case '*': - if ( !inQuoted && !inString && !inSqlComment && previous == '/' ) { - inCComment = true; - } - break; - case '/': - if ( previous == '*' ) { - inCComment = false; - } - break; - //TODO: $$-quoted strings - case '?': - if ( !inQuoted && !inString ) { - result.append( "@P" ).append( ++count ); - previous = '?'; - return; - } - } - } - previous = codePoint; - result.appendCodePoint( codePoint ); - } + super( "@P" ); } } From 8718f8fbcb691060729ff2543296321d5d6cf955 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Mon, 21 Aug 2023 23:29:04 +0200 Subject: [PATCH 4/4] [#1614] Add test for Parameters processors --- .../reactive/ParametersProcessorTest.java | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/ParametersProcessorTest.java diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ParametersProcessorTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ParametersProcessorTest.java new file mode 100644 index 000000000..ed4b3323c --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ParametersProcessorTest.java @@ -0,0 +1,67 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive; + +import java.util.stream.Stream; + +import org.hibernate.reactive.pool.impl.OracleParameters; +import org.hibernate.reactive.pool.impl.PostgresParameters; +import org.hibernate.reactive.pool.impl.SQLServerParameters; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +/** + * Test the {@link org.hibernate.reactive.pool.impl.Parameters} processor for each database + */ +public class ParametersProcessorTest { + + /** + * Each test will replace this placeholder with the correct parameter prefix for the selected database + */ + private static final String PARAM_PREFIX = "__paramPrefix__"; + + + /** + * Return the strings to process and the expected result for each one + */ + static Stream expectations() { + return Stream.of( + arguments( "/* One comment */ \\?", "/* One comment */ ?" ), + arguments( "/* One comment */ ?", "/* One comment */ " + PARAM_PREFIX + "1" ), + arguments( "'Sql text ?'", "'Sql text ?'" ), + arguments( "\\?", "?" ), + arguments( "???", PARAM_PREFIX + "1" + PARAM_PREFIX + "2" + PARAM_PREFIX + "3" ), + arguments( "\\?|?", "?|" + PARAM_PREFIX + "1" ), + arguments( " ? ", " " + PARAM_PREFIX + "1 " ) + ); + } + + @ParameterizedTest + @MethodSource("expectations") + public void testPostgreSQLProcessing(String unprocessed, String expected) { + assertThat( PostgresParameters.INSTANCE.process( unprocessed ) ) + .isEqualTo( expected.replaceAll( PARAM_PREFIX, "\\$" ) ); + } + + @ParameterizedTest + @MethodSource("expectations") + public void testSqlServerProcessing(String unprocessed, String expected) { + assertThat( SQLServerParameters.INSTANCE.process( unprocessed ) ) + .isEqualTo( expected.replaceAll( PARAM_PREFIX, "@P" ) ); + } + + @ParameterizedTest + @MethodSource("expectations") + public void testOracleProcessing(String unprocessed, String expected) { + assertThat( OracleParameters.INSTANCE.process( unprocessed ) ) + .isEqualTo( expected.replaceAll( PARAM_PREFIX, ":" ) ); + } +}