]> granicus.if.org Git - postgresql/commitdiff
Track typmods in plpgsql expression evaluation and assignment.
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 28 Feb 2015 19:34:35 +0000 (14:34 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 28 Feb 2015 19:34:35 +0000 (14:34 -0500)
The main value of this change is to avoid expensive I/O conversions when
assigning to a variable that has a typmod specification, if the value
to be assigned is already known to have the right typmod.  This is
particularly valuable for arrays with typmod specifications; formerly,
in an assignment to an array element the entire array would invariably
get put through double I/O conversion to check the typmod, to absolutely
no purpose since we'd already properly coerced the new element value.

Extracted from my "expanded arrays" patch; this seems worth committing
separately, whatever becomes of that patch, since it's really an
independent issue.

As long as we're changing the function signatures, take the opportunity
to rationalize the argument lists of exec_assign_value, exec_cast_value,
and exec_simple_cast_value; that is, put the arguments into a saner order,
and get rid of the bizarre choice to pass exec_assign_value's isNull flag
by reference.

src/pl/plpgsql/src/pl_exec.c
src/pl/plpgsql/src/plpgsql.h

index edcb230d5d529c7d8d166eda3af7f72cf9ccde4e..41a68f829ac4a347cb7d275d98fc8cb55cace797 100644 (file)
@@ -158,7 +158,8 @@ static bool exec_eval_simple_expr(PLpgSQL_execstate *estate,
                                          PLpgSQL_expr *expr,
                                          Datum *result,
                                          bool *isNull,
-                                         Oid *rettype);
+                                         Oid *rettype,
+                                         int32 *rettypmod);
 
 static void exec_assign_expr(PLpgSQL_execstate *estate,
                                 PLpgSQL_datum *target,
@@ -168,7 +169,8 @@ static void exec_assign_c_string(PLpgSQL_execstate *estate,
                                         const char *str);
 static void exec_assign_value(PLpgSQL_execstate *estate,
                                  PLpgSQL_datum *target,
-                                 Datum value, Oid valtype, bool *isNull);
+                                 Datum value, bool isNull,
+                                 Oid valtype, int32 valtypmod);
 static void exec_eval_datum(PLpgSQL_execstate *estate,
                                PLpgSQL_datum *datum,
                                Oid *typeid,
@@ -184,7 +186,8 @@ static bool exec_eval_boolean(PLpgSQL_execstate *estate,
 static Datum exec_eval_expr(PLpgSQL_execstate *estate,
                           PLpgSQL_expr *expr,
                           bool *isNull,
-                          Oid *rettype);
+                          Oid *rettype,
+                          int32 *rettypmod);
 static int exec_run_select(PLpgSQL_execstate *estate,
                                PLpgSQL_expr *expr, long maxtuples, Portal *portalP);
 static int exec_for_query(PLpgSQL_execstate *estate, PLpgSQL_stmt_forq *stmt,
@@ -208,16 +211,15 @@ static void exec_move_row_from_datum(PLpgSQL_execstate *estate,
 static char *convert_value_to_string(PLpgSQL_execstate *estate,
                                                Datum value, Oid valtype);
 static Datum exec_cast_value(PLpgSQL_execstate *estate,
-                               Datum value, Oid valtype,
-                               Oid reqtype,
+                               Datum value, bool isnull,
+                               Oid valtype, int32 valtypmod,
+                               Oid reqtype, int32 reqtypmod,
                                FmgrInfo *reqinput,
-                               Oid reqtypioparam,
-                               int32 reqtypmod,
-                               bool isnull);
+                               Oid reqtypioparam);
 static Datum exec_simple_cast_value(PLpgSQL_execstate *estate,
-                                          Datum value, Oid valtype,
-                                          Oid reqtype, int32 reqtypmod,
-                                          bool isnull);
+                                          Datum value, bool isnull,
+                                          Oid valtype, int32 valtypmod,
+                                          Oid reqtype, int32 reqtypmod);
 static void exec_init_tuple_store(PLpgSQL_execstate *estate);
 static void exec_set_found(PLpgSQL_execstate *estate, bool state);
 static void plpgsql_create_econtext(PLpgSQL_execstate *estate);
@@ -452,12 +454,13 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo,
                        /* Cast value to proper type */
                        estate.retval = exec_cast_value(&estate,
                                                                                        estate.retval,
+                                                                                       fcinfo->isnull,
                                                                                        estate.rettype,
+                                                                                       -1,
                                                                                        func->fn_rettype,
-                                                                                       &(func->fn_retinput),
-                                                                                       func->fn_rettypioparam,
                                                                                        -1,
-                                                                                       fcinfo->isnull);
+                                                                                       &(func->fn_retinput),
+                                                                                       func->fn_rettypioparam);
 
                        /*
                         * If the function's return type isn't by value, copy the value
@@ -1077,15 +1080,13 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
                                                 * exec_assign_value.)
                                                 */
                                                if (!var->datatype->typinput.fn_strict)
-                                               {
-                                                       bool            valIsNull = true;
-
                                                        exec_assign_value(estate,
                                                                                          (PLpgSQL_datum *) var,
                                                                                          (Datum) 0,
+                                                                                         true,
                                                                                          UNKNOWNOID,
-                                                                                         &valIsNull);
-                                               }
+                                                                                         -1);
+
                                                if (var->notnull)
                                                        ereport(ERROR,
                                                                        (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
@@ -1556,20 +1557,19 @@ exec_stmt_getdiag(PLpgSQL_execstate *estate, PLpgSQL_stmt_getdiag *stmt)
        {
                PLpgSQL_diag_item *diag_item = (PLpgSQL_diag_item *) lfirst(lc);
                PLpgSQL_datum *var = estate->datums[diag_item->target];
-               bool            isnull = false;
 
                switch (diag_item->kind)
                {
                        case PLPGSQL_GETDIAG_ROW_COUNT:
                                exec_assign_value(estate, var,
                                                                  UInt32GetDatum(estate->eval_processed),
-                                                                 INT4OID, &isnull);
+                                                                 false, INT4OID, -1);
                                break;
 
                        case PLPGSQL_GETDIAG_RESULT_OID:
                                exec_assign_value(estate, var,
                                                                  ObjectIdGetDatum(estate->eval_lastoid),
-                                                                 OIDOID, &isnull);
+                                                                 false, OIDOID, -1);
                                break;
 
                        case PLPGSQL_GETDIAG_ERROR_CONTEXT:
@@ -1688,9 +1688,11 @@ exec_stmt_case(PLpgSQL_execstate *estate, PLpgSQL_stmt_case *stmt)
        {
                /* simple case */
                Datum           t_val;
-               Oid                     t_oid;
+               Oid                     t_typoid;
+               int32           t_typmod;
 
-               t_val = exec_eval_expr(estate, stmt->t_expr, &isnull, &t_oid);
+               t_val = exec_eval_expr(estate, stmt->t_expr,
+                                                          &isnull, &t_typoid, &t_typmod);
 
                t_var = (PLpgSQL_var *) estate->datums[stmt->t_varno];
 
@@ -1699,17 +1701,19 @@ exec_stmt_case(PLpgSQL_execstate *estate, PLpgSQL_stmt_case *stmt)
                 * what we're modifying here is an execution copy of the datum, so
                 * this doesn't affect the originally stored function parse tree.
                 */
-               if (t_var->datatype->typoid != t_oid)
-                       t_var->datatype = plpgsql_build_datatype(t_oid,
-                                                                                                        -1,
+               if (t_var->datatype->typoid != t_typoid ||
+                       t_var->datatype->atttypmod != t_typmod)
+                       t_var->datatype = plpgsql_build_datatype(t_typoid,
+                                                                                                        t_typmod,
                                                                                   estate->func->fn_input_collation);
 
                /* now we can assign to the variable */
                exec_assign_value(estate,
                                                  (PLpgSQL_datum *) t_var,
                                                  t_val,
-                                                 t_oid,
-                                                 &isnull);
+                                                 isnull,
+                                                 t_typoid,
+                                                 t_typmod);
 
                exec_eval_cleanup(estate);
        }
@@ -1885,6 +1889,7 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt)
        Datum           value;
        bool            isnull;
        Oid                     valtype;
+       int32           valtypmod;
        int32           loop_value;
        int32           end_value;
        int32           step_value;
@@ -1896,11 +1901,14 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt)
        /*
         * Get the value of the lower bound
         */
-       value = exec_eval_expr(estate, stmt->lower, &isnull, &valtype);
-       value = exec_cast_value(estate, value, valtype, var->datatype->typoid,
+       value = exec_eval_expr(estate, stmt->lower,
+                                                  &isnull, &valtype, &valtypmod);
+       value = exec_cast_value(estate, value, isnull,
+                                                       valtype, valtypmod,
+                                                       var->datatype->typoid,
+                                                       var->datatype->atttypmod,
                                                        &(var->datatype->typinput),
-                                                       var->datatype->typioparam,
-                                                       var->datatype->atttypmod, isnull);
+                                                       var->datatype->typioparam);
        if (isnull)
                ereport(ERROR,
                                (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
@@ -1911,11 +1919,14 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt)
        /*
         * Get the value of the upper bound
         */
-       value = exec_eval_expr(estate, stmt->upper, &isnull, &valtype);
-       value = exec_cast_value(estate, value, valtype, var->datatype->typoid,
+       value = exec_eval_expr(estate, stmt->upper,
+                                                  &isnull, &valtype, &valtypmod);
+       value = exec_cast_value(estate, value, isnull,
+                                                       valtype, valtypmod,
+                                                       var->datatype->typoid,
+                                                       var->datatype->atttypmod,
                                                        &(var->datatype->typinput),
-                                                       var->datatype->typioparam,
-                                                       var->datatype->atttypmod, isnull);
+                                                       var->datatype->typioparam);
        if (isnull)
                ereport(ERROR,
                                (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
@@ -1928,11 +1939,14 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt)
         */
        if (stmt->step)
        {
-               value = exec_eval_expr(estate, stmt->step, &isnull, &valtype);
-               value = exec_cast_value(estate, value, valtype, var->datatype->typoid,
+               value = exec_eval_expr(estate, stmt->step,
+                                                          &isnull, &valtype, &valtypmod);
+               value = exec_cast_value(estate, value, isnull,
+                                                               valtype, valtypmod,
+                                                               var->datatype->typoid,
+                                                               var->datatype->atttypmod,
                                                                &(var->datatype->typinput),
-                                                               var->datatype->typioparam,
-                                                               var->datatype->atttypmod, isnull);
+                                                               var->datatype->typioparam);
                if (isnull)
                        ereport(ERROR,
                                        (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
@@ -2227,17 +2241,19 @@ exec_stmt_foreach_a(PLpgSQL_execstate *estate, PLpgSQL_stmt_foreach_a *stmt)
 {
        ArrayType  *arr;
        Oid                     arrtype;
+       int32           arrtypmod;
        PLpgSQL_datum *loop_var;
        Oid                     loop_var_elem_type;
        bool            found = false;
        int                     rc = PLPGSQL_RC_OK;
        ArrayIterator array_iterator;
        Oid                     iterator_result_type;
+       int32           iterator_result_typmod;
        Datum           value;
        bool            isnull;
 
        /* get the value of the array expression */
-       value = exec_eval_expr(estate, stmt->expr, &isnull, &arrtype);
+       value = exec_eval_expr(estate, stmt->expr, &isnull, &arrtype, &arrtypmod);
        if (isnull)
                ereport(ERROR,
                                (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
@@ -2305,11 +2321,13 @@ exec_stmt_foreach_a(PLpgSQL_execstate *estate, PLpgSQL_stmt_foreach_a *stmt)
        {
                /* When slicing, nominal type of result is same as array type */
                iterator_result_type = arrtype;
+               iterator_result_typmod = arrtypmod;
        }
        else
        {
                /* Without slicing, results are individual array elements */
                iterator_result_type = ARR_ELEMTYPE(arr);
+               iterator_result_typmod = arrtypmod;
        }
 
        /* Iterate over the array elements or slices */
@@ -2318,8 +2336,8 @@ exec_stmt_foreach_a(PLpgSQL_execstate *estate, PLpgSQL_stmt_foreach_a *stmt)
                found = true;                   /* looped at least once */
 
                /* Assign current element/slice to the loop variable */
-               exec_assign_value(estate, loop_var, value, iterator_result_type,
-                                                 &isnull);
+               exec_assign_value(estate, loop_var, value, isnull,
+                                                 iterator_result_type, iterator_result_typmod);
 
                /* In slice case, value is temporary; must free it to avoid leakage */
                if (stmt->slice > 0)
@@ -2503,9 +2521,12 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
 
        if (stmt->expr != NULL)
        {
+               int32           rettypmod;
+
                estate->retval = exec_eval_expr(estate, stmt->expr,
                                                                                &(estate->retisnull),
-                                                                               &(estate->rettype));
+                                                                               &(estate->rettype),
+                                                                               &rettypmod);
 
                if (estate->retistuple && !estate->retisnull)
                {
@@ -2595,10 +2616,11 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
                                        /* coerce type if needed */
                                        retval = exec_simple_cast_value(estate,
                                                                                                        retval,
+                                                                                                       isNull,
                                                                                                        var->datatype->typoid,
+                                                                                                       var->datatype->atttypmod,
                                                                                                 tupdesc->attrs[0]->atttypid,
-                                                                                               tupdesc->attrs[0]->atttypmod,
-                                                                                                       isNull);
+                                                                                          tupdesc->attrs[0]->atttypmod);
 
                                        tuplestore_putvalues(estate->tuple_store, tupdesc,
                                                                                 &retval, &isNull);
@@ -2654,11 +2676,13 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
                Datum           retval;
                bool            isNull;
                Oid                     rettype;
+               int32           rettypmod;
 
                retval = exec_eval_expr(estate,
                                                                stmt->expr,
                                                                &isNull,
-                                                               &rettype);
+                                                               &rettype,
+                                                               &rettypmod);
 
                if (estate->retistuple)
                {
@@ -2718,10 +2742,11 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
                        /* coerce type if needed */
                        retval = exec_simple_cast_value(estate,
                                                                                        retval,
+                                                                                       isNull,
                                                                                        rettype,
+                                                                                       rettypmod,
                                                                                        tupdesc->attrs[0]->atttypid,
-                                                                                       tupdesc->attrs[0]->atttypmod,
-                                                                                       isNull);
+                                                                                       tupdesc->attrs[0]->atttypmod);
 
                        tuplestore_putvalues(estate->tuple_store, tupdesc,
                                                                 &retval, &isNull);
@@ -2924,6 +2949,7 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
                        if (cp[0] == '%')
                        {
                                Oid                     paramtypeid;
+                               int32           paramtypmod;
                                Datum           paramvalue;
                                bool            paramisnull;
                                char       *extval;
@@ -2942,7 +2968,8 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
                                paramvalue = exec_eval_expr(estate,
                                                                          (PLpgSQL_expr *) lfirst(current_param),
                                                                                        &paramisnull,
-                                                                                       &paramtypeid);
+                                                                                       &paramtypeid,
+                                                                                       &paramtypmod);
 
                                if (paramisnull)
                                        extval = "<NULL>";
@@ -2972,11 +2999,13 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
                Datum           optionvalue;
                bool            optionisnull;
                Oid                     optiontypeid;
+               int32           optiontypmod;
                char       *extval;
 
                optionvalue = exec_eval_expr(estate, opt->expr,
                                                                         &optionisnull,
-                                                                        &optiontypeid);
+                                                                        &optiontypeid,
+                                                                        &optiontypmod);
                if (optionisnull)
                        ereport(ERROR,
                                        (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
@@ -3478,8 +3507,9 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
                                         PLpgSQL_stmt_dynexecute *stmt)
 {
        Datum           query;
-       bool            isnull = false;
+       bool            isnull;
        Oid                     restype;
+       int32           restypmod;
        char       *querystr;
        int                     exec_res;
        PreparedParamsData *ppd = NULL;
@@ -3488,7 +3518,7 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
         * First we evaluate the string expression after the EXECUTE keyword. Its
         * result is the querystring we have to execute.
         */
-       query = exec_eval_expr(estate, stmt->query, &isnull, &restype);
+       query = exec_eval_expr(estate, stmt->query, &isnull, &restype, &restypmod);
        if (isnull)
                ereport(ERROR,
                                (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
@@ -3982,11 +4012,12 @@ exec_assign_expr(PLpgSQL_execstate *estate, PLpgSQL_datum *target,
                                 PLpgSQL_expr *expr)
 {
        Datum           value;
+       bool            isnull;
        Oid                     valtype;
-       bool            isnull = false;
+       int32           valtypmod;
 
-       value = exec_eval_expr(estate, expr, &isnull, &valtype);
-       exec_assign_value(estate, target, value, valtype, &isnull);
+       value = exec_eval_expr(estate, expr, &isnull, &valtype, &valtypmod);
+       exec_assign_value(estate, target, value, isnull, valtype, valtypmod);
        exec_eval_cleanup(estate);
 }
 
@@ -4002,14 +4033,13 @@ exec_assign_c_string(PLpgSQL_execstate *estate, PLpgSQL_datum *target,
                                         const char *str)
 {
        text       *value;
-       bool            isnull = false;
 
        if (str != NULL)
                value = cstring_to_text(str);
        else
                value = cstring_to_text("");
-       exec_assign_value(estate, target, PointerGetDatum(value),
-                                         TEXTOID, &isnull);
+       exec_assign_value(estate, target, PointerGetDatum(value), false,
+                                         TEXTOID, -1);
        pfree(value);
 }
 
@@ -4025,7 +4055,8 @@ exec_assign_c_string(PLpgSQL_execstate *estate, PLpgSQL_datum *target,
 static void
 exec_assign_value(PLpgSQL_execstate *estate,
                                  PLpgSQL_datum *target,
-                                 Datum value, Oid valtype, bool *isNull)
+                                 Datum value, bool isNull,
+                                 Oid valtype, int32 valtypmod)
 {
        switch (target->dtype)
        {
@@ -4039,14 +4070,15 @@ exec_assign_value(PLpgSQL_execstate *estate,
 
                                newvalue = exec_cast_value(estate,
                                                                                   value,
+                                                                                  isNull,
                                                                                   valtype,
+                                                                                  valtypmod,
                                                                                   var->datatype->typoid,
-                                                                                  &(var->datatype->typinput),
-                                                                                  var->datatype->typioparam,
                                                                                   var->datatype->atttypmod,
-                                                                                  *isNull);
+                                                                                  &(var->datatype->typinput),
+                                                                                  var->datatype->typioparam);
 
-                               if (*isNull && var->notnull)
+                               if (isNull && var->notnull)
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
                                                         errmsg("null value cannot be assigned to variable \"%s\" declared NOT NULL",
@@ -4057,7 +4089,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
                                 * probably in the eval_econtext) into the procedure's memory
                                 * context.
                                 */
-                               if (!var->datatype->typbyval && !*isNull)
+                               if (!var->datatype->typbyval && !isNull)
                                        newvalue = datumCopy(newvalue,
                                                                                 false,
                                                                                 var->datatype->typlen);
@@ -4072,8 +4104,8 @@ exec_assign_value(PLpgSQL_execstate *estate,
                                free_var(var);
 
                                var->value = newvalue;
-                               var->isnull = *isNull;
-                               if (!var->datatype->typbyval && !*isNull)
+                               var->isnull = isNull;
+                               if (!var->datatype->typbyval && !isNull)
                                        var->freeval = true;
                                break;
                        }
@@ -4085,7 +4117,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
                                 */
                                PLpgSQL_row *row = (PLpgSQL_row *) target;
 
-                               if (*isNull)
+                               if (isNull)
                                {
                                        /* If source is null, just assign nulls to the row */
                                        exec_move_row(estate, NULL, row, NULL, NULL);
@@ -4109,7 +4141,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
                                 */
                                PLpgSQL_rec *rec = (PLpgSQL_rec *) target;
 
-                               if (*isNull)
+                               if (isNull)
                                {
                                        /* If source is null, just assign nulls to the record */
                                        exec_move_row(estate, rec, NULL, NULL, NULL);
@@ -4139,7 +4171,6 @@ exec_assign_value(PLpgSQL_execstate *estate,
                                Datum      *values;
                                bool       *nulls;
                                bool       *replaces;
-                               bool            attisnull;
                                Oid                     atttype;
                                int32           atttypmod;
 
@@ -4187,16 +4218,16 @@ exec_assign_value(PLpgSQL_execstate *estate,
                                 * Now insert the new value, being careful to cast it to the
                                 * right type.
                                 */
-                               atttype = SPI_gettypeid(rec->tupdesc, fno + 1);
+                               atttype = rec->tupdesc->attrs[fno]->atttypid;
                                atttypmod = rec->tupdesc->attrs[fno]->atttypmod;
-                               attisnull = *isNull;
                                values[fno] = exec_simple_cast_value(estate,
                                                                                                         value,
+                                                                                                        isNull,
                                                                                                         valtype,
+                                                                                                        valtypmod,
                                                                                                         atttype,
-                                                                                                        atttypmod,
-                                                                                                        attisnull);
-                               nulls[fno] = attisnull;
+                                                                                                        atttypmod);
+                               nulls[fno] = isNull;
 
                                /*
                                 * Now call heap_modify_tuple() to create a new tuple that
@@ -4354,10 +4385,11 @@ exec_assign_value(PLpgSQL_execstate *estate,
                                /* Coerce source value to match array element type. */
                                coerced_value = exec_simple_cast_value(estate,
                                                                                                           value,
+                                                                                                          isNull,
                                                                                                           valtype,
+                                                                                                          valtypmod,
                                                                                                           arrayelem->elemtypoid,
-                                                                                                          arrayelem->arraytypmod,
-                                                                                                          *isNull);
+                                                                                                        arrayelem->arraytypmod);
 
                                /*
                                 * If the original array is null, cons up an empty array so
@@ -4370,7 +4402,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
                                 * corresponds to the current behavior of ExecEvalArrayRef().
                                 */
                                if (arrayelem->arraytyplen > 0 &&               /* fixed-length array? */
-                                       (oldarrayisnull || *isNull))
+                                       (oldarrayisnull || isNull))
                                        return;
 
                                /* empty array, if any, and newarraydatum are short-lived */
@@ -4386,7 +4418,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
                                                                                                  nsubscripts,
                                                                                                  subscriptvals,
                                                                                                  coerced_value,
-                                                                                                 *isNull,
+                                                                                                 isNull,
                                                                                                  arrayelem->arraytyplen,
                                                                                                  arrayelem->elemtyplen,
                                                                                                  arrayelem->elemtypbyval,
@@ -4400,10 +4432,11 @@ exec_assign_value(PLpgSQL_execstate *estate,
                                 * coercing the base array type back up to the domain will
                                 * happen within exec_assign_value.
                                 */
-                               *isNull = false;
                                exec_assign_value(estate, target,
                                                                  newarraydatum,
-                                                                 arrayelem->arraytypoid, isNull);
+                                                                 false,
+                                                                 arrayelem->arraytypoid,
+                                                                 arrayelem->arraytypmod);
                                break;
                        }
 
@@ -4724,11 +4757,12 @@ exec_eval_integer(PLpgSQL_execstate *estate,
 {
        Datum           exprdatum;
        Oid                     exprtypeid;
+       int32           exprtypmod;
 
-       exprdatum = exec_eval_expr(estate, expr, isNull, &exprtypeid);
-       exprdatum = exec_simple_cast_value(estate, exprdatum, exprtypeid,
-                                                                          INT4OID, -1,
-                                                                          *isNull);
+       exprdatum = exec_eval_expr(estate, expr, isNull, &exprtypeid, &exprtypmod);
+       exprdatum = exec_simple_cast_value(estate, exprdatum, *isNull,
+                                                                          exprtypeid, exprtypmod,
+                                                                          INT4OID, -1);
        return DatumGetInt32(exprdatum);
 }
 
@@ -4746,17 +4780,18 @@ exec_eval_boolean(PLpgSQL_execstate *estate,
 {
        Datum           exprdatum;
        Oid                     exprtypeid;
+       int32           exprtypmod;
 
-       exprdatum = exec_eval_expr(estate, expr, isNull, &exprtypeid);
-       exprdatum = exec_simple_cast_value(estate, exprdatum, exprtypeid,
-                                                                          BOOLOID, -1,
-                                                                          *isNull);
+       exprdatum = exec_eval_expr(estate, expr, isNull, &exprtypeid, &exprtypmod);
+       exprdatum = exec_simple_cast_value(estate, exprdatum, *isNull,
+                                                                          exprtypeid, exprtypmod,
+                                                                          BOOLOID, -1);
        return DatumGetBool(exprdatum);
 }
 
 /* ----------
  * exec_eval_expr                      Evaluate an expression and return
- *                                     the result Datum.
+ *                                     the result Datum, along with data type/typmod.
  *
  * NOTE: caller must do exec_eval_cleanup when done with the Datum.
  * ----------
@@ -4765,7 +4800,8 @@ static Datum
 exec_eval_expr(PLpgSQL_execstate *estate,
                           PLpgSQL_expr *expr,
                           bool *isNull,
-                          Oid *rettype)
+                          Oid *rettype,
+                          int32 *rettypmod)
 {
        Datum           result = 0;
        int                     rc;
@@ -4780,7 +4816,8 @@ exec_eval_expr(PLpgSQL_execstate *estate,
         * If this is a simple expression, bypass SPI and use the executor
         * directly
         */
-       if (exec_eval_simple_expr(estate, expr, &result, isNull, rettype))
+       if (exec_eval_simple_expr(estate, expr,
+                                                         &result, isNull, rettype, rettypmod))
                return result;
 
        /*
@@ -4807,7 +4844,8 @@ exec_eval_expr(PLpgSQL_execstate *estate,
        /*
         * ... and get the column's datatype.
         */
-       *rettype = SPI_gettypeid(estate->eval_tuptable->tupdesc, 1);
+       *rettype = estate->eval_tuptable->tupdesc->attrs[0]->atttypid;
+       *rettypmod = estate->eval_tuptable->tupdesc->attrs[0]->atttypmod;
 
        /*
         * If there are no rows selected, the result is a NULL of that type.
@@ -5060,8 +5098,8 @@ loop_exit:
  * exec_eval_simple_expr -             Evaluate a simple expression returning
  *                                                             a Datum by directly calling ExecEvalExpr().
  *
- * If successful, store results into *result, *isNull, *rettype and return
- * TRUE.  If the expression cannot be handled by simple evaluation,
+ * If successful, store results into *result, *isNull, *rettype, *rettypmod
+ * and return TRUE.  If the expression cannot be handled by simple evaluation,
  * return FALSE.
  *
  * Because we only store one execution tree for a simple expression, we
@@ -5092,7 +5130,8 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
                                          PLpgSQL_expr *expr,
                                          Datum *result,
                                          bool *isNull,
-                                         Oid *rettype)
+                                         Oid *rettype,
+                                         int32 *rettypmod)
 {
        ExprContext *econtext = estate->eval_econtext;
        LocalTransactionId curlxid = MyProc->lxid;
@@ -5142,6 +5181,7 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
         * Pass back previously-determined result type.
         */
        *rettype = expr->expr_simple_type;
+       *rettypmod = expr->expr_simple_typmod;
 
        /*
         * Prepare the expression for execution, if it's not been done already in
@@ -5457,6 +5497,7 @@ exec_move_row(PLpgSQL_execstate *estate,
                        Datum           value;
                        bool            isnull;
                        Oid                     valtype;
+                       int32           valtypmod;
 
                        if (row->varnos[fnum] < 0)
                                continue;               /* skip dropped column in row struct */
@@ -5475,23 +5516,20 @@ exec_move_row(PLpgSQL_execstate *estate,
                                        value = (Datum) 0;
                                        isnull = true;
                                }
-                               valtype = SPI_gettypeid(tupdesc, anum + 1);
+                               valtype = tupdesc->attrs[anum]->atttypid;
+                               valtypmod = tupdesc->attrs[anum]->atttypmod;
                                anum++;
                        }
                        else
                        {
                                value = (Datum) 0;
                                isnull = true;
-
-                               /*
-                                * InvalidOid is OK because exec_assign_value doesn't care
-                                * about the type of a source NULL
-                                */
-                               valtype = InvalidOid;
+                               valtype = UNKNOWNOID;
+                               valtypmod = -1;
                        }
 
                        exec_assign_value(estate, (PLpgSQL_datum *) var,
-                                                         value, valtype, &isnull);
+                                                         value, isnull, valtype, valtypmod);
                }
 
                return;
@@ -5675,17 +5713,17 @@ convert_value_to_string(PLpgSQL_execstate *estate, Datum value, Oid valtype)
  */
 static Datum
 exec_cast_value(PLpgSQL_execstate *estate,
-                               Datum value, Oid valtype,
-                               Oid reqtype,
+                               Datum value, bool isnull,
+                               Oid valtype, int32 valtypmod,
+                               Oid reqtype, int32 reqtypmod,
                                FmgrInfo *reqinput,
-                               Oid reqtypioparam,
-                               int32 reqtypmod,
-                               bool isnull)
+                               Oid reqtypioparam)
 {
        /*
         * If the type of the given value isn't what's requested, convert it.
         */
-       if (valtype != reqtype || reqtypmod != -1)
+       if (valtype != reqtype ||
+               (valtypmod != reqtypmod && reqtypmod != -1))
        {
                MemoryContext oldcontext;
 
@@ -5719,11 +5757,12 @@ exec_cast_value(PLpgSQL_execstate *estate,
  */
 static Datum
 exec_simple_cast_value(PLpgSQL_execstate *estate,
-                                          Datum value, Oid valtype,
-                                          Oid reqtype, int32 reqtypmod,
-                                          bool isnull)
+                                          Datum value, bool isnull,
+                                          Oid valtype, int32 valtypmod,
+                                          Oid reqtype, int32 reqtypmod)
 {
-       if (valtype != reqtype || reqtypmod != -1)
+       if (valtype != reqtype ||
+               (valtypmod != reqtypmod && reqtypmod != -1))
        {
                Oid                     typinput;
                Oid                     typioparam;
@@ -5735,12 +5774,13 @@ exec_simple_cast_value(PLpgSQL_execstate *estate,
 
                value = exec_cast_value(estate,
                                                                value,
+                                                               isnull,
                                                                valtype,
+                                                               valtypmod,
                                                                reqtype,
-                                                               &finfo_input,
-                                                               typioparam,
                                                                reqtypmod,
-                                                               isnull);
+                                                               &finfo_input,
+                                                               typioparam);
        }
 
        return value;
@@ -6171,6 +6211,7 @@ exec_simple_recheck_plan(PLpgSQL_expr *expr, CachedPlan *cplan)
        expr->expr_simple_lxid = InvalidLocalTransactionId;
        /* Also stash away the expression result type */
        expr->expr_simple_type = exprType((Node *) tle->expr);
+       expr->expr_simple_typmod = exprTypmod((Node *) tle->expr);
 }
 
 /* ----------
@@ -6371,10 +6412,12 @@ exec_eval_using_params(PLpgSQL_execstate *estate, List *params)
        {
                PLpgSQL_expr *param = (PLpgSQL_expr *) lfirst(lc);
                bool            isnull;
+               int32           ppdtypmod;
 
                ppd->values[i] = exec_eval_expr(estate, param,
                                                                                &isnull,
-                                                                               &ppd->types[i]);
+                                                                               &ppd->types[i],
+                                                                               &ppdtypmod);
                ppd->nulls[i] = isnull ? 'n' : ' ';
                ppd->freevals[i] = false;
 
@@ -6452,13 +6495,14 @@ exec_dynquery_with_params(PLpgSQL_execstate *estate,
        Datum           query;
        bool            isnull;
        Oid                     restype;
+       int32           restypmod;
        char       *querystr;
 
        /*
         * Evaluate the string expression after the EXECUTE keyword. Its result is
         * the querystring we have to execute.
         */
-       query = exec_eval_expr(estate, dynquery, &isnull, &restype);
+       query = exec_eval_expr(estate, dynquery, &isnull, &restype, &restypmod);
        if (isnull)
                ereport(ERROR,
                                (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
index 337b98980af965c3d80f555d27fd1b1341a2fef6..624c91e468b8db2b9092ef6e8d1525036733d808 100644 (file)
@@ -226,6 +226,7 @@ typedef struct PLpgSQL_expr
        Expr       *expr_simple_expr;           /* NULL means not a simple expr */
        int                     expr_simple_generation; /* plancache generation we checked */
        Oid                     expr_simple_type;               /* result type Oid, if simple */
+       int32           expr_simple_typmod;             /* result typmod, if simple */
 
        /*
         * if expr is simple AND prepared in current transaction,