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

oracle.kv.impl.query.compiler.ExprInsertRow 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 java.util.List;

import oracle.kv.impl.api.table.EmptyValueImpl;
import oracle.kv.impl.api.table.FieldDefFactory;
import oracle.kv.impl.api.table.FieldDefImpl;
import oracle.kv.impl.api.table.FieldMap;
import oracle.kv.impl.api.table.FieldValueImpl;
import oracle.kv.impl.api.table.NullValueImpl;
import oracle.kv.impl.api.table.RecordDefImpl;
import oracle.kv.impl.api.table.RowImpl;
import oracle.kv.impl.api.table.TableImpl;
import oracle.kv.impl.query.QueryException;
import oracle.kv.impl.query.QueryException.Location;
import oracle.kv.impl.query.types.ExprType;
import oracle.kv.impl.query.types.ExprType.Quantifier;
import oracle.kv.impl.query.types.TypeManager;

/**
 * Implements the insert_statement
 *
 * Note: If there are any SET TTL clauses, only the last one is taken into
 * account (the others are ignored). This SET TTL clause is modeled as an
 * ExprUpdateField and is stored as the last entry in theArgs. However, this
 * ExprUpdateField is basically a placeholder for the TTL expr and the update
 * kind: no UpdateFieldIter is generated for it; instead, the actual work is
 * done by the UpdateRowIter.
 *
 * If the insert stmt has a RETURNING clause, another SFW is created on top
 * of the ExprInsertRow in order to do the projection over the inserted row.
 * So, an insert stmt of the form:
 *
 * insert into tab_name tab_alias values ...
 * returning select_list
 *
 * gets translated to the following form:
 *
 * select select_list
 * from (insert into tab_name tab_alias values ...) tab_alias
 */
public class ExprInsertRow extends Expr {

    /*
     * The type of the result when there is no RETURNING clause. It's a record
     * with just one field, named "NumRowsUpdated", whose value is the number
     * of rows inserted (currently it can be only 1 or 0).
     */
    public static RecordDefImpl theNumRowsInsertedType;

    static {
        FieldMap fmap = new FieldMap();
        fmap.put("NumRowsInserted", FieldDefImpl.integerDef, false,
                 FieldDefImpl.integerDef.createInteger(1));
        theNumRowsInsertedType = FieldDefFactory.createRecordDef(fmap, null);
    }

    private TableImpl theTable;

    private ArrayList theColPositions;

    private ArrayList theArgs;

    private UpdateKind theTTLKind;

    private boolean theIsUpsert;

    private boolean theHasReturningClause;

    private RowImpl theRow;

    ExprInsertRow(
        QueryControlBlock qcb,
        StaticContext sctx,
        Location location,
        TableImpl table,
        ArrayList colPositions,
        boolean isUpsert,
        boolean hasReturningClause) {

        super(qcb, sctx, ExprKind.INSERT_ROW, location);

        theTable = table;
        theColPositions = colPositions;
        theRow = table.createRow();
        theArgs = new ArrayList(theRow.getNumFields());

        theIsUpsert = isUpsert;
        theHasReturningClause = hasReturningClause;

        if (hasReturningClause) {
            theType = TypeManager.createTableRecordType(table, Quantifier.QSTN);
        } else {
            theType = TypeManager.createType(theNumRowsInsertedType,
                                             Quantifier.ONE);
        }
    }

    void addInsertClause(Expr arg, Location loc) {

        FieldValueImpl val = null;

        if (theArgs.size() >= theRow.getNumFields()) {
            throw new QueryException(
                "Insert statements contains more VALUES expressions " +
                "than the number of table columns", loc);
        }

        if (theColPositions != null &&
            theArgs.size() >= theColPositions.size()) {
            throw new QueryException(
                "Insert statements contains more VALUES expressions " +
                "than the number of specified columns", loc);
        }

        int fpos = (theColPositions == null ?
                    theArgs.size() + theRow.size() :
                    theColPositions.get(theArgs.size()));

        FieldDefImpl ftype = theRow.getFieldDef(fpos);

        if (arg != null &&
            theTable.hasIdentityColumn() &&
            theTable.getIdentityColumn() == fpos &&
            theTable.isIdentityGeneratedAlways()) {
            throw new QueryException("Generated always identity " +
                "column must use DEFAULT construct.", loc);
        }

        if (arg == null) {/*Users specify DEFAULT in INSERT statement*/

            /* if identity column then set val to EmptyValue */
            if (theTable.hasIdentityColumn() &&
                theTable.getIdentityColumn() == fpos) {
                /* Value will get generated later at runtime in
                   InsertRowIter.next() */
                theRow.putInternal(fpos, EmptyValueImpl.getInstance(), false);
                return;
            }
            if (theTable.isPrimKeyAtPos(fpos)) {
                 throw new QueryException(
                     "There is no default value for primary-key column " +
                     theRow.getFieldName(fpos), loc);
            }

            val = theRow.getDefinition().getDefaultValue(fpos);

            putValue(fpos, val);

            if (theColPositions != null) {
                theColPositions.remove(theArgs.size());
            }

            return;
        }

        /* Special treatment required for json null */
        if (arg.getKind() == ExprKind.CONST) {

            val = ((ExprConst)arg).getValue();

            if ( (val.isNull() || val.isJsonNull() ) &&
                theTable.hasIdentityColumn() &&
                theTable.getIdentityColumn() == fpos &&
                theTable.isIdentityOnNull()) {

                theRow.putInternal(fpos, NullValueImpl.getInstance(), false);

                if (theColPositions != null) {
                    theColPositions.remove(theArgs.size());
                }
                return;
            }

            if (val.isJsonNull() || val.getDefinition().equals(ftype)) {

                putValue(fpos, val);

                if (theColPositions != null) {
                    theColPositions.remove(theArgs.size());
                }
                return;
            }
        }

        arg = ExprCast.create(theQCB, theSctx, arg.getLocation(),
                              arg, ftype, Quantifier.QSTN);

        if (arg.getKind() == ExprKind.CONST) {

            val = ((ExprConst)arg).getValue();

        } else if (ConstKind.isCompileConst(arg)) {

            List vals = ExprUtils.computeConstExpr(arg);

            if (vals.size() > 1) {
                throw new QueryException(
                    "A VALUES expression returns more than one items",
                    loc);
            }

            val = (vals.size() == 1 ? vals.get(0) : NullValueImpl.getInstance());
        }

        if (val != null) {

            putValue(fpos, val);

            if (theColPositions != null) {
                theColPositions.remove(theArgs.size());
            }

        } else {
            theArgs.add(arg);
            arg.addParent(this);
        }
    }

    private void putValue(int fpos, FieldValueImpl val) {

        if (val.isNull()) {
            theRow.putNull(fpos);
        } else if (val.isJsonNull()) {

            FieldDefImpl ftype = theRow.getFieldDef(fpos);

            if (ftype.equals(FieldDefImpl.jsonDef)) {
                theRow.put(fpos, val);
            } else {
                theRow.putNull(fpos);
            }

        } else {
            theRow.put(fpos, val);
        }
    }

    void addTTLClause(Expr ttlExpr, UpdateKind ttlKind) {

        if (ttlExpr != null) {
            theArgs.add(ttlExpr);
            ttlExpr.addParent(this);
        }
        theTTLKind = ttlKind;
    }

    void validate() {

        int numCols = theRow.getNumFields();

        boolean haveTTLExpr = (theTTLKind == UpdateKind.TTL_HOURS ||
                               theTTLKind == UpdateKind.TTL_DAYS);

        if (theColPositions == null) {

            int numValues = (theRow.size() + theArgs.size() - 
                             (haveTTLExpr ? 1 : 0));

            if (numValues != numCols) {
                throw new QueryException(
                    "The number of VALUES expressions is not equal to the " +
                    "number of table columns", theLocation);
            }

            theColPositions = new ArrayList(numCols);

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

                if (theRow.get(i) == null) {
                    theColPositions.add(i);
                }
            }

            return;
        }

        if (theColPositions.size() != (theArgs.size() - (haveTTLExpr ? 1 : 0))) {
            throw new QueryException(
                "The number of VALUES expressions is not equal to the number " +
                "of specified table columns", theLocation);
        }

        int[] pkPositions = theTable.getPrimKeyPositions();

        for (int i = 0; i < pkPositions.length; ++i) {

            if (theRow.get(pkPositions[i]) != null) {
                continue;
            }

            int j;
            for (j = 0; j < theColPositions.size(); ++j) {
                if (theColPositions.get(j) == pkPositions[i]) {
                    break;
                }
            }
            boolean pkIsIdentity = theTable.hasIdentityColumn() &&
                theTable.getIdentityColumn() == pkPositions[i];
            if (j == theColPositions.size() && !pkIsIdentity) {
                throw new QueryException(
                    "No value specified for primary key column " +
                    theRow.getFieldName(pkPositions[i]), theLocation);
            }
        }

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

            if (theRow.get(i) != null || theTable.isPrimKeyAtPos(i) ||
                (theTable.hasIdentityColumn() &&
                    theTable.getIdentityColumn() == i)) {
                continue;
            }

            FieldValueImpl fv = theRow.getDefinition().getDefaultValue(i);
            theRow.putInternal(i, fv, false);
        }
    }

    TableImpl getTable() {
        return theTable;
    }

    @Override
    int getNumChildren() {
        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);
        theArgs.set(i, newExpr);
        newExpr.addParent(this);
    }

    RowImpl getRow() {
        return theRow;
    }

    ArrayList getColPositions() {
        return theColPositions;
    }

    boolean isUpsert() {
        return theIsUpsert;
    }

    boolean updateTTL() {
        return theTTLKind != null;
    }

    UpdateKind getTTLKind() {
        return theTTLKind;
    }

    Expr getTTLExpr() {

        if (theTTLKind == UpdateKind.TTL_HOURS ||
            theTTLKind == UpdateKind.TTL_DAYS) {
            return theArgs.get(theArgs.size() - 1);
        }
        return null;
    }

    boolean hasReturningClause() {
        return theHasReturningClause;
    }

    @Override
    ExprType computeType() {
        return theType;
    }

    @Override
    boolean mayReturnNULL() {
        return false;
    }

    @Override
    void displayContent(StringBuilder sb, QueryFormatter formatter) {

        for (int i = 1; i < theArgs.size(); ++i) {
            theArgs.get(i).display(sb, formatter);
            if (i < theArgs.size() - 1) {
                sb.append(",\n");
            }
        }

        sb.append("\n");
        theArgs.get(0).display(sb, formatter);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy