Skip to content

Fix escaping when processing parameters #1730

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
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( ':' ).append( ++count );
previous = '?';
return;
}
}
}
previous = codePoint;
result.appendCodePoint( codePoint );
}
super( ":" );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -29,31 +33,125 @@ 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();
}
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 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();
}

public abstract String process(String sql);
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();
}
}
}
Loading