LCOV - code coverage report
Current view: top level - src/pl/plpython - plpy_typeio.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 534 550 97.1 %
Date: 2025-02-22 07:14:56 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        1214 :     for (i = 0; i < desc->natts; i++)
     189             :     {
     190         836 :         Form_pg_attribute attr = TupleDescAttr(desc, i);
     191         836 :         PLyDatumToOb *att = &arg->u.tuple.atts[i];
     192             : 
     193         836 :         if (attr->attisdropped)
     194           6 :             continue;
     195             : 
     196         830 :         if (att->typoid == attr->atttypid && att->typmod == attr->atttypmod)
     197         464 :             continue;           /* already set up this entry */
     198             : 
     199         366 :         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        1396 :     for (i = 0; i < desc->natts; i++)
     239             :     {
     240         968 :         Form_pg_attribute attr = TupleDescAttr(desc, i);
     241         968 :         PLyObToDatum *att = &arg->u.tuple.atts[i];
     242             : 
     243         968 :         if (attr->attisdropped)
     244          56 :             continue;
     245             : 
     246         912 :         if (att->typoid == attr->atttypid && att->typmod == attr->atttypmod)
     247         594 :             continue;           /* already set up this entry */
     248             : 
     249         318 :         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         956 : 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         956 :     check_stack_depth();
     307             : 
     308         956 :     arg->typoid = typeOid;
     309         956 :     arg->typmod = typmod;
     310         956 :     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         956 :     if (typeOid != RECORDOID)
     318             :     {
     319         920 :         typentry = lookup_type_cache(typeOid, TYPECACHE_DOMAIN_BASE_INFO);
     320         920 :         typtype = typentry->typtype;
     321         920 :         arg->typbyval = typentry->typbyval;
     322         920 :         arg->typlen = typentry->typlen;
     323         920 :         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         956 :     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         928 :     else if (typentry &&
     356         892 :              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         850 :     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         826 :     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         680 :         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         640 :             default:
     402         640 :                 arg->func = PLyObject_ToScalar;
     403         640 :                 getTypeInputInfo(typeOid, &typinput, &arg->u.scalar.typioparam);
     404         640 :                 fmgr_info_cxt(typinput, &arg->u.scalar.typfunc, arg_mcxt);
     405         640 :                 break;
     406             :         }
     407             :     }
     408         956 : }
     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         930 : 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         930 :     check_stack_depth();
     430             : 
     431         930 :     arg->typoid = typeOid;
     432         930 :     arg->typmod = typmod;
     433         930 :     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         930 :     if (typeOid != RECORDOID)
     441             :     {
     442         774 :         typentry = lookup_type_cache(typeOid, TYPECACHE_DOMAIN_BASE_INFO);
     443         774 :         typtype = typentry->typtype;
     444         774 :         arg->typbyval = typentry->typbyval;
     445         774 :         arg->typlen = typentry->typlen;
     446         774 :         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         930 :     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         912 :     else if (typentry &&
     474         756 :              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         886 :     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         854 :     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         616 :         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         292 :             case INT4OID:
     524         292 :                 arg->func = PLyLong_FromInt32;
     525         292 :                 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         930 : }
     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        1562 :         for (i = 0; i < arg->u.tuple.natts; i++)
     831             :         {
     832        1066 :             PLyDatumToOb *att = &arg->u.tuple.atts[i];
     833        1066 :             Form_pg_attribute attr = TupleDescAttr(desc, i);
     834             :             char       *key;
     835             :             Datum       vattr;
     836             :             bool        is_null;
     837             :             PyObject   *value;
     838             : 
     839        1066 :             if (attr->attisdropped)
     840          30 :                 continue;
     841             : 
     842        1060 :             if (attr->attgenerated)
     843             :             {
     844             :                 /* don't include unless requested */
     845          36 :                 if (!include_generated)
     846          12 :                     continue;
     847             :                 /* never include virtual columns */
     848          24 :                 if (attr->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
     849          12 :                     continue;
     850             :             }
     851             : 
     852        1036 :             key = NameStr(attr->attname);
     853        1036 :             vattr = heap_getattr(tuple, (i + 1), desc, &is_null);
     854             : 
     855        1036 :             if (is_null)
     856          26 :                 PyDict_SetItemString(dict, key, Py_None);
     857             :             else
     858             :             {
     859        1010 :                 value = att->func(att, vattr);
     860        1010 :                 PyDict_SetItemString(dict, key, value);
     861        1010 :                 Py_DECREF(value);
     862             :             }
     863             :         }
     864             :     }
     865           0 :     PG_CATCH();
     866             :     {
     867           0 :         Py_DECREF(dict);
     868           0 :         PG_RE_THROW();
     869             :     }
     870         496 :     PG_END_TRY();
     871             : 
     872         496 :     return dict;
     873             : }
     874             : 
     875             : /*
     876             :  * Convert a Python object to a PostgreSQL bool datum.  This can't go
     877             :  * through the generic conversion function, because Python attaches a
     878             :  * Boolean value to everything, more things than the PostgreSQL bool
     879             :  * type can parse.
     880             :  */
     881             : static Datum
     882          46 : PLyObject_ToBool(PLyObToDatum *arg, PyObject *plrv,
     883             :                  bool *isnull, bool inarray)
     884             : {
     885          46 :     if (plrv == Py_None)
     886             :     {
     887           2 :         *isnull = true;
     888           2 :         return (Datum) 0;
     889             :     }
     890          44 :     *isnull = false;
     891          44 :     return BoolGetDatum(PyObject_IsTrue(plrv));
     892             : }
     893             : 
     894             : /*
     895             :  * Convert a Python object to a PostgreSQL bytea datum.  This doesn't
     896             :  * go through the generic conversion function to circumvent problems
     897             :  * with embedded nulls.  And it's faster this way.
     898             :  */
     899             : static Datum
     900          22 : PLyObject_ToBytea(PLyObToDatum *arg, PyObject *plrv,
     901             :                   bool *isnull, bool inarray)
     902             : {
     903          22 :     PyObject   *volatile plrv_so = NULL;
     904          22 :     Datum       rv = (Datum) 0;
     905             : 
     906          22 :     if (plrv == Py_None)
     907             :     {
     908           6 :         *isnull = true;
     909           6 :         return (Datum) 0;
     910             :     }
     911          16 :     *isnull = false;
     912             : 
     913          16 :     plrv_so = PyObject_Bytes(plrv);
     914          16 :     if (!plrv_so)
     915           0 :         PLy_elog(ERROR, "could not create bytes representation of Python object");
     916             : 
     917          16 :     PG_TRY();
     918             :     {
     919          16 :         char       *plrv_sc = PyBytes_AsString(plrv_so);
     920          16 :         size_t      len = PyBytes_Size(plrv_so);
     921          16 :         size_t      size = len + VARHDRSZ;
     922          16 :         bytea      *result = palloc(size);
     923             : 
     924          16 :         SET_VARSIZE(result, size);
     925          16 :         memcpy(VARDATA(result), plrv_sc, len);
     926          16 :         rv = PointerGetDatum(result);
     927             :     }
     928           0 :     PG_FINALLY();
     929             :     {
     930          16 :         Py_XDECREF(plrv_so);
     931             :     }
     932          16 :     PG_END_TRY();
     933             : 
     934          16 :     return rv;
     935             : }
     936             : 
     937             : 
     938             : /*
     939             :  * Convert a Python object to a composite type. First look up the type's
     940             :  * description, then route the Python object through the conversion function
     941             :  * for obtaining PostgreSQL tuples.
     942             :  */
     943             : static Datum
     944         582 : PLyObject_ToComposite(PLyObToDatum *arg, PyObject *plrv,
     945             :                       bool *isnull, bool inarray)
     946             : {
     947             :     Datum       rv;
     948             :     TupleDesc   desc;
     949             : 
     950         582 :     if (plrv == Py_None)
     951             :     {
     952          42 :         *isnull = true;
     953          42 :         return (Datum) 0;
     954             :     }
     955         540 :     *isnull = false;
     956             : 
     957             :     /*
     958             :      * The string conversion case doesn't require a tupdesc, nor per-field
     959             :      * conversion data, so just go for it if that's the case to use.
     960             :      */
     961         540 :     if (PyUnicode_Check(plrv))
     962          36 :         return PLyUnicode_ToComposite(arg, plrv, inarray);
     963             : 
     964             :     /*
     965             :      * If we're dealing with a named composite type, we must look up the
     966             :      * tupdesc every time, to protect against possible changes to the type.
     967             :      * RECORD types can't change between calls; but we must still be willing
     968             :      * to set up the info the first time, if nobody did yet.
     969             :      */
     970         504 :     if (arg->typoid != RECORDOID)
     971             :     {
     972         250 :         desc = lookup_rowtype_tupdesc(arg->typoid, arg->typmod);
     973             :         /* We should have the descriptor of the type's typcache entry */
     974             :         Assert(desc == arg->u.tuple.typentry->tupDesc);
     975             :         /* Detect change of descriptor, update cache if needed */
     976         250 :         if (arg->u.tuple.tupdescid != arg->u.tuple.typentry->tupDesc_identifier)
     977             :         {
     978          62 :             PLy_output_setup_tuple(arg, desc,
     979          62 :                                    PLy_current_execution_context()->curr_proc);
     980          62 :             arg->u.tuple.tupdescid = arg->u.tuple.typentry->tupDesc_identifier;
     981             :         }
     982             :     }
     983             :     else
     984             :     {
     985         254 :         desc = arg->u.tuple.recdesc;
     986         254 :         if (desc == NULL)
     987             :         {
     988          56 :             desc = lookup_rowtype_tupdesc(arg->typoid, arg->typmod);
     989          56 :             arg->u.tuple.recdesc = desc;
     990             :         }
     991             :         else
     992             :         {
     993             :             /* Pin descriptor to match unpin below */
     994         198 :             PinTupleDesc(desc);
     995             :         }
     996             :     }
     997             : 
     998             :     /* Simple sanity check on our caching */
     999             :     Assert(desc->natts == arg->u.tuple.natts);
    1000             : 
    1001             :     /*
    1002             :      * Convert, using the appropriate method depending on the type of the
    1003             :      * supplied Python object.
    1004             :      */
    1005         504 :     if (PySequence_Check(plrv))
    1006             :         /* composite type as sequence (tuple, list etc) */
    1007         264 :         rv = PLySequence_ToComposite(arg, desc, plrv);
    1008         240 :     else if (PyMapping_Check(plrv))
    1009             :         /* composite type as mapping (currently only dict) */
    1010         190 :         rv = PLyMapping_ToComposite(arg, desc, plrv);
    1011             :     else
    1012             :         /* returned as smth, must provide method __getattr__(name) */
    1013          50 :         rv = PLyGenericObject_ToComposite(arg, desc, plrv, inarray);
    1014             : 
    1015         486 :     ReleaseTupleDesc(desc);
    1016             : 
    1017         486 :     return rv;
    1018             : }
    1019             : 
    1020             : 
    1021             : /*
    1022             :  * Convert Python object to C string in server encoding.
    1023             :  *
    1024             :  * Note: this is exported for use by add-on transform modules.
    1025             :  */
    1026             : char *
    1027        3162 : PLyObject_AsString(PyObject *plrv)
    1028             : {
    1029             :     PyObject   *plrv_bo;
    1030             :     char       *plrv_sc;
    1031             :     size_t      plen;
    1032             :     size_t      slen;
    1033             : 
    1034        3162 :     if (PyUnicode_Check(plrv))
    1035         676 :         plrv_bo = PLyUnicode_Bytes(plrv);
    1036        2486 :     else if (PyFloat_Check(plrv))
    1037          14 :     {
    1038             :         /* use repr() for floats, str() is lossy */
    1039          14 :         PyObject   *s = PyObject_Repr(plrv);
    1040             : 
    1041          14 :         plrv_bo = PLyUnicode_Bytes(s);
    1042          14 :         Py_XDECREF(s);
    1043             :     }
    1044             :     else
    1045             :     {
    1046        2472 :         PyObject   *s = PyObject_Str(plrv);
    1047             : 
    1048        2472 :         plrv_bo = PLyUnicode_Bytes(s);
    1049        2472 :         Py_XDECREF(s);
    1050             :     }
    1051        3162 :     if (!plrv_bo)
    1052           0 :         PLy_elog(ERROR, "could not create string representation of Python object");
    1053             : 
    1054        3162 :     plrv_sc = pstrdup(PyBytes_AsString(plrv_bo));
    1055        3162 :     plen = PyBytes_Size(plrv_bo);
    1056        3162 :     slen = strlen(plrv_sc);
    1057             : 
    1058        3162 :     Py_XDECREF(plrv_bo);
    1059             : 
    1060        3162 :     if (slen < plen)
    1061           0 :         ereport(ERROR,
    1062             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    1063             :                  errmsg("could not convert Python object into cstring: Python string representation appears to contain null bytes")));
    1064        3162 :     else if (slen > plen)
    1065           0 :         elog(ERROR, "could not convert Python object into cstring: Python string longer than reported length");
    1066        3162 :     pg_verifymbstr(plrv_sc, slen, false);
    1067             : 
    1068        3162 :     return plrv_sc;
    1069             : }
    1070             : 
    1071             : 
    1072             : /*
    1073             :  * Generic output conversion function: convert PyObject to cstring and
    1074             :  * cstring into PostgreSQL type.
    1075             :  */
    1076             : static Datum
    1077        3198 : PLyObject_ToScalar(PLyObToDatum *arg, PyObject *plrv,
    1078             :                    bool *isnull, bool inarray)
    1079             : {
    1080             :     char       *str;
    1081             : 
    1082        3198 :     if (plrv == Py_None)
    1083             :     {
    1084         194 :         *isnull = true;
    1085         194 :         return (Datum) 0;
    1086             :     }
    1087        3004 :     *isnull = false;
    1088             : 
    1089        3004 :     str = PLyObject_AsString(plrv);
    1090             : 
    1091        3004 :     return InputFunctionCall(&arg->u.scalar.typfunc,
    1092             :                              str,
    1093             :                              arg->u.scalar.typioparam,
    1094             :                              arg->typmod);
    1095             : }
    1096             : 
    1097             : 
    1098             : /*
    1099             :  * Convert to a domain type.
    1100             :  */
    1101             : static Datum
    1102          58 : PLyObject_ToDomain(PLyObToDatum *arg, PyObject *plrv,
    1103             :                    bool *isnull, bool inarray)
    1104             : {
    1105             :     Datum       result;
    1106          58 :     PLyObToDatum *base = arg->u.domain.base;
    1107             : 
    1108          58 :     result = base->func(base, plrv, isnull, inarray);
    1109          54 :     domain_check(result, *isnull, arg->typoid,
    1110             :                  &arg->u.domain.domain_info, arg->mcxt);
    1111          32 :     return result;
    1112             : }
    1113             : 
    1114             : 
    1115             : /*
    1116             :  * Convert using a to-SQL transform function.
    1117             :  */
    1118             : static Datum
    1119          62 : PLyObject_ToTransform(PLyObToDatum *arg, PyObject *plrv,
    1120             :                       bool *isnull, bool inarray)
    1121             : {
    1122          62 :     if (plrv == Py_None)
    1123             :     {
    1124           2 :         *isnull = true;
    1125           2 :         return (Datum) 0;
    1126             :     }
    1127          60 :     *isnull = false;
    1128          60 :     return FunctionCall1(&arg->u.transform.typtransform, PointerGetDatum(plrv));
    1129             : }
    1130             : 
    1131             : 
    1132             : /*
    1133             :  * Convert Python sequence (or list of lists) to SQL array.
    1134             :  */
    1135             : static Datum
    1136         116 : PLySequence_ToArray(PLyObToDatum *arg, PyObject *plrv,
    1137             :                     bool *isnull, bool inarray)
    1138             : {
    1139         116 :     ArrayBuildState *astate = NULL;
    1140         116 :     int         ndims = 1;
    1141             :     int         dims[MAXDIM];
    1142             :     int         lbs[MAXDIM];
    1143             : 
    1144         116 :     if (plrv == Py_None)
    1145             :     {
    1146           4 :         *isnull = true;
    1147           4 :         return (Datum) 0;
    1148             :     }
    1149         112 :     *isnull = false;
    1150             : 
    1151             :     /*
    1152             :      * For historical reasons, we allow any sequence (not only a list) at the
    1153             :      * top level when converting a Python object to a SQL array.  However, a
    1154             :      * multi-dimensional array is recognized only when the object contains
    1155             :      * true lists.
    1156             :      */
    1157         112 :     if (!PySequence_Check(plrv))
    1158           6 :         ereport(ERROR,
    1159             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    1160             :                  errmsg("return value of function with array return type is not a Python sequence")));
    1161             : 
    1162             :     /* Initialize dimensionality info with first-level dimension */
    1163         106 :     memset(dims, 0, sizeof(dims));
    1164         106 :     dims[0] = PySequence_Length(plrv);
    1165             : 
    1166             :     /*
    1167             :      * Traverse the Python lists, in depth-first order, and collect all the
    1168             :      * elements at the bottom level into an ArrayBuildState.
    1169             :      */
    1170         106 :     PLySequence_ToArray_recurse(plrv, &astate,
    1171             :                                 &ndims, dims, 1,
    1172             :                                 arg->u.array.elm,
    1173             :                                 arg->u.array.elmbasetype);
    1174             : 
    1175             :     /* ensure we get zero-D array for no inputs, as per PG convention */
    1176          78 :     if (astate == NULL)
    1177           4 :         return PointerGetDatum(construct_empty_array(arg->u.array.elmbasetype));
    1178             : 
    1179         196 :     for (int i = 0; i < ndims; i++)
    1180         122 :         lbs[i] = 1;
    1181             : 
    1182          74 :     return makeMdArrayResult(astate, ndims, dims, lbs,
    1183             :                              CurrentMemoryContext, true);
    1184             : }
    1185             : 
    1186             : /*
    1187             :  * Helper function for PLySequence_ToArray. Traverse a Python list of lists in
    1188             :  * depth-first order, storing the elements in *astatep.
    1189             :  *
    1190             :  * The ArrayBuildState is created only when we first find a scalar element;
    1191             :  * if we didn't do it like that, we'd need some other convention for knowing
    1192             :  * whether we'd already found any scalars (and thus the number of dimensions
    1193             :  * is frozen).
    1194             :  */
    1195             : static void
    1196         510 : PLySequence_ToArray_recurse(PyObject *obj, ArrayBuildState **astatep,
    1197             :                             int *ndims, int *dims, int cur_depth,
    1198             :                             PLyObToDatum *elm, Oid elmbasetype)
    1199             : {
    1200             :     int         i;
    1201         510 :     int         len = PySequence_Length(obj);
    1202             : 
    1203             :     /* We should not get here with a non-sequence object */
    1204         510 :     if (len < 0)
    1205           0 :         PLy_elog(ERROR, "could not determine sequence length for function return value");
    1206             : 
    1207        2716 :     for (i = 0; i < len; i++)
    1208             :     {
    1209             :         /* fetch the array element */
    1210        2252 :         PyObject   *subobj = PySequence_GetItem(obj, i);
    1211             : 
    1212             :         /* need PG_TRY to ensure we release the subobj's refcount */
    1213        2252 :         PG_TRY();
    1214             :         {
    1215             :             /* multi-dimensional array? */
    1216        2252 :             if (PyList_Check(subobj))
    1217             :             {
    1218             :                 /* set size when at first element in this level, else compare */
    1219         416 :                 if (i == 0 && *ndims == cur_depth)
    1220             :                 {
    1221             :                     /* array after some scalars at same level? */
    1222          80 :                     if (*astatep != NULL)
    1223           2 :                         ereport(ERROR,
    1224             :                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
    1225             :                                  errmsg("multidimensional arrays must have array expressions with matching dimensions")));
    1226             :                     /* too many dimensions? */
    1227          78 :                     if (cur_depth >= MAXDIM)
    1228           2 :                         ereport(ERROR,
    1229             :                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    1230             :                                  errmsg("number of array dimensions exceeds the maximum allowed (%d)",
    1231             :                                         MAXDIM)));
    1232             :                     /* OK, add a dimension */
    1233          76 :                     dims[*ndims] = PySequence_Length(subobj);
    1234          76 :                     (*ndims)++;
    1235             :                 }
    1236         336 :                 else if (cur_depth >= *ndims ||
    1237         332 :                          PySequence_Length(subobj) != dims[cur_depth])
    1238           8 :                     ereport(ERROR,
    1239             :                             (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
    1240             :                              errmsg("multidimensional arrays must have array expressions with matching dimensions")));
    1241             : 
    1242             :                 /* recurse to fetch elements of this sub-array */
    1243         404 :                 PLySequence_ToArray_recurse(subobj, astatep,
    1244             :                                             ndims, dims, cur_depth + 1,
    1245             :                                             elm, elmbasetype);
    1246             :             }
    1247             :             else
    1248             :             {
    1249             :                 Datum       dat;
    1250             :                 bool        isnull;
    1251             : 
    1252             :                 /* scalar after some sub-arrays at same level? */
    1253        1836 :                 if (*ndims != cur_depth)
    1254           4 :                     ereport(ERROR,
    1255             :                             (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
    1256             :                              errmsg("multidimensional arrays must have array expressions with matching dimensions")));
    1257             : 
    1258             :                 /* convert non-list object to Datum */
    1259        1832 :                 dat = elm->func(elm, subobj, &isnull, true);
    1260             : 
    1261             :                 /* create ArrayBuildState if we didn't already */
    1262        1820 :                 if (*astatep == NULL)
    1263          86 :                     *astatep = initArrayResult(elmbasetype,
    1264             :                                                CurrentMemoryContext, true);
    1265             : 
    1266             :                 /* ... and save the element value in it */
    1267        1820 :                 (void) accumArrayResult(*astatep, dat, isnull,
    1268             :                                         elmbasetype, CurrentMemoryContext);
    1269             :             }
    1270             :         }
    1271          46 :         PG_FINALLY();
    1272             :         {
    1273        2252 :             Py_XDECREF(subobj);
    1274             :         }
    1275        2252 :         PG_END_TRY();
    1276             :     }
    1277         464 : }
    1278             : 
    1279             : 
    1280             : /*
    1281             :  * Convert a Python string to composite, using record_in.
    1282             :  */
    1283             : static Datum
    1284          36 : PLyUnicode_ToComposite(PLyObToDatum *arg, PyObject *string, bool inarray)
    1285             : {
    1286             :     char       *str;
    1287             : 
    1288             :     /*
    1289             :      * Set up call data for record_in, if we didn't already.  (We can't just
    1290             :      * use DirectFunctionCall, because record_in needs a fn_extra field.)
    1291             :      */
    1292          36 :     if (!OidIsValid(arg->u.tuple.recinfunc.fn_oid))
    1293          10 :         fmgr_info_cxt(F_RECORD_IN, &arg->u.tuple.recinfunc, arg->mcxt);
    1294             : 
    1295          36 :     str = PLyObject_AsString(string);
    1296             : 
    1297             :     /*
    1298             :      * If we are parsing a composite type within an array, and the string
    1299             :      * isn't a valid record literal, there's a high chance that the function
    1300             :      * did something like:
    1301             :      *
    1302             :      * CREATE FUNCTION .. RETURNS comptype[] AS $$ return [['foo', 'bar']] $$
    1303             :      * LANGUAGE plpython;
    1304             :      *
    1305             :      * Before PostgreSQL 10, that was interpreted as a single-dimensional
    1306             :      * array, containing record ('foo', 'bar'). PostgreSQL 10 added support
    1307             :      * for multi-dimensional arrays, and it is now interpreted as a
    1308             :      * two-dimensional array, containing two records, 'foo', and 'bar'.
    1309             :      * record_in() will throw an error, because "foo" is not a valid record
    1310             :      * literal.
    1311             :      *
    1312             :      * To make that less confusing to users who are upgrading from older
    1313             :      * versions, try to give a hint in the typical instances of that. If we
    1314             :      * are parsing an array of composite types, and we see a string literal
    1315             :      * that is not a valid record literal, give a hint. We only want to give
    1316             :      * the hint in the narrow case of a malformed string literal, not any
    1317             :      * error from record_in(), so check for that case here specifically.
    1318             :      *
    1319             :      * This check better match the one in record_in(), so that we don't forbid
    1320             :      * literals that are actually valid!
    1321             :      */
    1322          36 :     if (inarray)
    1323             :     {
    1324           2 :         char       *ptr = str;
    1325             : 
    1326             :         /* Allow leading whitespace */
    1327           2 :         while (*ptr && isspace((unsigned char) *ptr))
    1328           0 :             ptr++;
    1329           2 :         if (*ptr++ != '(')
    1330           2 :             ereport(ERROR,
    1331             :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
    1332             :                      errmsg("malformed record literal: \"%s\"", str),
    1333             :                      errdetail("Missing left parenthesis."),
    1334             :                      errhint("To return a composite type in an array, return the composite type as a Python tuple, e.g., \"[('foo',)]\".")));
    1335             :     }
    1336             : 
    1337          34 :     return InputFunctionCall(&arg->u.tuple.recinfunc,
    1338             :                              str,
    1339             :                              arg->typoid,
    1340             :                              arg->typmod);
    1341             : }
    1342             : 
    1343             : 
    1344             : static Datum
    1345         190 : PLyMapping_ToComposite(PLyObToDatum *arg, TupleDesc desc, PyObject *mapping)
    1346             : {
    1347             :     Datum       result;
    1348             :     HeapTuple   tuple;
    1349             :     Datum      *values;
    1350             :     bool       *nulls;
    1351             :     volatile int i;
    1352             : 
    1353             :     Assert(PyMapping_Check(mapping));
    1354             : 
    1355             :     /* Build tuple */
    1356         190 :     values = palloc(sizeof(Datum) * desc->natts);
    1357         190 :     nulls = palloc(sizeof(bool) * desc->natts);
    1358         730 :     for (i = 0; i < desc->natts; ++i)
    1359             :     {
    1360             :         char       *key;
    1361             :         PyObject   *volatile value;
    1362             :         PLyObToDatum *att;
    1363         546 :         Form_pg_attribute attr = TupleDescAttr(desc, i);
    1364             : 
    1365         546 :         if (attr->attisdropped)
    1366             :         {
    1367          94 :             values[i] = (Datum) 0;
    1368          94 :             nulls[i] = true;
    1369          94 :             continue;
    1370             :         }
    1371             : 
    1372         452 :         key = NameStr(attr->attname);
    1373         452 :         value = NULL;
    1374         452 :         att = &arg->u.tuple.atts[i];
    1375         452 :         PG_TRY();
    1376             :         {
    1377         452 :             value = PyMapping_GetItemString(mapping, key);
    1378         452 :             if (!value)
    1379           4 :                 ereport(ERROR,
    1380             :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
    1381             :                          errmsg("key \"%s\" not found in mapping", key),
    1382             :                          errhint("To return null in a column, "
    1383             :                                  "add the value None to the mapping with the key named after the column.")));
    1384             : 
    1385         448 :             values[i] = att->func(att, value, &nulls[i], false);
    1386             : 
    1387         446 :             Py_XDECREF(value);
    1388         446 :             value = NULL;
    1389             :         }
    1390           6 :         PG_CATCH();
    1391             :         {
    1392           6 :             Py_XDECREF(value);
    1393           6 :             PG_RE_THROW();
    1394             :         }
    1395         446 :         PG_END_TRY();
    1396             :     }
    1397             : 
    1398         184 :     tuple = heap_form_tuple(desc, values, nulls);
    1399         184 :     result = heap_copy_tuple_as_datum(tuple, desc);
    1400         184 :     heap_freetuple(tuple);
    1401             : 
    1402         184 :     pfree(values);
    1403         184 :     pfree(nulls);
    1404             : 
    1405         184 :     return result;
    1406             : }
    1407             : 
    1408             : 
    1409             : static Datum
    1410         264 : PLySequence_ToComposite(PLyObToDatum *arg, TupleDesc desc, PyObject *sequence)
    1411             : {
    1412             :     Datum       result;
    1413             :     HeapTuple   tuple;
    1414             :     Datum      *values;
    1415             :     bool       *nulls;
    1416             :     volatile int idx;
    1417             :     volatile int i;
    1418             : 
    1419             :     Assert(PySequence_Check(sequence));
    1420             : 
    1421             :     /*
    1422             :      * Check that sequence length is exactly same as PG tuple's. We actually
    1423             :      * can ignore exceeding items or assume missing ones as null but to avoid
    1424             :      * plpython developer's errors we are strict here
    1425             :      */
    1426         264 :     idx = 0;
    1427         886 :     for (i = 0; i < desc->natts; i++)
    1428             :     {
    1429         622 :         if (!TupleDescAttr(desc, i)->attisdropped)
    1430         520 :             idx++;
    1431             :     }
    1432         264 :     if (PySequence_Length(sequence) != idx)
    1433           6 :         ereport(ERROR,
    1434             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    1435             :                  errmsg("length of returned sequence did not match number of columns in row")));
    1436             : 
    1437             :     /* Build tuple */
    1438         258 :     values = palloc(sizeof(Datum) * desc->natts);
    1439         258 :     nulls = palloc(sizeof(bool) * desc->natts);
    1440         258 :     idx = 0;
    1441         860 :     for (i = 0; i < desc->natts; ++i)
    1442             :     {
    1443             :         PyObject   *volatile value;
    1444             :         PLyObToDatum *att;
    1445             : 
    1446         606 :         if (TupleDescAttr(desc, i)->attisdropped)
    1447             :         {
    1448          94 :             values[i] = (Datum) 0;
    1449          94 :             nulls[i] = true;
    1450          94 :             continue;
    1451             :         }
    1452             : 
    1453         512 :         value = NULL;
    1454         512 :         att = &arg->u.tuple.atts[i];
    1455         512 :         PG_TRY();
    1456             :         {
    1457         512 :             value = PySequence_GetItem(sequence, idx);
    1458             :             Assert(value);
    1459             : 
    1460         512 :             values[i] = att->func(att, value, &nulls[i], false);
    1461             : 
    1462         508 :             Py_XDECREF(value);
    1463         508 :             value = NULL;
    1464             :         }
    1465           4 :         PG_CATCH();
    1466             :         {
    1467           4 :             Py_XDECREF(value);
    1468           4 :             PG_RE_THROW();
    1469             :         }
    1470         508 :         PG_END_TRY();
    1471             : 
    1472         508 :         idx++;
    1473             :     }
    1474             : 
    1475         254 :     tuple = heap_form_tuple(desc, values, nulls);
    1476         254 :     result = heap_copy_tuple_as_datum(tuple, desc);
    1477         254 :     heap_freetuple(tuple);
    1478             : 
    1479         254 :     pfree(values);
    1480         254 :     pfree(nulls);
    1481             : 
    1482         254 :     return result;
    1483             : }
    1484             : 
    1485             : 
    1486             : static Datum
    1487          50 : PLyGenericObject_ToComposite(PLyObToDatum *arg, TupleDesc desc, PyObject *object, bool inarray)
    1488             : {
    1489             :     Datum       result;
    1490             :     HeapTuple   tuple;
    1491             :     Datum      *values;
    1492             :     bool       *nulls;
    1493             :     volatile int i;
    1494             : 
    1495             :     /* Build tuple */
    1496          50 :     values = palloc(sizeof(Datum) * desc->natts);
    1497          50 :     nulls = palloc(sizeof(bool) * desc->natts);
    1498         196 :     for (i = 0; i < desc->natts; ++i)
    1499             :     {
    1500             :         char       *key;
    1501             :         PyObject   *volatile value;
    1502             :         PLyObToDatum *att;
    1503         148 :         Form_pg_attribute attr = TupleDescAttr(desc, i);
    1504             : 
    1505         148 :         if (attr->attisdropped)
    1506             :         {
    1507          48 :             values[i] = (Datum) 0;
    1508          48 :             nulls[i] = true;
    1509          48 :             continue;
    1510             :         }
    1511             : 
    1512         100 :         key = NameStr(attr->attname);
    1513         100 :         value = NULL;
    1514         100 :         att = &arg->u.tuple.atts[i];
    1515         100 :         PG_TRY();
    1516             :         {
    1517         100 :             value = PyObject_GetAttrString(object, key);
    1518         100 :             if (!value)
    1519             :             {
    1520             :                 /*
    1521             :                  * No attribute for this column in the object.
    1522             :                  *
    1523             :                  * If we are parsing a composite type in an array, a likely
    1524             :                  * cause is that the function contained something like "[[123,
    1525             :                  * 'foo']]". Before PostgreSQL 10, that was interpreted as an
    1526             :                  * array, with a composite type (123, 'foo') in it. But now
    1527             :                  * it's interpreted as a two-dimensional array, and we try to
    1528             :                  * interpret "123" as the composite type. See also similar
    1529             :                  * heuristic in PLyObject_ToScalar().
    1530             :                  */
    1531           2 :                 ereport(ERROR,
    1532             :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
    1533             :                          errmsg("attribute \"%s\" does not exist in Python object", key),
    1534             :                          inarray ?
    1535             :                          errhint("To return a composite type in an array, return the composite type as a Python tuple, e.g., \"[('foo',)]\".") :
    1536             :                          errhint("To return null in a column, let the returned object have an attribute named after column with value None.")));
    1537             :             }
    1538             : 
    1539          98 :             values[i] = att->func(att, value, &nulls[i], false);
    1540             : 
    1541          98 :             Py_XDECREF(value);
    1542          98 :             value = NULL;
    1543             :         }
    1544           2 :         PG_CATCH();
    1545             :         {
    1546           2 :             Py_XDECREF(value);
    1547           2 :             PG_RE_THROW();
    1548             :         }
    1549          98 :         PG_END_TRY();
    1550             :     }
    1551             : 
    1552          48 :     tuple = heap_form_tuple(desc, values, nulls);
    1553          48 :     result = heap_copy_tuple_as_datum(tuple, desc);
    1554          48 :     heap_freetuple(tuple);
    1555             : 
    1556          48 :     pfree(values);
    1557          48 :     pfree(nulls);
    1558             : 
    1559          48 :     return result;
    1560             : }

Generated by: LCOV version 1.14