HHH-6409 : Add tests and make corrections to Value.getAlias() implementations

This commit is contained in:
Gail Badner 2012-06-01 18:20:51 -07:00
parent 860efbdff4
commit a23ec12e9c
7 changed files with 410 additions and 30 deletions

View File

@ -160,30 +160,51 @@ public class Column extends AbstractValue {
return getTable().getLoggableValueQualifier() + '.' + getColumnName();
}
// TODO: this is fairly complicated logic. It would be more straightforward
// to always include the column position and a table number in the unique
// suffix. That might cause unreadable aliases to be generated more often though...
@Override
public String getAlias(Dialect dialect) {
String alias = columnName.getName();
int lastLetter = StringHelper.lastIndexOfLetter( columnName.getName() );
public String getAlias(Dialect dialect, TableSpecification tableSpecification) {
final int lastLetter = StringHelper.lastIndexOfLetter( columnName.getName() );
final String colPositionSuffix = String.valueOf( getPosition() ) + '_';
final String tableNumberSuffix =
tableSpecification == null ?
"" :
String.valueOf( tableSpecification.getTableNumber() ) + "_";
String alias;
String suffix = colPositionSuffix + tableNumberSuffix;
boolean useRawName = false;
if ( lastLetter == -1 ) {
alias = "column";
alias = "column" ;
}
boolean useRawName =
columnName.getName().equals( alias ) &&
alias.length() <= dialect.getMaxAliasLength() &&
! columnName.isQuoted() &&
! columnName.getName().toLowerCase().equals( "rowid" );
else if ( columnName.getName().length() > lastLetter + 1 ) {
alias = columnName.getName().substring( 0, lastLetter + 1 );
}
else {
alias = columnName.getName();
if (columnName.getName().length() + tableNumberSuffix.length() <= dialect.getMaxAliasLength() &&
! columnName.isQuoted() &&
! columnName.getName().toLowerCase().equals( "rowid" ) ) {
useRawName = true;
suffix = tableNumberSuffix;
}
}
if ( ! useRawName ) {
String unique = String.valueOf( getPosition() ) + '_' + getTable().getTableNumber() + '_';
if ( unique.length() >= dialect.getMaxAliasLength() ) {
if ( suffix.length() >= dialect.getMaxAliasLength() ) {
throw new MappingException(
"Unique suffix [" + unique + "] length must be less than maximum [" + dialect.getMaxAliasLength() + "]"
String.format( "Unique suffix [%s%s] length must be less than maximum [%d]",
colPositionSuffix,
tableNumberSuffix,
dialect.getMaxAliasLength()
)
);
}
if ( alias.length() + unique.length() > dialect.getMaxAliasLength()) {
alias = alias.substring( 0, dialect.getMaxAliasLength() - unique.length() );
if ( alias.length() + suffix.length() > dialect.getMaxAliasLength() ) {
alias = alias.substring( 0, dialect.getMaxAliasLength() - suffix.length() );
}
alias = alias + unique;
}
return alias;
return alias + suffix;
}
}

View File

@ -49,7 +49,7 @@ public class DerivedValue extends AbstractValue {
* {@inheritDoc}
*/
@Override
public String getAlias(Dialect dialect) {
public String getAlias(Dialect dialect, TableSpecification tableSpecification) {
return "formula" + Integer.toString( getPosition() ) + '_';
}

View File

@ -55,16 +55,19 @@ public interface Value {
/**
* For any column name, generate an alias that is unique
* to that column name, unique across tables, and within
* alias size constraints determined by
* to that column name, and (optionally) unique across tables, and
* within alias size constraints determined by
* {@link org.hibernate.dialect.Dialect#getMaxAliasLength()}.
*
* todo : not sure this contract is the best place for this method
*
* @param dialect the dialect.
* @param tableSpecification, if non-null, the table specification to use
* to make the alias unambiguous across tables; if null, there is no need to need
* to use the table to make the alias unambiguous across tables.
* @return the alias.
*/
public String getAlias(Dialect dialect);
public String getAlias(Dialect dialect, TableSpecification tableSpecification);
/**
* Validate the value against the incoming JDBC type code array, both in terms of number of types

View File

@ -716,7 +716,10 @@ public abstract class AbstractCollectionPersister
// NativeSQL: collect key column and auto-aliases
keyColumnNames[k] = keyColumn.getColumnName().encloseInQuotesIfQuoted( dialect );
// TODO: does the owner root table need to be in alias?
keyColumnAliases[k] = keyColumn.getAlias( dialect );
keyColumnAliases[k] = keyColumn.getAlias(
dialect,
collection.getContainer().seekEntityBinding().getPrimaryTable()
);
// keyColumnAliases[k] = col.getAlias( dialect, collection.getOwner().getRootTable() );
k++;
}
@ -759,7 +762,7 @@ public abstract class AbstractCollectionPersister
if ( elementSpan > 0 ) {
for ( RelationalValueBinding relationalValueBinding : collection.getPluralAttributeElementBinding().getRelationalValueBindings() ) {
final Value value = relationalValueBinding.getValue();
elementColumnAliases[j] = value.getAlias( dialect );
elementColumnAliases[j] = value.getAlias( dialect, null );
if ( DerivedValue.class.isInstance( value ) ) {
DerivedValue form = (DerivedValue) value;
elementFormulaTemplates[j] = getTemplateFromString( form.getExpression(), factory);
@ -811,7 +814,7 @@ public abstract class AbstractCollectionPersister
indexFormulaTemplates = new String[ 1 ];
indexFormulas = new String[ 1 ];
Value value = indexBinding.getIndexRelationalValue();
indexColumnAliases[ 0 ] = value.getAlias( dialect );
indexColumnAliases[ 0 ] = value.getAlias( dialect, null );
if ( value instanceof Column ) {
indexColumnIsSettable[ 0 ] = true;
Column column = ( Column ) value;

View File

@ -102,7 +102,6 @@ import org.hibernate.metamodel.spi.binding.EntityBinding;
import org.hibernate.metamodel.spi.binding.RelationalValueBinding;
import org.hibernate.metamodel.spi.binding.SingularAssociationAttributeBinding;
import org.hibernate.metamodel.spi.binding.SingularAttributeBinding;
import org.hibernate.metamodel.spi.domain.SingularAttribute;
import org.hibernate.metamodel.spi.relational.DerivedValue;
import org.hibernate.metamodel.spi.relational.Value;
import org.hibernate.pretty.MessageHelper;
@ -816,7 +815,7 @@ public abstract class AbstractEntityPersister
rootTableKeyColumnReaders[i] = col.getReadFragment();
rootTableKeyColumnReaderTemplates[i] = getTemplateFromString( col.getReadFragment(), factory );
}
identifierAliases[i] = col.getAlias( factory.getDialect() );
identifierAliases[i] = col.getAlias( factory.getDialect(), entityBinding.getPrimaryTable() );
i++;
}
@ -905,7 +904,10 @@ public abstract class AbstractEntityPersister
int k = 0;
for ( RelationalValueBinding valueBinding : singularAttributeBinding.getRelationalValueBindings() ) {
colAliases[k] = valueBinding.getValue().getAlias( factory.getDialect() );
colAliases[k] = valueBinding.getValue().getAlias(
factory.getDialect(),
valueBinding.getValue().getTable()
);
if ( valueBinding.isDerived() ) {
foundFormula = true;
formulaTemplates[ k ] = getTemplateFromString( ( (DerivedValue) valueBinding.getValue() ).getExpression(), factory );
@ -1017,7 +1019,7 @@ public abstract class AbstractEntityPersister
formulaTemplates.add( template );
forms[l] = template;
formulas.add( derivedValue.getExpression() );
formulaAliases.add( derivedValue.getAlias( factory.getDialect() ) );
formulaAliases.add( derivedValue.getAlias( factory.getDialect(), null ) );
formulasLazy.add( lazy );
}
else {
@ -1027,7 +1029,12 @@ public abstract class AbstractEntityPersister
formnos[l] = -1;
columns.add( colName );
cols[l] = colName;
aliases.add( col.getAlias( factory.getDialect() ) );
aliases.add(
col.getAlias(
factory.getDialect(),
col.getTable()
)
);
columnsLazy.add( lazy );
// TODO: properties only selectable if they are non-plural???
columnSelectables.add( singularAttributeBinding.getAttribute().isSingular() );
@ -2228,7 +2235,10 @@ public abstract class AbstractEntityPersister
String[] cols = new String[span];
int l = 0;
for ( RelationalValueBinding relationalValueBinding : singularProp.getRelationalValueBindings() ) {
aliases[l] = relationalValueBinding.getValue().getAlias( getFactory().getDialect() );
aliases[l] = relationalValueBinding.getValue().getAlias(
getFactory().getDialect(),
relationalValueBinding.getValue().getTable()
);
if ( relationalValueBinding.isDerived() ) {
cols[l] = ( (DerivedValue) relationalValueBinding.getValue() ).getExpression(); // TODO: skip formulas?
}

View File

@ -558,7 +558,7 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
column.getColumnName().encloseInQuotesIfQuoted( factory.getDialect() ) :
column.getReadFragment();
discriminatorColumnReaderTemplate = getTemplateFromColumn( column, factory );
discriminatorAlias = column.getAlias( factory.getDialect() );
discriminatorAlias = column.getAlias( factory.getDialect(), entityBinding.getPrimaryTable() );
discriminatorFormula = null;
discriminatorFormulaTemplate = null;
}

View File

@ -0,0 +1,343 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.metamodel.spi.relational;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.hibernate.MappingException;
import org.hibernate.dialect.Dialect;
import org.hibernate.testing.junit4.BaseUnitTestCase;
/**
*
* NOTE: the table number is automatically statically incremented every time
* a table is created. Because of this, it is impossible to predict how
* large the table number can get when running the suite of unit tests.
* Since we don't know how large the table number is, we don't know how
* many characters to allot in the resulting alias for the table number.
*
* To workaround this unknown, Dialect instances are created
* to control whether the test will need to truncate the column
* name when creating the alias.
*
* @author Gail Badner
*/
public class ColumnAliasTest extends BaseUnitTestCase {
private final Schema schema = new Schema( null, null );
private final Table table0 = new Table(
schema,
Identifier.toIdentifier( "table0" ),
Identifier.toIdentifier( "table0" )
);
private final Table table1 = new Table(
schema,
Identifier.toIdentifier( "table1" ),
Identifier.toIdentifier( "table1" )
);
@Test
public void testNoCharactersInNameNoTruncation() {
// create dialect with a large enough max alias length so there is no trucation.
final Dialect dialect = createDialect( 25 );
Column column = table0.createColumn( "1" );
assertEquals( "column" + getExpectedSuffix( column, null ) , column.getAlias( dialect, null ) );
assertEquals( "column" + getExpectedSuffix( column, table1 ) , column.getAlias( dialect, table1 ) );
column = table0.createColumn( "`1`" );
assertEquals( "column" + getExpectedSuffix( column, null ) , column.getAlias( dialect, null ) );
assertEquals( "column" + getExpectedSuffix( column, table1 ) , column.getAlias( dialect, table1 ) );
}
public void testNameStartsWithNonCharacterNoTruncation() {
// create dialect with a large enough max alias length so there is no trucation.
final Dialect dialect = createDialect( 25 );
Column column = table0.createColumn( "1abc" );
assertEquals( "column" + getExpectedSuffix( column, null ) , column.getAlias( dialect, null ) );
assertEquals( "column" + getExpectedSuffix( column, table1 ) , column.getAlias( dialect, table1 ) );
column = table0.createColumn( "1abc`" );
assertEquals( "column" + getExpectedSuffix( column, null ) , column.getAlias( dialect, null ) );
assertEquals( "column" + getExpectedSuffix( column, table1 ) , column.getAlias( dialect, table1 ) );
column = table0.createColumn( "_abc" );
assertEquals( "column" + getExpectedSuffix( column, null ) , column.getAlias( dialect, null ) );
assertEquals( "column" + getExpectedSuffix( column, table1 ) , column.getAlias( dialect, table1 ) );
column = table0.createColumn( "`_abc`" );
assertEquals( "column" + getExpectedSuffix( column, null ) , column.getAlias( dialect, null ) );
assertEquals( "column" + getExpectedSuffix( column, table1 ) , column.getAlias( dialect, table1 ) );
}
@Test
public void testNameStartsWithNonCharacterTruncation() {
Column column = table0.createColumn( "1" );
String expectedSuffix = getExpectedSuffix( column, null );
// create dialect with maximum alias length that will force truncation.
Dialect dialect = createDialect( expectedSuffix.length() + "column".length() - 1 );
String nameTruncated = "column".substring( 0, dialect.getMaxAliasLength() - expectedSuffix.length() );
assertTrue( nameTruncated.length() < "column".length() );
String alias = column.getAlias( dialect, null );
assertEquals( dialect.getMaxAliasLength(), alias.length() );
assertEquals( nameTruncated + expectedSuffix , alias );
column = table0.createColumn( "_abc" );
// create dialect with maximum alias length that will force truncation.
expectedSuffix = getExpectedSuffix( column, table1 );
dialect = createDialect( expectedSuffix.length() + "column".length() - 1 );
nameTruncated = "column".substring( 0, dialect.getMaxAliasLength() - expectedSuffix.length() );
assertTrue( nameTruncated.length() < "column".length() );
alias = column.getAlias( dialect, table1 );
assertEquals( dialect.getMaxAliasLength(), alias.length() );
assertEquals( nameTruncated + expectedSuffix , alias );
}
@Test
public void testNameIncludingNonCharacter() {
// create dialect with a large enough max alias length so there is no trucation.
final Dialect dialect = createDialect( 10 );
Column column = table0.createColumn( "a1" );
assertEquals( "a" + getExpectedSuffix( column, null ) , column.getAlias( dialect, null ) );
assertEquals( "a" + getExpectedSuffix( column, table1 ) , column.getAlias( dialect, table1 ) );
column = table0.createColumn( "`a1`" );
assertEquals( "a" + getExpectedSuffix( column, null ) , column.getAlias( dialect, null ) );
assertEquals( "a" + getExpectedSuffix( column, table1 ) , column.getAlias( dialect, table1 ) );
column = table0.createColumn( "a1b" );
assertEquals( "a" + getExpectedSuffix( column, null ) , column.getAlias( dialect, null ) );
assertEquals( "a" + getExpectedSuffix( column, table1 ) , column.getAlias( dialect, table1 ) );
column = table0.createColumn( "`a1b`" );
assertEquals( "a" + getExpectedSuffix( column, null ) , column.getAlias( dialect, null ) );
assertEquals( "a" + getExpectedSuffix( column, table1 ) , column.getAlias( dialect, table1 ) );
column = table0.createColumn( "a_b" );
assertEquals( "a" + getExpectedSuffix( column, null ) , column.getAlias( dialect, null ) );
assertEquals( "a" + getExpectedSuffix( column, table1 ) , column.getAlias( dialect, table1 ) );
column = table0.createColumn( "`a_b`" );
assertEquals( "a" + getExpectedSuffix( column, null ) , column.getAlias( dialect, null ) );
assertEquals( "a" + getExpectedSuffix( column, table1 ) , column.getAlias( dialect, table1 ) );
column = table0.createColumn( "ab1" );
assertEquals( "ab" + getExpectedSuffix( column, null ) , column.getAlias( dialect, null ) );
assertEquals( "ab" + getExpectedSuffix( column, table1 ) , column.getAlias( dialect, table1 ) );
column = table0.createColumn( "`ab1`" );
assertEquals( "ab" + getExpectedSuffix( column, null ) , column.getAlias( dialect, null ) );
assertEquals( "ab" + getExpectedSuffix( column, table1 ) , column.getAlias( dialect, table1 ) );
column = table0.createColumn( "a1b2" );
assertEquals( "a" + getExpectedSuffix( column, null ) , column.getAlias( dialect, null ) );
assertEquals( "a" + getExpectedSuffix( column, table1 ) , column.getAlias( dialect, table1 ) );
column = table0.createColumn( "`a1b2`" );
assertEquals( "a" + getExpectedSuffix( column, null ) , column.getAlias( dialect, null ) );
assertEquals( "a" + getExpectedSuffix( column, table1 ) , column.getAlias( dialect, table1 ) );
}
@Test
public void testUseNameAsIs() {
// create dialect with a large enough max alias length so there is no trucation.
final Dialect dialect = createDialect( 25 );
Column column = table0.createColumn( "abc" );
assertEquals( "abc" + getExpectedSuffix( null, null ) , column.getAlias( dialect, null ) );
column = table0.createColumn( "abc" );
assertEquals( "abc" + getExpectedSuffix( null, table1 ) , column.getAlias( dialect, table1 ) );
}
@Test
public void testUseNameAsIsWithMaxLengthNoTableSuffix() {
// create dialect with a large enough max alias length so there is no trucation.
final Dialect dialect = createDialect( 10 );
String name = "abcdefghij";
Column column = table0.createColumn( name );
assertEquals( dialect.getMaxAliasLength(), column.getColumnName().getName().length() );
assertEquals( name + getExpectedSuffix( null, null ) , column.getAlias( dialect, null ) );
}
@Test
public void testUseNameAsIsWithMaxLengthWithTableSuffix() {
// create dialect with a large enough max alias length so there is no trucation.
final Dialect dialect = createDialect( 10 );
String name = "abcdefghij";
assertEquals( dialect.getMaxAliasLength(), name.length() );
name = name.substring( 0, name.length() - getExpectedTableSuffix( table1 ).length() );
Column column = table0.createColumn( name );
String expectedAlias = name + getExpectedTableSuffix( table1 );
assertEquals( dialect.getMaxAliasLength(), expectedAlias.length() );
assertEquals( expectedAlias , column.getAlias( dialect, table1 ) );
}
@Test
public void testQuotedNameAllCharactersNoTrucation() {
// create dialect with a large enough max alias length so there is no trucation.
final Dialect dialect = createDialect( 10 );
String name = "`abc`";
Column column = table0.createColumn( name );
assertEquals( column.getColumnName().getName() + getExpectedSuffix( column, null ), column.getAlias(
dialect,
null
) );
assertEquals( column.getColumnName().getName() + getExpectedSuffix( column, table1 ), column.getAlias( dialect, table1 ) );
}
@Test
public void testRowIdNameNoTruncation() {
// create dialect with a large enough max alias length so there is no trucation.
final Dialect dialect = createDialect( 25 );
Column column = table0.createColumn( "RowId" );
assertEquals( "RowId" + getExpectedSuffix( column, null ), column.getAlias( dialect, null ) );
assertEquals( "RowId" + getExpectedSuffix( column, table1 ), column.getAlias( dialect, table1 ) );
column = table0.createColumn( "`rowid`" );
assertEquals( "rowid" + getExpectedSuffix( column, null ), column.getAlias( dialect, null ) );
assertEquals( "rowid" + getExpectedSuffix( column, table1 ), column.getAlias( dialect, table1 ) );
}
@Test
public void testRowIdNameTruncation() {
Column column = table0.createColumn( "RowId" );
String expectedSuffix = getExpectedSuffix( column, null );
Dialect dialect = createDialect( column.getColumnName().getName().length() + expectedSuffix.length() - 1 );
String nameTruncated = "RowId".substring( 0, dialect.getMaxAliasLength() - expectedSuffix.length() );
assertTrue( nameTruncated.length() < "RowId".length() );
String alias = column.getAlias( dialect, null );
assertEquals( dialect.getMaxAliasLength(), alias.length() );
assertEquals( nameTruncated + expectedSuffix, alias );
expectedSuffix = getExpectedSuffix( column, table1 );
dialect = createDialect( column.getColumnName().getName().length() + expectedSuffix.length() - 1 );
nameTruncated = "RowId".substring( 0, dialect.getMaxAliasLength() - expectedSuffix.length() );
assertTrue( nameTruncated.length() < "column".length() );
alias = column.getAlias( dialect, table1 );
assertEquals( dialect.getMaxAliasLength(), alias.length() );
assertEquals( nameTruncated + expectedSuffix , alias );
}
@Test
public void testTruncatedName() {
Column column = table0.createColumn( "abcdefghijk" );
String expectedSuffix = getExpectedSuffix( column, null );
// Force max alias length to be less than the column name to that
// the name is not used as is (and the expected suffix will be used).
Dialect dialect = createDialect( column.getColumnName().getName().length() - 1 );
String nameTruncated =
column.getColumnName().getName().substring(
0,
dialect.getMaxAliasLength() - expectedSuffix.length()
);
String alias = column.getAlias( dialect, null );
assertEquals( dialect.getMaxAliasLength(), alias.length() );
assertEquals( nameTruncated + expectedSuffix, alias );
expectedSuffix = getExpectedSuffix( column, table1 );
dialect = createDialect( column.getColumnName().getName().length() - 1 );
nameTruncated =
column.getColumnName().getName().substring(
0,
dialect.getMaxAliasLength() - expectedSuffix.length()
);
alias = column.getAlias( dialect, table1 );
assertEquals( dialect.getMaxAliasLength(), alias.length() );
assertEquals( nameTruncated + expectedSuffix, alias );
}
@Test
public void testTruncatedQuotedName() {
Column column = table0.createColumn( "`abcdefghijk`" );
String expectedSuffix = getExpectedSuffix( column, null );
Dialect dialect = createDialect( column.getColumnName().getName().length() + expectedSuffix.length() - 1 );
String nameTruncated =
column.getColumnName().getName().substring(
0,
dialect.getMaxAliasLength() - expectedSuffix.length()
);
String alias = column.getAlias( dialect, null );
assertEquals( dialect.getMaxAliasLength(), alias.length() );
assertEquals( nameTruncated + expectedSuffix, alias );
expectedSuffix = getExpectedSuffix( column, table1 );
dialect = createDialect( column.getColumnName().getName().length() + expectedSuffix.length() - 1 );
nameTruncated =
column.getColumnName().getName().substring(
0,
dialect.getMaxAliasLength() - expectedSuffix.length()
);
alias = column.getAlias( dialect, table1 );
assertEquals( dialect.getMaxAliasLength(), alias.length() );
assertEquals( nameTruncated + expectedSuffix, alias );
}
@Test
public void testMaxAliasLengthTooSmall() {
Column column = table0.createColumn( "a" );
try {
column.getAlias(
new Dialect() {
public int getMaxAliasLength() {
return 2;
}
},
table1
);
fail( "should have failed because max alias length cannot accommodate more than just the unique suffix");
}
catch (MappingException exception) {
// expected
}
}
private Dialect createDialect(final int maxAliasLength) {
return new Dialect() {
public int getMaxAliasLength() {
return maxAliasLength;
}
};
}
private String getExpectedSuffix(Column column, TableSpecification table) {
return getExpectedColumnSuffix( column ) + getExpectedTableSuffix( table );
}
private String getExpectedColumnSuffix(Column column) {
return column == null ? "" : String.valueOf( column.getPosition() ) + "_";
}
private String getExpectedTableSuffix(TableSpecification table) {
return table == null ? "" : String.valueOf( table.getTableNumber() ) + "_";
}
}