allow collation names to be quoted

This commit is contained in:
Gavin King 2024-03-09 19:02:38 +01:00
parent 0e3791cb64
commit ef16d42c65
5 changed files with 34 additions and 9 deletions

View File

@ -797,6 +797,7 @@ Here we summarize the ones we've just seen in the second half of this chapter, a
| `@Struct` | Map an embeddable to a SQL UDT with the given name
| `@TimeZoneStorage` | Specify how the time zone information should be persisted
| `@JdbcType` or `@JdbcTypeCode` | Use an implementation of `JdbcType` to map an arbitrary SQL type
| `@Collate` | Specify a collation for a column
|===
In addition, there are some configuration properties which have a _global_ affect on how basic types map to SQL column types:

View File

@ -876,13 +876,17 @@ Its BNF is given by:
[discrete]
===== Collations
Selects a collation to be used for its string-valued argument.
The `collate()` function selects a collation to be used for its string-valued argument.
Collations are useful for <<relational-comparisons,binary comparisons>> with `<` or `>`, and in the <<order-by,order by clause>>.
For example, `collate(p.name as ucs_basic)` specifies the SQL standard collation `ucs_basic`.
IMPORTANT: Collations aren't very portable between databases.
TIP: Some PostgreSQL collation names must be quoted with backticks, for example, ``collate(name as \`zh_TW.UTF-8`)``.
TIP: The `@Collate` annotation may be used to specify the collation of a column, which is usually more convenient than using the `collate()` function.
[[functions-numeric]]
==== Numeric functions

View File

@ -742,7 +742,7 @@ public final class StringHelper {
final char first = name.charAt( 0 );
final char last = name.charAt( name.length() - 1 );
return ( ( first == last ) && ( first == '`' || first == '"' ) );
return first == last && ( first == '`' || first == '"' );
}
/**

View File

@ -39,7 +39,6 @@ import org.hibernate.grammars.hql.HqlLexer;
import org.hibernate.grammars.hql.HqlParser;
import org.hibernate.grammars.hql.HqlParserBaseVisitor;
import org.hibernate.internal.util.CharSequenceHelper;
import org.hibernate.internal.util.QuotingHelper;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.internal.util.collections.StandardStack;
import org.hibernate.metamodel.CollectionClassification;
@ -247,7 +246,10 @@ import static org.hibernate.grammars.hql.HqlParser.INTERSECT;
import static org.hibernate.grammars.hql.HqlParser.ListaggFunctionContext;
import static org.hibernate.grammars.hql.HqlParser.OnOverflowClauseContext;
import static org.hibernate.grammars.hql.HqlParser.PLUS;
import static org.hibernate.grammars.hql.HqlParser.QUOTED_IDENTIFIER;
import static org.hibernate.grammars.hql.HqlParser.UNION;
import static org.hibernate.internal.util.QuotingHelper.unquoteIdentifier;
import static org.hibernate.internal.util.QuotingHelper.unquoteJavaStringLiteral;
import static org.hibernate.internal.util.QuotingHelper.unquoteStringLiteral;
import static org.hibernate.query.hql.internal.SqmTreeCreationHelper.extractJpaCompliantAlias;
import static org.hibernate.query.sqm.TemporalUnit.DATE;
@ -1953,8 +1955,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
public String visitNakedIdentifier(HqlParser.NakedIdentifierContext ctx) {
final TerminalNode node = (TerminalNode) ctx.getChild( 0 );
final String text = node.getText();
return node.getSymbol().getType() == HqlParser.QUOTED_IDENTIFIER
? QuotingHelper.unquoteIdentifier( text )
return node.getSymbol().getType() == QUOTED_IDENTIFIER
? unquoteIdentifier( text )
: text;
}
@ -3166,9 +3168,21 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
@Override
public Object visitCollation(HqlParser.CollationContext ctx) {
return new SqmCollation(
ctx.simplePath().getText(),
null,
final StringBuilder collation = new StringBuilder();
final HqlParser.SimplePathContext simplePathContext = ctx.simplePath();
final boolean quoted = simplePathContext.getStart().getType() == QUOTED_IDENTIFIER;
if ( quoted ) {
collation.append("\"");
}
collation.append( visitIdentifier( simplePathContext.identifier() ) );
for ( HqlParser.SimplePathElementContext pathElementContext
: simplePathContext.simplePathElement() ) {
collation.append( visitIdentifier( pathElementContext.identifier() ) );
}
if ( quoted ) {
collation.append("\"");
}
return new SqmCollation( collation.toString(), null,
creationContext.getNodeBuilder() );
}
@ -3721,7 +3735,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
}
private SqmLiteral<String> javaStringLiteral(String text) {
String unquoted = QuotingHelper.unquoteJavaStringLiteral( text );
String unquoted = unquoteJavaStringLiteral( text );
return new SqmLiteral<>(
unquoted,
resolveExpressibleTypeBasic( String.class ),

View File

@ -51,6 +51,12 @@ public class CollateTests {
@Test @RequiresDialect(PostgreSQLDialect.class)
public void testCollatePostgreSQL(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery("from EntityOfBasics e where e.theString is not null order by collate(e.theString as `ucs_basic`)").getResultList();
assertThat( session.createQuery("select collate('bar' as `ucs_basic`) < 'foo'").getSingleResult(), is(true) );
}
);
scope.inTransaction(
session -> {
session.createQuery("from EntityOfBasics e where e.theString is not null order by collate(e.theString as ucs_basic)").getResultList();