Skip to content
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 @@ -43,6 +43,7 @@
import org.hibernate.query.common.TemporalUnit;
import org.hibernate.query.sqm.IntervalType;
import org.hibernate.query.sqm.SetOperator;
import org.hibernate.query.sqm.TrimSpec;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.spi.LockingClauseStrategy;
Expand Down Expand Up @@ -653,6 +654,15 @@ public static Replacer datetimeFormat(String format) {
.replace("xx", "%z"); //note special case
}

@Override
public String trimPattern(TrimSpec specification, boolean isWhitespace) {
return switch ( specification ) {
case LEADING -> isWhitespace ? "ltrim(?1)" : "ltrim(?1, ?2)";
case TRAILING -> isWhitespace ? "rtrim(?1)" : "rtrim(?1, ?2)";
default -> isWhitespace ? "trim(?1)" : "trim(?1, ?2)";
};
}

/* DDL-related functions */

@Override
Expand Down Expand Up @@ -955,6 +965,24 @@ public String getSetOperatorSqlString(SetOperator operator) {
};
}

@Override
public String getDual() {
return "unnest([1])";
}

@Override
public String getFromDualForSelectOnly() {
return " from " + getDual() + " dual";
}

@Override
public boolean supportsLateral() {
// Spanner does not support the `LATERAL` keyword natively.
// However, we return true here because `SpannerSqlAstTranslator` emulates
// lateral joins using the `UNNEST(ARRAY(select as struct..)) alias` syntax.
return true;
}

/* Type conversion and casting */

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.DerivedTableReference;
import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.predicate.InArrayPredicate;
import org.hibernate.sql.ast.tree.predicate.LikePredicate;
import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SelectClause;
Expand Down Expand Up @@ -112,6 +116,8 @@ protected void renderDerivedTableReference(DerivedTableReference tableReference)
if ( correlated ) {
this.correlated = oldCorrelated;
appendSql( CLOSE_PARENTHESIS );
// Spanner requires the alias to be outside the parentheses UNNEST(... ) alias
super.renderTableReferenceIdentificationVariable( tableReference );
}
}

Expand Down Expand Up @@ -142,6 +148,19 @@ protected void visitUpdateStatementOnly(UpdateStatement statement) {
}
}

@Override
protected void renderTableReferenceIdentificationVariable(TableReference tableReference) {
// Spanner requires `UNNEST(...) alias`. Standard rendering places the alias
// inside the parentheses UNNEST(... alias). We suppress it here to manually
// render it outside the UNNEST wrapper in `renderDerivedTableReference`.
if ( correlated
&& tableReference instanceof DerivedTableReference
&& ((DerivedTableReference) tableReference).isLateral() ) {
return;
}
super.renderTableReferenceIdentificationVariable( tableReference );
}

@Override
protected void renderDmlTargetTableExpression(NamedTableReference tableReference) {
super.renderDmlTargetTableExpression( tableReference );
Expand All @@ -150,4 +169,30 @@ protected void renderDmlTargetTableExpression(NamedTableReference tableReference
}
}

@Override
protected void renderDerivedTableReferenceIdentificationVariable(DerivedTableReference tableReference) {
renderTableReferenceIdentificationVariable( tableReference );
}

@Override
public void visitQueryPartTableReference(QueryPartTableReference tableReference) {
emulateQueryPartTableReferenceColumnAliasing( tableReference );
}

@Override
public void visitInArrayPredicate(InArrayPredicate inArrayPredicate) {
inArrayPredicate.getTestExpression().accept( this );
appendSql( " in unnest(" );
inArrayPredicate.getArrayParameter().accept( this );
appendSql( ')' );
}

@Override
public void visitLikePredicate(LikePredicate likePredicate) {
if ( likePredicate.getEscapeCharacter() != null ) {
throw new UnsupportedOperationException( "Escape character is not supported by Spanner" );
}
super.visitLikePredicate( likePredicate );
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import jakarta.persistence.criteria.Root;

import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.SpannerDialect;
import org.hibernate.query.Query;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;

Expand All @@ -19,6 +20,7 @@
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.hibernate.testing.orm.junit.SkipForDialect;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -88,6 +90,7 @@ public void testLike(SessionFactoryScope scope) {
}

@Test
@SkipForDialect(dialectClass = SpannerDialect.class, reason = "Spanner does not support escape character")
public void testLikeEscape(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
Expand Down Expand Up @@ -140,6 +143,7 @@ public void testNotLike(SessionFactoryScope scope) {
}

@Test
@SkipForDialect(dialectClass = SpannerDialect.class, reason = "Spanner does not support escape character")
public void testNotLikeEscape(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
Expand Down Expand Up @@ -192,6 +196,7 @@ public void testIlike(SessionFactoryScope scope) {
}

@Test
@SkipForDialect(dialectClass = SpannerDialect.class, reason = "Spanner does not support escape character")
public void testIlikeEscape(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
Expand Down Expand Up @@ -244,6 +249,7 @@ public void testNotIlike(SessionFactoryScope scope) {
}

@Test
@SkipForDialect(dialectClass = SpannerDialect.class, reason = "Spanner does not support escape character")
public void testNotIlikeEscape(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import java.util.List;

import org.hibernate.dialect.SpannerDialect;
import org.hibernate.query.Query;

import org.hibernate.testing.orm.domain.StandardDomainModel;
Expand All @@ -14,6 +15,7 @@
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.SkipForDialect;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
Expand Down Expand Up @@ -85,6 +87,7 @@ public void testNotLike(SessionFactoryScope scope) {
}

@Test
@SkipForDialect(dialectClass = SpannerDialect.class, reason = "Spanner does not support escape character")
public void testLikeEscape(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
Expand All @@ -96,6 +99,7 @@ public void testLikeEscape(SessionFactoryScope scope) {
}

@Test
@SkipForDialect(dialectClass = SpannerDialect.class, reason = "Spanner does not support escape character")
public void testLikeEscapeParam(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
Expand All @@ -108,6 +112,7 @@ public void testLikeEscapeParam(SessionFactoryScope scope) {
}

@Test
@SkipForDialect(dialectClass = SpannerDialect.class, reason = "Spanner does not support escape character")
public void testNotLikeEscape(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
Expand Down Expand Up @@ -141,6 +146,7 @@ public void testNotIlike(SessionFactoryScope scope) {
}

@Test
@SkipForDialect(dialectClass = SpannerDialect.class, reason = "Spanner does not support escape character")
public void testIlikeEscape(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
Expand All @@ -152,6 +158,7 @@ public void testIlikeEscape(SessionFactoryScope scope) {
}

@Test
@SkipForDialect(dialectClass = SpannerDialect.class, reason = "Spanner does not support escape character")
public void testNotIlikeEscape(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.dialect.HANADialect;
import org.hibernate.dialect.SpannerDialect;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.query.Query;

Expand Down Expand Up @@ -53,6 +54,7 @@ public void cleanUpTestData(SessionFactoryScope scope) {

@Test
@SkipForDialect(dialectClass = HANADialect.class, reason = "HANA supports only ResultSet.TYPE_FORWARD_ONLY")
@SkipForDialect(dialectClass = SpannerDialect.class, reason = "Spanner supports only ResultSet.TYPE_FORWARD_ONLY")
public void testCursorPositioning(SessionFactoryScope scope) {
// create an extra row so we can better test cursor positioning
scope.inTransaction(
Expand Down Expand Up @@ -262,8 +264,9 @@ private static <R> void verifyScroll(Query<R> query, Consumer<R> validator) {
}

final SessionImplementor session = (SessionImplementor) query.getSession();
// HANA supports only ResultSet.TYPE_FORWARD_ONLY
if ( !( session.getFactory().getJdbcServices().getDialect() instanceof HANADialect ) ) {
// HANA and Spanner support only ResultSet.TYPE_FORWARD_ONLY
if ( !(session.getFactory().getJdbcServices().getDialect() instanceof HANADialect) &&
!(session.getFactory().getJdbcServices().getDialect() instanceof SpannerDialect) ) {
try (final ScrollableResults<R> results = query.scroll( ScrollMode.SCROLL_INSENSITIVE )) {
assertThat( results.next(), is( true ) );
validator.accept( results.get() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import org.hibernate.ScrollMode;
import org.hibernate.dialect.HANADialect;
import org.hibernate.dialect.SpannerDialect;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.query.IllegalSelectQueryException;
import org.hibernate.query.SelectionQuery;
Expand All @@ -39,6 +40,7 @@
)
@SessionFactory
@SkipForDialect( dialectClass = HANADialect.class, reason = "HANA does not support scrollable results")
@SkipForDialect( dialectClass = SpannerDialect.class, reason = "Spanner does not support scrollable results")
public class BasicSelectionQueryTests {
@Test
public void typedEntitySelectTest(SessionFactoryScope scope) {
Expand Down
Loading