/*****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.cayenne.dba.sqlserver;
import org.apache.cayenne.access.translator.select.QueryAssembler;
import org.apache.cayenne.access.translator.select.TrimmingQualifierTranslator;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.parser.ASTExtract;
import org.apache.cayenne.exp.parser.ASTFunctionCall;
import org.apache.cayenne.map.DbAttribute;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
/**
* @since 3.0
*/
class SQLServerTrimmingQualifierTranslator extends TrimmingQualifierTranslator {
// since LIKE IGNORE CASE requires more contextual information than the
// super
// translator can provide, we are using an internal element stack to trace
// translation
// context.. Maybe it is a good idea to introduce it in the superclass?
private List<Expression> expressionStack;
SQLServerTrimmingQualifierTranslator(QueryAssembler queryAssembler, String trimFunction) {
super(queryAssembler, trimFunction);
expressionStack = new ArrayList<>();
}
@Override
public void startNode(Expression node, Expression parentNode) {
push(node);
super.startNode(node, parentNode);
}
@Override
protected void processColumn(DbAttribute dbAttr) {
Expression node = peek(1);
boolean likeCI = node != null && dbAttr.getType() == Types.CLOB
&& (node.getType() == Expression.LIKE_IGNORE_CASE || node.getType() == Expression.NOT_LIKE_IGNORE_CASE);
if (likeCI) {
out.append("CAST(");
}
super.processColumn(dbAttr);
if (likeCI) {
out.append(" AS NVARCHAR(MAX))");
}
}
@Override
protected void processColumnWithQuoteSqlIdentifiers(DbAttribute dbAttr, Expression pathExp) {
Expression node = peek(1);
boolean likeCI = node != null && dbAttr.getType() == Types.CLOB
&& (node.getType() == Expression.LIKE_IGNORE_CASE || node.getType() == Expression.NOT_LIKE_IGNORE_CASE);
if (likeCI) {
out.append("CAST(");
}
super.processColumnWithQuoteSqlIdentifiers(dbAttr, node);
if (likeCI) {
out.append(" AS NVARCHAR(MAX))");
}
}
@Override
public void endNode(Expression node, Expression parentNode) {
super.endNode(node, parentNode);
pop();
}
private void push(Expression node) {
expressionStack.add(node);
}
private void pop() {
int len = expressionStack.size();
if (len > 0) {
expressionStack.remove(len - 1);
}
}
private Expression peek(int tailIndex) {
int index = expressionStack.size() - tailIndex - 1;
if (index < 0) {
return null;
}
return expressionStack.get(index);
}
/**
* @since 4.0
*/
@Override
protected void appendFunction(ASTFunctionCall functionExpression) {
switch (functionExpression.getFunctionName()) {
case "LENGTH":
out.append("LEN");
break;
case "LOCATE":
out.append("CHARINDEX");
break;
case "MOD":
// noop
break;
case "TRIM":
out.append("LTRIM(RTRIM");
break;
case "CURRENT_DATE":
out.append("{fn CURDATE()}");
break;
case "CURRENT_TIME":
out.append("{fn CURTIME()}");
break;
default:
super.appendFunction(functionExpression);
}
}
/**
* @since 4.0
*/
@Override
protected void appendFunctionArgDivider(ASTFunctionCall functionExpression) {
if("MOD".equals(functionExpression.getFunctionName())) {
out.append(" % ");
} else {
super.appendFunctionArgDivider(functionExpression);
}
}
/**
* @since 4.0
*/
@Override
protected void clearLastFunctionArgDivider(ASTFunctionCall functionExpression) {
if("MOD".equals(functionExpression.getFunctionName())) {
out.delete(out.length() - " % ".length(), out.length());
} else {
super.clearLastFunctionArgDivider(functionExpression);
if("TRIM".equals(functionExpression.getFunctionName())) {
out.append(")");
}
}
if(functionExpression instanceof ASTExtract) {
out.append(")");
}
}
@Override
protected boolean parenthesisNeeded(Expression node, Expression parentNode) {
if (node.getType() == Expression.FUNCTION_CALL) {
if (node instanceof ASTExtract) {
return false;
}
}
return super.parenthesisNeeded(node, parentNode);
}
@Override
protected void appendExtractFunction(ASTExtract functionExpression) {
out.append("DATEPART(");
switch (functionExpression.getPart()) {
case DAY_OF_MONTH:
out.append("DAY");
break;
case DAY_OF_WEEK:
out.append("WEEKDAY");
break;
case DAY_OF_YEAR:
out.append("DAYOFYEAR");
break;
default:
out.append(functionExpression.getPart().name());
}
out.append(" , ");
}
}