Commit c35f82ca by Ashutosh Mestry Committed by apoorvnaik

ATLAS-2419: DSL Semantic Validation. Fix for limit & range.

parent 1fee4a5b
......@@ -103,7 +103,23 @@ public enum AtlasErrorCode {
SAVED_SEARCH_CHANGE_USER(400, "ATLAS-400-00-056", "saved-search {0} can not be moved from user {1} to {2}"),
INVALID_QUERY_PARAM_LENGTH(400, "ATLAS-400-00-057" , "Length of query param {0} exceeds the limit"),
INVALID_QUERY_LENGTH(400, "ATLAS-400-00-058" , "Invalid query length, update {0} to change the limit" ),
// DSL related error codes
INVALID_DSL_QUERY(400, "ATLAS-400-00-059" , "Invalid DSL query: {0} | Reason: {1}. Please refer to Atlas DSL grammar for more information" ),
INVALID_DSL_GROUPBY(400, "ATLAS-400-00-05A", "DSL Semantic Error - GroupBy attribute {0} is non-primitive"),
INVALID_DSL_UNKNOWN_TYPE(400, "ATLAS-400-00-05B", "DSL Semantic Error - {0} type not found"),
INVALID_DSL_UNKNOWN_CLASSIFICATION(400, "ATLAS-400-00-05C", "DSL Semantic Error - {0} classification not found"),
INVALID_DSL_UNKNOWN_ATTR_TYPE(400, "ATLAS-400-00-05D", "DSL Semantic Error - {0} attribute not found for type {1}"),
INVALID_DSL_ORDERBY(400, "ATLAS-400-00-05E", "DSL Semantic Error - OrderBy attribute {0} is non-primitive"),
INVALID_DSL_FROM(400, "ATLAS-400-00-05F", "DSL Semantic Error - From source {0} is not a valid Entity/Classification type"),
INVALID_DSL_SELECT_REFERRED_ATTR(400, "ATLAS-400-00-060", "DSL Semantic Error - Select clause has multiple referred attributes {0}"),
INVALID_DSL_SELECT_INVALID_AGG(400, "ATLAS-400-00-061", "DSL Semantic Error - Select clause has aggregation on referred attributes {0}"),
INVALID_DSL_SELECT_ATTR_MIXING(400, "ATLAS-400-00-062", "DSL Semantic Error - Select clause has simple and referred attributes"),
INVALID_DSL_HAS_ATTRIBUTE(400, "ATLAS-400-00-063", "DSL Semantic Error - No attribute {0} exists for type {1}"),
INVALID_DSL_QUALIFIED_NAME(400, "ATLAS-400-00-064", "DSL Semantic Error - Qualified name for {0} failed!"),
INVALID_DSL_QUALIFIED_NAME2(400, "ATLAS-400-00-065", "DSL Semantic Error - Qualified name for {0} failed for type {1}. Cause: {2}"),
INVALID_DSL_DUPLICATE_ALIAS(400, "ATLAS-400-00-066", "DSL Semantic Error - Duplicate alias found: '{0}' for type '{1}' already present."),
INVALID_DSL_INVALID_DATE(400, "ATLAS-400-00-067", "DSL Semantic Error - Date format: {0}."),
INVALID_DSL_HAS_PROPERTY(400, "ATLAS-400-00-068", "DSL Semantic Error - Property needs to be a primitive type: {0}"),
// All Not found enums go here
TYPE_NAME_NOT_FOUND(404, "ATLAS-404-00-001", "Given typename {0} was invalid"),
......
......@@ -104,6 +104,8 @@ public class AtlasDSL {
}
public static class Translator {
private static final Logger LOG = LoggerFactory.getLogger(Translator.class);
private final AtlasDSLParser.QueryContext queryContext;
private final AtlasTypeRegistry typeRegistry;
private final int offset;
......@@ -123,33 +125,21 @@ public class AtlasDSL {
GremlinQueryComposer gremlinQueryComposer = new GremlinQueryComposer(typeRegistry, queryMetadata, limit, offset);
DSLVisitor dslVisitor = new DSLVisitor(gremlinQueryComposer);
try {
queryContext.accept(dslVisitor);
processErrorList(gremlinQueryComposer, null);
processErrorList(gremlinQueryComposer);
return new GremlinQuery(gremlinQueryComposer.get(), queryMetadata.hasSelect());
} catch (Exception e) {
processErrorList(gremlinQueryComposer, e);
}
String gremlinQuery = gremlinQueryComposer.get();
return null;
return new GremlinQuery(gremlinQuery, queryMetadata.hasSelect());
}
private void processErrorList(GremlinQueryComposer gremlinQueryComposer, Exception e) throws AtlasBaseException {
private void processErrorList(GremlinQueryComposer gremlinQueryComposer) throws AtlasBaseException {
final String errorMessage;
if (CollectionUtils.isNotEmpty(gremlinQueryComposer.getErrorList())) {
errorMessage = StringUtils.join(gremlinQueryComposer.getErrorList(), ", ");
} else {
errorMessage = e != null ? (e.getMessage() != null ? e.getMessage() : e.toString()) : null;
}
if (errorMessage != null) {
if (e != null) {
throw new AtlasBaseException(AtlasErrorCode.INVALID_DSL_QUERY, e, this.query, errorMessage);
}
LOG.warn("DSL Errors: {}", errorMessage);
throw new AtlasBaseException(AtlasErrorCode.INVALID_DSL_QUERY, this.query, errorMessage);
}
}
......
......@@ -26,15 +26,12 @@ import org.slf4j.LoggerFactory;
import java.util.*;
import static org.apache.atlas.query.antlr4.AtlasDSLParser.RULE_whereClause;
public class DSLVisitor extends AtlasDSLParserBaseVisitor<Void> {
private static final Logger LOG = LoggerFactory.getLogger(DSLVisitor.class);
private static final String AND = "AND";
private static final String OR = "OR";
private Set<Integer> visitedRuleIndexes = new HashSet<>();
private final GremlinQueryComposer gremlinQueryComposer;
public DSLVisitor(GremlinQueryComposer gremlinQueryComposer) {
......@@ -42,44 +39,6 @@ public class DSLVisitor extends AtlasDSLParserBaseVisitor<Void> {
}
@Override
public Void visitSpaceDelimitedQueries(SpaceDelimitedQueriesContext ctx) {
addVisitedRule(ctx.getRuleIndex());
return super.visitSpaceDelimitedQueries(ctx);
}
@Override
public Void visitCommaDelimitedQueries(CommaDelimitedQueriesContext ctx) {
addVisitedRule(ctx.getRuleIndex());
return super.visitCommaDelimitedQueries(ctx);
}
@Override
public Void visitIsClause(IsClauseContext ctx) {
if (LOG.isDebugEnabled()) {
LOG.debug("=> DSLVisitor.visitIsClause({})", ctx);
}
if(!hasVisitedRule(RULE_whereClause)) {
gremlinQueryComposer.addFromIsA(ctx.arithE().getText(), ctx.identifier().getText());
}
return super.visitIsClause(ctx);
}
@Override
public Void visitHasClause(HasClauseContext ctx) {
if (LOG.isDebugEnabled()) {
LOG.debug("=> DSLVisitor.visitHasClause({})", ctx);
}
if(!hasVisitedRule(RULE_whereClause)) {
gremlinQueryComposer.addFromProperty(ctx.arithE().getText(), ctx.identifier().getText());
}
return super.visitHasClause(ctx);
}
@Override
public Void visitLimitOffset(LimitOffsetContext ctx) {
if (LOG.isDebugEnabled()) {
LOG.debug("=> DSLVisitor.visitLimitOffset({})", ctx);
......@@ -159,7 +118,6 @@ public class DSLVisitor extends AtlasDSLParserBaseVisitor<Void> {
LOG.debug("=> DSLVisitor.visitWhereClause({})", ctx);
}
addVisitedRule(ctx.getRuleIndex());
ExprContext expr = ctx.expr();
processExpr(expr, gremlinQueryComposer);
return super.visitWhereClause(ctx);
......@@ -188,13 +146,15 @@ public class DSLVisitor extends AtlasDSLParserBaseVisitor<Void> {
@Override
public Void visitSingleQrySrc(SingleQrySrcContext ctx) {
if (!hasVisitedRule(RULE_whereClause)) {
if (ctx.fromExpression() == null) {
if (ctx.expr() != null && !gremlinQueryComposer.hasFromClause()) {
inferFromClause(ctx);
}
if (ctx.expr() != null && gremlinQueryComposer.hasFromClause()) {
processExpr(ctx.expr(), gremlinQueryComposer);
}
}
}
return super.visitSingleQrySrc(ctx);
}
......@@ -210,6 +170,43 @@ public class DSLVisitor extends AtlasDSLParserBaseVisitor<Void> {
return super.visitGroupByExpression(ctx);
}
private Void visitIsClause(GremlinQueryComposer gqc, IsClauseContext ctx) {
if (LOG.isDebugEnabled()) {
LOG.debug("=> DSLVisitor.visitIsClause({})", ctx);
}
gqc.addIsA(ctx.arithE().getText(), ctx.identifier().getText());
return super.visitIsClause(ctx);
}
private void visitHasClause(GremlinQueryComposer gqc, HasClauseContext ctx) {
if (LOG.isDebugEnabled()) {
LOG.debug("=> DSLVisitor.visitHasClause({})", ctx);
}
gqc.addFromProperty(ctx.arithE().getText(), ctx.identifier().getText());
super.visitHasClause(ctx);
}
private void inferFromClause(SingleQrySrcContext ctx) {
if (ctx.fromExpression() != null) {
return;
}
if (ctx.expr() != null && gremlinQueryComposer.hasFromClause()) {
return;
}
if (ctx.expr().compE() != null && ctx.expr().compE().isClause() != null && ctx.expr().compE().isClause().arithE() != null) {
gremlinQueryComposer.addFrom(ctx.expr().compE().isClause().arithE().getText());
return;
}
if (ctx.expr().compE() != null && ctx.expr().compE().hasClause() != null && ctx.expr().compE().hasClause().arithE() != null) {
gremlinQueryComposer.addFrom(ctx.expr().compE().hasClause().arithE().getText());
}
}
private void processExpr(final ExprContext expr, GremlinQueryComposer gremlinQueryComposer) {
if (CollectionUtils.isNotEmpty(expr.exprRight())) {
processExprRight(expr, gremlinQueryComposer);
......@@ -300,11 +297,11 @@ public class DSLVisitor extends AtlasDSLParserBaseVisitor<Void> {
}
if (compE != null && compE.isClause() != null) {
gremlinQueryComposer.addFromIsA(compE.isClause().arithE().getText(), compE.isClause().identifier().getText());
visitIsClause(gremlinQueryComposer, compE.isClause());
}
if (compE != null && compE.hasClause() != null) {
gremlinQueryComposer.addFromProperty(compE.hasClause().arithE().getText(), compE.hasClause().identifier().getText());
visitHasClause(gremlinQueryComposer, compE.hasClause());
}
}
......@@ -319,12 +316,4 @@ public class DSLVisitor extends AtlasDSLParserBaseVisitor<Void> {
return sb.toString();
}
private void addVisitedRule(int ruleIndex) {
visitedRuleIndexes.add(ruleIndex);
}
private boolean hasVisitedRule(int ruleIndex) {
return visitedRuleIndexes.contains(ruleIndex);
}
}
\ No newline at end of file
......@@ -36,11 +36,11 @@ enum GremlinClause {
AND("and(%s)"),
NESTED_START("__"),
NESTED_HAS_OPERATOR("has('%s', %s(%s))"),
LIMIT("limit(%s)"),
LIMIT("limit(local, %s).limit(%s)"),
ORDER_BY("order().by('%s')"),
ORDER_BY_DESC("order().by('%s', decr)"),
OUT("out('%s')"),
RANGE("range(%s, %s + %s)"),
RANGE("range(local, %s, %s + %s).range(%s, %s + %s)"),
SELECT("select('%s')"),
TO_LIST("toList()"),
TEXT_CONTAINS("has('%s', org.janusgraph.core.attribute.Text.textRegex(%s))"),
......
/**
* 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.atlas.query;
import org.apache.atlas.type.AtlasEntityType;
import java.util.LinkedList;
import java.util.List;
class GremlinClauseList {
private final List<GremlinQueryComposer.GremlinClauseValue> list;
GremlinClauseList() {
this.list = new LinkedList<>();
}
public void add(GremlinQueryComposer.GremlinClauseValue g) {
list.add(g);
}
public void add(int idx, GremlinQueryComposer.GremlinClauseValue g) {
list.add(idx, g);
}
public void add(GremlinQueryComposer.GremlinClauseValue g, AtlasEntityType t) {
add(g);
}
public void add(int idx, GremlinQueryComposer.GremlinClauseValue g, AtlasEntityType t) {
add(idx, g);
}
public void add(GremlinClause clause, String... args) {
list.add(new GremlinQueryComposer.GremlinClauseValue(clause, clause.get(args)));
}
public void add(int i, GremlinClause clause, String... args) {
list.add(i, new GremlinQueryComposer.GremlinClauseValue(clause, clause.get(args)));
}
public GremlinQueryComposer.GremlinClauseValue getAt(int i) {
return list.get(i);
}
public String getValue(int i) {
return list.get(i).getValue();
}
public GremlinQueryComposer.GremlinClauseValue get(int i) {
return list.get(i);
}
public int size() {
return list.size();
}
public int contains(GremlinClause clause) {
for (int i = 0; i < list.size(); i++) {
if (list.get(i).getClause() == clause)
return i;
}
return -1;
}
public boolean isEmpty() {
return list.size() == 0 || containsGVLimit();
}
private boolean containsGVLimit() {
return list.size() == 3 &&
list.get(0).getClause() == GremlinClause.G &&
list.get(1).getClause() == GremlinClause.V &&
list.get(2).getClause() == GremlinClause.LIMIT;
}
public void clear() {
list.clear();
}
public GremlinQueryComposer.GremlinClauseValue remove(int index) {
GremlinQueryComposer.GremlinClauseValue gcv = get(index);
list.remove(index);
return gcv;
}
}
......@@ -6,9 +6,9 @@
* 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
*
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
*
* <p>
* 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.
......@@ -18,6 +18,7 @@
package org.apache.atlas.query;
import org.apache.atlas.AtlasErrorCode;
import org.apache.atlas.exception.AtlasBaseException;
import org.apache.commons.lang.StringUtils;
......@@ -46,10 +47,10 @@ public class IdentifierHelper {
return ret;
}
public static Advice create(GremlinQueryComposer.Context context,
public static IdentifierMetadata create(GremlinQueryComposer.Context context,
org.apache.atlas.query.Lookup lookup,
String identifier) {
Advice ia = new Advice(identifier);
IdentifierMetadata ia = new IdentifierMetadata(identifier);
ia.update(lookup, context);
return ia;
}
......@@ -65,7 +66,7 @@ public class IdentifierHelper {
try {
return lookup.getQualifiedName(context, name);
} catch (AtlasBaseException e) {
context.getErrorList().add(String.format("Error for %s.%s: %s", context.getActiveTypeName(), name, e.getMessage()));
context.error(e, AtlasErrorCode.INVALID_DSL_QUALIFIED_NAME, context.getActiveTypeName(), name);
}
return "";
......@@ -100,7 +101,12 @@ public class IdentifierHelper {
return rhs.equalsIgnoreCase("true") || rhs.equalsIgnoreCase("false");
}
public static class Advice {
public static String getFixedRegEx(String s) {
return s.replace("*", ".*").replace('?', '.');
}
public static class IdentifierMetadata {
private String raw;
private String actual;
private String[] parts;
......@@ -117,7 +123,7 @@ public class IdentifierHelper {
private String qualifiedName;
private boolean isDate;
public Advice(String s) {
public IdentifierMetadata(String s) {
this.raw = removeQuotes(s);
this.actual = IdentifierHelper.get(raw);
}
......@@ -132,7 +138,7 @@ public class IdentifierHelper {
updateParts();
updateTypeInfo(lookup, context);
isTrait = lookup.isTraitType(context);
setIsTrait(context, lookup, attributeName);
updateEdgeInfo(lookup, context);
introduceType = !isPrimitive() && !context.hasAlias(parts[0]);
updateSubTypes(lookup, context);
......@@ -142,26 +148,30 @@ public class IdentifierHelper {
}
}
private void setIsTrait(GremlinQueryComposer.Context ctx, Lookup lookup, String s) {
isTrait = lookup.isTraitType(s);
}
private void updateSubTypes(org.apache.atlas.query.Lookup lookup, GremlinQueryComposer.Context context) {
if(isTrait) {
if (isTrait) {
return;
}
hasSubtypes = lookup.doesTypeHaveSubTypes(context);
if(hasSubtypes) {
if (hasSubtypes) {
subTypes = lookup.getTypeAndSubTypes(context);
}
}
private void updateEdgeInfo(org.apache.atlas.query.Lookup lookup, GremlinQueryComposer.Context context) {
if(isPrimitive == false && isTrait == false) {
if (!isPrimitive && !isTrait && typeName != attributeName) {
edgeLabel = lookup.getRelationshipEdgeLabel(context, attributeName);
typeName = lookup.getTypeFromEdge(context, attributeName);
}
}
private void updateTypeInfo(org.apache.atlas.query.Lookup lookup, GremlinQueryComposer.Context context) {
if(parts.length == 1) {
if (parts.length == 1) {
typeName = context.hasAlias(parts[0]) ?
context.getTypeNameFromAlias(parts[0]) :
context.getActiveTypeName();
......@@ -169,9 +179,9 @@ public class IdentifierHelper {
attributeName = parts[0];
}
if(parts.length == 2) {
if (parts.length == 2) {
boolean isAttrOfActiveType = lookup.hasAttribute(context, parts[0]);
if(isAttrOfActiveType) {
if (isAttrOfActiveType) {
attributeName = parts[0];
} else {
typeName = context.hasAlias(parts[0]) ?
......@@ -190,7 +200,7 @@ public class IdentifierHelper {
private String getDefaultQualifiedNameForSinglePartName(GremlinQueryComposer.Context context, String s) {
String qn = context.getTypeNameFromAlias(s);
if(StringUtils.isEmpty(qn) && SelectClauseComposer.isKeyword(s)) {
if (StringUtils.isEmpty(qn) && SelectClauseComposer.isKeyword(s)) {
return s;
}
......@@ -198,22 +208,17 @@ public class IdentifierHelper {
}
private void setQualifiedName(Lookup lookup, GremlinQueryComposer.Context context, boolean isAttribute, String attrName) {
if(isAttribute) {
if (isAttribute) {
qualifiedName = getQualifiedName(lookup, context, attrName);
}
}
private String getQualifiedName(Lookup lookup, GremlinQueryComposer.Context context, String name) {
try {
return lookup.getQualifiedName(context, name);
} catch (AtlasBaseException e) {
context.getErrorList().add(String.format("Error for %s.%s: %s", context.getActiveTypeName(), name, e.getMessage()));
return "";
}
return IdentifierHelper.getQualifiedName(lookup, context, name);
}
private void setIsDate(Lookup lookup, GremlinQueryComposer.Context context, boolean isPrimitive, String attrName) {
if(isPrimitive) {
if (isPrimitive) {
isDate = lookup.isDate(context, attrName);
}
}
......@@ -246,7 +251,7 @@ public class IdentifierHelper {
return typeName;
}
public boolean getIntroduceType() {
public boolean isReferredType() {
return introduceType;
}
......@@ -277,5 +282,6 @@ public class IdentifierHelper {
public String getRaw() {
return raw;
}
}
}
......@@ -21,9 +21,6 @@ package org.apache.atlas.query;
import org.apache.atlas.exception.AtlasBaseException;
import org.apache.atlas.type.AtlasType;
import java.util.Collection;
import java.util.List;
public interface Lookup {
AtlasType getType(String typeName) throws AtlasBaseException;
......@@ -39,7 +36,7 @@ public interface Lookup {
String getTypeAndSubTypes(GremlinQueryComposer.Context context);
boolean isTraitType(GremlinQueryComposer.Context context);
boolean isTraitType(String s);
String getTypeFromEdge(GremlinQueryComposer.Context context, String item);
......
......@@ -20,20 +20,27 @@ package org.apache.atlas.query;
import org.apache.atlas.exception.AtlasBaseException;
import org.apache.atlas.model.TypeCategory;
import org.apache.atlas.model.instance.AtlasObjectId;
import org.apache.atlas.model.typedef.AtlasBaseTypeDef;
import org.apache.atlas.repository.Constants;
import org.apache.atlas.type.*;
import org.apache.commons.lang.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
class RegistryBasedLookup implements Lookup {
private final List<String> errorList;
private static final Set<String> SYSTEM_ATTRIBUTES = new HashSet<>(
Arrays.asList(Constants.GUID_PROPERTY_KEY,
Constants.MODIFIED_BY_KEY,
Constants.CREATED_BY_KEY,
Constants.STATE_PROPERTY_KEY,
Constants.TIMESTAMP_PROPERTY_KEY,
Constants.MODIFICATION_TIMESTAMP_PROPERTY_KEY));
private final AtlasTypeRegistry typeRegistry;
public RegistryBasedLookup(AtlasTypeRegistry typeRegistry) {
this.errorList = new ArrayList<>();
this.typeRegistry = typeRegistry;
}
......@@ -49,8 +56,16 @@ class RegistryBasedLookup implements Lookup {
return "";
}
if(isSystemAttribute(name)) {
return name;
} else {
return et.getQualifiedAttributeName(name);
}
}
private boolean isSystemAttribute(String s) {
return SYSTEM_ATTRIBUTES.contains(s);
}
@Override
public boolean isPrimitive(GremlinQueryComposer.Context context, String attributeName) {
......@@ -59,6 +74,10 @@ class RegistryBasedLookup implements Lookup {
return false;
}
if(isSystemAttribute(attributeName)) {
return true;
}
AtlasType at = et.getAttributeType(attributeName);
if(at == null) {
return false;
......@@ -97,7 +116,8 @@ class RegistryBasedLookup implements Lookup {
@Override
public boolean hasAttribute(GremlinQueryComposer.Context context, String typeName) {
return (context.getActiveEntityType() != null) && context.getActiveEntityType().getAttribute(typeName) != null;
return (context.getActiveEntityType() != null) &&
(isSystemAttribute(typeName) || context.getActiveEntityType().getAttribute(typeName) != null);
}
@Override
......@@ -123,9 +143,19 @@ class RegistryBasedLookup implements Lookup {
}
@Override
public boolean isTraitType(GremlinQueryComposer.Context context) {
return (context.getActiveType() != null &&
context.getActiveType().getTypeCategory() == TypeCategory.CLASSIFICATION);
public boolean isTraitType(String typeName) {
AtlasType t = null;
try {
t = typeRegistry.getType(typeName);
} catch (AtlasBaseException e) {
return false;
}
return isTraitType(t);
}
private boolean isTraitType(AtlasType t) {
return (t != null && t.getTypeCategory() == TypeCategory.CLASSIFICATION);
}
@Override
......
......@@ -41,9 +41,18 @@ class SelectClauseComposer {
private int maxIdx = -1;
private int minIdx = -1;
private int aggCount = 0;
private int introducedTypesCount = 0;
private int primitiveTypeCount = 0;
public SelectClauseComposer() {}
public static boolean isKeyword(String s) {
return COUNT_STR.equals(s) ||
MIN_STR.equals(s) ||
MAX_STR.equals(s) ||
SUM_STR.equals(s);
}
public String[] getItems() {
return items;
}
......@@ -73,13 +82,6 @@ class SelectClauseComposer {
return ret;
}
public static boolean isKeyword(String s) {
return COUNT_STR.equals(s) ||
MIN_STR.equals(s) ||
MAX_STR.equals(s) ||
SUM_STR.equals(s);
}
public String[] getAttributes() {
return attributes;
}
......@@ -164,7 +166,7 @@ class SelectClauseComposer {
return assign(items[i], inline.get(s, clause.get(p1, p1)));
}
private int getCountIdx() {
public int getCountIdx() {
return countIdx;
}
......@@ -173,7 +175,7 @@ class SelectClauseComposer {
aggCount++;
}
private int getSumIdx() {
public int getSumIdx() {
return sumIdx;
}
......@@ -182,7 +184,7 @@ class SelectClauseComposer {
aggCount++;
}
private int getMaxIdx() {
public int getMaxIdx() {
return maxIdx;
}
......@@ -191,7 +193,7 @@ class SelectClauseComposer {
aggCount++;
}
private int getMinIdx() {
public int getMinIdx() {
return minIdx;
}
......@@ -207,4 +209,32 @@ class SelectClauseComposer {
.forEach(joiner::add);
return joiner.toString();
}
public boolean isAggregatorWithArgument(int i) {
return i == getMaxIdx() || i == getMinIdx() || i == getSumIdx();
}
public void incrementTypesIntroduced() {
introducedTypesCount++;
}
public int getIntroducedTypesCount() {
return introducedTypesCount;
}
public void incrementPrimitiveType() {
primitiveTypeCount++;
}
public boolean hasMultipleReferredTypes() {
return getIntroducedTypesCount() > 1;
}
public boolean hasMixedAttributes() {
return getIntroducedTypesCount() > 0 && getPrimitiveTypeCount() > 0;
}
private int getPrimitiveTypeCount() {
return primitiveTypeCount;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment