LCOV - code coverage report
Current view: top level - src/pl/plpython - plpy_typeio.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 97.1 % 548 532
Test Date: 2026-03-14 12:15:02 Functions: 100.0 % 36 36
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*
       2              :  * transforming Datums to Python objects and vice versa
       3              :  *
       4              :  * src/pl/plpython/plpy_typeio.c
       5              :  */
       6              : 
       7              : #include "postgres.h"
       8              : 
       9              : #include "access/htup_details.h"
      10              : #include "catalog/pg_type.h"
      11              : #include "funcapi.h"
      12              : #include "mb/pg_wchar.h"
      13              : #include "miscadmin.h"
      14              : #include "plpy_elog.h"
      15              : #include "plpy_main.h"
      16              : #include "plpy_typeio.h"
      17              : #include "plpy_util.h"
      18              : #include "utils/array.h"
      19              : #include "utils/builtins.h"
      20              : #include "utils/fmgroids.h"
      21              : #include "utils/lsyscache.h"
      22              : #include "utils/memutils.h"
      23              : 
      24              : /* conversion from Datums to Python objects */
      25              : static PyObject *PLyBool_FromBool(PLyDatumToOb *arg, Datum d);
      26              : static PyObject *PLyFloat_FromFloat4(PLyDatumToOb *arg, Datum d);
      27              : static PyObject *PLyFloat_FromFloat8(PLyDatumToOb *arg, Datum d);
      28              : static PyObject *PLyDecimal_FromNumeric(PLyDatumToOb *arg, Datum d);
      29              : static PyObject *PLyLong_FromInt16(PLyDatumToOb *arg, Datum d);
      30              : static PyObject *PLyLong_FromInt32(PLyDatumToOb *arg, Datum d);
      31              : static PyObject *PLyLong_FromInt64(PLyDatumToOb *arg, Datum d);
      32              : static PyObject *PLyLong_FromOid(PLyDatumToOb *arg, Datum d);
      33              : static PyObject *PLyBytes_FromBytea(PLyDatumToOb *arg, Datum d);
      34              : static PyObject *PLyUnicode_FromScalar(PLyDatumToOb *arg, Datum d);
      35              : static PyObject *PLyObject_FromTransform(PLyDatumToOb *arg, Datum d);
      36              : static PyObject *PLyList_FromArray(PLyDatumToOb *arg, Datum d);
      37              : static PyObject *PLyList_FromArray_recurse(PLyDatumToOb *elm, int *dims, int ndim, int dim,
      38              :                                            char **dataptr_p, bits8 **bitmap_p, int *bitmask_p);
      39              : static PyObject *PLyDict_FromComposite(PLyDatumToOb *arg, Datum d);
      40              : static PyObject *PLyDict_FromTuple(PLyDatumToOb *arg, HeapTuple tuple, TupleDesc desc, bool include_generated);
      41              : 
      42              : /* conversion from Python objects to Datums */
      43              : static Datum PLyObject_ToBool(PLyObToDatum *arg, PyObject *plrv,
      44              :                               bool *isnull, bool inarray);
      45              : static Datum PLyObject_ToBytea(PLyObToDatum *arg, PyObject *plrv,
      46              :                                bool *isnull, bool inarray);
      47              : static Datum PLyObject_ToComposite(PLyObToDatum *arg, PyObject *plrv,
      48              :                                    bool *isnull, bool inarray);
      49              : static Datum PLyObject_ToScalar(PLyObToDatum *arg, PyObject *plrv,
      50              :                                 bool *isnull, bool inarray);
      51              : static Datum PLyObject_ToDomain(PLyObToDatum *arg, PyObject *plrv,
      52              :                                 bool *isnull, bool inarray);
      53              : static Datum PLyObject_ToTransform(PLyObToDatum *arg, PyObject *plrv,
      54              :                                    bool *isnull, bool inarray);
      55              : static Datum PLySequence_ToArray(PLyObToDatum *arg, PyObject *plrv,
      56              :                                  bool *isnull, bool inarray);
      57              : static void PLySequence_ToArray_recurse(PyObject *obj,
      58              :                                         ArrayBuildState **astatep,
      59              :                                         int *ndims, int *dims, int cur_depth,
      60              :                                         PLyObToDatum *elm, Oid elmbasetype);
      61              : 
      62              : /* conversion from Python objects to composite Datums */
      63              : static Datum PLyUnicode_ToComposite(PLyObToDatum *arg, PyObject *string, bool inarray);
      64              : static Datum PLyMapping_ToComposite(PLyObToDatum *arg, TupleDesc desc, PyObject *mapping);
      65              : static Datum PLySequence_ToComposite(PLyObToDatum *arg, TupleDesc desc, PyObject *sequence);
      66              : static Datum PLyGenericObject_ToComposite(PLyObToDatum *arg, TupleDesc desc, PyObject *object, bool inarray);
      67              : 
      68              : 
      69              : /*
      70              :  * Conversion functions.  Remember output from Python is input to
      71              :  * PostgreSQL, and vice versa.
      72              :  */
      73              : 
      74              : /*
      75              :  * Perform input conversion, given correctly-set-up state information.
      76              :  *
      77              :  * This is the outer-level entry point for any input conversion.  Internally,
      78              :  * the conversion functions recurse directly to each other.
      79              :  */
      80              : PyObject *
      81          594 : PLy_input_convert(PLyDatumToOb *arg, Datum val)
      82              : {
      83              :     PyObject   *result;
      84          594 :     PLyExecutionContext *exec_ctx = PLy_current_execution_context();
      85          594 :     MemoryContext scratch_context = PLy_get_scratch_context(exec_ctx);
      86              :     MemoryContext oldcontext;
      87              : 
      88              :     /*
      89              :      * Do the work in the scratch context to avoid leaking memory from the
      90              :      * datatype output function calls.  (The individual PLyDatumToObFunc
      91              :      * functions can't reset the scratch context, because they recurse and an
      92              :      * inner one might clobber data an outer one still needs.  So we do it
      93              :      * once at the outermost recursion level.)
      94              :      *
      95              :      * We reset the scratch context before, not after, each conversion cycle.
      96              :      * This way we aren't on the hook to release a Python refcount on the
      97              :      * result object in case MemoryContextReset throws an error.
      98              :      */
      99          594 :     MemoryContextReset(scratch_context);
     100              : 
     101          594 :     oldcontext = MemoryContextSwitchTo(scratch_context);
     102              : 
     103          594 :     result = arg->func(arg, val);
     104              : 
     105          594 :     MemoryContextSwitchTo(oldcontext);
     106              : 
     107          594 :     return result;
     108              : }
     109              : 
     110              : /*
     111              :  * Perform output conversion, given correctly-set-up state information.
     112              :  *
     113              :  * This is the outer-level entry point for any output conversion.  Internally,
     114              :  * the conversion functions recurse directly to each other.
     115              :  *
     116              :  * The result, as well as any cruft generated along the way, are in the
     117              :  * current memory context.  Caller is responsible for cleanup.
     118              :  */
     119              : Datum
     120          572 : PLy_output_convert(PLyObToDatum *arg, PyObject *val, bool *isnull)
     121              : {
     122              :     /* at outer level, we are not considering an array element */
     123          572 :     return arg->func(arg, val, isnull, false);
     124              : }
     125              : 
     126              : /*
     127              :  * Transform a tuple into a Python dict object.
     128              :  *
     129              :  * Note: the tupdesc must match the one used to set up *arg.  We could
     130              :  * insist that this function lookup the tupdesc from what is in *arg,
     131              :  * but in practice all callers have the right tupdesc available.
     132              :  */
     133              : PyObject *
     134          199 : PLy_input_from_tuple(PLyDatumToOb *arg, HeapTuple tuple, TupleDesc desc, bool include_generated)
     135              : {
     136              :     PyObject   *dict;
     137          199 :     PLyExecutionContext *exec_ctx = PLy_current_execution_context();
     138          199 :     MemoryContext scratch_context = PLy_get_scratch_context(exec_ctx);
     139              :     MemoryContext oldcontext;
     140              : 
     141              :     /*
     142              :      * As in PLy_input_convert, do the work in the scratch context.
     143              :      */
     144          199 :     MemoryContextReset(scratch_context);
     145              : 
     146          199 :     oldcontext = MemoryContextSwitchTo(scratch_context);
     147              : 
     148          199 :     dict = PLyDict_FromTuple(arg, tuple, desc, include_generated);
     149              : 
     150          199 :     MemoryContextSwitchTo(oldcontext);
     151              : 
     152          199 :     return dict;
     153              : }
     154              : 
     155              : /*
     156              :  * Initialize, or re-initialize, per-column input info for a composite type.
     157              :  *
     158              :  * This is separate from PLy_input_setup_func() because in cases involving
     159              :  * anonymous record types, we need to be passed the tupdesc explicitly.
     160              :  * It's caller's responsibility that the tupdesc has adequate lifespan
     161              :  * in such cases.  If the tupdesc is for a named composite or registered
     162              :  * record type, it does not need to be long-lived.
     163              :  */
     164              : void
     165          189 : PLy_input_setup_tuple(PLyDatumToOb *arg, TupleDesc desc, PLyProcedure *proc)
     166              : {
     167              :     int         i;
     168              : 
     169              :     /* We should be working on a previously-set-up struct */
     170              :     Assert(arg->func == PLyDict_FromComposite);
     171              : 
     172              :     /* Save pointer to tupdesc, but only if this is an anonymous record type */
     173          189 :     if (arg->typoid == RECORDOID && arg->typmod < 0)
     174           91 :         arg->tuple.recdesc = desc;
     175              : 
     176              :     /* (Re)allocate atts array as needed */
     177          189 :     if (arg->tuple.natts != desc->natts)
     178              :     {
     179          109 :         if (arg->tuple.atts)
     180            1 :             pfree(arg->tuple.atts);
     181          109 :         arg->tuple.natts = desc->natts;
     182          109 :         arg->tuple.atts = (PLyDatumToOb *)
     183          109 :             MemoryContextAllocZero(arg->mcxt,
     184          109 :                                    desc->natts * sizeof(PLyDatumToOb));
     185              :     }
     186              : 
     187              :     /* Fill the atts entries, except for dropped columns */
     188          607 :     for (i = 0; i < desc->natts; i++)
     189              :     {
     190          418 :         Form_pg_attribute attr = TupleDescAttr(desc, i);
     191          418 :         PLyDatumToOb *att = &arg->tuple.atts[i];
     192              : 
     193          418 :         if (attr->attisdropped)
     194            3 :             continue;
     195              : 
     196          415 :         if (att->typoid == attr->atttypid && att->typmod == attr->atttypmod)
     197          232 :             continue;           /* already set up this entry */
     198              : 
     199          183 :         PLy_input_setup_func(att, arg->mcxt,
     200              :                              attr->atttypid, attr->atttypmod,
     201              :                              proc);
     202              :     }
     203          189 : }
     204              : 
     205              : /*
     206              :  * Initialize, or re-initialize, per-column output info for a composite type.
     207              :  *
     208              :  * This is separate from PLy_output_setup_func() because in cases involving
     209              :  * anonymous record types, we need to be passed the tupdesc explicitly.
     210              :  * It's caller's responsibility that the tupdesc has adequate lifespan
     211              :  * in such cases.  If the tupdesc is for a named composite or registered
     212              :  * record type, it does not need to be long-lived.
     213              :  */
     214              : void
     215          214 : PLy_output_setup_tuple(PLyObToDatum *arg, TupleDesc desc, PLyProcedure *proc)
     216              : {
     217              :     int         i;
     218              : 
     219              :     /* We should be working on a previously-set-up struct */
     220              :     Assert(arg->func == PLyObject_ToComposite);
     221              : 
     222              :     /* Save pointer to tupdesc, but only if this is an anonymous record type */
     223          214 :     if (arg->typoid == RECORDOID && arg->typmod < 0)
     224            0 :         arg->tuple.recdesc = desc;
     225              : 
     226              :     /* (Re)allocate atts array as needed */
     227          214 :     if (arg->tuple.natts != desc->natts)
     228              :     {
     229           74 :         if (arg->tuple.atts)
     230            3 :             pfree(arg->tuple.atts);
     231           74 :         arg->tuple.natts = desc->natts;
     232           74 :         arg->tuple.atts = (PLyObToDatum *)
     233           74 :             MemoryContextAllocZero(arg->mcxt,
     234           74 :                                    desc->natts * sizeof(PLyObToDatum));
     235              :     }
     236              : 
     237              :     /* Fill the atts entries, except for dropped columns */
     238          698 :     for (i = 0; i < desc->natts; i++)
     239              :     {
     240          484 :         Form_pg_attribute attr = TupleDescAttr(desc, i);
     241          484 :         PLyObToDatum *att = &arg->tuple.atts[i];
     242              : 
     243          484 :         if (attr->attisdropped)
     244           28 :             continue;
     245              : 
     246          456 :         if (att->typoid == attr->atttypid && att->typmod == attr->atttypmod)
     247          297 :             continue;           /* already set up this entry */
     248              : 
     249          159 :         PLy_output_setup_func(att, arg->mcxt,
     250              :                               attr->atttypid, attr->atttypmod,
     251              :                               proc);
     252              :     }
     253          214 : }
     254              : 
     255              : /*
     256              :  * Set up output info for a PL/Python function returning record.
     257              :  *
     258              :  * Note: the given tupdesc is not necessarily long-lived.
     259              :  */
     260              : void
     261          134 : PLy_output_setup_record(PLyObToDatum *arg, TupleDesc desc, PLyProcedure *proc)
     262              : {
     263              :     /* Makes no sense unless RECORD */
     264              :     Assert(arg->typoid == RECORDOID);
     265              :     Assert(desc->tdtypeid == RECORDOID);
     266              : 
     267              :     /*
     268              :      * Bless the record type if not already done.  We'd have to do this anyway
     269              :      * to return a tuple, so we might as well force the issue so we can use
     270              :      * the known-record-type code path.
     271              :      */
     272          134 :     BlessTupleDesc(desc);
     273              : 
     274              :     /*
     275              :      * Update arg->typmod, and clear the recdesc link if it's changed. The
     276              :      * next call of PLyObject_ToComposite will look up a long-lived tupdesc
     277              :      * for the record type.
     278              :      */
     279          134 :     arg->typmod = desc->tdtypmod;
     280          134 :     if (arg->tuple.recdesc &&
     281          116 :         arg->tuple.recdesc->tdtypmod != arg->typmod)
     282           10 :         arg->tuple.recdesc = NULL;
     283              : 
     284              :     /* Update derived data if necessary */
     285          134 :     PLy_output_setup_tuple(arg, desc, proc);
     286          134 : }
     287              : 
     288              : /*
     289              :  * Recursively initialize the PLyObToDatum structure(s) needed to construct
     290              :  * a SQL value of the specified typeOid/typmod from a Python value.
     291              :  * (But note that at this point we may have RECORDOID/-1, ie, an indeterminate
     292              :  * record type.)
     293              :  * proc is used to look up transform functions.
     294              :  */
     295              : void
     296          479 : PLy_output_setup_func(PLyObToDatum *arg, MemoryContext arg_mcxt,
     297              :                       Oid typeOid, int32 typmod,
     298              :                       PLyProcedure *proc)
     299              : {
     300              :     TypeCacheEntry *typentry;
     301              :     char        typtype;
     302              :     Oid         trfuncid;
     303              :     Oid         typinput;
     304              : 
     305              :     /* Since this is recursive, it could theoretically be driven to overflow */
     306          479 :     check_stack_depth();
     307              : 
     308          479 :     arg->typoid = typeOid;
     309          479 :     arg->typmod = typmod;
     310          479 :     arg->mcxt = arg_mcxt;
     311              : 
     312              :     /*
     313              :      * Fetch typcache entry for the target type, asking for whatever info
     314              :      * we'll need later.  RECORD is a special case: just treat it as composite
     315              :      * without bothering with the typcache entry.
     316              :      */
     317          479 :     if (typeOid != RECORDOID)
     318              :     {
     319          461 :         typentry = lookup_type_cache(typeOid, TYPECACHE_DOMAIN_BASE_INFO);
     320          461 :         typtype = typentry->typtype;
     321          461 :         arg->typbyval = typentry->typbyval;
     322          461 :         arg->typlen = typentry->typlen;
     323          461 :         arg->typalign = typentry->typalign;
     324              :     }
     325              :     else
     326              :     {
     327           18 :         typentry = NULL;
     328           18 :         typtype = TYPTYPE_COMPOSITE;
     329              :         /* hard-wired knowledge about type RECORD: */
     330           18 :         arg->typbyval = false;
     331           18 :         arg->typlen = -1;
     332           18 :         arg->typalign = TYPALIGN_DOUBLE;
     333              :     }
     334              : 
     335              :     /*
     336              :      * Choose conversion method.  Note that transform functions are checked
     337              :      * for composite and scalar types, but not for arrays or domains.  This is
     338              :      * somewhat historical, but we'd have a problem allowing them on domains,
     339              :      * since we drill down through all levels of a domain nest without looking
     340              :      * at the intermediate levels at all.
     341              :      */
     342          479 :     if (typtype == TYPTYPE_DOMAIN)
     343              :     {
     344              :         /* Domain */
     345           14 :         arg->func = PLyObject_ToDomain;
     346           14 :         arg->domain.domain_info = NULL;
     347              :         /* Recursively set up conversion info for the element type */
     348           14 :         arg->domain.base = (PLyObToDatum *)
     349           14 :             MemoryContextAllocZero(arg_mcxt, sizeof(PLyObToDatum));
     350           14 :         PLy_output_setup_func(arg->domain.base, arg_mcxt,
     351              :                               typentry->domainBaseType,
     352              :                               typentry->domainBaseTypmod,
     353              :                               proc);
     354              :     }
     355          465 :     else if (typentry &&
     356          447 :              IsTrueArrayType(typentry))
     357              :     {
     358              :         /* Standard array */
     359           39 :         arg->func = PLySequence_ToArray;
     360              :         /* Get base type OID to insert into constructed array */
     361              :         /* (note this might not be the same as the immediate child type) */
     362           39 :         arg->array.elmbasetype = getBaseType(typentry->typelem);
     363              :         /* Recursively set up conversion info for the element type */
     364           39 :         arg->array.elm = (PLyObToDatum *)
     365           39 :             MemoryContextAllocZero(arg_mcxt, sizeof(PLyObToDatum));
     366           39 :         PLy_output_setup_func(arg->array.elm, arg_mcxt,
     367              :                               typentry->typelem, typmod,
     368              :                               proc);
     369              :     }
     370          426 :     else if ((trfuncid = get_transform_tosql(typeOid,
     371              :                                              proc->langid,
     372              :                                              proc->trftypes)))
     373              :     {
     374           12 :         arg->func = PLyObject_ToTransform;
     375           12 :         fmgr_info_cxt(trfuncid, &arg->transform.typtransform, arg_mcxt);
     376              :     }
     377          414 :     else if (typtype == TYPTYPE_COMPOSITE)
     378              :     {
     379              :         /* Named composite type, or RECORD */
     380           73 :         arg->func = PLyObject_ToComposite;
     381              :         /* We'll set up the per-field data later */
     382           73 :         arg->tuple.recdesc = NULL;
     383           73 :         arg->tuple.typentry = typentry;
     384           73 :         arg->tuple.tupdescid = INVALID_TUPLEDESC_IDENTIFIER;
     385           73 :         arg->tuple.atts = NULL;
     386           73 :         arg->tuple.natts = 0;
     387              :         /* Mark this invalid till needed, too */
     388           73 :         arg->tuple.recinfunc.fn_oid = InvalidOid;
     389              :     }
     390              :     else
     391              :     {
     392              :         /* Scalar type, but we have a couple of special cases */
     393          341 :         switch (typeOid)
     394              :         {
     395           14 :             case BOOLOID:
     396           14 :                 arg->func = PLyObject_ToBool;
     397           14 :                 break;
     398            6 :             case BYTEAOID:
     399            6 :                 arg->func = PLyObject_ToBytea;
     400            6 :                 break;
     401          321 :             default:
     402          321 :                 arg->func = PLyObject_ToScalar;
     403          321 :                 getTypeInputInfo(typeOid, &typinput, &arg->scalar.typioparam);
     404          321 :                 fmgr_info_cxt(typinput, &arg->scalar.typfunc, arg_mcxt);
     405          321 :                 break;
     406              :         }
     407              :     }
     408          479 : }
     409              : 
     410              : /*
     411              :  * Recursively initialize the PLyDatumToOb structure(s) needed to construct
     412              :  * a Python value from a SQL value of the specified typeOid/typmod.
     413              :  * (But note that at this point we may have RECORDOID/-1, ie, an indeterminate
     414              :  * record type.)
     415              :  * proc is used to look up transform functions.
     416              :  */
     417              : void
     418          467 : PLy_input_setup_func(PLyDatumToOb *arg, MemoryContext arg_mcxt,
     419              :                      Oid typeOid, int32 typmod,
     420              :                      PLyProcedure *proc)
     421              : {
     422              :     TypeCacheEntry *typentry;
     423              :     char        typtype;
     424              :     Oid         trfuncid;
     425              :     Oid         typoutput;
     426              :     bool        typisvarlena;
     427              : 
     428              :     /* Since this is recursive, it could theoretically be driven to overflow */
     429          467 :     check_stack_depth();
     430              : 
     431          467 :     arg->typoid = typeOid;
     432          467 :     arg->typmod = typmod;
     433          467 :     arg->mcxt = arg_mcxt;
     434              : 
     435              :     /*
     436              :      * Fetch typcache entry for the target type, asking for whatever info
     437              :      * we'll need later.  RECORD is a special case: just treat it as composite
     438              :      * without bothering with the typcache entry.
     439              :      */
     440          467 :     if (typeOid != RECORDOID)
     441              :     {
     442          389 :         typentry = lookup_type_cache(typeOid, TYPECACHE_DOMAIN_BASE_INFO);
     443          389 :         typtype = typentry->typtype;
     444          389 :         arg->typbyval = typentry->typbyval;
     445          389 :         arg->typlen = typentry->typlen;
     446          389 :         arg->typalign = typentry->typalign;
     447              :     }
     448              :     else
     449              :     {
     450           78 :         typentry = NULL;
     451           78 :         typtype = TYPTYPE_COMPOSITE;
     452              :         /* hard-wired knowledge about type RECORD: */
     453           78 :         arg->typbyval = false;
     454           78 :         arg->typlen = -1;
     455           78 :         arg->typalign = TYPALIGN_DOUBLE;
     456              :     }
     457              : 
     458              :     /*
     459              :      * Choose conversion method.  Note that transform functions are checked
     460              :      * for composite and scalar types, but not for arrays or domains.  This is
     461              :      * somewhat historical, but we'd have a problem allowing them on domains,
     462              :      * since we drill down through all levels of a domain nest without looking
     463              :      * at the intermediate levels at all.
     464              :      */
     465          467 :     if (typtype == TYPTYPE_DOMAIN)
     466              :     {
     467              :         /* Domain --- we don't care, just recurse down to the base type */
     468            9 :         PLy_input_setup_func(arg, arg_mcxt,
     469              :                              typentry->domainBaseType,
     470              :                              typentry->domainBaseTypmod,
     471              :                              proc);
     472              :     }
     473          458 :     else if (typentry &&
     474          380 :              IsTrueArrayType(typentry))
     475              :     {
     476              :         /* Standard array */
     477           13 :         arg->func = PLyList_FromArray;
     478              :         /* Recursively set up conversion info for the element type */
     479           13 :         arg->array.elm = (PLyDatumToOb *)
     480           13 :             MemoryContextAllocZero(arg_mcxt, sizeof(PLyDatumToOb));
     481           13 :         PLy_input_setup_func(arg->array.elm, arg_mcxt,
     482              :                              typentry->typelem, typmod,
     483              :                              proc);
     484              :     }
     485          445 :     else if ((trfuncid = get_transform_fromsql(typeOid,
     486              :                                                proc->langid,
     487              :                                                proc->trftypes)))
     488              :     {
     489           16 :         arg->func = PLyObject_FromTransform;
     490           16 :         fmgr_info_cxt(trfuncid, &arg->transform.typtransform, arg_mcxt);
     491              :     }
     492          429 :     else if (typtype == TYPTYPE_COMPOSITE)
     493              :     {
     494              :         /* Named composite type, or RECORD */
     495          119 :         arg->func = PLyDict_FromComposite;
     496              :         /* We'll set up the per-field data later */
     497          119 :         arg->tuple.recdesc = NULL;
     498          119 :         arg->tuple.typentry = typentry;
     499          119 :         arg->tuple.tupdescid = INVALID_TUPLEDESC_IDENTIFIER;
     500          119 :         arg->tuple.atts = NULL;
     501          119 :         arg->tuple.natts = 0;
     502              :     }
     503              :     else
     504              :     {
     505              :         /* Scalar type, but we have a couple of special cases */
     506          310 :         switch (typeOid)
     507              :         {
     508           14 :             case BOOLOID:
     509           14 :                 arg->func = PLyBool_FromBool;
     510           14 :                 break;
     511            1 :             case FLOAT4OID:
     512            1 :                 arg->func = PLyFloat_FromFloat4;
     513            1 :                 break;
     514            1 :             case FLOAT8OID:
     515            1 :                 arg->func = PLyFloat_FromFloat8;
     516            1 :                 break;
     517            1 :             case NUMERICOID:
     518            1 :                 arg->func = PLyDecimal_FromNumeric;
     519            1 :                 break;
     520            4 :             case INT2OID:
     521            4 :                 arg->func = PLyLong_FromInt16;
     522            4 :                 break;
     523          147 :             case INT4OID:
     524          147 :                 arg->func = PLyLong_FromInt32;
     525          147 :                 break;
     526            5 :             case INT8OID:
     527            5 :                 arg->func = PLyLong_FromInt64;
     528            5 :                 break;
     529            1 :             case OIDOID:
     530            1 :                 arg->func = PLyLong_FromOid;
     531            1 :                 break;
     532            7 :             case BYTEAOID:
     533            7 :                 arg->func = PLyBytes_FromBytea;
     534            7 :                 break;
     535          129 :             default:
     536          129 :                 arg->func = PLyUnicode_FromScalar;
     537          129 :                 getTypeOutputInfo(typeOid, &typoutput, &typisvarlena);
     538          129 :                 fmgr_info_cxt(typoutput, &arg->scalar.typfunc, arg_mcxt);
     539          129 :                 break;
     540              :         }
     541              :     }
     542          467 : }
     543              : 
     544              : 
     545              : /*
     546              :  * Special-purpose input converters.
     547              :  */
     548              : 
     549              : static PyObject *
     550          121 : PLyBool_FromBool(PLyDatumToOb *arg, Datum d)
     551              : {
     552          121 :     if (DatumGetBool(d))
     553           26 :         Py_RETURN_TRUE;
     554           95 :     Py_RETURN_FALSE;
     555              : }
     556              : 
     557              : static PyObject *
     558            3 : PLyFloat_FromFloat4(PLyDatumToOb *arg, Datum d)
     559              : {
     560            3 :     return PyFloat_FromDouble(DatumGetFloat4(d));
     561              : }
     562              : 
     563              : static PyObject *
     564            4 : PLyFloat_FromFloat8(PLyDatumToOb *arg, Datum d)
     565              : {
     566            4 :     return PyFloat_FromDouble(DatumGetFloat8(d));
     567              : }
     568              : 
     569              : static PyObject *
     570            7 : PLyDecimal_FromNumeric(PLyDatumToOb *arg, Datum d)
     571              : {
     572              :     static PyObject *decimal_constructor;
     573              :     char       *str;
     574              :     PyObject   *pyvalue;
     575              : 
     576              :     /* Try to import cdecimal.  If it doesn't exist, fall back to decimal. */
     577            7 :     if (!decimal_constructor)
     578              :     {
     579              :         PyObject   *decimal_module;
     580              : 
     581            1 :         decimal_module = PyImport_ImportModule("cdecimal");
     582            1 :         if (!decimal_module)
     583              :         {
     584            1 :             PyErr_Clear();
     585            1 :             decimal_module = PyImport_ImportModule("decimal");
     586              :         }
     587            1 :         if (!decimal_module)
     588            0 :             PLy_elog(ERROR, "could not import a module for Decimal constructor");
     589              : 
     590            1 :         decimal_constructor = PyObject_GetAttrString(decimal_module, "Decimal");
     591            1 :         if (!decimal_constructor)
     592            0 :             PLy_elog(ERROR, "no Decimal attribute in module");
     593              :     }
     594              : 
     595            7 :     str = DatumGetCString(DirectFunctionCall1(numeric_out, d));
     596            7 :     pyvalue = PyObject_CallFunction(decimal_constructor, "s", str);
     597            7 :     if (!pyvalue)
     598            0 :         PLy_elog(ERROR, "conversion from numeric to Decimal failed");
     599              : 
     600            7 :     return pyvalue;
     601              : }
     602              : 
     603              : static PyObject *
     604            7 : PLyLong_FromInt16(PLyDatumToOb *arg, Datum d)
     605              : {
     606            7 :     return PyLong_FromLong(DatumGetInt16(d));
     607              : }
     608              : 
     609              : static PyObject *
     610          409 : PLyLong_FromInt32(PLyDatumToOb *arg, Datum d)
     611              : {
     612          409 :     return PyLong_FromLong(DatumGetInt32(d));
     613              : }
     614              : 
     615              : static PyObject *
     616           15 : PLyLong_FromInt64(PLyDatumToOb *arg, Datum d)
     617              : {
     618           15 :     return PyLong_FromLongLong(DatumGetInt64(d));
     619              : }
     620              : 
     621              : static PyObject *
     622            2 : PLyLong_FromOid(PLyDatumToOb *arg, Datum d)
     623              : {
     624            2 :     return PyLong_FromUnsignedLong(DatumGetObjectId(d));
     625              : }
     626              : 
     627              : static PyObject *
     628           11 : PLyBytes_FromBytea(PLyDatumToOb *arg, Datum d)
     629              : {
     630           11 :     text       *txt = DatumGetByteaPP(d);
     631           11 :     char       *str = VARDATA_ANY(txt);
     632           11 :     size_t      size = VARSIZE_ANY_EXHDR(txt);
     633              : 
     634           11 :     return PyBytes_FromStringAndSize(str, size);
     635              : }
     636              : 
     637              : 
     638              : /*
     639              :  * Generic input conversion using a SQL type's output function.
     640              :  */
     641              : static PyObject *
     642          487 : PLyUnicode_FromScalar(PLyDatumToOb *arg, Datum d)
     643              : {
     644          487 :     char       *x = OutputFunctionCall(&arg->scalar.typfunc, d);
     645          487 :     PyObject   *r = PLyUnicode_FromString(x);
     646              : 
     647          487 :     pfree(x);
     648          487 :     return r;
     649              : }
     650              : 
     651              : /*
     652              :  * Convert using a from-SQL transform function.
     653              :  */
     654              : static PyObject *
     655           35 : PLyObject_FromTransform(PLyDatumToOb *arg, Datum d)
     656              : {
     657              :     Datum       t;
     658              : 
     659           35 :     t = FunctionCall1(&arg->transform.typtransform, d);
     660           35 :     return (PyObject *) DatumGetPointer(t);
     661              : }
     662              : 
     663              : /*
     664              :  * Convert a SQL array to a Python list.
     665              :  */
     666              : static PyObject *
     667           21 : PLyList_FromArray(PLyDatumToOb *arg, Datum d)
     668              : {
     669           21 :     ArrayType  *array = DatumGetArrayTypeP(d);
     670           21 :     PLyDatumToOb *elm = arg->array.elm;
     671              :     int         ndim;
     672              :     int        *dims;
     673              :     char       *dataptr;
     674              :     bits8      *bitmap;
     675              :     int         bitmask;
     676              : 
     677           21 :     if (ARR_NDIM(array) == 0)
     678            1 :         return PyList_New(0);
     679              : 
     680              :     /* Array dimensions and left bounds */
     681           20 :     ndim = ARR_NDIM(array);
     682           20 :     dims = ARR_DIMS(array);
     683              :     Assert(ndim <= MAXDIM);
     684              : 
     685              :     /*
     686              :      * We iterate the SQL array in the physical order it's stored in the
     687              :      * datum. For example, for a 3-dimensional array the order of iteration
     688              :      * would be the following: [0,0,0] elements through [0,0,k], then [0,1,0]
     689              :      * through [0,1,k] till [0,m,k], then [1,0,0] through [1,0,k] till
     690              :      * [1,m,k], and so on.
     691              :      *
     692              :      * In Python, there are no multi-dimensional lists as such, but they are
     693              :      * represented as a list of lists. So a 3-d array of [n,m,k] elements is a
     694              :      * list of n m-element arrays, each element of which is k-element array.
     695              :      * PLyList_FromArray_recurse() builds the Python list for a single
     696              :      * dimension, and recurses for the next inner dimension.
     697              :      */
     698           20 :     dataptr = ARR_DATA_PTR(array);
     699           20 :     bitmap = ARR_NULLBITMAP(array);
     700           20 :     bitmask = 1;
     701              : 
     702           20 :     return PLyList_FromArray_recurse(elm, dims, ndim, 0,
     703              :                                      &dataptr, &bitmap, &bitmask);
     704              : }
     705              : 
     706              : static PyObject *
     707           48 : PLyList_FromArray_recurse(PLyDatumToOb *elm, int *dims, int ndim, int dim,
     708              :                           char **dataptr_p, bits8 **bitmap_p, int *bitmask_p)
     709              : {
     710              :     int         i;
     711              :     PyObject   *list;
     712              : 
     713           48 :     list = PyList_New(dims[dim]);
     714           48 :     if (!list)
     715            0 :         return NULL;
     716              : 
     717           48 :     if (dim < ndim - 1)
     718              :     {
     719              :         /* Outer dimension. Recurse for each inner slice. */
     720           42 :         for (i = 0; i < dims[dim]; i++)
     721              :         {
     722              :             PyObject   *sublist;
     723              : 
     724           28 :             sublist = PLyList_FromArray_recurse(elm, dims, ndim, dim + 1,
     725              :                                                 dataptr_p, bitmap_p, bitmask_p);
     726           28 :             PyList_SetItem(list, i, sublist);
     727              :         }
     728              :     }
     729              :     else
     730              :     {
     731              :         /*
     732              :          * Innermost dimension. Fill the list with the values from the array
     733              :          * for this slice.
     734              :          */
     735           34 :         char       *dataptr = *dataptr_p;
     736           34 :         bits8      *bitmap = *bitmap_p;
     737           34 :         int         bitmask = *bitmask_p;
     738           34 :         uint8       typalignby = typalign_to_alignby(elm->typalign);
     739              : 
     740          120 :         for (i = 0; i < dims[dim]; i++)
     741              :         {
     742              :             /* checking for NULL */
     743           86 :             if (bitmap && (*bitmap & bitmask) == 0)
     744              :             {
     745              :                 Py_INCREF(Py_None);
     746           14 :                 PyList_SetItem(list, i, Py_None);
     747              :             }
     748              :             else
     749              :             {
     750              :                 Datum       itemvalue;
     751              : 
     752           72 :                 itemvalue = fetch_att(dataptr, elm->typbyval, elm->typlen);
     753           72 :                 PyList_SetItem(list, i, elm->func(elm, itemvalue));
     754           72 :                 dataptr = att_addlength_pointer(dataptr, elm->typlen, dataptr);
     755           72 :                 dataptr = (char *) att_nominal_alignby(dataptr, typalignby);
     756              :             }
     757              : 
     758              :             /* advance bitmap pointer if any */
     759           86 :             if (bitmap)
     760              :             {
     761           52 :                 bitmask <<= 1;
     762           52 :                 if (bitmask == 0x100 /* (1<<8) */ )
     763              :                 {
     764            4 :                     bitmap++;
     765            4 :                     bitmask = 1;
     766              :                 }
     767              :             }
     768              :         }
     769              : 
     770           34 :         *dataptr_p = dataptr;
     771           34 :         *bitmap_p = bitmap;
     772           34 :         *bitmask_p = bitmask;
     773              :     }
     774              : 
     775           48 :     return list;
     776              : }
     777              : 
     778              : /*
     779              :  * Convert a composite SQL value to a Python dict.
     780              :  */
     781              : static PyObject *
     782           49 : PLyDict_FromComposite(PLyDatumToOb *arg, Datum d)
     783              : {
     784              :     PyObject   *dict;
     785              :     HeapTupleHeader td;
     786              :     Oid         tupType;
     787              :     int32       tupTypmod;
     788              :     TupleDesc   tupdesc;
     789              :     HeapTupleData tmptup;
     790              : 
     791           49 :     td = DatumGetHeapTupleHeader(d);
     792              :     /* Extract rowtype info and find a tupdesc */
     793           49 :     tupType = HeapTupleHeaderGetTypeId(td);
     794           49 :     tupTypmod = HeapTupleHeaderGetTypMod(td);
     795           49 :     tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
     796              : 
     797              :     /* Set up I/O funcs if not done yet */
     798           49 :     PLy_input_setup_tuple(arg, tupdesc,
     799           49 :                           PLy_current_execution_context()->curr_proc);
     800              : 
     801              :     /* Build a temporary HeapTuple control structure */
     802           49 :     tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
     803           49 :     tmptup.t_data = td;
     804              : 
     805           49 :     dict = PLyDict_FromTuple(arg, &tmptup, tupdesc, true);
     806              : 
     807           49 :     ReleaseTupleDesc(tupdesc);
     808              : 
     809           49 :     return dict;
     810              : }
     811              : 
     812              : /*
     813              :  * Transform a tuple into a Python dict object.
     814              :  */
     815              : static PyObject *
     816          248 : PLyDict_FromTuple(PLyDatumToOb *arg, HeapTuple tuple, TupleDesc desc, bool include_generated)
     817              : {
     818              :     PyObject   *volatile dict;
     819              : 
     820              :     /* Simple sanity check that desc matches */
     821              :     Assert(desc->natts == arg->tuple.natts);
     822              : 
     823          248 :     dict = PyDict_New();
     824          248 :     if (dict == NULL)
     825            0 :         return NULL;
     826              : 
     827          248 :     PG_TRY();
     828              :     {
     829              :         int         i;
     830              : 
     831          781 :         for (i = 0; i < arg->tuple.natts; i++)
     832              :         {
     833          533 :             PLyDatumToOb *att = &arg->tuple.atts[i];
     834          533 :             Form_pg_attribute attr = TupleDescAttr(desc, i);
     835              :             char       *key;
     836              :             Datum       vattr;
     837              :             bool        is_null;
     838              :             PyObject   *value;
     839              : 
     840          533 :             if (attr->attisdropped)
     841           15 :                 continue;
     842              : 
     843          530 :             if (attr->attgenerated)
     844              :             {
     845              :                 /* don't include unless requested */
     846           18 :                 if (!include_generated)
     847            6 :                     continue;
     848              :                 /* never include virtual columns */
     849           12 :                 if (attr->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
     850            6 :                     continue;
     851              :             }
     852              : 
     853          518 :             key = NameStr(attr->attname);
     854          518 :             vattr = heap_getattr(tuple, (i + 1), desc, &is_null);
     855              : 
     856          518 :             if (is_null)
     857           13 :                 PyDict_SetItemString(dict, key, Py_None);
     858              :             else
     859              :             {
     860          505 :                 value = att->func(att, vattr);
     861          505 :                 PyDict_SetItemString(dict, key, value);
     862              :                 Py_DECREF(value);
     863              :             }
     864              :         }
     865              :     }
     866            0 :     PG_CATCH();
     867              :     {
     868            0 :         Py_DECREF(dict);
     869            0 :         PG_RE_THROW();
     870              :     }
     871          248 :     PG_END_TRY();
     872              : 
     873          248 :     return dict;
     874              : }
     875              : 
     876              : /*
     877              :  * Convert a Python object to a PostgreSQL bool datum.  This can't go
     878              :  * through the generic conversion function, because Python attaches a
     879              :  * Boolean value to everything, more things than the PostgreSQL bool
     880              :  * type can parse.
     881              :  */
     882              : static Datum
     883           23 : PLyObject_ToBool(PLyObToDatum *arg, PyObject *plrv,
     884              :                  bool *isnull, bool inarray)
     885              : {
     886           23 :     if (plrv == Py_None)
     887              :     {
     888            1 :         *isnull = true;
     889            1 :         return (Datum) 0;
     890              :     }
     891           22 :     *isnull = false;
     892           22 :     return BoolGetDatum(PyObject_IsTrue(plrv));
     893              : }
     894              : 
     895              : /*
     896              :  * Convert a Python object to a PostgreSQL bytea datum.  This doesn't
     897              :  * go through the generic conversion function to circumvent problems
     898              :  * with embedded nulls.  And it's faster this way.
     899              :  */
     900              : static Datum
     901           11 : PLyObject_ToBytea(PLyObToDatum *arg, PyObject *plrv,
     902              :                   bool *isnull, bool inarray)
     903              : {
     904           11 :     PyObject   *volatile plrv_so = NULL;
     905           11 :     Datum       rv = (Datum) 0;
     906              : 
     907           11 :     if (plrv == Py_None)
     908              :     {
     909            3 :         *isnull = true;
     910            3 :         return (Datum) 0;
     911              :     }
     912            8 :     *isnull = false;
     913              : 
     914            8 :     plrv_so = PyObject_Bytes(plrv);
     915            8 :     if (!plrv_so)
     916            0 :         PLy_elog(ERROR, "could not create bytes representation of Python object");
     917              : 
     918            8 :     PG_TRY();
     919              :     {
     920            8 :         char       *plrv_sc = PyBytes_AsString(plrv_so);
     921            8 :         size_t      len = PyBytes_Size(plrv_so);
     922            8 :         size_t      size = len + VARHDRSZ;
     923            8 :         bytea      *result = palloc(size);
     924              : 
     925            8 :         SET_VARSIZE(result, size);
     926            8 :         memcpy(VARDATA(result), plrv_sc, len);
     927            8 :         rv = PointerGetDatum(result);
     928              :     }
     929            0 :     PG_FINALLY();
     930              :     {
     931            8 :         Py_XDECREF(plrv_so);
     932              :     }
     933            8 :     PG_END_TRY();
     934              : 
     935            8 :     return rv;
     936              : }
     937              : 
     938              : 
     939              : /*
     940              :  * Convert a Python object to a composite type. First look up the type's
     941              :  * description, then route the Python object through the conversion function
     942              :  * for obtaining PostgreSQL tuples.
     943              :  */
     944              : static Datum
     945          291 : PLyObject_ToComposite(PLyObToDatum *arg, PyObject *plrv,
     946              :                       bool *isnull, bool inarray)
     947              : {
     948              :     Datum       rv;
     949              :     TupleDesc   desc;
     950              : 
     951          291 :     if (plrv == Py_None)
     952              :     {
     953           21 :         *isnull = true;
     954           21 :         return (Datum) 0;
     955              :     }
     956          270 :     *isnull = false;
     957              : 
     958              :     /*
     959              :      * The string conversion case doesn't require a tupdesc, nor per-field
     960              :      * conversion data, so just go for it if that's the case to use.
     961              :      */
     962          270 :     if (PyUnicode_Check(plrv))
     963           18 :         return PLyUnicode_ToComposite(arg, plrv, inarray);
     964              : 
     965              :     /*
     966              :      * If we're dealing with a named composite type, we must look up the
     967              :      * tupdesc every time, to protect against possible changes to the type.
     968              :      * RECORD types can't change between calls; but we must still be willing
     969              :      * to set up the info the first time, if nobody did yet.
     970              :      */
     971          252 :     if (arg->typoid != RECORDOID)
     972              :     {
     973          125 :         desc = lookup_rowtype_tupdesc(arg->typoid, arg->typmod);
     974              :         /* We should have the descriptor of the type's typcache entry */
     975              :         Assert(desc == arg->tuple.typentry->tupDesc);
     976              :         /* Detect change of descriptor, update cache if needed */
     977          125 :         if (arg->tuple.tupdescid != arg->tuple.typentry->tupDesc_identifier)
     978              :         {
     979           31 :             PLy_output_setup_tuple(arg, desc,
     980           31 :                                    PLy_current_execution_context()->curr_proc);
     981           31 :             arg->tuple.tupdescid = arg->tuple.typentry->tupDesc_identifier;
     982              :         }
     983              :     }
     984              :     else
     985              :     {
     986          127 :         desc = arg->tuple.recdesc;
     987          127 :         if (desc == NULL)
     988              :         {
     989           28 :             desc = lookup_rowtype_tupdesc(arg->typoid, arg->typmod);
     990           28 :             arg->tuple.recdesc = desc;
     991              :         }
     992              :         else
     993              :         {
     994              :             /* Pin descriptor to match unpin below */
     995           99 :             PinTupleDesc(desc);
     996              :         }
     997              :     }
     998              : 
     999              :     /* Simple sanity check on our caching */
    1000              :     Assert(desc->natts == arg->tuple.natts);
    1001              : 
    1002              :     /*
    1003              :      * Convert, using the appropriate method depending on the type of the
    1004              :      * supplied Python object.
    1005              :      */
    1006          252 :     if (PySequence_Check(plrv))
    1007              :         /* composite type as sequence (tuple, list etc) */
    1008          132 :         rv = PLySequence_ToComposite(arg, desc, plrv);
    1009          120 :     else if (PyMapping_Check(plrv))
    1010              :         /* composite type as mapping (currently only dict) */
    1011           95 :         rv = PLyMapping_ToComposite(arg, desc, plrv);
    1012              :     else
    1013              :         /* returned as smth, must provide method __getattr__(name) */
    1014           25 :         rv = PLyGenericObject_ToComposite(arg, desc, plrv, inarray);
    1015              : 
    1016          243 :     ReleaseTupleDesc(desc);
    1017              : 
    1018          243 :     return rv;
    1019              : }
    1020              : 
    1021              : 
    1022              : /*
    1023              :  * Convert Python object to C string in server encoding.
    1024              :  *
    1025              :  * Note: this is exported for use by add-on transform modules.
    1026              :  */
    1027              : char *
    1028         1584 : PLyObject_AsString(PyObject *plrv)
    1029              : {
    1030              :     PyObject   *plrv_bo;
    1031              :     char       *plrv_sc;
    1032              :     size_t      plen;
    1033              :     size_t      slen;
    1034              : 
    1035         1584 :     if (PyUnicode_Check(plrv))
    1036          341 :         plrv_bo = PLyUnicode_Bytes(plrv);
    1037         1243 :     else if (PyFloat_Check(plrv))
    1038              :     {
    1039              :         /* use repr() for floats, str() is lossy */
    1040            7 :         PyObject   *s = PyObject_Repr(plrv);
    1041              : 
    1042            7 :         plrv_bo = PLyUnicode_Bytes(s);
    1043            7 :         Py_XDECREF(s);
    1044              :     }
    1045              :     else
    1046              :     {
    1047         1236 :         PyObject   *s = PyObject_Str(plrv);
    1048              : 
    1049         1236 :         plrv_bo = PLyUnicode_Bytes(s);
    1050         1236 :         Py_XDECREF(s);
    1051              :     }
    1052         1584 :     if (!plrv_bo)
    1053            0 :         PLy_elog(ERROR, "could not create string representation of Python object");
    1054              : 
    1055         1584 :     plrv_sc = pstrdup(PyBytes_AsString(plrv_bo));
    1056         1584 :     plen = PyBytes_Size(plrv_bo);
    1057         1584 :     slen = strlen(plrv_sc);
    1058              : 
    1059         1584 :     Py_XDECREF(plrv_bo);
    1060              : 
    1061         1584 :     if (slen < plen)
    1062            0 :         ereport(ERROR,
    1063              :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    1064              :                  errmsg("could not convert Python object into cstring: Python string representation appears to contain null bytes")));
    1065         1584 :     else if (slen > plen)
    1066            0 :         elog(ERROR, "could not convert Python object into cstring: Python string longer than reported length");
    1067         1584 :     pg_verifymbstr(plrv_sc, slen, false);
    1068              : 
    1069         1584 :     return plrv_sc;
    1070              : }
    1071              : 
    1072              : 
    1073              : /*
    1074              :  * Generic output conversion function: convert PyObject to cstring and
    1075              :  * cstring into PostgreSQL type.
    1076              :  */
    1077              : static Datum
    1078         1603 : PLyObject_ToScalar(PLyObToDatum *arg, PyObject *plrv,
    1079              :                    bool *isnull, bool inarray)
    1080              : {
    1081              :     char       *str;
    1082              : 
    1083         1603 :     if (plrv == Py_None)
    1084              :     {
    1085           98 :         *isnull = true;
    1086           98 :         return (Datum) 0;
    1087              :     }
    1088         1505 :     *isnull = false;
    1089              : 
    1090         1505 :     str = PLyObject_AsString(plrv);
    1091              : 
    1092         1505 :     return InputFunctionCall(&arg->scalar.typfunc,
    1093              :                              str,
    1094              :                              arg->scalar.typioparam,
    1095              :                              arg->typmod);
    1096              : }
    1097              : 
    1098              : 
    1099              : /*
    1100              :  * Convert to a domain type.
    1101              :  */
    1102              : static Datum
    1103           29 : PLyObject_ToDomain(PLyObToDatum *arg, PyObject *plrv,
    1104              :                    bool *isnull, bool inarray)
    1105              : {
    1106              :     Datum       result;
    1107           29 :     PLyObToDatum *base = arg->domain.base;
    1108              : 
    1109           29 :     result = base->func(base, plrv, isnull, inarray);
    1110           27 :     domain_check(result, *isnull, arg->typoid,
    1111              :                  &arg->domain.domain_info, arg->mcxt);
    1112           16 :     return result;
    1113              : }
    1114              : 
    1115              : 
    1116              : /*
    1117              :  * Convert using a to-SQL transform function.
    1118              :  */
    1119              : static Datum
    1120           31 : PLyObject_ToTransform(PLyObToDatum *arg, PyObject *plrv,
    1121              :                       bool *isnull, bool inarray)
    1122              : {
    1123           31 :     if (plrv == Py_None)
    1124              :     {
    1125            1 :         *isnull = true;
    1126            1 :         return (Datum) 0;
    1127              :     }
    1128           30 :     *isnull = false;
    1129           30 :     return FunctionCall1(&arg->transform.typtransform, PointerGetDatum(plrv));
    1130              : }
    1131              : 
    1132              : 
    1133              : /*
    1134              :  * Convert Python sequence (or list of lists) to SQL array.
    1135              :  */
    1136              : static Datum
    1137           58 : PLySequence_ToArray(PLyObToDatum *arg, PyObject *plrv,
    1138              :                     bool *isnull, bool inarray)
    1139              : {
    1140           58 :     ArrayBuildState *astate = NULL;
    1141           58 :     int         ndims = 1;
    1142              :     int         dims[MAXDIM];
    1143              :     int         lbs[MAXDIM];
    1144              : 
    1145           58 :     if (plrv == Py_None)
    1146              :     {
    1147            2 :         *isnull = true;
    1148            2 :         return (Datum) 0;
    1149              :     }
    1150           56 :     *isnull = false;
    1151              : 
    1152              :     /*
    1153              :      * For historical reasons, we allow any sequence (not only a list) at the
    1154              :      * top level when converting a Python object to a SQL array.  However, a
    1155              :      * multi-dimensional array is recognized only when the object contains
    1156              :      * true lists.
    1157              :      */
    1158           56 :     if (!PySequence_Check(plrv))
    1159            3 :         ereport(ERROR,
    1160              :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    1161              :                  errmsg("return value of function with array return type is not a Python sequence")));
    1162              : 
    1163              :     /* Initialize dimensionality info with first-level dimension */
    1164           53 :     memset(dims, 0, sizeof(dims));
    1165           53 :     dims[0] = PySequence_Length(plrv);
    1166              : 
    1167              :     /*
    1168              :      * Traverse the Python lists, in depth-first order, and collect all the
    1169              :      * elements at the bottom level into an ArrayBuildState.
    1170              :      */
    1171           53 :     PLySequence_ToArray_recurse(plrv, &astate,
    1172              :                                 &ndims, dims, 1,
    1173              :                                 arg->array.elm,
    1174              :                                 arg->array.elmbasetype);
    1175              : 
    1176              :     /* ensure we get zero-D array for no inputs, as per PG convention */
    1177           39 :     if (astate == NULL)
    1178            2 :         return PointerGetDatum(construct_empty_array(arg->array.elmbasetype));
    1179              : 
    1180           98 :     for (int i = 0; i < ndims; i++)
    1181           61 :         lbs[i] = 1;
    1182              : 
    1183           37 :     return makeMdArrayResult(astate, ndims, dims, lbs,
    1184              :                              CurrentMemoryContext, true);
    1185              : }
    1186              : 
    1187              : /*
    1188              :  * Helper function for PLySequence_ToArray. Traverse a Python list of lists in
    1189              :  * depth-first order, storing the elements in *astatep.
    1190              :  *
    1191              :  * The ArrayBuildState is created only when we first find a scalar element;
    1192              :  * if we didn't do it like that, we'd need some other convention for knowing
    1193              :  * whether we'd already found any scalars (and thus the number of dimensions
    1194              :  * is frozen).
    1195              :  */
    1196              : static void
    1197          255 : PLySequence_ToArray_recurse(PyObject *obj, ArrayBuildState **astatep,
    1198              :                             int *ndims, int *dims, int cur_depth,
    1199              :                             PLyObToDatum *elm, Oid elmbasetype)
    1200              : {
    1201              :     int         i;
    1202          255 :     int         len = PySequence_Length(obj);
    1203              : 
    1204              :     /* We should not get here with a non-sequence object */
    1205          255 :     if (len < 0)
    1206            0 :         PLy_elog(ERROR, "could not determine sequence length for function return value");
    1207              : 
    1208         1358 :     for (i = 0; i < len; i++)
    1209              :     {
    1210              :         /* fetch the array element */
    1211         1126 :         PyObject   *subobj = PySequence_GetItem(obj, i);
    1212              : 
    1213              :         /* need PG_TRY to ensure we release the subobj's refcount */
    1214         1126 :         PG_TRY();
    1215              :         {
    1216              :             /* multi-dimensional array? */
    1217         1126 :             if (PyList_Check(subobj))
    1218              :             {
    1219              :                 /* set size when at first element in this level, else compare */
    1220          208 :                 if (i == 0 && *ndims == cur_depth)
    1221              :                 {
    1222              :                     /* array after some scalars at same level? */
    1223           40 :                     if (*astatep != NULL)
    1224            1 :                         ereport(ERROR,
    1225              :                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
    1226              :                                  errmsg("multidimensional arrays must have array expressions with matching dimensions")));
    1227              :                     /* too many dimensions? */
    1228           39 :                     if (cur_depth >= MAXDIM)
    1229            1 :                         ereport(ERROR,
    1230              :                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    1231              :                                  errmsg("number of array dimensions exceeds the maximum allowed (%d)",
    1232              :                                         MAXDIM)));
    1233              :                     /* OK, add a dimension */
    1234           38 :                     dims[*ndims] = PySequence_Length(subobj);
    1235           38 :                     (*ndims)++;
    1236              :                 }
    1237          168 :                 else if (cur_depth >= *ndims ||
    1238          166 :                          PySequence_Length(subobj) != dims[cur_depth])
    1239            4 :                     ereport(ERROR,
    1240              :                             (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
    1241              :                              errmsg("multidimensional arrays must have array expressions with matching dimensions")));
    1242              : 
    1243              :                 /* recurse to fetch elements of this sub-array */
    1244          202 :                 PLySequence_ToArray_recurse(subobj, astatep,
    1245              :                                             ndims, dims, cur_depth + 1,
    1246              :                                             elm, elmbasetype);
    1247              :             }
    1248              :             else
    1249              :             {
    1250              :                 Datum       dat;
    1251              :                 bool        isnull;
    1252              : 
    1253              :                 /* scalar after some sub-arrays at same level? */
    1254          918 :                 if (*ndims != cur_depth)
    1255            2 :                     ereport(ERROR,
    1256              :                             (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
    1257              :                              errmsg("multidimensional arrays must have array expressions with matching dimensions")));
    1258              : 
    1259              :                 /* convert non-list object to Datum */
    1260          916 :                 dat = elm->func(elm, subobj, &isnull, true);
    1261              : 
    1262              :                 /* create ArrayBuildState if we didn't already */
    1263          910 :                 if (*astatep == NULL)
    1264           43 :                     *astatep = initArrayResult(elmbasetype,
    1265              :                                                CurrentMemoryContext, true);
    1266              : 
    1267              :                 /* ... and save the element value in it */
    1268          910 :                 (void) accumArrayResult(*astatep, dat, isnull,
    1269              :                                         elmbasetype, CurrentMemoryContext);
    1270              :             }
    1271              :         }
    1272           23 :         PG_FINALLY();
    1273              :         {
    1274         1126 :             Py_XDECREF(subobj);
    1275              :         }
    1276         1126 :         PG_END_TRY();
    1277              :     }
    1278          232 : }
    1279              : 
    1280              : 
    1281              : /*
    1282              :  * Convert a Python string to composite, using record_in.
    1283              :  */
    1284              : static Datum
    1285           18 : PLyUnicode_ToComposite(PLyObToDatum *arg, PyObject *string, bool inarray)
    1286              : {
    1287              :     char       *str;
    1288              : 
    1289              :     /*
    1290              :      * Set up call data for record_in, if we didn't already.  (We can't just
    1291              :      * use DirectFunctionCall, because record_in needs a fn_extra field.)
    1292              :      */
    1293           18 :     if (!OidIsValid(arg->tuple.recinfunc.fn_oid))
    1294            5 :         fmgr_info_cxt(F_RECORD_IN, &arg->tuple.recinfunc, arg->mcxt);
    1295              : 
    1296           18 :     str = PLyObject_AsString(string);
    1297              : 
    1298              :     /*
    1299              :      * If we are parsing a composite type within an array, and the string
    1300              :      * isn't a valid record literal, there's a high chance that the function
    1301              :      * did something like:
    1302              :      *
    1303              :      * CREATE FUNCTION .. RETURNS comptype[] AS $$ return [['foo', 'bar']] $$
    1304              :      * LANGUAGE plpython;
    1305              :      *
    1306              :      * Before PostgreSQL 10, that was interpreted as a single-dimensional
    1307              :      * array, containing record ('foo', 'bar'). PostgreSQL 10 added support
    1308              :      * for multi-dimensional arrays, and it is now interpreted as a
    1309              :      * two-dimensional array, containing two records, 'foo', and 'bar'.
    1310              :      * record_in() will throw an error, because "foo" is not a valid record
    1311              :      * literal.
    1312              :      *
    1313              :      * To make that less confusing to users who are upgrading from older
    1314              :      * versions, try to give a hint in the typical instances of that. If we
    1315              :      * are parsing an array of composite types, and we see a string literal
    1316              :      * that is not a valid record literal, give a hint. We only want to give
    1317              :      * the hint in the narrow case of a malformed string literal, not any
    1318              :      * error from record_in(), so check for that case here specifically.
    1319              :      *
    1320              :      * This check better match the one in record_in(), so that we don't forbid
    1321              :      * literals that are actually valid!
    1322              :      */
    1323           18 :     if (inarray)
    1324              :     {
    1325            1 :         char       *ptr = str;
    1326              : 
    1327              :         /* Allow leading whitespace */
    1328            1 :         while (*ptr && isspace((unsigned char) *ptr))
    1329            0 :             ptr++;
    1330            1 :         if (*ptr++ != '(')
    1331            1 :             ereport(ERROR,
    1332              :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
    1333              :                      errmsg("malformed record literal: \"%s\"", str),
    1334              :                      errdetail("Missing left parenthesis."),
    1335              :                      errhint("To return a composite type in an array, return the composite type as a Python tuple, e.g., \"[('foo',)]\".")));
    1336              :     }
    1337              : 
    1338           17 :     return InputFunctionCall(&arg->tuple.recinfunc,
    1339              :                              str,
    1340              :                              arg->typoid,
    1341              :                              arg->typmod);
    1342              : }
    1343              : 
    1344              : 
    1345              : static Datum
    1346           95 : PLyMapping_ToComposite(PLyObToDatum *arg, TupleDesc desc, PyObject *mapping)
    1347              : {
    1348              :     Datum       result;
    1349              :     HeapTuple   tuple;
    1350              :     Datum      *values;
    1351              :     bool       *nulls;
    1352              :     volatile int i;
    1353              : 
    1354              :     Assert(PyMapping_Check(mapping));
    1355              : 
    1356              :     /* Build tuple */
    1357           95 :     values = palloc_array(Datum, desc->natts);
    1358           95 :     nulls = palloc_array(bool, desc->natts);
    1359          365 :     for (i = 0; i < desc->natts; ++i)
    1360              :     {
    1361              :         char       *key;
    1362              :         PyObject   *volatile value;
    1363              :         PLyObToDatum *att;
    1364          273 :         Form_pg_attribute attr = TupleDescAttr(desc, i);
    1365              : 
    1366          273 :         if (attr->attisdropped)
    1367              :         {
    1368           47 :             values[i] = (Datum) 0;
    1369           47 :             nulls[i] = true;
    1370           47 :             continue;
    1371              :         }
    1372              : 
    1373          226 :         key = NameStr(attr->attname);
    1374          226 :         value = NULL;
    1375          226 :         att = &arg->tuple.atts[i];
    1376          226 :         PG_TRY();
    1377              :         {
    1378          226 :             value = PyMapping_GetItemString(mapping, key);
    1379          226 :             if (!value)
    1380            2 :                 ereport(ERROR,
    1381              :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
    1382              :                          errmsg("key \"%s\" not found in mapping", key),
    1383              :                          errhint("To return null in a column, "
    1384              :                                  "add the value None to the mapping with the key named after the column.")));
    1385              : 
    1386          224 :             values[i] = att->func(att, value, &nulls[i], false);
    1387              : 
    1388          223 :             Py_XDECREF(value);
    1389          223 :             value = NULL;
    1390              :         }
    1391            3 :         PG_CATCH();
    1392              :         {
    1393            3 :             Py_XDECREF(value);
    1394            3 :             PG_RE_THROW();
    1395              :         }
    1396          223 :         PG_END_TRY();
    1397              :     }
    1398              : 
    1399           92 :     tuple = heap_form_tuple(desc, values, nulls);
    1400           92 :     result = heap_copy_tuple_as_datum(tuple, desc);
    1401           92 :     heap_freetuple(tuple);
    1402              : 
    1403           92 :     pfree(values);
    1404           92 :     pfree(nulls);
    1405              : 
    1406           92 :     return result;
    1407              : }
    1408              : 
    1409              : 
    1410              : static Datum
    1411          132 : PLySequence_ToComposite(PLyObToDatum *arg, TupleDesc desc, PyObject *sequence)
    1412              : {
    1413              :     Datum       result;
    1414              :     HeapTuple   tuple;
    1415              :     Datum      *values;
    1416              :     bool       *nulls;
    1417              :     volatile int idx;
    1418              :     volatile int i;
    1419              : 
    1420              :     Assert(PySequence_Check(sequence));
    1421              : 
    1422              :     /*
    1423              :      * Check that sequence length is exactly same as PG tuple's. We actually
    1424              :      * can ignore exceeding items or assume missing ones as null but to avoid
    1425              :      * plpython developer's errors we are strict here
    1426              :      */
    1427          132 :     idx = 0;
    1428          443 :     for (i = 0; i < desc->natts; i++)
    1429              :     {
    1430          311 :         if (!TupleDescCompactAttr(desc, i)->attisdropped)
    1431          260 :             idx++;
    1432              :     }
    1433          132 :     if (PySequence_Length(sequence) != idx)
    1434            3 :         ereport(ERROR,
    1435              :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    1436              :                  errmsg("length of returned sequence did not match number of columns in row")));
    1437              : 
    1438              :     /* Build tuple */
    1439          129 :     values = palloc_array(Datum, desc->natts);
    1440          129 :     nulls = palloc_array(bool, desc->natts);
    1441          129 :     idx = 0;
    1442          430 :     for (i = 0; i < desc->natts; ++i)
    1443              :     {
    1444              :         PyObject   *volatile value;
    1445              :         PLyObToDatum *att;
    1446              : 
    1447          303 :         if (TupleDescCompactAttr(desc, i)->attisdropped)
    1448              :         {
    1449           47 :             values[i] = (Datum) 0;
    1450           47 :             nulls[i] = true;
    1451           47 :             continue;
    1452              :         }
    1453              : 
    1454          256 :         value = NULL;
    1455          256 :         att = &arg->tuple.atts[i];
    1456          256 :         PG_TRY();
    1457              :         {
    1458          256 :             value = PySequence_GetItem(sequence, idx);
    1459              :             Assert(value);
    1460              : 
    1461          256 :             values[i] = att->func(att, value, &nulls[i], false);
    1462              : 
    1463          254 :             Py_XDECREF(value);
    1464          254 :             value = NULL;
    1465              :         }
    1466            2 :         PG_CATCH();
    1467              :         {
    1468            2 :             Py_XDECREF(value);
    1469            2 :             PG_RE_THROW();
    1470              :         }
    1471          254 :         PG_END_TRY();
    1472              : 
    1473          254 :         idx++;
    1474              :     }
    1475              : 
    1476          127 :     tuple = heap_form_tuple(desc, values, nulls);
    1477          127 :     result = heap_copy_tuple_as_datum(tuple, desc);
    1478          127 :     heap_freetuple(tuple);
    1479              : 
    1480          127 :     pfree(values);
    1481          127 :     pfree(nulls);
    1482              : 
    1483          127 :     return result;
    1484              : }
    1485              : 
    1486              : 
    1487              : static Datum
    1488           25 : PLyGenericObject_ToComposite(PLyObToDatum *arg, TupleDesc desc, PyObject *object, bool inarray)
    1489              : {
    1490              :     Datum       result;
    1491              :     HeapTuple   tuple;
    1492              :     Datum      *values;
    1493              :     bool       *nulls;
    1494              :     volatile int i;
    1495              : 
    1496              :     /* Build tuple */
    1497           25 :     values = palloc_array(Datum, desc->natts);
    1498           25 :     nulls = palloc_array(bool, desc->natts);
    1499           98 :     for (i = 0; i < desc->natts; ++i)
    1500              :     {
    1501              :         char       *key;
    1502              :         PyObject   *volatile value;
    1503              :         PLyObToDatum *att;
    1504           74 :         Form_pg_attribute attr = TupleDescAttr(desc, i);
    1505              : 
    1506           74 :         if (attr->attisdropped)
    1507              :         {
    1508           24 :             values[i] = (Datum) 0;
    1509           24 :             nulls[i] = true;
    1510           24 :             continue;
    1511              :         }
    1512              : 
    1513           50 :         key = NameStr(attr->attname);
    1514           50 :         value = NULL;
    1515           50 :         att = &arg->tuple.atts[i];
    1516           50 :         PG_TRY();
    1517              :         {
    1518           50 :             value = PyObject_GetAttrString(object, key);
    1519           50 :             if (!value)
    1520              :             {
    1521              :                 /*
    1522              :                  * No attribute for this column in the object.
    1523              :                  *
    1524              :                  * If we are parsing a composite type in an array, a likely
    1525              :                  * cause is that the function contained something like "[[123,
    1526              :                  * 'foo']]". Before PostgreSQL 10, that was interpreted as an
    1527              :                  * array, with a composite type (123, 'foo') in it. But now
    1528              :                  * it's interpreted as a two-dimensional array, and we try to
    1529              :                  * interpret "123" as the composite type. See also similar
    1530              :                  * heuristic in PLyObject_ToScalar().
    1531              :                  */
    1532            1 :                 ereport(ERROR,
    1533              :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
    1534              :                          errmsg("attribute \"%s\" does not exist in Python object", key),
    1535              :                          inarray ?
    1536              :                          errhint("To return a composite type in an array, return the composite type as a Python tuple, e.g., \"[('foo',)]\".") :
    1537              :                          errhint("To return null in a column, let the returned object have an attribute named after column with value None.")));
    1538              :             }
    1539              : 
    1540           49 :             values[i] = att->func(att, value, &nulls[i], false);
    1541              : 
    1542           49 :             Py_XDECREF(value);
    1543           49 :             value = NULL;
    1544              :         }
    1545            1 :         PG_CATCH();
    1546              :         {
    1547            1 :             Py_XDECREF(value);
    1548            1 :             PG_RE_THROW();
    1549              :         }
    1550           49 :         PG_END_TRY();
    1551              :     }
    1552              : 
    1553           24 :     tuple = heap_form_tuple(desc, values, nulls);
    1554           24 :     result = heap_copy_tuple_as_datum(tuple, desc);
    1555           24 :     heap_freetuple(tuple);
    1556              : 
    1557           24 :     pfree(values);
    1558           24 :     pfree(nulls);
    1559              : 
    1560           24 :     return result;
    1561              : }
        

Generated by: LCOV version 2.0-1