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