Skip to content
Closed
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 @@ -6,11 +6,14 @@
*/
package org.hibernate.dialect;

import org.hibernate.dialect.function.DB2SubstringFunction;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.hql.spi.id.IdTableSupportStandardImpl;
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
import org.hibernate.hql.spi.id.global.GlobalTemporaryTableBulkIdStrategy;
import org.hibernate.hql.spi.id.local.AfterUseAction;
import org.hibernate.hql.spi.id.local.LocalTemporaryTableBulkIdStrategy;
import org.hibernate.type.StandardBasicTypes;

/**
* An SQL dialect for DB2 9.7.
Expand All @@ -19,6 +22,11 @@
*/
public class DB297Dialect extends DB2Dialect {

public DB297Dialect() {
super();
registerFunction( "substring", new DB2SubstringFunction() );
}

@Override
public String getCrossJoinSeparator() {
// DB2 9.7 and later support "cross join"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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.dialect.function;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.hibernate.type.StandardBasicTypes;

/**
* When "substring" function is used for DB2, this implementation of {@link StandardSQLFunction}
* will render "substr" or "substring", depending on the last argument being used. If the last
* argument is a string unit ("CODEUNITS16", "CODEUNITS32", or "OCTETS"), then the function
* will be rendered as "substring"; otherwise, it will be rendered as "substr".
* <p/>
* ANSI SQL-92 standard defines "substring" without string units, which is more similar to DB2's "substr",
* so it makes sense to use DB2's "substr" function when string units are not provided.
* <p/>
* Background: DB2 has both "substr" and "substring", which are different functions that are not
* interchangeable. Prior to DB2 11.1, DB2's "substring" function requires an argument for string
* units; without this argument, DB2 throws an exception. DB2's "substr" function throws an exception
* if string unit is provided as an argument.
*
* @author Gail Badner
*/
public class DB2SubstringFunction extends StandardSQLFunction {
private static final Set<String> possibleStringUnits = new HashSet<String>(
Arrays.asList( "CODEUNITS16", "CODEUNITS32", "OCTETS" )
);

public DB2SubstringFunction() {
super( "substring", StandardBasicTypes.STRING );
}

@Override
protected String getRenderedName(List arguments) {
final String lastArgument = (String) arguments.get( arguments.size() - 1 );
if ( lastArgument != null && possibleStringUnits.contains( lastArgument.toUpperCase() ) ) {
return getName();
}
else{
return "substr";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public Type getReturnType(Type firstArgumentType, Mapping mapping) {
@Override
public String render(Type firstArgumentType, List arguments, SessionFactoryImplementor sessionFactory) {
final StringBuilder buf = new StringBuilder();
buf.append( name ).append( '(' );
buf.append( getRenderedName( arguments) ).append( '(' );
for ( int i = 0; i < arguments.size(); i++ ) {
buf.append( arguments.get( i ) );
if ( i < arguments.size() - 1 ) {
Expand All @@ -96,6 +96,10 @@ public String render(Type firstArgumentType, List arguments, SessionFactoryImple
return buf.append( ')' ).toString();
}

protected String getRenderedName(List arguments) {
return getName();
}

@Override
public String toString() {
return name;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/*
* 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.test.hql;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.PersistenceException;

import org.hibernate.Session;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.DB297Dialect;
import org.hibernate.exception.SQLGrammarException;
import org.hibernate.resource.jdbc.spi.StatementInspector;

import org.hibernate.testing.RequiresDialect;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

/**
* DB2 has 2 functions for getting a substring: "substr" and "substring"
*
* @author Gail Badner
*/
@RequiresDialect(DB297Dialect.class)
public class DB297SubStringFunctionsTest extends BaseCoreFunctionalTestCase {
private static final MostRecentStatementInspector mostRecentStatementInspector = new MostRecentStatementInspector();

@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] {
AnEntity.class
};
}

@Override
protected void configure(Configuration configuration) {
super.configure( configuration );
configuration.getProperties().put( Environment.STATEMENT_INSPECTOR, mostRecentStatementInspector );
}

@Before
public void setup() {
AnEntity anEntity = new AnEntity();
anEntity.description = "A very long, boring description.";

Session session = openSession();
session.beginTransaction();
{
session.persist( anEntity );
}
session.getTransaction().commit();
session.close();
}

@After
public void cleanup() {
Session session = openSession();
session.beginTransaction();
{
session.createQuery( "delete from AnEntity" ).executeUpdate();
}
session.getTransaction().commit();
session.close();
}

@Test
@TestForIssue( jiraKey = "HHH-11957")
public void testSubstringWithStringUnits() {

mostRecentStatementInspector.clear();

Session session = openSession();
session.beginTransaction();
{
String value = (String) session.createQuery(
"select substring( e.description, 21, 11, octets ) from AnEntity e"
).uniqueResult();
assertEquals( "description", value );
}
session.getTransaction().commit();
session.close();

assertTrue( mostRecentStatementInspector.mostRecentSql.contains( "substring(" ) );
assertTrue( mostRecentStatementInspector.mostRecentSql.contains( "octets" ) );
}

@Test
@TestForIssue( jiraKey = "HHH-11957")
public void testSubstringWithoutStringUnits() {

mostRecentStatementInspector.clear();

Session session = openSession();
session.beginTransaction();
{
String value = (String) session.createQuery(
"select substring( e.description, 21, 11 ) from AnEntity e"
).uniqueResult();
assertEquals( "description", value );
}
session.getTransaction().commit();
session.close();

assertTrue( mostRecentStatementInspector.mostRecentSql.contains( "substr(" ) );
}

@Test
@TestForIssue( jiraKey = "HHH-11957")
public void testSubstrWithStringUnits() {

mostRecentStatementInspector.clear();

Session session = openSession();
session.beginTransaction();

try {
String value = (String) session.createQuery(
"select substr( e.description, 21, 11, octets ) from AnEntity e"
).uniqueResult();
assertEquals( "description", value );
fail( "Should have failed because substr cannot be used with string units." );
}
catch (SQLGrammarException expected) {
// expected
}
finally {
session.getTransaction().rollback();
session.close();
}

assertTrue( mostRecentStatementInspector.mostRecentSql.contains( "substr(" ) );
assertTrue( mostRecentStatementInspector.mostRecentSql.contains( "octets" ) );
}

@Test
@TestForIssue( jiraKey = "HHH-11957")
public void testSubstrWithoutStringUnits() {

mostRecentStatementInspector.clear();

Session session = openSession();
session.beginTransaction();
{
String value = (String) session.createQuery(
"select substr( e.description, 21, 11 ) from AnEntity e"
).uniqueResult();
assertEquals( "description", value );
}
session.getTransaction().commit();
session.close();

assertTrue( mostRecentStatementInspector.mostRecentSql.contains( "substr(" ) );
}

@Entity(name="AnEntity")
public static class AnEntity {
@Id
@GeneratedValue
private long id;
private String description;
}

private static class MostRecentStatementInspector implements StatementInspector {
private String mostRecentSql;

public String inspect(String sql) {
mostRecentSql = sql;
return sql;
}
private void clear() {
mostRecentSql = null;
}
}
}