Skip to content

Commit

Permalink
TEIID-5063 addressing bigdecimal issues
Browse files Browse the repository at this point in the history
  • Loading branch information
shawkins committed Sep 13, 2017
1 parent 9e60ee4 commit 5d36ce5
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 63 deletions.
Expand Up @@ -23,12 +23,12 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
import org.teiid.language.*;
import org.teiid.language.SQLConstants.Tokens;
import org.teiid.language.visitor.HierarchyVisitor;
import org.teiid.metadata.BaseColumn;
import org.teiid.metadata.Column;
import org.teiid.metadata.FunctionMethod;
import org.teiid.metadata.RuntimeMetadata;
Expand All @@ -52,11 +52,11 @@ public class ODataFilterVisitor extends HierarchyVisitor {
}
protected StringBuilder filter = new StringBuilder();
private ODataExecutionFactory ef;
private Stack<String> exprType = new Stack<String>();
protected ArrayList<TranslatorException> exceptions = new ArrayList<TranslatorException>();
private ODataQuery query;
private RuntimeMetadata metadata;
private ODataDocumentNode filterOnElement;
private BaseColumn currentExpression;

public ODataFilterVisitor(ODataExecutionFactory ef, RuntimeMetadata metadata, ODataQuery query) {
this.ef = ef;
Expand Down Expand Up @@ -99,7 +99,23 @@ public void visit(Comparison obj) {
break;
}
this.filter.append(Tokens.SPACE);
BaseColumn old = setCurrentExpression(obj.getLeftExpression());
appendRightComparison(obj);
this.currentExpression = old;
}

private BaseColumn setCurrentExpression(Expression leftExpression) {
BaseColumn old = currentExpression;
if (leftExpression instanceof ColumnReference) {
ColumnReference cr = (ColumnReference)leftExpression;
currentExpression = cr.getMetadataObject();
} else if (leftExpression instanceof Function) {
Function function = (Function)leftExpression;
currentExpression = function.getMetadataObject().getOutputParameter();
} else {
currentExpression = null;
}
return old;
}

protected void appendRightComparison(Comparison obj) {
Expand Down Expand Up @@ -168,8 +184,6 @@ public void visit(ColumnReference obj) {
// check if the column on psedo column, then move it to the parent.
String pseudo = ODataMetadataProcessor.getPseudo(column);

this.exprType.push(odataType(column.getNativeType(), column.getRuntimeType()));

ODataDocumentNode schemaElement = this.query.getSchemaElement((Table)column.getParent());
if (pseudo != null) {
try {
Expand Down Expand Up @@ -243,25 +257,24 @@ public void visit(Function obj) {
.odataType((String) literal.getValue())
.getFullQualifiedName().getFullQualifiedNameAsString();
this.filter.append(type);
this.exprType.push(type);
} else {
if (args != null && args.size() != 0) {
if (SourceSystemFunctions.ENDSWITH.equalsIgnoreCase(name)) {
append(args.get(1));
this.filter.append(Tokens.COMMA);
append(args.get(0));
} else {
for (int i = 0; i < args.size(); i++) {
BaseColumn old = currentExpression;
for (int i = 0; i < args.size(); i++) {
currentExpression = method.getInputParameters().get(Math.min(i, method.getInputParameters().size() -1));
append(args.get(i));
if (i < args.size()-1) {
this.filter.append(Tokens.COMMA);
}
}
currentExpression = old;
}
}
this.exprType.push(odataType(
method.getOutputParameter().getNativeType(),
method.getOutputParameter().getRuntimeType()));
}
this.filter.append(Tokens.RPAREN);
}
Expand All @@ -274,14 +287,21 @@ public void visit(In obj) {
}
visitNode(obj.getLeftExpression());
this.filter.append(" eq ");
BaseColumn old = setCurrentExpression(obj.getLeftExpression());
visitNode(expr);
this.currentExpression = old;
}
}

@Override
public void visit(Literal obj) {
try {
String odataType = this.exprType.pop();
String odataType = ODataTypeManager.odataType(obj.getType()).toString();
if (currentExpression != null) {
//TODO: this is an attempt at contextually figuring out the type, but it
//may not be sufficient in all cases
odataType = odataType(currentExpression.getNativeType(), currentExpression.getRuntimeType());
}
this.filter.append(ODataTypeManager.convertToODataURIValue(obj.getValue(), odataType));
} catch (EdmPrimitiveTypeException e) {
this.exceptions.add(new TranslatorException(e));
Expand Down
Expand Up @@ -72,7 +72,7 @@ private ProcedureParameter getReturnParameter() {
return null;
}

private String getQueryParameters(Call obj) throws EdmPrimitiveTypeException {
static String getQueryParameters(Call obj) throws EdmPrimitiveTypeException {
StringBuilder sb = new StringBuilder();
final List<Argument> params = obj.getArguments();
if (params != null && params.size() != 0) {
Expand Down
Expand Up @@ -17,12 +17,13 @@
*/
package org.teiid.translator.odata4;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.*;

import java.net.URLDecoder;

import org.junit.Test;
import org.teiid.cdk.api.TranslationUtility;
import org.teiid.language.Call;
import org.teiid.language.Select;
import org.teiid.metadata.MetadataFactory;
import org.teiid.translator.TranslatorException;
Expand Down Expand Up @@ -74,7 +75,23 @@ public void testPKBasedFilter2() throws Exception {
public void testMultiKeyKeyBasedFilter() throws Exception {
helpExecute("select Price from PurchaseDetails where ItemId = 1 and SaleId = 12 and Quantity = 2",
"PurchaseDetails?$select=Price&$filter=ItemId eq 1 and SaleId eq 12 and Quantity eq 2");
}
}

@Test
public void testBigDecimalLiteral() throws Exception {
helpExecute("select ItemId from PurchaseDetails where price > 12.0",
"PurchaseDetails?$select=ItemId&$filter=Price gt 12.0");
}

@Test
public void testBigDecimalParameter() throws Exception {
ODataExecutionFactory ef = new ODataExecutionFactory();
TranslationUtility utility = new TranslationUtility(TestODataMetadataProcessor.getTransformationMetadata(TestODataMetadataProcessor.tripPinMetadata(), ef));

Call cmd = (Call)utility.parseCommand("call GetNearestAirport(1.1, 1.2, 2.0)");
String params = ODataProcedureExecution.getQueryParameters(cmd);
assertEquals("lat=1.1&lon=1.2&within=2.0", params);
}

@Test
public void testAddFilter() throws Exception {
Expand Down Expand Up @@ -182,6 +199,9 @@ public void testLimitOffset() throws Exception {
public void testUseAirthmaticFunction() throws Exception {
helpExecute("SELECT UserName FROM People WHERE Concurrency/10 > Concurrency",
"People?$select=UserName&$filter=(Concurrency div 10) gt Concurrency");

helpExecute("SELECT UserName FROM People WHERE 10/Concurrency > Concurrency",
"People?$select=UserName&$filter=(10 div Concurrency) gt Concurrency");
}

@Test
Expand Down
Expand Up @@ -49,7 +49,7 @@
</Key>
<Property Name="ItemId" Type="Edm.Int64" Nullable="false"/>
<Property Name="SaleId" Type="Edm.Int64" Nullable="false"/>
<Property Name="Price" Type="Edm.Double" />
<Property Name="Price" Type="Edm.Decimal" Precision="8" Scale="2" />
<Property Name="Quantity" Type="Edm.Int32" />
</EntityType>
<EntityType Name="Photo" HasStream="true">
Expand Down Expand Up @@ -181,6 +181,7 @@
<Function Name="GetNearestAirport" IsComposable="true">
<Parameter Name="lat" Type="Edm.Double" Nullable="false" />
<Parameter Name="lon" Type="Edm.Double" Nullable="false" />
<Parameter Name="within" Type="Edm.Decimal" Nullable="true" Precision="6" Scale="3" />
<ReturnType Type="Microsoft.OData.SampleService.Models.TripPin.Airport" Nullable="false" />
</Function>
<Action Name="ResetDataSource" />
Expand Down
Expand Up @@ -24,6 +24,8 @@
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Date;
import java.sql.SQLXML;
import java.sql.Time;
Expand Down Expand Up @@ -366,14 +368,35 @@ public static Object parseLiteral(String odataType, String value)
}
}

public static Object rationalizePrecision(Integer precision, Integer scale, Object value) {
if (precision == null || !(value instanceof BigDecimal)) {
return value;
}
BigDecimal bigDecimalValue = (BigDecimal)value;
//if precision is set, then try to set an appropriate scale to pass the facet check
final int digits = bigDecimalValue.scale() >= 0
? Math.max(bigDecimalValue.precision(), bigDecimalValue.scale())
: bigDecimalValue.precision() - bigDecimalValue.scale();

if (bigDecimalValue.scale() > (scale == null ? 0 : scale) || (digits > precision)) {
BigDecimal newBigDecimal = bigDecimalValue.setScale(Math.min(digits > precision ? bigDecimalValue.scale() - digits + precision : bigDecimalValue.scale(), scale == null ? 0 : scale), RoundingMode.HALF_UP);
//only allow for trimming trailing zeros
if (newBigDecimal.compareTo(bigDecimalValue) == 0) {
bigDecimalValue = newBigDecimal;
}
}

return bigDecimalValue;
}

public static String convertToODataURIValue(Object val, String odataType) throws EdmPrimitiveTypeException {
if (val == null) {
return "null"; // is this correct? //$NON-NLS-1$
}
if(odataType.startsWith("Edm.")) { //$NON-NLS-1$
odataType = odataType.substring(4);
}
if (odataType.startsWith("Geometry") && val instanceof GeometryType) { //$NON-NLS-1$
if (val instanceof GeometryType) {
Geometry g;
try {
g = GeometryUtils.getGeometry((GeometryType)val);
Expand All @@ -395,7 +418,7 @@ public static String convertToODataURIValue(Object val, String odataType) throws
}
EdmPrimitiveTypeKind kind = EdmPrimitiveTypeKind.valueOf(odataType);
String value = EdmPrimitiveTypeFactory.getInstance(kind).valueToString(
val, true, DataTypeManager.MAX_STRING_LENGTH, 0, 0, true);
val, true, null, null, Integer.MAX_VALUE, true);
if (kind == EdmPrimitiveTypeKind.String) {
return EdmString.getInstance().toUriLiteral(value);
}
Expand Down
Expand Up @@ -21,8 +21,6 @@
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URI;
import java.net.URISyntaxException;
import java.sql.Array;
Expand Down Expand Up @@ -332,57 +330,14 @@ private static Property createCollection(final String name,
ValueType.COLLECTION_PRIMITIVE, values);
}

/* static Object getPropertyValue(SingletonPrimitiveType expectedType, boolean isArray,
Object value, String invalidCharacterReplacement)
throws TransformationException, SQLException, IOException {
if (value == null) {
return null;
}
Class<?> sourceType = DataTypeManager.getRuntimeType(value.getClass());
Class<?> targetType = DataTypeManager.getDataTypeClass(ODataTypeManager.teiidType(expectedType, isArray));
if (sourceType != targetType) {
Transform t = DataTypeManager.getTransform(sourceType, targetType);
if (t == null && BlobType.class == targetType) {
if (sourceType == ClobType.class) {
return ClobType.getString((Clob) value).getBytes();
}
if (sourceType == SQLXML.class) {
return ((SQLXML) value).getString().getBytes();
}
}
value = t != null ? t.transform(value, targetType) : value;
value = replaceInvalidCharacters(expectedType, value, invalidCharacterReplacement);
return value;
}
value = replaceInvalidCharacters(expectedType, value, invalidCharacterReplacement);
return value;
}
*/

static Object getPropertyValue(SingletonPrimitiveType expectedType, Integer precision, Integer scale, boolean isArray,
Object value)
throws TransformationException, SQLException, IOException {
if (value == null) {
return null;
}
value = getPropertyValueInternal(expectedType, isArray, value);
if (value instanceof BigDecimal) {
BigDecimal bigDecimalValue = (BigDecimal)value;

//if precision is set, then try to set an appropriate scale to pass the facet check
if (precision != null) {
final int digits = bigDecimalValue.scale() >= 0
? Math.max(bigDecimalValue.precision(), bigDecimalValue.scale())
: bigDecimalValue.precision() - bigDecimalValue.scale();

if (bigDecimalValue.scale() > (scale == null ? 0 : scale) || (digits > precision)) {
bigDecimalValue = bigDecimalValue.setScale(Math.min(digits > precision ? bigDecimalValue.scale() - digits + precision : bigDecimalValue.scale(), scale == null ? 0 : scale), RoundingMode.HALF_UP);
}
}

value = bigDecimalValue;
}
value = ODataTypeManager.rationalizePrecision(precision, scale, value);
return value;
}

Expand Down
Expand Up @@ -2816,7 +2816,7 @@ public void testDecimalPrecisionScale() throws Exception {
mmd.setName("vw");
mmd.addSourceMetadata("ddl", "CREATE VIEW SimpleTable(\n" +
" intkey integer PRIMARY KEY,\n" +
" decimalval decimal(3, 1)) as select 1,12.31 union all select 2, 1.0001 union all select 3, 123.1;");
" decimalval decimal(3, 1)) as select 1,12.30 union all select 2, 1.000 union all select 3, 123.0;");
mmd.setModelType(Model.Type.VIRTUAL);
teiid.deployVDB("northwind", mmd);

Expand Down

0 comments on commit 5d36ce5

Please sign in to comment.