Skip to content
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

Changes for SQL Server support #13

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ subprojects {
// Only enable these for local development, never push it:
// mavenLocal()
// jcenter()
// maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
// Example: ./gradlew build -PenableSonatypeOpenSourceSnapshotsRep
if ( project.hasProperty('enableSonatypeOpenSourceSnapshotsRep') ) {
maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' }
}
mavenCentral()

// Example: ./gradlew build -PenableJBossSnapshotsRep
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.hibernate.dialect.MariaDB103Dialect;
import org.hibernate.dialect.MySQL8Dialect;
import org.hibernate.dialect.PostgreSQL10Dialect;
import org.hibernate.dialect.SQLServerDialect;
import org.hibernate.hql.spi.id.IdTableSupportStandardImpl;

/**
Expand All @@ -28,7 +29,7 @@ public ReactiveIdTableSupport(Dialect dialect) {

@Override
public String generateIdTableName(String baseName) {
return "ht_" + baseName;
return (dialect instanceof SQLServerDialect ? "#" : "ht_") + baseName;
}

@Override
Expand All @@ -42,6 +43,9 @@ else if (dialect instanceof MySQL8Dialect || dialect instanceof MariaDB103Dialec
else if (dialect instanceof DB297Dialect) {
return "create global temporary table";
}
else if (dialect instanceof SQLServerDialect) {
return "create table";
}
else {
return "create local temporary table";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ private int defaultPort(String scheme) {
return 50000;
case "cockroachdb":
return 26257;
case "mssqlserver":
case "sqlserver":
return 1433;
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,17 @@
import org.hibernate.dialect.CockroachDB192Dialect;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.PostgreSQL9Dialect;
import org.hibernate.dialect.SQLServerDialect;

/**
* PostgreSQL has a "funny" parameter syntax of form {@code $n}, which
* Some databases have a different parameter syntax, which
* the Vert.x {@link io.vertx.sqlclient.SqlClient} does not abstract.
* This class converts JDBC/ODBC-style {@code ?} parameters generated
* by Hibernate ORM to this native format.
* by Hibernate ORM to the native format.
*/
public class Parameters {
public abstract class Parameters {

private static Parameters INSTANCE = new Parameters();

private static Parameters NO_PARSING = new Parameters() {
private static final Parameters NO_PARSING = new Parameters() {
@Override
public String process(String sql) {
return sql;
Expand All @@ -37,124 +36,20 @@ public String processLimit(String sql, Object[] parameterArray, boolean hasOffse
};

public static Parameters instance(Dialect dialect) {
return dialect instanceof PostgreSQL9Dialect || dialect instanceof CockroachDB192Dialect
? INSTANCE
: NO_PARSING;
}

private Parameters() {
}

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;
if (dialect instanceof PostgreSQL9Dialect || dialect instanceof CockroachDB192Dialect) return PostgresParameters.INSTANCE;
if (dialect instanceof SQLServerDialect) return SQLServerParameters.INSTANCE;
return NO_PARSING;
}

/**
* 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 boolean isProcessingNotRequired(String sql) {
public static boolean isProcessingNotRequired(String sql) {
return sql == null
// There aren't any parameters
|| sql.indexOf( '?' ) == -1;
|| sql.indexOf('?') == -1;
}

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

private Parser(String sql) {
this( sql, 10 );
}
public abstract String process(String sql, int parameterCount);

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 );
}
}
public abstract String processLimit(String sql, Object[] parameterArray, boolean hasOffset);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.reactive.pool.impl;

public class PostgresParameters extends Parameters {

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);
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);
}
}
}