/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.support;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Arrays;
import javax.sql.DataSource;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.mockito.BDDMockito.*;
/**
* Tests for SQLErrorCodes loading.
*
* @author Rod Johnson
* @author Thomas Risberg
* @author Stephane Nicoll
*/
public class SQLErrorCodesFactoryTests {
/**
* Check that a default instance returns empty error codes for an unknown database.
*/
@Test
public void testDefaultInstanceWithNoSuchDatabase() {
SQLErrorCodes sec = SQLErrorCodesFactory.getInstance().getErrorCodes("xx");
assertTrue(sec.getBadSqlGrammarCodes().length == 0);
assertTrue(sec.getDataIntegrityViolationCodes().length == 0);
}
/**
* Check that a known database produces recognizable codes.
*/
@Test
public void testDefaultInstanceWithOracle() {
SQLErrorCodes sec = SQLErrorCodesFactory.getInstance().getErrorCodes("Oracle");
assertIsOracle(sec);
}
private void assertIsOracle(SQLErrorCodes sec) {
assertTrue(sec.getBadSqlGrammarCodes().length > 0);
assertTrue(sec.getDataIntegrityViolationCodes().length > 0);
// These had better be a Bad SQL Grammar code
assertTrue(Arrays.binarySearch(sec.getBadSqlGrammarCodes(), "942") >= 0);
assertTrue(Arrays.binarySearch(sec.getBadSqlGrammarCodes(), "6550") >= 0);
// This had better NOT be
assertFalse(Arrays.binarySearch(sec.getBadSqlGrammarCodes(), "9xx42") >= 0);
}
private void assertIsSQLServer(SQLErrorCodes sec) {
assertThat(sec.getDatabaseProductName(), equalTo("Microsoft SQL Server"));
assertTrue(sec.getBadSqlGrammarCodes().length > 0);
assertTrue(Arrays.binarySearch(sec.getBadSqlGrammarCodes(), "156") >= 0);
assertTrue(Arrays.binarySearch(sec.getBadSqlGrammarCodes(), "170") >= 0);
assertTrue(Arrays.binarySearch(sec.getBadSqlGrammarCodes(), "207") >= 0);
assertTrue(Arrays.binarySearch(sec.getBadSqlGrammarCodes(), "208") >= 0);
assertTrue(Arrays.binarySearch(sec.getBadSqlGrammarCodes(), "209") >= 0);
assertFalse(Arrays.binarySearch(sec.getBadSqlGrammarCodes(), "9xx42") >= 0);
assertTrue(sec.getPermissionDeniedCodes().length > 0);
assertTrue(Arrays.binarySearch(sec.getPermissionDeniedCodes(), "229") >= 0);
assertTrue(sec.getDuplicateKeyCodes().length > 0);
assertTrue(Arrays.binarySearch(sec.getDuplicateKeyCodes(), "2601") >= 0);
assertTrue(Arrays.binarySearch(sec.getDuplicateKeyCodes(), "2627") >= 0);
assertTrue(sec.getDataIntegrityViolationCodes().length > 0);
assertTrue(Arrays.binarySearch(sec.getDataIntegrityViolationCodes(), "544") >= 0);
assertTrue(Arrays.binarySearch(sec.getDataIntegrityViolationCodes(), "8114") >= 0);
assertTrue(Arrays.binarySearch(sec.getDataIntegrityViolationCodes(), "8115") >= 0);
assertTrue(sec.getDataAccessResourceFailureCodes().length > 0);
assertTrue(Arrays.binarySearch(sec.getDataAccessResourceFailureCodes(), "4060") >= 0);
assertTrue(sec.getCannotAcquireLockCodes().length > 0);
assertTrue(Arrays.binarySearch(sec.getCannotAcquireLockCodes(), "1222") >= 0);
assertTrue(sec.getDeadlockLoserCodes().length > 0);
assertTrue(Arrays.binarySearch(sec.getDeadlockLoserCodes(), "1205") >= 0);
}
private void assertIsHsql(SQLErrorCodes sec) {
assertTrue(sec.getBadSqlGrammarCodes().length > 0);
assertTrue(sec.getDataIntegrityViolationCodes().length > 0);
// This had better be a Bad SQL Grammar code
assertTrue(Arrays.binarySearch(sec.getBadSqlGrammarCodes(), "-22") >= 0);
// This had better NOT be
assertFalse(Arrays.binarySearch(sec.getBadSqlGrammarCodes(), "-9") >= 0);
}
private void assertIsDB2(SQLErrorCodes sec) {
assertTrue(sec.getBadSqlGrammarCodes().length > 0);
assertTrue(sec.getDataIntegrityViolationCodes().length > 0);
assertFalse(Arrays.binarySearch(sec.getBadSqlGrammarCodes(), "942") >= 0);
// This had better NOT be
assertTrue(Arrays.binarySearch(sec.getBadSqlGrammarCodes(), "-204") >= 0);
}
private void assertIsHana(SQLErrorCodes sec) {
assertTrue(sec.getBadSqlGrammarCodes().length > 0);
assertTrue(sec.getDataIntegrityViolationCodes().length > 0);
assertTrue(Arrays.binarySearch(sec.getBadSqlGrammarCodes(), "368") >= 0);
assertTrue(Arrays.binarySearch(sec.getPermissionDeniedCodes(), "10") >= 0);
assertTrue(Arrays.binarySearch(sec.getDuplicateKeyCodes(), "301") >= 0);
assertTrue(Arrays.binarySearch(sec.getDataIntegrityViolationCodes(), "461") >= 0);
assertTrue(Arrays.binarySearch(sec.getDataAccessResourceFailureCodes(), "-813") >=0);
assertTrue(Arrays.binarySearch(sec.getInvalidResultSetAccessCodes(), "582") >=0);
assertTrue(Arrays.binarySearch(sec.getCannotAcquireLockCodes(), "131") >= 0);
assertTrue(Arrays.binarySearch(sec.getCannotSerializeTransactionCodes(), "138") >= 0);
assertTrue(Arrays.binarySearch(sec.getDeadlockLoserCodes(), "133") >= 0);
}
@Test
public void testLookupOrder() {
class TestSQLErrorCodesFactory extends SQLErrorCodesFactory {
private int lookups = 0;
@Override
protected Resource loadResource(String path) {
++lookups;
if (lookups == 1) {
assertEquals(SQLErrorCodesFactory.SQL_ERROR_CODE_DEFAULT_PATH, path);
return null;
}
else {
// Should have only one more lookup
assertEquals(2, lookups);
assertEquals(SQLErrorCodesFactory.SQL_ERROR_CODE_OVERRIDE_PATH, path);
return null;
}
}
}
// Should have failed to load without error
TestSQLErrorCodesFactory sf = new TestSQLErrorCodesFactory();
assertTrue(sf.getErrorCodes("XX").getBadSqlGrammarCodes().length == 0);
assertTrue(sf.getErrorCodes("Oracle").getDataIntegrityViolationCodes().length == 0);
}
/**
* Check that user defined error codes take precedence.
*/
@Test
public void testFindUserDefinedCodes() {
class TestSQLErrorCodesFactory extends SQLErrorCodesFactory {
@Override
protected Resource loadResource(String path) {
if (SQLErrorCodesFactory.SQL_ERROR_CODE_OVERRIDE_PATH.equals(path)) {
return new ClassPathResource("test-error-codes.xml", SQLErrorCodesFactoryTests.class);
}
return null;
}
}
// Should have loaded without error
TestSQLErrorCodesFactory sf = new TestSQLErrorCodesFactory();
assertTrue(sf.getErrorCodes("XX").getBadSqlGrammarCodes().length == 0);
assertEquals(2, sf.getErrorCodes("Oracle").getBadSqlGrammarCodes().length);
assertEquals("1", sf.getErrorCodes("Oracle").getBadSqlGrammarCodes()[0]);
assertEquals("2", sf.getErrorCodes("Oracle").getBadSqlGrammarCodes()[1]);
}
@Test
public void testInvalidUserDefinedCodeFormat() {
class TestSQLErrorCodesFactory extends SQLErrorCodesFactory {
@Override
protected Resource loadResource(String path) {
if (SQLErrorCodesFactory.SQL_ERROR_CODE_OVERRIDE_PATH.equals(path)) {
// Guaranteed to be on the classpath, but most certainly NOT XML
return new ClassPathResource("SQLExceptionTranslator.class", SQLErrorCodesFactoryTests.class);
}
return null;
}
}
// Should have failed to load without error
TestSQLErrorCodesFactory sf = new TestSQLErrorCodesFactory();
assertTrue(sf.getErrorCodes("XX").getBadSqlGrammarCodes().length == 0);
assertEquals(0, sf.getErrorCodes("Oracle").getBadSqlGrammarCodes().length);
}
/**
* Check that custom error codes take precedence.
*/
@Test
public void testFindCustomCodes() {
class TestSQLErrorCodesFactory extends SQLErrorCodesFactory {
@Override
protected Resource loadResource(String path) {
if (SQLErrorCodesFactory.SQL_ERROR_CODE_OVERRIDE_PATH.equals(path)) {
return new ClassPathResource("custom-error-codes.xml", SQLErrorCodesFactoryTests.class);
}
return null;
}
}
// Should have loaded without error
TestSQLErrorCodesFactory sf = new TestSQLErrorCodesFactory();
assertEquals(1, sf.getErrorCodes("Oracle").getCustomTranslations().length);
CustomSQLErrorCodesTranslation translation =
sf.getErrorCodes("Oracle").getCustomTranslations()[0];
assertEquals(CustomErrorCodeException.class, translation.getExceptionClass());
assertEquals(1, translation.getErrorCodes().length);
}
@Test
public void testDataSourceWithNullMetadata() throws Exception {
Connection connection = mock(Connection.class);
DataSource dataSource = mock(DataSource.class);
given(dataSource.getConnection()).willReturn(connection);
SQLErrorCodes sec = SQLErrorCodesFactory.getInstance().getErrorCodes(dataSource);
assertIsEmpty(sec);
verify(connection).close();
}
@Test
public void testGetFromDataSourceWithSQLException() throws Exception {
SQLException expectedSQLException = new SQLException();
DataSource dataSource = mock(DataSource.class);
given(dataSource.getConnection()).willThrow(expectedSQLException);
SQLErrorCodes sec = SQLErrorCodesFactory.getInstance().getErrorCodes(dataSource);
assertIsEmpty(sec);
}
private void assertIsEmpty(SQLErrorCodes sec) {
// Codes should be empty
assertEquals(0, sec.getBadSqlGrammarCodes().length);
assertEquals(0, sec.getDataIntegrityViolationCodes().length);
}
private SQLErrorCodes getErrorCodesFromDataSource(String productName, SQLErrorCodesFactory factory) throws Exception {
DatabaseMetaData databaseMetaData = mock(DatabaseMetaData.class);
given(databaseMetaData.getDatabaseProductName()).willReturn(productName);
Connection connection = mock(Connection.class);
given(connection.getMetaData()).willReturn(databaseMetaData);
DataSource dataSource = mock(DataSource.class);
given(dataSource.getConnection()).willReturn(connection);
SQLErrorCodesFactory secf = null;
if (factory != null) {
secf = factory;
}
else {
secf = SQLErrorCodesFactory.getInstance();
}
SQLErrorCodes sec = secf.getErrorCodes(dataSource);
SQLErrorCodes sec2 = secf.getErrorCodes(dataSource);
assertSame("Cached per DataSource", sec2, sec);
verify(connection).close();
return sec;
}
@Test
public void testSQLServerRecognizedFromMetadata() throws Exception {
SQLErrorCodes sec = getErrorCodesFromDataSource("MS-SQL", null);
assertIsSQLServer(sec);
}
@Test
public void testOracleRecognizedFromMetadata() throws Exception {
SQLErrorCodes sec = getErrorCodesFromDataSource("Oracle", null);
assertIsOracle(sec);
}
@Test
public void testHsqlRecognizedFromMetadata() throws Exception {
SQLErrorCodes sec = getErrorCodesFromDataSource("HSQL Database Engine", null);
assertIsHsql(sec);
}
@Test
public void testDB2RecognizedFromMetadata() throws Exception {
SQLErrorCodes sec = getErrorCodesFromDataSource("DB2", null);
assertIsDB2(sec);
sec = getErrorCodesFromDataSource("DB2/", null);
assertIsDB2(sec);
sec = getErrorCodesFromDataSource("DB-2", null);
assertIsEmpty(sec);
}
@Test
public void testHanaIsRecognizedFromMetadata() throws Exception {
SQLErrorCodes sec = getErrorCodesFromDataSource("SAP DB", null);
assertIsHana(sec);
}
/**
* Check that wild card database name works.
*/
@Test
public void testWildCardNameRecognized() throws Exception {
class WildcardSQLErrorCodesFactory extends SQLErrorCodesFactory {
@Override
protected Resource loadResource(String path) {
if (SQLErrorCodesFactory.SQL_ERROR_CODE_OVERRIDE_PATH.equals(path)) {
return new ClassPathResource("wildcard-error-codes.xml", SQLErrorCodesFactoryTests.class);
}
return null;
}
}
WildcardSQLErrorCodesFactory factory = new WildcardSQLErrorCodesFactory();
SQLErrorCodes sec = getErrorCodesFromDataSource("DB2", factory);
assertIsDB2(sec);
sec = getErrorCodesFromDataSource("DB2 UDB for Xxxxx", factory);
assertIsDB2(sec);
sec = getErrorCodesFromDataSource("DB3", factory);
assertIsDB2(sec);
sec = getErrorCodesFromDataSource("DB3/", factory);
assertIsDB2(sec);
sec = getErrorCodesFromDataSource("/DB3", factory);
assertIsDB2(sec);
sec = getErrorCodesFromDataSource("/DB3", factory);
assertIsDB2(sec);
sec = getErrorCodesFromDataSource("/DB3/", factory);
assertIsDB2(sec);
sec = getErrorCodesFromDataSource("DB-3", factory);
assertIsEmpty(sec);
sec = getErrorCodesFromDataSource("DB1", factory);
assertIsDB2(sec);
sec = getErrorCodesFromDataSource("DB1/", factory);
assertIsDB2(sec);
sec = getErrorCodesFromDataSource("/DB1", factory);
assertIsEmpty(sec);
sec = getErrorCodesFromDataSource("/DB1/", factory);
assertIsEmpty(sec);
sec = getErrorCodesFromDataSource("DB0", factory);
assertIsDB2(sec);
sec = getErrorCodesFromDataSource("/DB0", factory);
assertIsDB2(sec);
sec = getErrorCodesFromDataSource("DB0/", factory);
assertIsEmpty(sec);
sec = getErrorCodesFromDataSource("/DB0/", factory);
assertIsEmpty(sec);
}
}