Search in sources :

Example 81 with SqlIdentifier

use of org.apache.calcite.sql.SqlIdentifier in project calcite by apache.

the class DelegatingScope method fullyQualify.

/**
 * Converts an identifier into a fully-qualified identifier. For example,
 * the "empno" in "select empno from emp natural join dept" becomes
 * "emp.empno".
 *
 * <p>If the identifier cannot be resolved, throws. Never returns null.
 */
public SqlQualified fullyQualify(SqlIdentifier identifier) {
    if (identifier.isStar()) {
        return SqlQualified.create(this, 1, null, identifier);
    }
    final SqlIdentifier previous = identifier;
    final SqlNameMatcher nameMatcher = validator.catalogReader.nameMatcher();
    String columnName;
    final String tableName;
    final SqlValidatorNamespace namespace;
    switch(identifier.names.size()) {
        case 1:
            {
                columnName = identifier.names.get(0);
                final Map<String, ScopeChild> map = findQualifyingTableNames(columnName, identifier, nameMatcher);
                switch(map.size()) {
                    case 0:
                        if (nameMatcher.isCaseSensitive()) {
                            final SqlNameMatcher liberalMatcher = SqlNameMatchers.liberal();
                            final Map<String, ScopeChild> map2 = findQualifyingTableNames(columnName, identifier, liberalMatcher);
                            if (!map2.isEmpty()) {
                                final List<String> list = new ArrayList<>();
                                for (ScopeChild entry : map2.values()) {
                                    final RelDataTypeField field = liberalMatcher.field(entry.namespace.getRowType(), columnName);
                                    list.add(field.getName());
                                }
                                Collections.sort(list);
                                throw validator.newValidationError(identifier, RESOURCE.columnNotFoundDidYouMean(columnName, Util.sepList(list, "', '")));
                            }
                        }
                        throw validator.newValidationError(identifier, RESOURCE.columnNotFound(columnName));
                    case 1:
                        tableName = map.keySet().iterator().next();
                        namespace = map.get(tableName).namespace;
                        break;
                    default:
                        throw validator.newValidationError(identifier, RESOURCE.columnAmbiguous(columnName));
                }
                final ResolvedImpl resolved = new ResolvedImpl();
                resolveInNamespace(namespace, false, identifier.names, nameMatcher, Path.EMPTY, resolved);
                final RelDataTypeField field = nameMatcher.field(namespace.getRowType(), columnName);
                if (field != null) {
                    if (hasAmbiguousUnresolvedStar(namespace.getRowType(), field, columnName)) {
                        throw validator.newValidationError(identifier, RESOURCE.columnAmbiguous(columnName));
                    }
                    // use resolved field name
                    columnName = field.getName();
                }
                // todo: do implicit collation here
                final SqlParserPos pos = identifier.getParserPosition();
                identifier = new SqlIdentifier(ImmutableList.of(tableName, columnName), null, pos, ImmutableList.of(SqlParserPos.ZERO, pos));
            }
        // fall through
        default:
            {
                SqlValidatorNamespace fromNs = null;
                Path fromPath = null;
                RelDataType fromRowType = null;
                final ResolvedImpl resolved = new ResolvedImpl();
                int size = identifier.names.size();
                int i = size - 1;
                for (; i > 0; i--) {
                    final SqlIdentifier prefix = identifier.getComponent(0, i);
                    resolved.clear();
                    resolve(prefix.names, nameMatcher, false, resolved);
                    if (resolved.count() == 1) {
                        final Resolve resolve = resolved.only();
                        fromNs = resolve.namespace;
                        fromPath = resolve.path;
                        fromRowType = resolve.rowType();
                        break;
                    }
                    // Look for a table alias that is the wrong case.
                    if (nameMatcher.isCaseSensitive()) {
                        final SqlNameMatcher liberalMatcher = SqlNameMatchers.liberal();
                        resolved.clear();
                        resolve(prefix.names, liberalMatcher, false, resolved);
                        if (resolved.count() == 1) {
                            final Step lastStep = Util.last(resolved.only().path.steps());
                            throw validator.newValidationError(prefix, RESOURCE.tableNameNotFoundDidYouMean(prefix.toString(), lastStep.name));
                        }
                    }
                }
                if (fromNs == null || fromNs instanceof SchemaNamespace) {
                    // Look for a column not qualified by a table alias.
                    columnName = identifier.names.get(0);
                    final Map<String, ScopeChild> map = findQualifyingTableNames(columnName, identifier, nameMatcher);
                    switch(map.size()) {
                        default:
                            final SqlIdentifier prefix1 = identifier.skipLast(1);
                            throw validator.newValidationError(prefix1, RESOURCE.tableNameNotFound(prefix1.toString()));
                        case 1:
                            {
                                final Map.Entry<String, ScopeChild> entry = map.entrySet().iterator().next();
                                final String tableName2 = map.keySet().iterator().next();
                                fromNs = entry.getValue().namespace;
                                fromPath = Path.EMPTY;
                                // Adding table name is for RecordType column with StructKind.PEEK_FIELDS or
                                // StructKind.PEEK_FIELDS only. Access to a field in a RecordType column of
                                // other StructKind should always be qualified with table name.
                                final RelDataTypeField field = nameMatcher.field(fromNs.getRowType(), columnName);
                                if (field != null) {
                                    switch(field.getType().getStructKind()) {
                                        case PEEK_FIELDS:
                                        case PEEK_FIELDS_DEFAULT:
                                        case PEEK_FIELDS_NO_EXPAND:
                                            // use resolved field name
                                            columnName = field.getName();
                                            resolve(ImmutableList.of(tableName2), nameMatcher, false, resolved);
                                            if (resolved.count() == 1) {
                                                final Resolve resolve = resolved.only();
                                                fromNs = resolve.namespace;
                                                fromPath = resolve.path;
                                                fromRowType = resolve.rowType();
                                                identifier = identifier.setName(0, columnName).add(0, tableName2, SqlParserPos.ZERO);
                                                ++i;
                                                ++size;
                                            }
                                            break;
                                        default:
                                            // Throw an error if the table was not found.
                                            // If one or more of the child namespaces allows peeking
                                            // (e.g. if they are Phoenix column families) then we relax the SQL
                                            // standard requirement that record fields are qualified by table alias.
                                            final SqlIdentifier prefix = identifier.skipLast(1);
                                            throw validator.newValidationError(prefix, RESOURCE.tableNameNotFound(prefix.toString()));
                                    }
                                }
                            }
                    }
                }
                // change "e.empno" to "E.empno".
                if (fromNs.getEnclosingNode() != null && !(this instanceof MatchRecognizeScope)) {
                    String alias = SqlValidatorUtil.getAlias(fromNs.getEnclosingNode(), -1);
                    if (alias != null && i > 0 && !alias.equals(identifier.names.get(i - 1))) {
                        identifier = identifier.setName(i - 1, alias);
                    }
                }
                if (fromPath.stepCount() > 1) {
                    assert fromRowType != null;
                    for (Step p : fromPath.steps()) {
                        fromRowType = fromRowType.getFieldList().get(p.i).getType();
                    }
                    ++i;
                }
                final SqlIdentifier suffix = identifier.getComponent(i, size);
                resolved.clear();
                resolveInNamespace(fromNs, false, suffix.names, nameMatcher, Path.EMPTY, resolved);
                final Path path;
                switch(resolved.count()) {
                    case 0:
                        // Maybe the last component was correct, just wrong case
                        if (nameMatcher.isCaseSensitive()) {
                            SqlNameMatcher liberalMatcher = SqlNameMatchers.liberal();
                            resolved.clear();
                            resolveInNamespace(fromNs, false, suffix.names, liberalMatcher, Path.EMPTY, resolved);
                            if (resolved.count() > 0) {
                                int k = size - 1;
                                final SqlIdentifier prefix = identifier.getComponent(0, i);
                                final SqlIdentifier suffix3 = identifier.getComponent(i, k + 1);
                                final Step step = Util.last(resolved.resolves.get(0).path.steps());
                                throw validator.newValidationError(suffix3, RESOURCE.columnNotFoundInTableDidYouMean(suffix3.toString(), prefix.toString(), step.name));
                            }
                        }
                        // Find the shortest suffix that also fails. Suppose we cannot resolve
                        // "a.b.c"; we find we cannot resolve "a.b" but can resolve "a". So,
                        // the error will be "Column 'a.b' not found".
                        int k = size - 1;
                        for (; k > i; --k) {
                            SqlIdentifier suffix2 = identifier.getComponent(i, k);
                            resolved.clear();
                            resolveInNamespace(fromNs, false, suffix2.names, nameMatcher, Path.EMPTY, resolved);
                            if (resolved.count() > 0) {
                                break;
                            }
                        }
                        final SqlIdentifier prefix = identifier.getComponent(0, i);
                        final SqlIdentifier suffix3 = identifier.getComponent(i, k + 1);
                        throw validator.newValidationError(suffix3, RESOURCE.columnNotFoundInTable(suffix3.toString(), prefix.toString()));
                    case 1:
                        path = resolved.only().path;
                        break;
                    default:
                        final Comparator<Resolve> c = new Comparator<Resolve>() {

                            public int compare(Resolve o1, Resolve o2) {
                                // Name resolution that uses fewer implicit steps wins.
                                int c = Integer.compare(worstKind(o1.path), worstKind(o2.path));
                                if (c != 0) {
                                    return c;
                                }
                                // Shorter path wins
                                return Integer.compare(o1.path.stepCount(), o2.path.stepCount());
                            }

                            private int worstKind(Path path) {
                                int kind = -1;
                                for (Step step : path.steps()) {
                                    kind = Math.max(kind, step.kind.ordinal());
                                }
                                return kind;
                            }
                        };
                        Collections.sort(resolved.resolves, c);
                        if (c.compare(resolved.resolves.get(0), resolved.resolves.get(1)) == 0) {
                            throw validator.newValidationError(suffix, RESOURCE.columnAmbiguous(suffix.toString()));
                        }
                        path = resolved.resolves.get(0).path;
                }
                // Normalize case to match definition, make elided fields explicit,
                // and check that references to dynamic stars ("**") are unambiguous.
                int k = i;
                for (Step step : path.steps()) {
                    final String name = identifier.names.get(k);
                    if (step.i < 0) {
                        throw validator.newValidationError(identifier, RESOURCE.columnNotFound(name));
                    }
                    final RelDataTypeField field0 = step.rowType.getFieldList().get(step.i);
                    final String fieldName = field0.getName();
                    switch(step.kind) {
                        case PEEK_FIELDS:
                        case PEEK_FIELDS_DEFAULT:
                        case PEEK_FIELDS_NO_EXPAND:
                            identifier = identifier.add(k, fieldName, SqlParserPos.ZERO);
                            break;
                        default:
                            if (!fieldName.equals(name)) {
                                identifier = identifier.setName(k, fieldName);
                            }
                            if (hasAmbiguousUnresolvedStar(step.rowType, field0, name)) {
                                throw validator.newValidationError(identifier, RESOURCE.columnAmbiguous(name));
                            }
                    }
                    ++k;
                }
                // CustomResolvingTable.
                if (identifier.names.size() > k) {
                    identifier = identifier.getComponent(0, k);
                }
                if (i > 1) {
                    // Simplify overqualified identifiers.
                    // For example, schema.emp.deptno becomes emp.deptno.
                    // 
                    // It is safe to convert schema.emp or database.schema.emp to emp
                    // because it would not have resolved if the FROM item had an alias. The
                    // following query is invalid:
                    // SELECT schema.emp.deptno FROM schema.emp AS e
                    identifier = identifier.getComponent(i - 1, identifier.names.size());
                }
                if (!previous.equals(identifier)) {
                    validator.setOriginal(identifier, previous);
                }
                return SqlQualified.create(this, i, fromNs, identifier);
            }
    }
}
Also used : SqlParserPos(org.apache.calcite.sql.parser.SqlParserPos) RelDataType(org.apache.calcite.rel.type.RelDataType) SqlIdentifier(org.apache.calcite.sql.SqlIdentifier) Comparator(java.util.Comparator) RelDataTypeField(org.apache.calcite.rel.type.RelDataTypeField) ArrayList(java.util.ArrayList) List(java.util.List) ImmutableList(com.google.common.collect.ImmutableList) SqlNodeList(org.apache.calcite.sql.SqlNodeList) Map(java.util.Map)

Example 82 with SqlIdentifier

use of org.apache.calcite.sql.SqlIdentifier in project calcite by apache.

the class SqlTypeUtil method convertTypeToSpec.

/**
 * Converts an instance of RelDataType to an instance of SqlDataTypeSpec.
 *
 * @param type type descriptor
 * @return corresponding parse representation
 */
public static SqlDataTypeSpec convertTypeToSpec(RelDataType type) {
    SqlTypeName typeName = type.getSqlTypeName();
    // interval types, multiset types, etc
    assert typeName != null;
    SqlIdentifier typeIdentifier = new SqlIdentifier(typeName.name(), SqlParserPos.ZERO);
    String charSetName = null;
    if (inCharFamily(type)) {
        charSetName = type.getCharset().name();
    // TODO jvs 28-Dec-2004:  collation
    }
    if (typeName.allowsScale()) {
        return new SqlDataTypeSpec(typeIdentifier, type.getPrecision(), type.getScale(), charSetName, null, SqlParserPos.ZERO);
    } else if (typeName.allowsPrec()) {
        return new SqlDataTypeSpec(typeIdentifier, type.getPrecision(), -1, charSetName, null, SqlParserPos.ZERO);
    } else {
        return new SqlDataTypeSpec(typeIdentifier, -1, -1, charSetName, null, SqlParserPos.ZERO);
    }
}
Also used : SqlDataTypeSpec(org.apache.calcite.sql.SqlDataTypeSpec) SqlIdentifier(org.apache.calcite.sql.SqlIdentifier)

Example 83 with SqlIdentifier

use of org.apache.calcite.sql.SqlIdentifier in project calcite by apache.

the class AggChecker method visit.

public Void visit(SqlCall call) {
    final SqlValidatorScope scope = scopes.peek();
    if (call.getOperator().isAggregator()) {
        if (distinct) {
            if (scope instanceof AggregatingSelectScope) {
                SqlNodeList selectList = ((SqlSelect) scope.getNode()).getSelectList();
                // Check if this aggregation function is just an element in the select
                for (SqlNode sqlNode : selectList) {
                    if (sqlNode.getKind() == SqlKind.AS) {
                        sqlNode = ((SqlCall) sqlNode).operand(0);
                    }
                    if (validator.expand(sqlNode, scope).equalsDeep(call, Litmus.IGNORE)) {
                        return null;
                    }
                }
            }
            // Cannot use agg fun in ORDER BY clause if have SELECT DISTINCT.
            SqlNode originalExpr = validator.getOriginal(call);
            final String exprString = originalExpr.toString();
            throw validator.newValidationError(call, RESOURCE.notSelectDistinctExpr(exprString));
        }
        // BY deptno'
        return null;
    }
    if (call.getKind() == SqlKind.FILTER) {
        call.operand(0).accept(this);
        return null;
    }
    // Visit the operand in window function
    if (call.getKind() == SqlKind.OVER) {
        for (SqlNode operand : call.<SqlCall>operand(0).getOperandList()) {
            operand.accept(this);
        }
        // Check the OVER clause
        final SqlNode over = call.operand(1);
        if (over instanceof SqlCall) {
            over.accept(this);
        } else if (over instanceof SqlIdentifier) {
            // Check the corresponding SqlWindow in WINDOW clause
            final SqlWindow window = scope.lookupWindow(((SqlIdentifier) over).getSimple());
            window.getPartitionList().accept(this);
            window.getOrderList().accept(this);
        }
    }
    if (isGroupExpr(call)) {
        // This call matches an expression in the GROUP BY clause.
        return null;
    }
    final SqlCall groupCall = SqlStdOperatorTable.convertAuxiliaryToGroupCall(call);
    if (groupCall != null) {
        if (isGroupExpr(groupCall)) {
            // TUMBLE(rowtime, INTERVAL '1' HOUR')
            return null;
        }
        throw validator.newValidationError(groupCall, RESOURCE.auxiliaryWithoutMatchingGroupCall(call.getOperator().getName(), groupCall.getOperator().getName()));
    }
    if (call.isA(SqlKind.QUERY)) {
        // references to forbidden columns.
        return null;
    }
    // Switch to new scope.
    SqlValidatorScope newScope = scope.getOperandScope(call);
    scopes.push(newScope);
    // Visit the operands (only expressions).
    call.getOperator().acceptCall(this, call, true, ArgHandlerImpl.<Void>instance());
    // Restore scope.
    scopes.pop();
    return null;
}
Also used : SqlSelect(org.apache.calcite.sql.SqlSelect) SqlCall(org.apache.calcite.sql.SqlCall) SqlWindow(org.apache.calcite.sql.SqlWindow) SqlNodeList(org.apache.calcite.sql.SqlNodeList) SqlIdentifier(org.apache.calcite.sql.SqlIdentifier) SqlNode(org.apache.calcite.sql.SqlNode)

Example 84 with SqlIdentifier

use of org.apache.calcite.sql.SqlIdentifier in project calcite by apache.

the class SqlCreateForeignSchema method execute.

public void execute(CalcitePrepare.Context context) {
    final Pair<CalciteSchema, String> pair = SqlDdlNodes.schema(context, true, name);
    final SchemaPlus subSchema0 = pair.left.plus().getSubSchema(pair.right);
    if (subSchema0 != null) {
        if (!getReplace() && !ifNotExists) {
            throw SqlUtil.newContextException(name.getParserPosition(), RESOURCE.schemaExists(pair.right));
        }
    }
    final Schema subSchema;
    final String libraryName;
    if (type != null) {
        Preconditions.checkArgument(library == null);
        final String typeName = (String) value(this.type);
        final JsonSchema.Type type = Util.enumVal(JsonSchema.Type.class, typeName.toUpperCase(Locale.ROOT));
        if (type != null) {
            switch(type) {
                case JDBC:
                    libraryName = JdbcSchema.Factory.class.getName();
                    break;
                default:
                    libraryName = null;
            }
        } else {
            libraryName = null;
        }
        if (libraryName == null) {
            throw SqlUtil.newContextException(this.type.getParserPosition(), RESOURCE.schemaInvalidType(typeName, Arrays.toString(JsonSchema.Type.values())));
        }
    } else {
        Preconditions.checkArgument(library != null);
        libraryName = (String) value(library);
    }
    final SchemaFactory schemaFactory = AvaticaUtils.instantiatePlugin(SchemaFactory.class, libraryName);
    final Map<String, Object> operandMap = new LinkedHashMap<>();
    for (Pair<SqlIdentifier, SqlNode> option : options(optionList)) {
        operandMap.put(option.left.getSimple(), value(option.right));
    }
    subSchema = schemaFactory.create(pair.left.plus(), pair.right, operandMap);
    pair.left.add(pair.right, subSchema);
}
Also used : SchemaFactory(org.apache.calcite.schema.SchemaFactory) JdbcSchema(org.apache.calcite.adapter.jdbc.JdbcSchema) Schema(org.apache.calcite.schema.Schema) CalciteSchema(org.apache.calcite.jdbc.CalciteSchema) JsonSchema(org.apache.calcite.model.JsonSchema) JsonSchema(org.apache.calcite.model.JsonSchema) SchemaPlus(org.apache.calcite.schema.SchemaPlus) SchemaFactory(org.apache.calcite.schema.SchemaFactory) NlsString(org.apache.calcite.util.NlsString) SqlIdentifier(org.apache.calcite.sql.SqlIdentifier) LinkedHashMap(java.util.LinkedHashMap) CalciteSchema(org.apache.calcite.jdbc.CalciteSchema) SqlNode(org.apache.calcite.sql.SqlNode)

Example 85 with SqlIdentifier

use of org.apache.calcite.sql.SqlIdentifier in project calcite by apache.

the class SqlAdvisorValidator method registerId.

private void registerId(SqlIdentifier id, SqlValidatorScope scope) {
    for (int i = 0; i < id.names.size(); i++) {
        final SqlParserPos subPos = id.getComponentParserPosition(i);
        SqlIdentifier subId = i == id.names.size() - 1 ? id : new SqlIdentifier(id.names.subList(0, i + 1), subPos);
        idPositions.put(subPos.toString(), new IdInfo(scope, subId));
    }
}
Also used : SqlParserPos(org.apache.calcite.sql.parser.SqlParserPos) SqlIdentifier(org.apache.calcite.sql.SqlIdentifier)

Aggregations

SqlIdentifier (org.apache.calcite.sql.SqlIdentifier)131 SqlNode (org.apache.calcite.sql.SqlNode)84 RelDataType (org.apache.calcite.rel.type.RelDataType)46 SqlNodeList (org.apache.calcite.sql.SqlNodeList)43 ArrayList (java.util.ArrayList)41 SqlCall (org.apache.calcite.sql.SqlCall)32 BitString (org.apache.calcite.util.BitString)28 RelDataTypeField (org.apache.calcite.rel.type.RelDataTypeField)21 SqlSelect (org.apache.calcite.sql.SqlSelect)21 SqlParserPos (org.apache.calcite.sql.parser.SqlParserPos)12 SchemaPlus (org.apache.calcite.schema.SchemaPlus)11 SqlOperator (org.apache.calcite.sql.SqlOperator)11 NlsString (org.apache.calcite.util.NlsString)11 List (java.util.List)9 Map (java.util.Map)9 RelOptTable (org.apache.calcite.plan.RelOptTable)9 RexNode (org.apache.calcite.rex.RexNode)9 AbstractSchema (org.apache.drill.exec.store.AbstractSchema)9 ImmutableList (com.google.common.collect.ImmutableList)8 RelNode (org.apache.calcite.rel.RelNode)7