HHH-10879 SqlServer dialect doesn't respect "key" reserved keyword

This commit is contained in:
Marvin Froeder 2016-06-22 17:05:41 +12:00 committed by Vlad Mihalcea
parent a9318ce656
commit 74e959f1bc
5 changed files with 219 additions and 200 deletions

View File

@ -55,6 +55,7 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
registerFunction( "trim", new AnsiTrimEmulationFunction() ); registerFunction( "trim", new AnsiTrimEmulationFunction() );
registerKeyword( "top" ); registerKeyword( "top" );
registerKeyword( "key" );
this.limitHandler = new TopLimitHandler( false, false ); this.limitHandler = new TopLimitHandler( false, false );
} }

View File

@ -74,7 +74,7 @@ public class NormalizingIdentifierHelperImpl implements IdentifierHelper {
} }
if ( autoQuoteKeywords && isReservedWord( identifier.getText() ) ) { if ( autoQuoteKeywords && isReservedWord( identifier.getText() ) ) {
log.tracef( "Forcing identifier [%s] to quoted as recognized reserveed word", identifier ); log.tracef( "Forcing identifier [%s] to quoted as recognized reserved word", identifier );
return Identifier.toIdentifier( identifier.getText(), true ); return Identifier.toIdentifier( identifier.getText(), true );
} }

View File

@ -13,7 +13,6 @@ import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.ManyToOne; import javax.persistence.ManyToOne;
import javax.persistence.Table;
/** /**
* @author Guenther Demetz * @author Guenther Demetz

View File

@ -6,32 +6,43 @@
*/ */
package org.hibernate.test.dialect.functional; package org.hibernate.test.dialect.functional;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.sql.Connection; import java.sql.Connection;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.LockOptions; import org.hibernate.LockOptions;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.Transaction; import org.hibernate.Transaction;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.criterion.Order; import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections; import org.hibernate.criterion.Projections;
import org.hibernate.dialect.SQLServer2005Dialect; import org.hibernate.dialect.SQLServer2005Dialect;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.exception.LockTimeoutException; import org.hibernate.exception.LockTimeoutException;
import org.hibernate.jdbc.ReturningWork; import org.hibernate.jdbc.ReturningWork;
import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.RequiresDialect;
import org.hibernate.testing.TestForIssue; import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/** /**
* used driver hibernate.connection.driver_class com.microsoft.sqlserver.jdbc.SQLServerDriver * used driver hibernate.connection.driver_class com.microsoft.sqlserver.jdbc.SQLServerDriver
* *
@ -39,41 +50,48 @@ import org.junit.Test;
*/ */
@RequiresDialect(value = { SQLServer2005Dialect.class }) @RequiresDialect(value = { SQLServer2005Dialect.class })
public class SQLServerDialectTest extends BaseCoreFunctionalTestCase { public class SQLServerDialectTest extends BaseCoreFunctionalTestCase {
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
@Override
protected Configuration constructConfiguration() {
Configuration configuration = super.constructConfiguration();
configuration.setProperty( AvailableSettings.KEYWORD_AUTO_QUOTING_ENABLED, Boolean.TRUE.toString() );
return configuration;
}
@Test @Test
@TestForIssue(jiraKey = "HHH-7198") @TestForIssue(jiraKey = "HHH-7198")
public void testMaxResultsSqlServerWithCaseSensitiveCollation() throws Exception { public void testMaxResultsSqlServerWithCaseSensitiveCollation() throws Exception {
final Session s = openSession(); final Session s1 = openSession();
s.beginTransaction(); s1.beginTransaction();
String defaultCollationName = s.doReturningWork( new ReturningWork<String>() { String defaultCollationName = s1.doReturningWork( connection -> {
@Override
public String execute(Connection connection) throws SQLException {
String databaseName = connection.getCatalog(); String databaseName = connection.getCatalog();
Statement st = ((SessionImplementor)s).getJdbcCoordinator().getStatementPreparer().createStatement(); Statement st = ((SessionImplementor)s1).getJdbcCoordinator().getStatementPreparer().createStatement();
ResultSet rs = ((SessionImplementor)s).getJdbcCoordinator().getResultSetReturn().extract( st, "SELECT collation_name FROM sys.databases WHERE name = '"+databaseName+ "';" ); ResultSet rs = ((SessionImplementor)s1).getJdbcCoordinator().getResultSetReturn().extract( st, "SELECT collation_name FROM sys.databases WHERE name = '"+databaseName+ "';" );
while(rs.next()){ while(rs.next()){
return rs.getString( "collation_name" ); return rs.getString( "collation_name" );
} }
throw new AssertionError( "can't get collation name of database "+databaseName ); throw new AssertionError( "can't get collation name of database "+databaseName );
}
} ); } );
s.getTransaction().commit(); s1.getTransaction().commit();
s.close(); s1.close();
Session s2 = openSession(); Session s2 = openSession();
Transaction tx = s2.beginTransaction();
String databaseName = s2.doReturningWork( new ReturningWork<String>() { String databaseName = s2.doReturningWork( new ReturningWork<String>() {
@Override @Override
public String execute(Connection connection) throws SQLException { public String execute(Connection connection) throws SQLException {
return connection.getCatalog(); return connection.getCatalog();
} }
} ); } );
s2.createSQLQuery( "ALTER DATABASE " + databaseName + " set single_user with rollback immediate" ) s2.createNativeQuery( "ALTER DATABASE " + databaseName + " set single_user with rollback immediate" )
.executeUpdate(); .executeUpdate();
s2.createSQLQuery( "ALTER DATABASE " + databaseName + " COLLATE Latin1_General_CS_AS" ).executeUpdate(); s2.createNativeQuery( "ALTER DATABASE " + databaseName + " COLLATE Latin1_General_CS_AS" ).executeUpdate();
s2.createSQLQuery( "ALTER DATABASE " + databaseName + " set multi_user" ).executeUpdate(); s2.createNativeQuery( "ALTER DATABASE " + databaseName + " set multi_user" ).executeUpdate();
Transaction tx = s2.beginTransaction();
for ( int i = 1; i <= 20; i++ ) { for ( int i = 1; i <= 20; i++ ) {
s2.persist( new Product2( i, "Kit" + i ) ); s2.persist( new Product2( i, "Kit" + i ) );
@ -89,58 +107,50 @@ public class SQLServerDialectTest extends BaseCoreFunctionalTestCase {
tx.rollback(); tx.rollback();
s2.close(); s2.close();
s2 = openSession(); executorService.execute( () -> {
s2.createSQLQuery( "ALTER DATABASE " + databaseName + " set single_user with rollback immediate" ) doInHibernate( this::sessionFactory, s3 -> {
s3.createNativeQuery( "ALTER DATABASE " + databaseName + " set single_user with rollback immediate" )
.executeUpdate(); .executeUpdate();
s2.createSQLQuery( "ALTER DATABASE " + databaseName + " COLLATE " + defaultCollationName ).executeUpdate(); s3.createNativeQuery( "ALTER DATABASE " + databaseName + " COLLATE " + defaultCollationName ).executeUpdate();
s2.createSQLQuery( "ALTER DATABASE " + databaseName + " set multi_user" ).executeUpdate(); s3.createNativeQuery( "ALTER DATABASE " + databaseName + " set multi_user" ).executeUpdate();
s2.close(); } );
} } );
private void doWork(Session s) {
} }
@Test @Test
@TestForIssue(jiraKey = "HHH-7369") @TestForIssue(jiraKey = "HHH-7369")
public void testPaginationWithScalarQuery() throws Exception { public void testPaginationWithScalarQuery() throws Exception {
Session s = openSession(); doInHibernate( this::sessionFactory, session -> {
Transaction tx = s.beginTransaction();
for ( int i = 0; i < 10; i++ ) { for ( int i = 0; i < 10; i++ ) {
s.persist( new Product2( i, "Kit" + i ) ); session.persist( new Product2( i, "Kit" + i ) );
} }
s.flush(); session.flush();
s.clear(); session.clear();
List list = s.createSQLQuery( "select id from Product2 where description like 'Kit%' order by id" ).list(); List list = session.createNativeQuery( "select id from Product2 where description like 'Kit%' order by id" ).list();
assertEquals(Integer.class, list.get(0).getClass()); // scalar result is an Integer assertEquals(Integer.class, list.get(0).getClass()); // scalar result is an Integer
list = s.createSQLQuery( "select id from Product2 where description like 'Kit%' order by id" ).setFirstResult( 2 ).setMaxResults( 2 ).list(); list = session.createNativeQuery( "select id from Product2 where description like 'Kit%' order by id" ).setFirstResult( 2 ).setMaxResults( 2 ).list();
assertEquals(Integer.class, list.get(0).getClass()); // this fails without patch, as result suddenly has become an array assertEquals(Integer.class, list.get(0).getClass()); // this fails without patch, as result suddenly has become an array
// same once again with alias // same once again with alias
list = s.createSQLQuery( "select id as myint from Product2 where description like 'Kit%' order by id asc" ).setFirstResult( 2 ).setMaxResults( 2 ).list(); list = session.createNativeQuery( "select id as myint from Product2 where description like 'Kit%' order by id asc" ).setFirstResult( 2 ).setMaxResults( 2 ).list();
assertEquals(Integer.class, list.get(0).getClass()); assertEquals(Integer.class, list.get(0).getClass());
} );
tx.rollback();
s.close();
} }
@Test @Test
@TestForIssue(jiraKey = "HHH-7368") @TestForIssue(jiraKey = "HHH-7368")
public void testPaginationWithTrailingSemicolon() throws Exception { public void testPaginationWithTrailingSemicolon() throws Exception {
Session s = openSession(); doInHibernate( this::sessionFactory, session -> {
s.createSQLQuery( "select id from Product2 where description like 'Kit%' order by id;" ) session.createNativeQuery( "select id from Product2 where description like 'Kit%' order by id;" )
.setFirstResult( 2 ).setMaxResults( 2 ).list(); .setFirstResult( 2 ).setMaxResults( 2 ).list();
s.close(); } );
} }
@Test @Test
public void testPaginationWithHQLProjection() { public void testPaginationWithHQLProjection() {
Session session = openSession(); doInHibernate( this::sessionFactory, session -> {
Transaction tx = session.beginTransaction();
for ( int i = 10; i < 20; i++ ) { for ( int i = 10; i < 20; i++ ) {
session.persist( new Product2( i, "Kit" + i ) ); session.persist( new Product2( i, "Kit" + i ) );
} }
@ -157,16 +167,12 @@ public class SQLServerDialectTest extends BaseCoreFunctionalTestCase {
assertEquals( 2, list.size() ); assertEquals( 2, list.size() );
assertArrayEquals( new Object[] {12, "Kit12", 19}, (Object[]) list.get( 0 )); assertArrayEquals( new Object[] {12, "Kit12", 19}, (Object[]) list.get( 0 ));
assertArrayEquals( new Object[] {13, "Kit13", 19}, (Object[]) list.get( 1 )); assertArrayEquals( new Object[] {13, "Kit13", 19}, (Object[]) list.get( 1 ));
} );
tx.rollback();
session.close();
} }
@Test @Test
public void testPaginationWithHQL() { public void testPaginationWithHQL() {
Session session = openSession(); doInHibernate( this::sessionFactory, session -> {
Transaction tx = session.beginTransaction();
for ( int i = 20; i < 30; i++ ) { for ( int i = 20; i < 30; i++ ) {
session.persist( new Product2( i, "Kit" + i ) ); session.persist( new Product2( i, "Kit" + i ) );
} }
@ -175,17 +181,13 @@ public class SQLServerDialectTest extends BaseCoreFunctionalTestCase {
List list = session.createQuery( "from Product2 order by id" ).setFirstResult( 3 ).setMaxResults( 2 ).list(); List list = session.createQuery( "from Product2 order by id" ).setFirstResult( 3 ).setMaxResults( 2 ).list();
assertEquals( Arrays.asList( new Product2( 23, "Kit23" ), new Product2( 24, "Kit24" ) ), list ); assertEquals( Arrays.asList( new Product2( 23, "Kit23" ), new Product2( 24, "Kit24" ) ), list );
} );
tx.rollback();
session.close();
} }
@Test @Test
@TestForIssue(jiraKey = "HHH-7370") @TestForIssue(jiraKey = "HHH-7370")
public void testPaginationWithMaxOnly() { public void testPaginationWithMaxOnly() {
Session session = openSession(); doInHibernate( this::sessionFactory, session -> {
Transaction tx = session.beginTransaction();
for ( int i = 30; i < 40; i++ ) { for ( int i = 30; i < 40; i++ ) {
session.persist( new Product2( i, "Kit" + i ) ); session.persist( new Product2( i, "Kit" + i ) );
} }
@ -196,18 +198,14 @@ public class SQLServerDialectTest extends BaseCoreFunctionalTestCase {
assertEquals( Arrays.asList( new Product2( 30, "Kit30" ), new Product2( 31, "Kit31" ) ), list ); assertEquals( Arrays.asList( new Product2( 30, "Kit30" ), new Product2( 31, "Kit31" ) ), list );
list = session.createQuery( "select distinct p from Product2 p order by p.id" ).setMaxResults( 1 ).list(); list = session.createQuery( "select distinct p from Product2 p order by p.id" ).setMaxResults( 1 ).list();
assertEquals( Arrays.asList( new Product2( 30, "Kit30" ) ), list ); assertEquals( Collections.singletonList( new Product2( 30, "Kit30" ) ), list );
} );
tx.rollback();
session.close();
} }
@Test @Test
@TestForIssue(jiraKey = "HHH-6627") @TestForIssue(jiraKey = "HHH-6627")
public void testPaginationWithAggregation() { public void testPaginationWithAggregation() {
Session session = openSession(); doInHibernate( this::sessionFactory, session -> {
Transaction tx = session.beginTransaction();
// populating test data // populating test data
Category category1 = new Category( 1, "Category1" ); Category category1 = new Category( 1, "Category1" );
Category category2 = new Category( 2, "Category2" ); Category category2 = new Category( 2, "Category2" );
@ -238,17 +236,13 @@ public class SQLServerDialectTest extends BaseCoreFunctionalTestCase {
assertEquals( 2, result.size() ); assertEquals( 2, result.size() );
assertArrayEquals( new Object[] { 2, 2L }, result.get( 0 ) ); // two products of second category assertArrayEquals( new Object[] { 2, 2L }, result.get( 0 ) ); // two products of second category
assertArrayEquals( new Object[] { 3, 1L }, result.get( 1 ) ); // one products of third category assertArrayEquals( new Object[] { 3, 1L }, result.get( 1 ) ); // one products of third category
} );
tx.rollback();
session.close();
} }
@Test @Test
@TestForIssue(jiraKey = "HHH-7752") @TestForIssue(jiraKey = "HHH-7752")
public void testPaginationWithFormulaSubquery() { public void testPaginationWithFormulaSubquery() {
Session session = openSession(); doInHibernate( this::sessionFactory, session -> {
Transaction tx = session.beginTransaction();
// populating test data // populating test data
Folder folder1 = new Folder( 1L, "Folder1" ); Folder folder1 = new Folder( 1L, "Folder1" );
Folder folder2 = new Folder( 2L, "Folder2" ); Folder folder2 = new Folder( 2L, "Folder2" );
@ -271,17 +265,13 @@ public class SQLServerDialectTest extends BaseCoreFunctionalTestCase {
List<Folder> distinctFolders = session.createQuery( "select distinct f from Folder f order by f.id desc" ) List<Folder> distinctFolders = session.createQuery( "select distinct f from Folder f order by f.id desc" )
.setFirstResult( 1 ).setMaxResults( 2 ).list(); .setFirstResult( 1 ).setMaxResults( 2 ).list();
assertEquals( Arrays.asList( folder2, folder1 ), distinctFolders ); assertEquals( Arrays.asList( folder2, folder1 ), distinctFolders );
} );
tx.rollback();
session.close();
} }
@Test @Test
@TestForIssue(jiraKey = "HHH-7781") @TestForIssue(jiraKey = "HHH-7781")
public void testPaginationWithCastOperator() { public void testPaginationWithCastOperator() {
Session session = openSession(); doInHibernate( this::sessionFactory, session -> {
Transaction tx = session.beginTransaction();
for ( int i = 40; i < 50; i++ ) { for ( int i = 40; i < 50; i++ ) {
session.persist( new Product2( i, "Kit" + i ) ); session.persist( new Product2( i, "Kit" + i ) );
} }
@ -293,9 +283,7 @@ public class SQLServerDialectTest extends BaseCoreFunctionalTestCase {
assertEquals( 2, list.size() ); assertEquals( 2, list.size() );
assertArrayEquals( new Object[] { 41, "41" }, list.get( 0 ) ); assertArrayEquals( new Object[] { 41, "41" }, list.get( 0 ) );
assertArrayEquals( new Object[] { 42, "42" }, list.get( 1 ) ); assertArrayEquals( new Object[] { 42, "42" }, list.get( 1 ) );
} );
tx.rollback();
session.close();
} }
@Test @Test
@ -309,31 +297,23 @@ public class SQLServerDialectTest extends BaseCoreFunctionalTestCase {
kit.description = "m"; kit.description = "m";
s.persist( kit ); s.persist( kit );
s.getTransaction().commit(); s.getTransaction().commit();
final Transaction tx = s.beginTransaction();
final Transaction tx = s.beginTransaction();
Session s2 = openSession(); Session s2 = openSession();
Transaction tx2 = s2.beginTransaction(); s2.beginTransaction();
Product2 kit2 = (Product2) s2.byId( Product2.class ).load( kit.id ); Product2 kit2 = s2.byId( Product2.class ).load( kit.id );
kit.description = "change!"; kit.description = "change!";
s.flush(); // creates write lock on kit until we end the transaction s.flush(); // creates write lock on kit until we end the transaction
Thread thread = new Thread( Thread thread = new Thread(
new Runnable() { () -> {
@Override sleep( TimeUnit.SECONDS.toMillis( 1 ) );
public void run() {
try {
Thread.sleep( 3000 );
}
catch ( InterruptedException e ) {
e.printStackTrace();
}
tx.commit(); tx.commit();
} }
}
); );
LockOptions opt = new LockOptions( LockMode.UPGRADE_NOWAIT ); LockOptions opt = new LockOptions( LockMode.UPGRADE_NOWAIT );
@ -348,10 +328,10 @@ public class SQLServerDialectTest extends BaseCoreFunctionalTestCase {
} }
long end = System.currentTimeMillis(); long end = System.currentTimeMillis();
thread.join(); thread.join();
long differenceInMillisecs = end - start; long differenceInMillis = end - start;
assertTrue( assertTrue(
"Lock NoWait blocked for " + differenceInMillisecs + " ms, this is definitely to much for Nowait", "Lock NoWait blocked for " + differenceInMillis + " ms, this is definitely to much for Nowait",
differenceInMillisecs < 2000 differenceInMillis < 2000
); );
s2.getTransaction().rollback(); s2.getTransaction().rollback();
@ -360,10 +340,40 @@ public class SQLServerDialectTest extends BaseCoreFunctionalTestCase {
s.getTransaction().commit(); s.getTransaction().commit();
} }
@Test
@TestForIssue(jiraKey = "HHH-10879")
public void testKeyReservedKeyword() {
doInHibernate( this::sessionFactory, session -> {
final KeyHolder keyHolder = new KeyHolder();
keyHolder.key = 4000;
session.persist( keyHolder );
} );
}
@Override @Override
protected java.lang.Class<?>[] getAnnotatedClasses() { protected java.lang.Class<?>[] getAnnotatedClasses() {
return new java.lang.Class[] { return new java.lang.Class[] {
Product2.class, Category.class, Folder.class, Contact.class Product2.class, Category.class, Folder.class, Contact.class, KeyHolder.class
}; };
} }
@Entity(name = "KeyHolder")
public static class KeyHolder {
@Id
public Integer key;
}
@Override
protected boolean rebuildSessionFactoryOnError() {
return false;
}
@Override
protected boolean isCleanupTestDataRequired() {
//SQL Server Driver does not deallocate connections right away
sleep(100);
return true;
}
} }

View File

@ -62,4 +62,13 @@ public abstract class BaseUnitTestCase {
} }
} }
} }
protected void sleep(long millis) {
try {
Thread.sleep( millis );
}
catch ( InterruptedException e ) {
Thread.interrupted();
}
}
} }