LCOV - code coverage report
Current view: top level - src/pl/plpython - plpy_typeio.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 96.9 % 552 535
Test Date: 2026-07-18 10:15:36 Functions: 100.0 % 36 36
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 83.7 % 283 237

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

Generated by: LCOV version 2.0-1