Skip to content

Commit

Permalink
TEIID-4974 TEIID-5030 TEIID-5031 TEIID-5033 TEIID-5034 TEIID-5035
Browse files Browse the repository at this point in the history
TEIID-5036 TEIID-5037 addressing various couchbase issues
  • Loading branch information
shawkins committed Aug 31, 2017
1 parent f84f6eb commit 0bbc7bb
Show file tree
Hide file tree
Showing 13 changed files with 380 additions and 145 deletions.
Expand Up @@ -70,7 +70,7 @@ public CouchbaseExecutionFactory() {
@Override
public void start() throws TranslatorException {
super.start();
registerFunctionModifier(SourceSystemFunctions.SUBSTRING, new AliasModifier("SUBSTR"));//$NON-NLS-1$
registerFunctionModifier(SourceSystemFunctions.SUBSTRING, new SubstringFunctionModifier());
registerFunctionModifier(SourceSystemFunctions.CEILING, new AliasModifier("CEIL"));//$NON-NLS-1$
registerFunctionModifier(SourceSystemFunctions.LOG, new AliasModifier("LN"));//$NON-NLS-1$
registerFunctionModifier(SourceSystemFunctions.LOG10, new AliasModifier("LOG"));//$NON-NLS-1$
Expand Down Expand Up @@ -148,7 +148,6 @@ public List<String> getSupportedFunctions() {
supportedFunctions.add(SourceSystemFunctions.LENGTH);
supportedFunctions.add(SourceSystemFunctions.LCASE);
supportedFunctions.add(SourceSystemFunctions.REPEAT);
supportedFunctions.add(SourceSystemFunctions.TRANSLATE);
supportedFunctions.add(SourceSystemFunctions.SUBSTRING);
supportedFunctions.add(SourceSystemFunctions.UCASE);
supportedFunctions.add(SourceSystemFunctions.REPLACE);
Expand Down Expand Up @@ -400,9 +399,19 @@ public boolean supportsConvert(int fromType, int toType) {
if (toType == FunctionModifier.STRING || toType == FunctionModifier.INTEGER || toType == FunctionModifier.LONG
|| toType == FunctionModifier.DOUBLE || toType == FunctionModifier.BOOLEAN || toType == FunctionModifier.BIGINTEGER
|| toType == FunctionModifier.BIGDECIMAL || toType == FunctionModifier.OBJECT) {
return true;
return super.supportsConvert(fromType, toType);
}
return super.supportsConvert(fromType, toType);
return false;
}

@Override
public NullOrder getDefaultNullOrder() {
return NullOrder.LOW;
}

@Override
public boolean supportsSearchedCaseExpressions() {
return true;
}

}
Expand Up @@ -36,6 +36,7 @@
import org.teiid.translator.ExecutionContext;
import org.teiid.translator.ResultSetExecution;
import org.teiid.translator.TranslatorException;
import org.teiid.translator.TypeFacility;

import com.couchbase.client.java.document.json.JsonObject;
import com.couchbase.client.java.query.N1qlQueryResult;
Expand Down Expand Up @@ -89,9 +90,29 @@ public List<?> next() throws TranslatorException, DataNotAvailableException {

for(int i = 0 ; i < expectedTypes.length ; i ++){
String columnName = this.visitor.getSelectColumns().get(i);
Object value = json.get(columnName);
Object value = null;
Class<?> type = expectedTypes[i];
//retrieve as the specific value type since couchbase can mix
//TODO: this could be moved into retrieve value if used in more than 1 place
if (type == TypeFacility.RUNTIME_TYPES.STRING) {
value = json.getString(columnName);
} else if (type == TypeFacility.RUNTIME_TYPES.INTEGER) {
value = json.getInt(columnName);
} else if (type == TypeFacility.RUNTIME_TYPES.LONG) {
value = json.getLong(columnName);
} else if (type == TypeFacility.RUNTIME_TYPES.DOUBLE) {
value = json.getDouble(columnName);
} else if (type == TypeFacility.RUNTIME_TYPES.BOOLEAN) {
value = json.getBoolean(columnName);
} else if (type == TypeFacility.RUNTIME_TYPES.BIG_INTEGER) {
value = json.getBigInteger(columnName);
} else if (type == TypeFacility.RUNTIME_TYPES.BIG_DECIMAL) {
value = json.getBigDecimal(columnName);
} else {
value = json.getObject(columnName);
}

row.add(this.executionFactory.retrieveValue(expectedTypes[i], value));
row.add(this.executionFactory.retrieveValue(type, value));
}
return row;
}
Expand All @@ -103,6 +124,5 @@ public void close() {

@Override
public void cancel() throws TranslatorException {
close();
}
}
Expand Up @@ -29,7 +29,6 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -61,13 +60,51 @@ public class N1QLVisitor extends SQLStringVisitor{
private String topTableAlias;

private int placeHolderIndex = 1;

private Map<String, Expression> aliasedExpressions = new LinkedHashMap<String, Expression>();


public N1QLVisitor(CouchbaseExecutionFactory ef) {
this.ef = ef;
}

@Override
public void visit(SetQuery obj) {
boolean nestLeft = obj.getLeftQuery().getOrderBy() != null || obj.getLeftQuery().getLimit() != null;
if (nestLeft) {
buffer.append(LPAREN);
}
append(obj.getLeftQuery());
if (nestLeft) {
buffer.append(RPAREN);
}
//because we hold a lot of state, it's easier to construct a new visitor for the right
N1QLVisitor rightVisitor = new N1QLVisitor(this.ef);
rightVisitor.append(obj.getRightQuery());
this.buffer.append(SPACE).append(obj.getOperation()).append(SPACE);
if (obj.isAll()) {
this.buffer.append(ALL).append(SPACE);
}
boolean nestRight = obj.getRightQuery().getOrderBy() != null || obj.getRightQuery().getLimit() != null;
if (nestRight) {
buffer.append(LPAREN);
}
this.buffer.append(rightVisitor.toString());
if (nestRight) {
buffer.append(RPAREN);
}
appendQueryExpressionEnd(obj);
}

private void appendQueryExpressionEnd(QueryExpression obj) {
if (obj.getOrderBy() != null) {
buffer.append(Tokens.SPACE);
append(obj.getOrderBy());
}

if (obj.getLimit() != null) {
buffer.append(Tokens.SPACE);
append(obj.getLimit());
}
}

@Override
public void visit(Select obj) {

Expand Down Expand Up @@ -97,15 +134,7 @@ public void visit(Select obj) {
append(obj.getHaving());
}

if (obj.getOrderBy() != null) {
buffer.append(Tokens.SPACE);
append(obj.getOrderBy());
}

if (obj.getLimit() != null) {
buffer.append(Tokens.SPACE);
append(obj.getLimit());
}
appendQueryExpressionEnd(obj);
}

private void appendLet(Select obj) {
Expand Down Expand Up @@ -357,27 +386,23 @@ protected String buildMeta(String alias) {

@Override
public void visit(DerivedColumn obj) {
if (obj.getAlias() != null) {
aliasedExpressions.put(obj.getAlias(), obj.getExpression());
String name = obj.getAlias();
if (name == null) {
name = "c" + placeHolderIndex++; //$NON-NLS-1$
}

if (obj.getExpression() instanceof ColumnReference) {
ColumnReference columnReference = (ColumnReference)obj.getExpression();
CBColumn column = defineColumn(columnReference);
String aliasName = column.getNameReference();
buffer.append(this.nameInSource(aliasName));
this.selectColumns.add(column.getNameReference());
} else {
//this will be retrieved by a placeholder
this.selectColumns.add(nextPlaceholder());
append(obj.getExpression());
}
buffer.append(SPACE).append(name);
selectColumns.add(name);
}

private String nextPlaceholder() {
return PLACEHOLDER + placeHolderIndex++;
}

private CBColumn defineColumn(ColumnReference columnReference) {
retrieveTableProperty(columnReference.getTable());

Expand All @@ -398,12 +423,7 @@ public void visit(ColumnReference obj) {
String aliasName = column.getNameReference();
buffer.append(this.nameInSource(aliasName));
} else {
Expression ex = aliasedExpressions.get(obj.getName());
if (ex != null) {
append(ex);
} else {
super.visit(obj);
}
buffer.append(obj.getName());
}
}

Expand Down Expand Up @@ -495,14 +515,14 @@ public List<String> getSelectColumns() {
return selectColumns;
}

public AliasGenerator getColumnAliasGenerator() {
protected AliasGenerator getColumnAliasGenerator() {
if(this.columnAliasGenerator == null) {
this.columnAliasGenerator = new AliasGenerator(N1QL_COLUMN_ALIAS_PREFIX);
}
return columnAliasGenerator;
}

public AliasGenerator getTableAliasGenerator() {
protected AliasGenerator getTableAliasGenerator() {
if(this.tableAliasGenerator == null) {
this.tableAliasGenerator = new AliasGenerator(N1QL_TABLE_ALIAS_PREFIX);
}
Expand Down
@@ -0,0 +1,125 @@
/*
* Copyright Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags and
* the COPYRIGHT.txt file distributed with this work.
*
* 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.teiid.translator.couchbase;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.teiid.language.Comparison;
import org.teiid.language.Comparison.Operator;
import org.teiid.language.Expression;
import org.teiid.language.Function;
import org.teiid.language.Literal;
import org.teiid.language.SearchedCase;
import org.teiid.language.SearchedWhenClause;
import org.teiid.translator.SourceSystemFunctions;
import org.teiid.translator.TypeFacility;
import org.teiid.translator.jdbc.AliasModifier;

public class SubstringFunctionModifier extends AliasModifier {

public SubstringFunctionModifier() {
super("substr"); //$NON-NLS-1$
}

@Override
public List<?> translate(Function function) {
this.modify(function);

Expression from = function.getParameters().get(1);
Boolean isFromNegative = isNegative(from);

Function length = new Function(
SourceSystemFunctions.LENGTH,
Arrays.asList(function.getParameters().get(0)),
TypeFacility.RUNTIME_TYPES.INTEGER);

if (function.getParameters().size() == 2 && (isFromNegative == null || isFromNegative)) {
//couchbase does not handle default length with a negative from index
function.getParameters().add(length);
}

if (function.getParameters().size() == 3) {
//case when length > LENGTH(string) - start + 1 then LENGTH(string) - start + 1 case when length > 0 then length end
Expression forLength = function.getParameters().get(2);
List<SearchedWhenClause> clauses = new ArrayList<SearchedWhenClause>(2);
Boolean isNegative = isNegative(forLength);

Expression adjustedFrom = from;
if (isFromNegative == null || isFromNegative) {
adjustedFrom = new SearchedCase(Arrays.asList(new SearchedWhenClause(new Comparison(from, new Literal(0, TypeFacility.RUNTIME_TYPES.INTEGER), Operator.LT), new Function(
SourceSystemFunctions.ADD_OP,
Arrays.asList(
new Function(SourceSystemFunctions.ADD_OP, Arrays.asList(length, new Literal(1, TypeFacility.RUNTIME_TYPES.INTEGER)),TypeFacility.RUNTIME_TYPES.INTEGER),
from),
TypeFacility.RUNTIME_TYPES.INTEGER))),
from, TypeFacility.RUNTIME_TYPES.INTEGER);
}

Expression maxLength = new Function(
SourceSystemFunctions.SUBTRACT_OP,
Arrays.asList(length,
new Function(
SourceSystemFunctions.SUBTRACT_OP,
Arrays.asList(
adjustedFrom,
new Literal(1, TypeFacility.RUNTIME_TYPES.INTEGER)),
TypeFacility.RUNTIME_TYPES.INTEGER)),
TypeFacility.RUNTIME_TYPES.INTEGER);
clauses.add(new SearchedWhenClause(new Comparison(forLength, maxLength, Operator.GT), maxLength));
Expression defaultExpr = null;
if (isNegative == null) {
clauses.add(new SearchedWhenClause(new Comparison(forLength, new Literal(0, TypeFacility.RUNTIME_TYPES.INTEGER), Operator.GT), forLength));
} else if (isNegative) {
//TODO: could be done in the rewriter
return Arrays.asList(new Literal(null, TypeFacility.RUNTIME_TYPES.STRING));
} else {
defaultExpr = forLength;
}
SearchedCase sc = new SearchedCase(clauses,
defaultExpr, TypeFacility.RUNTIME_TYPES.INTEGER);
function.getParameters().set(2, sc);
}

Expression adjustedFrom = function.getParameters().get(1);
if (isFromNegative == null) {
//case when start > 0 then start - 1 else start end
SearchedCase sc = new SearchedCase(Arrays.asList(new SearchedWhenClause(new Comparison(adjustedFrom, new Literal(0, TypeFacility.RUNTIME_TYPES.INTEGER), Operator.GT),
new Function(SourceSystemFunctions.SUBTRACT_OP, Arrays.asList(adjustedFrom, new Literal(1, TypeFacility.RUNTIME_TYPES.INTEGER)),TypeFacility.RUNTIME_TYPES.INTEGER))),
from, TypeFacility.RUNTIME_TYPES.INTEGER);
function.getParameters().set(1, sc);
} else if (!isFromNegative){
function.getParameters().set(1, new Function(SourceSystemFunctions.SUBTRACT_OP, Arrays.asList(adjustedFrom, new Literal(1, TypeFacility.RUNTIME_TYPES.INTEGER)),TypeFacility.RUNTIME_TYPES.INTEGER));
}

return null;
}

private Boolean isNegative(Expression ex) {
Boolean isNegative = null;
if (ex instanceof Literal) {
Literal l = (Literal)ex;
int value = (Integer)l.getValue();
isNegative = value < 0;
}
return isNegative;
}

}
@@ -0,0 +1,37 @@
/*
* Copyright Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags and
* the COPYRIGHT.txt file distributed with this work.
*
* 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.teiid.translator.couchbase;

import static org.junit.Assert.*;

import org.junit.Ignore;
import org.junit.Test;
import org.teiid.translator.TranslatorException;
import org.teiid.translator.TypeFacility;

@Ignore
public class TestCouchbase {

@Test public void testConvert() throws TranslatorException {
CouchbaseExecutionFactory cef = new CouchbaseExecutionFactory();
cef.start();
assertFalse(cef.supportsConvert(TypeFacility.RUNTIME_CODES.STRING, TypeFacility.RUNTIME_CODES.DATE));
assertTrue(cef.supportsConvert(TypeFacility.RUNTIME_CODES.STRING, TypeFacility.RUNTIME_CODES.DOUBLE));
}
}

0 comments on commit 0bbc7bb

Please sign in to comment.