blob: d442417982c06de9e37ab4f49a71e87d2ac9b886 [file] [log] [blame]
/*
* 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.
*
* Authors:
* Florent Guillaume, Nuxeo
*/
/**
* CMISQL tree grammar, walker for the inmemory implementation.
* This aims at implementing proper semantics without any speed
* optimization.
*/
tree grammar CmisBaseWalker;
options {
tokenVocab = CmisQlStrictLexer;
ASTLabelType = CommonTree;
output = AST;
}
@members {
private QueryObject queryObj;
private Tree wherePredicateTree;
private boolean doFullTextParse = true;
private int noContains = 0;
public Tree getWherePredicateTree() {
return wherePredicateTree;
}
protected void mismatch(IntStream input, int ttype, BitSet follow)
throws RecognitionException
{
throw new MismatchedTokenException(ttype, input);
}
public void recoverFromMismatchedSet(IntStream input, RecognitionException e, antlr.collections.impl.BitSet follow)
throws RecognitionException
{
throw e;
}
public void setDoFullTextParse(boolean value) {
doFullTextParse = value;
}
public boolean getDoFullTextParse() {
return doFullTextParse;
}
public int getNumberOfContainsClauses() {
return noContains;
}
private static CommonTree parseTextSearchPredicate(String expr) throws RecognitionException {
String unescapedExpr = StringUtil.unescape(expr.substring(1, expr.length()-1), null);
CharStream input = new ANTLRStringStream(unescapedExpr);
TokenSource lexer = new TextSearchLexer(input);
TokenStream tokens = new CommonTokenStream(lexer);
TextSearchParser parser = new TextSearchParser(tokens);
TextSearchParser.text_search_expression_return parsedStatement = parser.text_search_expression();
return (CommonTree) parsedStatement.getTree();
}
}
// For CMIS SQL it will be sufficient to stop on first error:
@rulecatch {
catch (RecognitionException e) {
throw e;
}
}
query [QueryObject qo, PredicateWalkerBase pw]
@init {
queryObj = qo;
}:
^(SELECT select_list from_clause order_by_clause? where_clause)
{
wherePredicateTree = $where_clause.tree==null ? null : $where_clause.tree.getChild(0);
boolean resolved = queryObj.resolveTypes();
if (null != pw && null != $where_clause.tree)
pw.walkPredicate(wherePredicateTree);
}
{
resolved
}?
;
catch[FailedPredicateException e]
{
// change default text to preserved text which is useful
e.predicateText = queryObj.getErrorMessage();
throw e;
}
select_list:
STAR
{
queryObj.addSelectReference($STAR, new ColumnReference($STAR.text));
}
| ^(SEL_LIST select_sublist+)
;
select_sublist
scope { String current; }
:
value_expression column_name?
{
// add selector
queryObj.addSelectReference($value_expression.start, $value_expression.result);
// add alias for column
if ($column_name.text != null) {
queryObj.addAlias($column_name.text, $value_expression.result);
}
}
| s=qualifier DOT STAR
{
queryObj.addSelectReference($s.start, new ColumnReference($qualifier.value, $STAR.text));
}
;
value_expression returns [CmisSelector result]:
column_reference
{
$result = $column_reference.result;
}
| SCORE^
{
$result = new FunctionReference(FunctionReference.CmisQlFunction.SCORE);
}
;
column_reference returns [ColumnReference result]:
^(COL qualifier? column_name)
{
$result = new ColumnReference($qualifier.value, $column_name.text);
}
;
multi_valued_column_reference returns [ColumnReference result]:
^(COL qualifier? column_name)
{
$result = new ColumnReference($qualifier.value, $column_name.text);
}
;
qualifier returns [String value]:
table_name
// | correlation_name
{
$value = $table_name.text;
}
;
from_clause:
^(FROM table_reference)
;
table_reference:
one_table table_join*
;
table_join:
^(JOIN join_kind one_table join_specification?)
{
boolean hasSpec = $join_specification.tree != null;
queryObj.addJoin($join_kind.kind, $one_table.alias, hasSpec);
}
;
one_table returns [String alias]:
^(TABLE table_name correlation_name?)
{
($alias = queryObj.addType($correlation_name.text, $table_name.text)) != null
}?
;
catch[FailedPredicateException e]
{
// change default text to preserved text which is useful
e.predicateText = queryObj.getErrorMessage();
throw e;
}
join_kind returns [String kind]:
INNER { $kind = "INNER"; }
| LEFT { $kind = "LEFT"; }
| RIGHT { $kind = "RIGHT"; }
;
join_specification:
^(ON cr1=column_reference EQ cr2=column_reference)
{
queryObj.addJoinReference($cr1.start, $cr1.result);
queryObj.addJoinReference($cr2.start, $cr2.result);
}
;
where_clause:
^(WHERE search_condition)
| /* nothing */
;
search_condition
@init {
List<Object> listLiterals;
}:
^(OR s1=search_condition s2=search_condition)
| ^(AND s1=search_condition s2=search_condition)
| ^(NOT search_condition)
| ^(EQ search_condition search_condition)
| ^(NEQ search_condition search_condition)
| ^(LT search_condition search_condition)
| ^(GT search_condition search_condition)
| ^(GTEQ search_condition search_condition)
| ^(LTEQ search_condition search_condition)
| ^(LIKE search_condition search_condition)
| ^(NOT_LIKE search_condition search_condition)
| ^(IS_NULL search_condition)
| ^(IS_NOT_NULL search_condition)
| ^(EQ_ANY literal mvcr=multi_valued_column_reference)
{
queryObj.addWhereReference($mvcr.start, $mvcr.result);
}
| ^(IN_ANY mvcr=multi_valued_column_reference in_value_list )
{
queryObj.addWhereReference($mvcr.start, $mvcr.result);
}
| ^(NOT_IN_ANY mvcr=multi_valued_column_reference in_value_list)
{
queryObj.addWhereReference($mvcr.start, $mvcr.result);
}
| ^(CONTAINS qualifier? text_search_expression)
{
queryObj.addWhereTypeReference($qualifier.start, $qualifier.value);
++noContains;
}
| ^(IN_FOLDER qualifier? search_condition)
{
queryObj.addWhereTypeReference($qualifier.start, $qualifier.value);
}
| ^(IN_TREE qualifier? search_condition)
{
queryObj.addWhereTypeReference($qualifier.start, $qualifier.value);
}
| ^(IN column_reference in_value_list)
{
queryObj.addWhereReference($column_reference.start, $column_reference.result);
}
| ^(NOT_IN column_reference in_value_list)
{
queryObj.addWhereReference($column_reference.start, $column_reference.result);
}
| value_expression
{
queryObj.addWhereReference($value_expression.start, $value_expression.result);
}
| literal
;
in_value_list returns [Object inList]
@init {
List<Object> inLiterals = new ArrayList<Object>();
}:
^(IN_LIST (l=literal {inLiterals.add($l.value);})+ )
{
$inList = inLiterals;
}
;
text_search_expression
@init {
CommonTree tse = null;
}
@after {
if (doFullTextParse) {
$tree = tse;
}
} :
STRING_LIT
{
if (doFullTextParse) {
tse = parseTextSearchPredicate($STRING_LIT.text);
}
}
;
literal returns [Object value]:
NUM_LIT
{
try {
$value = Long.valueOf($NUM_LIT.text);
} catch (NumberFormatException e) {
$value = new BigDecimal($NUM_LIT.text);
}
}
| STRING_LIT
{
String s = $STRING_LIT.text;
$value = s!= null ? s.substring(1, s.length() - 1) : null;
}
| TIME_LIT
{
String s = $TIME_LIT.text;
s = s!= null ? s.substring(s.indexOf('\'') + 1, s.length() - 1) : null;
}
| BOOL_LIT
{
$value = Boolean.valueOf($BOOL_LIT.text);
}
;
order_by_clause:
^(ORDER_BY sort_specification+)
;
sort_specification:
column_reference ASC
{
queryObj.addSortCriterium($column_reference.start, $column_reference.result, true);
}
| column_reference DESC
{
queryObj.addSortCriterium($column_reference.start, $column_reference.result, false);
}
;
correlation_name:
ID;
table_name:
ID;
column_name:
ID;