All Downloads are FREE. Search and download functionalities are using the official Maven repository.

oracle.kv.impl.query.compiler.ExprMapConstr Maven / Gradle / Ivy

/*-
 * Copyright (C) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This file was distributed by Oracle as part of a version of Oracle NoSQL
 * Database made available at:
 *
 * http://www.oracle.com/technetwork/database/database-technologies/nosqldb/downloads/index.html
 *
 * Please see the LICENSE file included in the top-level directory of the
 * appropriate version of Oracle NoSQL Database for a copy of the license and
 * additional information.
 */

package oracle.kv.impl.query.compiler;

import java.util.ArrayList;

import oracle.kv.impl.api.table.FieldDefImpl;
import oracle.kv.impl.api.table.MapDefImpl;
import oracle.kv.impl.query.QueryException;
import oracle.kv.impl.query.QueryStateException;
import oracle.kv.impl.query.types.ExprType;
import oracle.kv.impl.query.types.ExprType.Quantifier;
import oracle.kv.impl.query.types.TypeManager;

/**
 * Constructs a map containing the entries computed by the input pairs of exprs.
 *
 * The element type of the map to be constructed during runtiem is determined
 * during compilation time. Initially, this is done based only on the types of
 * the input exprs to this ExprMapConstr. However, just before codegen,
 * theMustBeJson will be set to true if the map is going to be inserted into
 * another constructed array or map whose element type is JSON (see 
 * ExprUtils.adjustConstructorTypes()).  In this case, the element type of the
 * constructed map will also be JSON. We do this to guarantee that strongly
 * type data does not get inserted into JSON data.
 *
 * If there are no input exprs, the type is ARRAY(JSON).
 *
 * theArgs:
 * For each even i, theArgs[i] is an expr that must return a string, which is
 * used as the name of the i-th field to be inserted in the map. theArgs[i+1]
 * is an expr that returns the value of the i-th field. If either theArgs[i] or
 * theArgs[i+1] returns an empty result, no field is added to the map. If 
 * theArgs[i+1] returns more than one items, implicit array construction is 
 * used so that the field value will be an array containing the returned items.
 */
public class ExprMapConstr extends Expr {

    final ArrayList theArgs;

    private boolean theMustBeJson;

    ExprMapConstr(
        QueryControlBlock qcb,
        StaticContext sctx,
        QueryException.Location location,
        ArrayList args) {

        super(qcb, sctx, ExprKind.MAP_CONSTR, location);
        theArgs = args;

        int numArgs = args.size();

        for (int i = 0; i < numArgs; ++i) {

            Expr arg = args.get(i);

            if (i % 2 == 0) {
                arg = ExprPromote.create(null, arg, TypeManager.STRING_QSTN());
                theArgs.set(i, arg);

            } else if (arg.isMultiValued()) {
                ArrayList arrargs = new ArrayList(1);
                arrargs.add(arg);
                arg = new ExprArrayConstr(theQCB, theSctx, arg.theLocation,
                                          arrargs, true/*conditional*/);
                theArgs.set(i, arg);
            }

            arg.addParent(this);
        }

        /*
         * Must compute the type here to guarantee that 
         * theQCB.theHaveJsonConstructors will be set, if necessary, BEFORE
         * ExprUtils.adjustConstructorTypes() is called.
         */
        theType = computeType();
    }

    @Override
    int getNumChildren() {
        return theArgs.size();
    }

    int getNumArgs() {
        return theArgs.size();
    }

    Expr getArg(int i) {
        return theArgs.get(i);
    }

    void setArg(int i, Expr newExpr, boolean destroy) {

        theArgs.get(i).removeParent(this, destroy);

        if (i % 2 == 0) {
            newExpr = ExprPromote.create(null, newExpr,
                                         TypeManager.STRING_QSTN());

        } else if (newExpr.isMultiValued()) {
            ArrayList arrargs = new ArrayList(1);
            arrargs.add(newExpr);
            newExpr = new ExprArrayConstr(theQCB, theSctx, newExpr.theLocation,
                                          arrargs, true/*conditional*/);
        }

        theArgs.set(i, newExpr);
        newExpr.addParent(this);
    }

    void removeArg(int i, boolean destroy) {
        if (!theArgs.get(i).getType().isEmpty()) {
            throw new QueryStateException(
                "Cannot remove non-empty input expr from map " +
                "constructor expr");
        }
        theArgs.get(i).removeParent(this, destroy);
        theArgs.remove(i);
    }

    void setJsonMapType() {
        theMustBeJson = true;
    }

    public MapDefImpl getMapType() {
        return (MapDefImpl)getType().getDef();
    }

    @Override
    ExprType computeType() {

        int numArgs = theArgs.size();

        if (numArgs == 0) {
            return TypeManager.createMapType(TypeManager.JSON_ONE(),
                                             Quantifier.ONE); 
        }

        ExprType elemType = theArgs.get(1).getType();

        for (int i = 3; i < numArgs; i += 2) {
            ExprType type = theArgs.get(i).getType();
            elemType = TypeManager.getConcatType(elemType, type);

            if (elemType.isAny()) {
                break;
            }
        }

        if (theMustBeJson &&
            elemType.getDef().isSubtype(FieldDefImpl.jsonDef)) {
            elemType = TypeManager.JSON_ONE();
        }

        if (elemType.isAnyJson()) {
            theQCB.theHaveJsonConstructors = true;
        }

        return TypeManager.createMapType(elemType, Quantifier.ONE);
    }

    @Override
    public boolean mayReturnNULL() {
        return false;
    }

    @Override
    void displayContent(StringBuilder sb, QueryFormatter formatter) {
        for (int i = 0; i < theArgs.size(); ++i) {
            theArgs.get(i).display(sb, formatter);
            if (i < theArgs.size() - 1) {
                sb.append(",\n");
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy