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

Generated by: LCOV version 1.14