Package org.apache.cassandra.cql.compiler.sem

Source Code of org.apache.cassandra.cql.compiler.sem.SemanticPhase

/**
* 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.cassandra.cql.compiler.sem;

import java.util.Map;

import org.antlr.runtime.tree.CommonTree;

import org.apache.cassandra.cql.common.*;
import org.apache.cassandra.cql.compiler.common.*;
import org.apache.cassandra.cql.compiler.parse.*;

import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql.common.ColumnMapExpr;
import org.apache.cassandra.cql.common.ColumnRangeQueryRSD;
import org.apache.cassandra.cql.common.ConstantOperand;
import org.apache.cassandra.cql.common.ExplainPlan;
import org.apache.cassandra.cql.common.OperandDef;
import org.apache.cassandra.cql.common.Pair;
import org.apache.cassandra.cql.common.Plan;
import org.apache.cassandra.cql.common.QueryPlan;
import org.apache.cassandra.cql.common.RowSourceDef;
import org.apache.cassandra.cql.common.SetColumnMap;
import org.apache.cassandra.cql.common.SetSuperColumnMap;
import org.apache.cassandra.cql.common.SetUniqueKey;
import org.apache.cassandra.cql.common.SuperColumnMapExpr;
import org.apache.cassandra.cql.common.SuperColumnRangeQueryRSD;
import org.apache.cassandra.cql.common.UniqueKeyQueryRSD;
import org.apache.cassandra.cql.common.Utils;
import org.apache.cassandra.cql.compiler.common.CompilerErrorMsg;
import org.apache.cassandra.cql.compiler.parse.CqlParser;
import org.apache.log4j.Logger;

//
// Note: This class is CQL related work in progress.
//
// Currently, this phase combines both semantic analysis and code-gen.
// I expect that as my ideas get refined/cleared up, I'll be drawing
// a more clear distinction between semantic analysis phase and code-gen.
//
public class SemanticPhase
{
    private final static Logger logger_ = Logger.getLogger(SemanticPhase.class);   

    // Current code-gen also happens in this phase!
    public static Plan doSemanticAnalysis(CommonTree ast) throws SemanticException
    {
        Plan plan = null;

        logger_.debug("AST: " + ast.toStringTree());

        switch (ast.getType())
        {
        case CqlParser.A_GET:
            plan = compileGet(ast);
            break;
        case CqlParser.A_SET:
            plan = compileSet(ast);
            break;
        case CqlParser.A_DELETE:
            compileDelete(ast);
            break;
        case CqlParser.A_SELECT:
            compileSelect(ast);
            break;
        case CqlParser.A_EXPLAIN_PLAN:
            // Case: EXPLAN PLAN <stmt>
            // first, generate a plan for <stmt>
            // and then, wrapper it with a special ExplainPlan plan
            // whose execution will result in an explain plan rather
            // than a normal execution of the statement.
            plan = doSemanticAnalysis((CommonTree)(ast.getChild(0)));
            plan = new ExplainPlan(plan);
            break;
        default:
            // Unhandled AST node. Raise an internal error.
            throw new SemanticException(CompilerErrorMsg.INTERNAL_ERROR.getMsg(ast, "Unknown Node Type: " + ast.getType()));
        }
        return plan;
    }

    /**
     * Given a CommonTree AST node of type, A_COLUMN_ACCESS related functions, do semantic
     * checking to ensure table name, column family name, and number of key dimensions
     * specified are all valid.
     */
    private static CFMetaData getColumnFamilyInfo(CommonTree ast) throws SemanticException
    {
        assert(ast.getType() == CqlParser.A_COLUMN_ACCESS);

        CommonTree columnFamilyNode = (CommonTree)(ast.getChild(1));
        CommonTree tableNode = (CommonTree)(ast.getChild(0));

        String columnFamily = columnFamilyNode.getText();
        String table = tableNode.getText();

        Map<String, CFMetaData> columnFamilies = DatabaseDescriptor.getTableMetaData(table);
        if (columnFamilies == null)
        {
            throw new SemanticException(CompilerErrorMsg.INVALID_TABLE.getMsg(ast, table));
        }

        CFMetaData cfMetaData = columnFamilies.get(columnFamily);
        if (cfMetaData == null)
        {
            throw new SemanticException(CompilerErrorMsg.INVALID_COLUMN_FAMILY.getMsg(ast, columnFamily, table));
        }

        // Once you have drilled down to a row using a rowKey, a super column
        // map can be indexed only 2 further levels deep; and a column map may
        // be indexed up to 1 level deep.
        int dimensions = numColumnDimensions(ast);
        if (("Super".equals(cfMetaData.columnType) && (dimensions > 2)) ||
            ("Standard".equals(cfMetaData.columnType) && dimensions > 1))
        {
            throw new SemanticException(CompilerErrorMsg.TOO_MANY_DIMENSIONS.getMsg(ast, cfMetaData.columnType));
        }

        return cfMetaData;
    }

    private static String getRowKey(CommonTree ast)
    {
        assert(ast.getType() == CqlParser.A_COLUMN_ACCESS);
        return Utils.unescapeSQLString(ast.getChild(2).getText());
    }

    private static int numColumnDimensions(CommonTree ast)
    {
        // Skip over table name, column family and rowKey
        return ast.getChildCount() - 3;
    }

    // Returns the pos'th (0-based index) column specifier in the astNode
    private static String getColumn(CommonTree ast, int pos)
    {
        // Skip over table name, column family and rowKey
        return Utils.unescapeSQLString(ast.getChild(pos + 3).getText());
    }

    // Compile a GET statement
    private static Plan compileGet(CommonTree ast) throws SemanticException
    {
        int childCount = ast.getChildCount();
        assert(childCount == 1);

        CommonTree columnFamilySpec = (CommonTree)ast.getChild(0);
        assert(columnFamilySpec.getType() == CqlParser.A_COLUMN_ACCESS);

        CFMetaData cfMetaData = getColumnFamilyInfo(columnFamilySpec);
        ConstantOperand rowKey = new ConstantOperand(getRowKey(columnFamilySpec));
        int dimensionCnt = numColumnDimensions(columnFamilySpec);

        RowSourceDef rwsDef;
        if ("Super".equals(cfMetaData.columnType))
        {
            if (dimensionCnt > 2)
            {
                // We don't expect this case to arise, since Cql.g grammar disallows this.
                // therefore, raise this case as an "internal error".
                throw new SemanticException(CompilerErrorMsg.INTERNAL_ERROR.getMsg(columnFamilySpec));
            }

            if (dimensionCnt == 2)
            {
                // Case: table.super_cf[<rowKey>][<superColumnKey>][<columnKey>]
                ConstantOperand superColumnKey = new ConstantOperand(getColumn(columnFamilySpec, 0));               
                ConstantOperand columnKey = new ConstantOperand(getColumn(columnFamilySpec, 1));
                rwsDef = new UniqueKeyQueryRSD(cfMetaData, rowKey, superColumnKey, columnKey);
            }
            else if (dimensionCnt == 1)
            {
                // Case: table.super_cf[<rowKey>][<superColumnKey>]
                ConstantOperand superColumnKey = new ConstantOperand(getColumn(columnFamilySpec, 0));               
                rwsDef = new ColumnRangeQueryRSD(cfMetaData, rowKey, superColumnKey, -1, Integer.MAX_VALUE);
            }
            else
            {
                // Case: table.super_cf[<rowKey>]            
                rwsDef = new SuperColumnRangeQueryRSD(cfMetaData, rowKey, -1, Integer.MAX_VALUE);
            }
        }
        else  // Standard Column Family
        {
            if (dimensionCnt == 1)
            {
                // Case: table.standard_cf[<rowKey>][<columnKey>]
                ConstantOperand columnKey = new ConstantOperand(getColumn(columnFamilySpec, 0));
                rwsDef = new UniqueKeyQueryRSD(cfMetaData, rowKey, columnKey);
            }
            else
            {
                // Case: table.standard_cf[<rowKey>]
                logger_.assertLog((dimensionCnt == 0), "invalid dimensionCnt: " + dimensionCnt);
                rwsDef = new ColumnRangeQueryRSD(cfMetaData, rowKey, -1, Integer.MAX_VALUE);
            }
        }
        return new QueryPlan(rwsDef);
    }
   
    private static OperandDef  getSimpleExpr(CommonTree ast) throws SemanticException
    {
        int type = ast.getType();

        // for now, the only simple expressions support are of string type
        if (type != CqlParser.StringLiteral)
        {
            throw new SemanticException(CompilerErrorMsg.INVALID_TYPE.getMsg(ast));
        }
        return new ConstantOperand(Utils.unescapeSQLString(ast.getText()));
    }

    private static ColumnMapExpr getColumnMapExpr(CommonTree ast) throws SemanticException
    {
        int type = ast.getType();
        if (type != CqlParser.A_COLUMN_MAP_VALUE)
        {
            throw new SemanticException(CompilerErrorMsg.INVALID_TYPE.getMsg(ast));
        }
       
        int size = ast.getChildCount();
        ColumnMapExpr result = new ColumnMapExpr();
        for (int idx = 0; idx < size; idx++)
        {
            CommonTree entryNode = (CommonTree)(ast.getChild(idx));
            OperandDef columnKey   = getSimpleExpr((CommonTree)(entryNode.getChild(0)));
            OperandDef columnValue = getSimpleExpr((CommonTree)(entryNode.getChild(1)));           

            Pair<OperandDef, OperandDef> entry = new Pair<OperandDef, OperandDef>(columnKey, columnValue);
            result.add(entry);
        }
        return result;
    }

    private static SuperColumnMapExpr getSuperColumnMapExpr(CommonTree ast) throws SemanticException
    {
        int type = ast.getType();       
        if (type != CqlParser.A_SUPERCOLUMN_MAP_VALUE)
        {
            throw new SemanticException(CompilerErrorMsg.INVALID_TYPE.getMsg(ast));
        }
        int size = ast.getChildCount();
        SuperColumnMapExpr result = new SuperColumnMapExpr();
        for (int idx = 0; idx < size; idx++)
        {
            CommonTree entryNode = (CommonTree)(ast.getChild(idx));
            OperandDef    superColumnKey = getSimpleExpr((CommonTree)(entryNode.getChild(0)));
            ColumnMapExpr columnMapExpr  = getColumnMapExpr((CommonTree)(entryNode.getChild(1)));           

            Pair<OperandDef, ColumnMapExpr> entry = new Pair<OperandDef, ColumnMapExpr>(superColumnKey, columnMapExpr);
            result.add(entry);
        }
        return result;
    }

    // compile a SET statement
    private static Plan compileSet(CommonTree ast) throws SemanticException
    {
        int childCount = ast.getChildCount();
        assert(childCount == 2);

        CommonTree columnFamilySpec = (CommonTree)ast.getChild(0);
        assert(columnFamilySpec.getType() == CqlParser.A_COLUMN_ACCESS);

        CFMetaData cfMetaData = getColumnFamilyInfo(columnFamilySpec);
        ConstantOperand rowKey = new ConstantOperand(getRowKey(columnFamilySpec));
        int dimensionCnt = numColumnDimensions(columnFamilySpec);

        CommonTree  valueNode = (CommonTree)(ast.getChild(1));

        Plan plan = null;
        if ("Super".equals(cfMetaData.columnType))
        {
            if (dimensionCnt == 2)
            {
                // Case: set table.super_cf['key']['supercolumn']['column'] = 'value'
                OperandDef value = getSimpleExpr(valueNode);
                ConstantOperand superColumnKey = new ConstantOperand(getColumn(columnFamilySpec, 0));
                ConstantOperand columnKey = new ConstantOperand(getColumn(columnFamilySpec, 1));
                plan = new SetUniqueKey(cfMetaData, rowKey, superColumnKey, columnKey, value);
            }
            else if (dimensionCnt == 1)
            {
                // Case: set table.super_cf['key']['supercolumn'] = <column_map>;
                ColumnMapExpr columnMapExpr = getColumnMapExpr(valueNode);               
                ConstantOperand superColumnKey = new ConstantOperand(getColumn(columnFamilySpec, 0));
                plan = new SetColumnMap(cfMetaData, rowKey, superColumnKey, columnMapExpr);
            }
            else
            {
                // Case: set table.super_cf['key'] = <super_column_map>;
                logger_.assertLog(dimensionCnt == 0, "invalid dimensionCnt: " + dimensionCnt);
                SuperColumnMapExpr superColumnMapExpr = getSuperColumnMapExpr(valueNode);               
                plan = new SetSuperColumnMap(cfMetaData, rowKey, superColumnMapExpr);
            }
        }
        else  // Standard column family
        {
            if (dimensionCnt == 1)
            {
                // Case: set table.standard_cf['key']['column'] = 'value'
                OperandDef value = getSimpleExpr(valueNode);               
                ConstantOperand columnKey = new ConstantOperand(getColumn(columnFamilySpec, 0));
                plan = new SetUniqueKey(cfMetaData, rowKey, columnKey, value);
            }
            else
            {
                // Case: set table.standard_cf['key'] = <column_map>;
                logger_.assertLog(dimensionCnt == 0, "invalid dimensionCnt: " + dimensionCnt);
                ColumnMapExpr columnMapExpr = getColumnMapExpr(valueNode);               
                plan = new SetColumnMap(cfMetaData, rowKey, columnMapExpr);
            }
        }
        return plan;
    }

    private static void compileSelect(CommonTree ast) throws SemanticException
    {
        // stub; tbd.
    }
    private static void compileDelete(CommonTree ast) throws SemanticException
    {
        // stub; tbd.
    }
}
TOP

Related Classes of org.apache.cassandra.cql.compiler.sem.SemanticPhase

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.