HHH-4780 - Allow BigDecimal and BigInteger to be specified as numeric literal types

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18508 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Steve Ebersole 2010-01-11 22:55:36 +00:00
parent 50cbcd3e00
commit 9b3c518d5e
8 changed files with 182 additions and 19 deletions

View File

@ -598,6 +598,8 @@ literal
| NUM_LONG { processNumericLiteral( #literal ); }
| NUM_FLOAT { processNumericLiteral( #literal ); }
| NUM_DOUBLE { processNumericLiteral( #literal ); }
| NUM_BIG_INTEGER { processNumericLiteral( #literal ); }
| NUM_BIG_DECIMAL { processNumericLiteral( #literal ); }
| QUOTED_STRING
;

View File

@ -133,6 +133,8 @@ tokens
NUM_DOUBLE;
NUM_FLOAT;
NUM_LONG;
NUM_BIG_INTEGER;
NUM_BIG_DECIMAL;
JAVA_CONSTANT;
}
@ -693,6 +695,8 @@ constant
| NUM_FLOAT
| NUM_LONG
| NUM_DOUBLE
| NUM_BIG_INTEGER
| NUM_BIG_DECIMAL
| QUOTED_STRING
| NULL
| TRUE
@ -821,12 +825,13 @@ NUM_INT
: '.' {_ttype = DOT;}
( ('0'..'9')+ (EXPONENT)? (f1:FLOAT_SUFFIX {t=f1;})?
{
if (t != null && t.getText().toUpperCase().indexOf('F')>=0)
{
if ( t != null && t.getText().toUpperCase().indexOf("BD")>=0) {
_ttype = NUM_BIG_DECIMAL;
}
else if (t != null && t.getText().toUpperCase().indexOf('F')>=0) {
_ttype = NUM_FLOAT;
}
else
{
else {
_ttype = NUM_DOUBLE; // assume double
}
}
@ -847,6 +852,7 @@ NUM_INT
| ('1'..'9') ('0'..'9')* {isDecimal=true;} // non-zero decimal
)
( ('l') { _ttype = NUM_LONG; }
| ('b''i') { _ttype = NUM_BIG_INTEGER; }
// only check to see if it's a float if looks like decimal so far
| {isDecimal}?
@ -855,12 +861,13 @@ NUM_INT
| f4:FLOAT_SUFFIX {t=f4;}
)
{
if (t != null && t.getText().toUpperCase() .indexOf('F') >= 0)
{
if ( t != null && t.getText().toUpperCase().indexOf("BD")>=0) {
_ttype = NUM_BIG_DECIMAL;
}
else if (t != null && t.getText().toUpperCase() .indexOf('F') >= 0) {
_ttype = NUM_FLOAT;
}
else
{
else {
_ttype = NUM_DOUBLE; // assume double
}
}
@ -881,6 +888,6 @@ EXPONENT
protected
FLOAT_SUFFIX
: 'f'|'d'
: 'f'|'d'|'b''d'
;

View File

@ -353,6 +353,8 @@ constant
| NUM_FLOAT
| NUM_INT
| NUM_LONG
| NUM_BIG_INTEGER
| NUM_BIG_DECIMAL
| QUOTED_STRING
| CONSTANT
| JAVA_CONSTANT

View File

@ -145,6 +145,8 @@ public class SqlASTFactory extends ASTFactory implements HqlSqlTokenTypes {
case NUM_FLOAT:
case NUM_LONG:
case NUM_DOUBLE:
case NUM_BIG_INTEGER:
case NUM_BIG_DECIMAL:
case QUOTED_STRING:
return LiteralNode.class;
case TRUE:

View File

@ -22,7 +22,6 @@
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.hql.ast.tree;
import org.hibernate.Hibernate;
@ -53,6 +52,10 @@ public class LiteralNode extends AbstractSelectExpression implements HqlSqlToken
return Hibernate.LONG;
case NUM_DOUBLE:
return Hibernate.DOUBLE;
case NUM_BIG_INTEGER:
return Hibernate.BIG_INTEGER;
case NUM_BIG_DECIMAL:
return Hibernate.BIG_DECIMAL;
case QUOTED_STRING:
return Hibernate.STRING;
case TRUE:

View File

@ -50,6 +50,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;
/**
@ -229,10 +230,14 @@ public class LiteralProcessor implements HqlSqlTokenTypes {
}
public void processNumeric(AST literal) {
if ( literal.getType() == NUM_INT || literal.getType() == NUM_LONG ) {
if ( literal.getType() == NUM_INT
|| literal.getType() == NUM_LONG
|| literal.getType() == NUM_BIG_INTEGER ) {
literal.setText( determineIntegerRepresentation( literal.getText(), literal.getType() ) );
}
else if ( literal.getType() == NUM_FLOAT || literal.getType() == NUM_DOUBLE ) {
else if ( literal.getType() == NUM_FLOAT
|| literal.getType() == NUM_DOUBLE
|| literal.getType() == NUM_BIG_DECIMAL ) {
literal.setText( determineDecimalRepresentation( literal.getText(), literal.getType() ) );
}
else {
@ -250,12 +255,22 @@ public class LiteralProcessor implements HqlSqlTokenTypes {
log.trace( "could not format incoming text [" + text + "] as a NUM_INT; assuming numeric overflow and attempting as NUM_LONG" );
}
}
else if ( type == NUM_LONG ) {
String literalValue = text;
if ( literalValue.endsWith( "l" ) || literalValue.endsWith( "L" ) ) {
literalValue = literalValue.substring( 0, literalValue.length() - 1 );
}
return Long.valueOf( literalValue ).toString();
}
else if ( type == NUM_BIG_INTEGER ) {
String literalValue = text;
if ( literalValue.endsWith( "bi" ) || literalValue.endsWith( "BI" ) ) {
literalValue = literalValue.substring( 0, literalValue.length() - 2 );
}
return new BigInteger( literalValue ).toString();
}
throw new HibernateException( "Unknown literal type to parse as integer" );
}
catch( Throwable t ) {
throw new HibernateException( "Could not parse literal [" + text + "] as integer", t );
}
@ -273,6 +288,11 @@ public class LiteralProcessor implements HqlSqlTokenTypes {
literalValue = literalValue.substring( 0, literalValue.length() - 1 );
}
}
else if ( type == NUM_BIG_DECIMAL ) {
if ( literalValue.endsWith( "bd" ) || literalValue.endsWith( "BD" ) ) {
literalValue = literalValue.substring( 0, literalValue.length() - 2 );
}
}
BigDecimal number = null;
try {
@ -285,6 +305,7 @@ public class LiteralProcessor implements HqlSqlTokenTypes {
return formatters[ DECIMAL_LITERAL_FORMAT ].format( number );
}
private static interface DecimalFormatter {
String format(BigDecimal number);
}

View File

@ -217,6 +217,11 @@ public class ValueHandlerFactory {
}
throw unknownConversion( value, BigInteger.class );
}
@Override
public String render(BigInteger value) {
return "cast( " + value.toString() + " as BigInteger )";
}
}
public static class BigDecimalValueHandler extends BaseValueHandler<BigDecimal> implements Serializable {
@ -236,6 +241,11 @@ public class ValueHandlerFactory {
}
throw unknownConversion( value, BigDecimal.class );
}
@Override
public String render(BigDecimal value) {
return "cast( " + value.toString() + " as BigDecimal )";
}
}
public static class StringValueHandler extends BaseValueHandler<String> implements Serializable {

View File

@ -2,6 +2,7 @@
package org.hibernate.test.hql;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
@ -1195,9 +1196,124 @@ public class ASTParserLoadingTest extends FunctionalTestCase {
a.setBodyWeight(12.4f);
a.setDescription("an animal");
s.persist(a);
Integer bw = (Integer) s.createQuery("select cast(bodyWeight as integer) from Animal").uniqueResult();
bw = (Integer) s.createQuery("select cast(a.bodyWeight as integer) from Animal a").uniqueResult();
bw.toString();
Object bodyWeight = s.createQuery("select cast(bodyWeight as integer) from Animal").uniqueResult();
assertTrue( Integer.class.isInstance( bodyWeight ) );
assertEquals( 12, bodyWeight );
bodyWeight = s.createQuery("select cast(bodyWeight as big_decimal) from Animal").uniqueResult();
assertTrue( BigDecimal.class.isInstance( bodyWeight ) );
assertEquals( BigDecimal.valueOf( a.getBodyWeight() ), bodyWeight );
Object literal = s.createQuery("select cast(10000000 as big_integer) from Animal").uniqueResult();
assertTrue( BigInteger.class.isInstance( literal ) );
assertEquals( BigInteger.valueOf( 10000000 ), literal );
s.delete(a);
t.commit();
s.close();
}
/**
* Test the numeric expression rules specified in section 4.8.6 of the JPA 2 specification
*/
public void testNumericExpressionReturnTypes() {
Session s = openSession();
Transaction t = s.beginTransaction();
Animal a = new Animal();
a.setBodyWeight(12.4f);
a.setDescription("an animal");
s.persist(a);
Object result;
// addition ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
result = s.createQuery( "select 1 + 1 from Animal as a" ).uniqueResult();
assertTrue( "int + int", Integer.class.isInstance( result ) );
assertEquals( 2, result );
result = s.createQuery( "select 1 + 1L from Animal a" ).uniqueResult();
assertTrue( "int + long", Long.class.isInstance( result ) );
assertEquals( Long.valueOf( 2 ), result );
result = s.createQuery( "select 1 + 1BI from Animal a" ).uniqueResult();
assertTrue( "int + BigInteger", BigInteger.class.isInstance( result ) );
assertEquals( BigInteger.valueOf( 2 ), result );
result = s.createQuery( "select 1 + 1F from Animal a" ).uniqueResult();
assertTrue( "int + float", Float.class.isInstance( result ) );
assertEquals( Float.valueOf( 2 ), result );
result = s.createQuery( "select 1 + 1D from Animal a" ).uniqueResult();
assertTrue( "int + double", Double.class.isInstance( result ) );
assertEquals( Double.valueOf( 2 ), result );
result = s.createQuery( "select 1 + 1BD from Animal a" ).uniqueResult();
assertTrue( "int + BigDecimal", BigDecimal.class.isInstance( result ) );
assertEquals( BigDecimal.valueOf( 2 ), result );
result = s.createQuery( "select 1F + 1D from Animal a" ).uniqueResult();
assertTrue( "float + double", Double.class.isInstance( result ) );
assertEquals( Double.valueOf( 2 ), result );
result = s.createQuery( "select 1F + 1BD from Animal a" ).uniqueResult();
assertTrue( "float + BigDecimal", Float.class.isInstance( result ) );
assertEquals( Float.valueOf( 2 ), result );
// subtraction ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
result = s.createQuery( "select 1 - 1 from Animal as a" ).uniqueResult();
assertTrue( "int - int", Integer.class.isInstance( result ) );
assertEquals( 0, result );
result = s.createQuery( "select 1 - 1L from Animal a" ).uniqueResult();
assertTrue( "int - long", Long.class.isInstance( result ) );
assertEquals( Long.valueOf( 0 ), result );
result = s.createQuery( "select 1 - 1BI from Animal a" ).uniqueResult();
assertTrue( "int - BigInteger", BigInteger.class.isInstance( result ) );
assertEquals( BigInteger.valueOf( 0 ), result );
result = s.createQuery( "select 1 - 1F from Animal a" ).uniqueResult();
assertTrue( "int - float", Float.class.isInstance( result ) );
assertEquals( Float.valueOf( 0 ), result );
result = s.createQuery( "select 1 - 1D from Animal a" ).uniqueResult();
assertTrue( "int - double", Double.class.isInstance( result ) );
assertEquals( Double.valueOf( 0 ), result );
result = s.createQuery( "select 1 - 1BD from Animal a" ).uniqueResult();
assertTrue( "int - BigDecimal", BigDecimal.class.isInstance( result ) );
assertEquals( BigDecimal.valueOf( 0 ), result );
result = s.createQuery( "select 1F - 1D from Animal a" ).uniqueResult();
assertTrue( "float - double", Double.class.isInstance( result ) );
assertEquals( Double.valueOf( 0 ), result );
result = s.createQuery( "select 1F - 1BD from Animal a" ).uniqueResult();
assertTrue( "float - BigDecimal", Float.class.isInstance( result ) );
assertEquals( Float.valueOf( 0 ), result );
// multiplication ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
result = s.createQuery( "select 1 * 1 from Animal as a" ).uniqueResult();
assertTrue( "int * int", Integer.class.isInstance( result ) );
assertEquals( 1, result );
result = s.createQuery( "select 1 * 1L from Animal a" ).uniqueResult();
assertTrue( "int * long", Long.class.isInstance( result ) );
assertEquals( Long.valueOf( 1 ), result );
result = s.createQuery( "select 1 * 1BI from Animal a" ).uniqueResult();
assertTrue( "int * BigInteger", BigInteger.class.isInstance( result ) );
assertEquals( BigInteger.valueOf( 1 ), result );
result = s.createQuery( "select 1 * 1F from Animal a" ).uniqueResult();
assertTrue( "int * float", Float.class.isInstance( result ) );
assertEquals( Float.valueOf( 1 ), result );
result = s.createQuery( "select 1 * 1D from Animal a" ).uniqueResult();
assertTrue( "int * double", Double.class.isInstance( result ) );
assertEquals( Double.valueOf( 1 ), result );
result = s.createQuery( "select 1 * 1BD from Animal a" ).uniqueResult();
assertTrue( "int * BigDecimal", BigDecimal.class.isInstance( result ) );
assertEquals( BigDecimal.valueOf( 1 ), result );
s.delete(a);
t.commit();
s.close();