/*
// Licensed to DynamoBI Corporation (DynamoBI) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  DynamoBI 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 net.sf.farrago.query;

import java.util.*;

import net.sf.farrago.cwm.behavioral.*;
import net.sf.farrago.fem.security.*;
import net.sf.farrago.fem.sql2003.*;

import org.eigenbase.reltype.*;
import org.eigenbase.resgen.*;
import org.eigenbase.sql.*;
import org.eigenbase.sql.parser.*;
import org.eigenbase.sql.type.*;
import org.eigenbase.sql.validate.*;
import org.eigenbase.util.*;


/**
 * FarragoSqlValidator refines SqlValidator with some Farrago-specifics.
 *
 * @author John V. Sichi
 * @version $Id$
 */
public class FarragoSqlValidator
    extends SqlValidatorImpl
{
    //~ Instance fields --------------------------------------------------------

    final FarragoPreparingStmt preparingStmt;

    //~ Constructors -----------------------------------------------------------

    /**
     * Constructor that allows caller to specify dependant objects rather than
     * relying on the preparingStmt to supply them. This constructor is is
     * friendlier to class extension as well as providing more control during
     * test setup.
     */
    public FarragoSqlValidator(
        SqlOperatorTable opTab,
        SqlValidatorCatalogReader catalogReader,
        RelDataTypeFactory typeFactory,
        SqlConformance conformance,
        FarragoPreparingStmt preparingStmt)
    {
        super(
            opTab,
            catalogReader,
            typeFactory,
            conformance);

        this.preparingStmt = preparingStmt;
    }

    /**
     * Constructor that relies on the preparingStmt object to provide various
     * other objects during initialization.
     */
    public FarragoSqlValidator(
        FarragoPreparingStmt preparingStmt,
        SqlConformance conformance)
    {
        super(
            preparingStmt.getSqlOperatorTable(),
            preparingStmt,
            preparingStmt.getFarragoTypeFactory(),
            conformance);

        this.preparingStmt = preparingStmt;
    }

    //~ Methods ----------------------------------------------------------------

    // override SqlValidator
    public SqlNode validate(SqlNode topNode)
    {
        try {
            SqlNode node = super.validate(topNode);
            getPreparingStmt().analyzeRoutineDependencies(node);
            return node;
        } catch (EigenbaseContextException e) {
            e.setOriginalStatement(preparingStmt.getSql());
            throw e;
        }
    }

    // override SqlValidator
    public boolean shouldExpandIdentifiers()
    {
        // Farrago always wants to expand stars and identifiers during
        // validation since we use the validated representation as a canonical
        // form.
        return true;
    }

    // override SqlValidator
    protected boolean shouldAllowIntermediateOrderBy()
    {
        // Farrago follows the SQL standard on this.
        return false;
    }

    public void validateDataType(SqlDataTypeSpec dataType)
    {
        super.validateDataType(dataType);
        FarragoPreparingStmt preparingStmt = getPreparingStmt();
        try {
            preparingStmt.getStmtValidator().validateDataType(dataType);
        } catch (SqlValidatorException ex) {
            throw newValidationError(dataType, ex);
        }
    }

    protected FarragoPreparingStmt getPreparingStmt()
    {
        return preparingStmt;
    }

    // override SqlValidatorImpl
    public void validateInsert(SqlInsert call)
    {
        getPreparingStmt().setDmlValidation(
            call.getTargetTable(),
            PrivilegedActionEnum.INSERT);
        super.validateInsert(call);
        getPreparingStmt().clearDmlValidation();
    }

    // override SqlValidatorImpl
    public void validateUpdate(SqlUpdate call)
    {
        getPreparingStmt().setDmlValidation(
            call.getTargetTable(),
            PrivilegedActionEnum.UPDATE);
        super.validateUpdate(call);
        getPreparingStmt().clearDmlValidation();
    }

    // override SqlValidatorImpl
    public void validateDelete(SqlDelete call)
    {
        getPreparingStmt().setDmlValidation(
            call.getTargetTable(),
            PrivilegedActionEnum.DELETE);
        super.validateDelete(call);
        getPreparingStmt().clearDmlValidation();
    }

    // override SqlValidatorImpl
    public void validateMerge(SqlMerge call)
    {
        getPreparingStmt().setDmlValidation(
            call.getTargetTable(),
            PrivilegedActionEnum.UPDATE);
        super.validateMerge(call);
        getPreparingStmt().clearDmlValidation();
    }

    // override SqlValidatorImpl
    protected void validateFeature(
        ResourceDefinition feature,
        SqlParserPos context)
    {
        super.validateFeature(feature, context);
        getPreparingStmt().getStmtValidator().validateFeature(
            feature,
            context);
    }

    // override SqlValidatorImpl
    public void validateColumnListParams(
        SqlFunction function,
        RelDataType [] argTypes,
        SqlNode [] operands)
    {
        // get the UDR that the function corresponds to
        FarragoUserDefinedRoutine routine =
            (FarragoUserDefinedRoutine) function;
        FemRoutine femRoutine = routine.getFemRoutine();
        List<CwmParameter> params = femRoutine.getParameter();
        FunctionParamInfo funcParamInfo = functionCallStack.peek();
        Map<Integer, SqlSelect> cursorMap = funcParamInfo.cursorPosToSelectMap;
        Map<String, String> parentCursorMap =
            funcParamInfo.columnListParamToParentCursorMap;

        // locate arguments that are COLUMN_LIST types; locate the select
        // scope corresponding to the source cursor and revalidate the
        // function operand using that scope
        for (int i = 0; i < argTypes.length; i++) {
            if (argTypes[i].getSqlTypeName() == SqlTypeName.COLUMN_LIST) {
                FemColumnListRoutineParameter clParam =
                    (FemColumnListRoutineParameter) params.get(i);
                String sourceCursor = clParam.getSourceCursorName();
                int cursorPosition = -1;
                for (
                    FemRoutineParameter p
                    : Util.cast(params, FemRoutineParameter.class))
                {
                    if (p.getType().getName().equals("CURSOR")) {
                        cursorPosition++;
                        if (p.getName().equals(sourceCursor)) {
                            SqlSelect sourceSelect =
                                cursorMap.get(cursorPosition);
                            SqlValidatorScope cursorScope =
                                getCursorScope(sourceSelect);

                            // save the original node type so we can reset it
                            // after we've validated the column references
                            RelDataType origNodeType =
                                getValidatedNodeType(operands[i]);
                            removeValidatedNodeType(operands[i]);
                            deriveType(cursorScope, operands[i]);
                            setValidatedNodeType(operands[i], origNodeType);
                            parentCursorMap.put(
                                clParam.getName(),
                                sourceCursor);
                            break;
                        }
                    }
                }
            }
        }
    }
}

// End FarragoSqlValidator.java
