diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/QualifiedNameParser.java b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/QualifiedNameParser.java index 9aabf934ff..62d8ad160b 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/QualifiedNameParser.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/QualifiedNameParser.java @@ -11,6 +11,7 @@ import java.util.Objects; import org.hibernate.HibernateException; import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.naming.IllegalIdentifierException; +import org.hibernate.internal.util.StringHelper; /** * Parses a qualified name. @@ -114,6 +115,17 @@ public class QualifiedNameParser { throw new IllegalIdentifierException( "Object name to parse must be specified, but found null" ); } + final int quoteCharCount = StringHelper.count( text, "`" ); + final boolean wasQuotedInEntirety = quoteCharCount == 2 && text.startsWith( "`" ) && text.endsWith( "`" ); + + if ( wasQuotedInEntirety ) { + return new NameParts( + defaultCatalog, + defaultSchema, + Identifier.toIdentifier( unquote( text ), true ) + ); + } + String catalogName = null; String schemaName = null; String name; @@ -122,15 +134,6 @@ public class QualifiedNameParser { boolean schemaWasQuoted = false; boolean nameWasQuoted; - // Note that we try to handle both forms of quoting, - // 1) where the entire string was quoted - // 2) where one or more individual parts were quoted - - boolean wasQuotedInEntirety = text.startsWith( "`" ) && text.endsWith( "`" ); - if ( wasQuotedInEntirety ) { - text = unquote( text ); - } - final String[] tokens = text.split( "\\." ); if ( tokens.length == 0 || tokens.length == 1 ) { // we have just a local name... diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/naming/QualifiedNameParserTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/naming/QualifiedNameParserTests.java new file mode 100644 index 0000000000..164118448d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/naming/QualifiedNameParserTests.java @@ -0,0 +1,147 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html. + */ +package org.hibernate.orm.test.naming; + +import org.hibernate.boot.model.naming.Identifier; +import org.hibernate.boot.model.relational.QualifiedNameParser; +import org.hibernate.boot.model.relational.QualifiedNameParser.NameParts; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Steve Ebersole + */ +public class QualifiedNameParserTests { + private final Identifier NO_CATALOG = Identifier.toIdentifier( "" ); + private final Identifier NO_SCHEMA = Identifier.toIdentifier( "" ); + + private final Identifier CATALOG1 = Identifier.toIdentifier( "catalog1", false ); + private final Identifier SCHEMA1 = Identifier.toIdentifier( "schema1", false ); + + private final Identifier CATALOG2 = Identifier.toIdentifier( "catalog2", true ); + private final Identifier SCHEMA2 = Identifier.toIdentifier( "schema2", true ); + + @Test + void testSimpleUnquoted() { + final String nameText = "tbl"; + final Identifier name = Identifier.toIdentifier( nameText ); + assert !name.isQuoted(); + + test( + nameText, + NO_CATALOG, + NO_SCHEMA, + NO_CATALOG, + NO_SCHEMA, + name + ); + test( + nameText, + CATALOG1, + SCHEMA1, + CATALOG1, + SCHEMA1, + name + ); + } + + @Test + void testSimpleQuoted() { + final String nameText = "`tbl`"; + final Identifier name = Identifier.toIdentifier( nameText ); + assert name.isQuoted(); + + test( + nameText, + NO_CATALOG, + NO_SCHEMA, + NO_CATALOG, + NO_SCHEMA, + name + ); + test( + nameText, + CATALOG1, + SCHEMA1, + CATALOG1, + SCHEMA1, + name + ); + } + + @Test + void testIndividualQuotes() { + final String name = "`schema2`.`catalog2`.`tbl`"; + test( + name, + NO_CATALOG, + NO_SCHEMA, + CATALOG2, + SCHEMA2, + Identifier.toIdentifier( "tbl", true ) + ); + test( + name, + CATALOG1, + SCHEMA1, + CATALOG2, + SCHEMA2, + Identifier.toIdentifier( "tbl", true ) + ); + } + + @Test + void testCrazyName() { + final String nameText = "`abc.def.ghi::other.stuff`"; + final Identifier name = Identifier.toIdentifier( nameText ); + assert name.isQuoted(); + + test( + nameText, + NO_CATALOG, + NO_SCHEMA, + NO_CATALOG, + NO_SCHEMA, + name + ); + + test( + nameText, + CATALOG1, + SCHEMA1, + CATALOG1, + SCHEMA1, + name + ); + } + + + private static void test( + String name, + Identifier defaultCatalogName, + Identifier defaultSchemaName, + Identifier expectedCatalogName, + Identifier expectedSchemaName, + Identifier expectedName) { + final NameParts parsed = QualifiedNameParser.INSTANCE.parse( name, defaultCatalogName, defaultSchemaName ); + + assertSame( parsed.getCatalogName(), expectedCatalogName ); + assertSame( parsed.getSchemaName(), expectedSchemaName ); + assertSame( parsed.getObjectName(), expectedName ); + } + + private static void assertSame(Identifier one, Identifier another) { + if ( one == null ) { + assertThat( another ).isNull(); + } + else { + assertThat( one ).isEqualTo( another ); + } + } +}