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 "access/transam.h"
11 #include "catalog/pg_type.h"
12 #include "funcapi.h"
13 #include "mb/pg_wchar.h"
14 #include "parser/parse_type.h"
15 #include "utils/array.h"
16 #include "utils/builtins.h"
17 #include "utils/lsyscache.h"
18 #include "utils/memutils.h"
19 #include "utils/numeric.h"
20 #include "utils/syscache.h"
21 #include "utils/typcache.h"
22
23 #include "plpython.h"
24
25 #include "plpy_typeio.h"
26
27 #include "plpy_elog.h"
28 #include "plpy_main.h"
29
30
31 /* I/O function caching */
32 static void PLy_input_datum_func2(PLyDatumToOb *arg, MemoryContext arg_mcxt, Oid typeOid, HeapTuple typeTup, Oid langid, List *trftypes);
33 static void PLy_output_datum_func2(PLyObToDatum *arg, MemoryContext arg_mcxt, HeapTuple typeTup, Oid langid, List *trftypes);
34
35 /* conversion from Datums to Python objects */
36 static PyObject *PLyBool_FromBool(PLyDatumToOb *arg, Datum d);
37 static PyObject *PLyFloat_FromFloat4(PLyDatumToOb *arg, Datum d);
38 static PyObject *PLyFloat_FromFloat8(PLyDatumToOb *arg, Datum d);
39 static PyObject *PLyDecimal_FromNumeric(PLyDatumToOb *arg, Datum d);
40 static PyObject *PLyInt_FromInt16(PLyDatumToOb *arg, Datum d);
41 static PyObject *PLyInt_FromInt32(PLyDatumToOb *arg, Datum d);
42 static PyObject *PLyLong_FromInt64(PLyDatumToOb *arg, Datum d);
43 static PyObject *PLyLong_FromOid(PLyDatumToOb *arg, Datum d);
44 static PyObject *PLyBytes_FromBytea(PLyDatumToOb *arg, Datum d);
45 static PyObject *PLyString_FromDatum(PLyDatumToOb *arg, Datum d);
46 static PyObject *PLyObject_FromTransform(PLyDatumToOb *arg, Datum d);
47 static PyObject *PLyList_FromArray(PLyDatumToOb *arg, Datum d);
48
49 /* conversion from Python objects to Datums */
50 static Datum PLyObject_ToBool(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
51 static Datum PLyObject_ToBytea(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
52 static Datum PLyObject_ToComposite(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
53 static Datum PLyObject_ToDatum(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
54 static Datum PLyObject_ToTransform(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
55 static Datum PLySequence_ToArray(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
56
57 /* conversion from Python objects to composite Datums (used by triggers and SRFs) */
58 static Datum PLyString_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *string);
59 static Datum PLyMapping_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *mapping);
60 static Datum PLySequence_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *sequence);
61 static Datum PLyGenericObject_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *object);
62
63 void
PLy_typeinfo_init(PLyTypeInfo * arg,MemoryContext mcxt)64 PLy_typeinfo_init(PLyTypeInfo *arg, MemoryContext mcxt)
65 {
66 arg->is_rowtype = -1;
67 arg->in.r.natts = arg->out.r.natts = 0;
68 arg->in.r.atts = NULL;
69 arg->out.r.atts = NULL;
70 arg->typ_relid = InvalidOid;
71 arg->typrel_xmin = InvalidTransactionId;
72 ItemPointerSetInvalid(&arg->typrel_tid);
73 arg->mcxt = mcxt;
74 }
75
76 /*
77 * Conversion functions. Remember output from Python is input to
78 * PostgreSQL, and vice versa.
79 */
80 void
PLy_input_datum_func(PLyTypeInfo * arg,Oid typeOid,HeapTuple typeTup,Oid langid,List * trftypes)81 PLy_input_datum_func(PLyTypeInfo *arg, Oid typeOid, HeapTuple typeTup, Oid langid, List *trftypes)
82 {
83 if (arg->is_rowtype > 0)
84 elog(ERROR, "PLyTypeInfo struct is initialized for Tuple");
85 arg->is_rowtype = 0;
86 PLy_input_datum_func2(&(arg->in.d), arg->mcxt, typeOid, typeTup, langid, trftypes);
87 }
88
89 void
PLy_output_datum_func(PLyTypeInfo * arg,HeapTuple typeTup,Oid langid,List * trftypes)90 PLy_output_datum_func(PLyTypeInfo *arg, HeapTuple typeTup, Oid langid, List *trftypes)
91 {
92 if (arg->is_rowtype > 0)
93 elog(ERROR, "PLyTypeInfo struct is initialized for a Tuple");
94 arg->is_rowtype = 0;
95 PLy_output_datum_func2(&(arg->out.d), arg->mcxt, typeTup, langid, trftypes);
96 }
97
98 void
PLy_input_tuple_funcs(PLyTypeInfo * arg,TupleDesc desc)99 PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
100 {
101 int i;
102 PLyExecutionContext *exec_ctx = PLy_current_execution_context();
103 MemoryContext oldcxt;
104
105 oldcxt = MemoryContextSwitchTo(arg->mcxt);
106
107 if (arg->is_rowtype == 0)
108 elog(ERROR, "PLyTypeInfo struct is initialized for a Datum");
109 arg->is_rowtype = 1;
110
111 if (arg->in.r.natts != desc->natts)
112 {
113 if (arg->in.r.atts)
114 pfree(arg->in.r.atts);
115 arg->in.r.natts = desc->natts;
116 arg->in.r.atts = palloc0(desc->natts * sizeof(PLyDatumToOb));
117 }
118
119 /* Can this be an unnamed tuple? If not, then an Assert would be enough */
120 if (desc->tdtypmod != -1)
121 elog(ERROR, "received unnamed record type as input");
122
123 Assert(OidIsValid(desc->tdtypeid));
124
125 /*
126 * RECORDOID means we got called to create input functions for a tuple
127 * fetched by plpy.execute or for an anonymous record type
128 */
129 if (desc->tdtypeid != RECORDOID)
130 {
131 HeapTuple relTup;
132
133 /* Get the pg_class tuple corresponding to the type of the input */
134 arg->typ_relid = typeidTypeRelid(desc->tdtypeid);
135 relTup = SearchSysCache1(RELOID, ObjectIdGetDatum(arg->typ_relid));
136 if (!HeapTupleIsValid(relTup))
137 elog(ERROR, "cache lookup failed for relation %u", arg->typ_relid);
138
139 /* Remember XMIN and TID for later validation if cache is still OK */
140 arg->typrel_xmin = HeapTupleHeaderGetRawXmin(relTup->t_data);
141 arg->typrel_tid = relTup->t_self;
142
143 ReleaseSysCache(relTup);
144 }
145
146 for (i = 0; i < desc->natts; i++)
147 {
148 HeapTuple typeTup;
149
150 if (desc->attrs[i]->attisdropped)
151 continue;
152
153 if (arg->in.r.atts[i].typoid == desc->attrs[i]->atttypid)
154 continue; /* already set up this entry */
155
156 typeTup = SearchSysCache1(TYPEOID,
157 ObjectIdGetDatum(desc->attrs[i]->atttypid));
158 if (!HeapTupleIsValid(typeTup))
159 elog(ERROR, "cache lookup failed for type %u",
160 desc->attrs[i]->atttypid);
161
162 PLy_input_datum_func2(&(arg->in.r.atts[i]), arg->mcxt,
163 desc->attrs[i]->atttypid,
164 typeTup,
165 exec_ctx->curr_proc->langid,
166 exec_ctx->curr_proc->trftypes);
167
168 ReleaseSysCache(typeTup);
169 }
170
171 MemoryContextSwitchTo(oldcxt);
172 }
173
174 void
PLy_output_tuple_funcs(PLyTypeInfo * arg,TupleDesc desc)175 PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
176 {
177 int i;
178 PLyExecutionContext *exec_ctx = PLy_current_execution_context();
179 MemoryContext oldcxt;
180
181 oldcxt = MemoryContextSwitchTo(arg->mcxt);
182
183 if (arg->is_rowtype == 0)
184 elog(ERROR, "PLyTypeInfo struct is initialized for a Datum");
185 arg->is_rowtype = 1;
186
187 if (arg->out.r.natts != desc->natts)
188 {
189 if (arg->out.r.atts)
190 pfree(arg->out.r.atts);
191 arg->out.r.natts = desc->natts;
192 arg->out.r.atts = palloc0(desc->natts * sizeof(PLyObToDatum));
193 }
194
195 Assert(OidIsValid(desc->tdtypeid));
196
197 /*
198 * RECORDOID means we got called to create output functions for an
199 * anonymous record type
200 */
201 if (desc->tdtypeid != RECORDOID)
202 {
203 HeapTuple relTup;
204
205 /* Get the pg_class tuple corresponding to the type of the output */
206 arg->typ_relid = typeidTypeRelid(desc->tdtypeid);
207 relTup = SearchSysCache1(RELOID, ObjectIdGetDatum(arg->typ_relid));
208 if (!HeapTupleIsValid(relTup))
209 elog(ERROR, "cache lookup failed for relation %u", arg->typ_relid);
210
211 /* Remember XMIN and TID for later validation if cache is still OK */
212 arg->typrel_xmin = HeapTupleHeaderGetRawXmin(relTup->t_data);
213 arg->typrel_tid = relTup->t_self;
214
215 ReleaseSysCache(relTup);
216 }
217
218 for (i = 0; i < desc->natts; i++)
219 {
220 HeapTuple typeTup;
221
222 if (desc->attrs[i]->attisdropped)
223 continue;
224
225 if (arg->out.r.atts[i].typoid == desc->attrs[i]->atttypid)
226 continue; /* already set up this entry */
227
228 typeTup = SearchSysCache1(TYPEOID,
229 ObjectIdGetDatum(desc->attrs[i]->atttypid));
230 if (!HeapTupleIsValid(typeTup))
231 elog(ERROR, "cache lookup failed for type %u",
232 desc->attrs[i]->atttypid);
233
234 PLy_output_datum_func2(&(arg->out.r.atts[i]), arg->mcxt, typeTup,
235 exec_ctx->curr_proc->langid,
236 exec_ctx->curr_proc->trftypes);
237
238 ReleaseSysCache(typeTup);
239 }
240
241 MemoryContextSwitchTo(oldcxt);
242 }
243
244 void
PLy_output_record_funcs(PLyTypeInfo * arg,TupleDesc desc)245 PLy_output_record_funcs(PLyTypeInfo *arg, TupleDesc desc)
246 {
247 /*
248 * If the output record functions are already set, we just have to check
249 * if the record descriptor has not changed
250 */
251 if ((arg->is_rowtype == 1) &&
252 (arg->out.d.typmod != -1) &&
253 (arg->out.d.typmod == desc->tdtypmod))
254 return;
255
256 /* bless the record to make it known to the typcache lookup code */
257 BlessTupleDesc(desc);
258 /* save the freshly generated typmod */
259 arg->out.d.typmod = desc->tdtypmod;
260 /* proceed with normal I/O function caching */
261 PLy_output_tuple_funcs(arg, desc);
262
263 /*
264 * it should change is_rowtype to 1, so we won't go through this again
265 * unless the output record description changes
266 */
267 Assert(arg->is_rowtype == 1);
268 }
269
270 /*
271 * Transform a tuple into a Python dict object.
272 */
273 PyObject *
PLyDict_FromTuple(PLyTypeInfo * info,HeapTuple tuple,TupleDesc desc)274 PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, TupleDesc desc)
275 {
276 PyObject *volatile dict;
277 PLyExecutionContext *exec_ctx = PLy_current_execution_context();
278 MemoryContext scratch_context = PLy_get_scratch_context(exec_ctx);
279 MemoryContext oldcontext = CurrentMemoryContext;
280
281 if (info->is_rowtype != 1)
282 elog(ERROR, "PLyTypeInfo structure describes a datum");
283
284 dict = PyDict_New();
285 if (dict == NULL)
286 PLy_elog(ERROR, "could not create new dictionary");
287
288 PG_TRY();
289 {
290 int i;
291
292 /*
293 * Do the work in the scratch context to avoid leaking memory from the
294 * datatype output function calls.
295 */
296 MemoryContextSwitchTo(scratch_context);
297 for (i = 0; i < info->in.r.natts; i++)
298 {
299 char *key;
300 Datum vattr;
301 bool is_null;
302 PyObject *value;
303
304 if (desc->attrs[i]->attisdropped)
305 continue;
306
307 key = NameStr(desc->attrs[i]->attname);
308 vattr = heap_getattr(tuple, (i + 1), desc, &is_null);
309
310 if (is_null || info->in.r.atts[i].func == NULL)
311 PyDict_SetItemString(dict, key, Py_None);
312 else
313 {
314 value = (info->in.r.atts[i].func) (&info->in.r.atts[i], vattr);
315 PyDict_SetItemString(dict, key, value);
316 Py_DECREF(value);
317 }
318 }
319 MemoryContextSwitchTo(oldcontext);
320 MemoryContextReset(scratch_context);
321 }
322 PG_CATCH();
323 {
324 MemoryContextSwitchTo(oldcontext);
325 Py_DECREF(dict);
326 PG_RE_THROW();
327 }
328 PG_END_TRY();
329
330 return dict;
331 }
332
333 /*
334 * Convert a Python object to a composite Datum, using all supported
335 * conversion methods: composite as a string, as a sequence, as a mapping or
336 * as an object that has __getattr__ support.
337 */
338 Datum
PLyObject_ToCompositeDatum(PLyTypeInfo * info,TupleDesc desc,PyObject * plrv)339 PLyObject_ToCompositeDatum(PLyTypeInfo *info, TupleDesc desc, PyObject *plrv)
340 {
341 Datum datum;
342
343 if (PyString_Check(plrv) || PyUnicode_Check(plrv))
344 datum = PLyString_ToComposite(info, desc, plrv);
345 else if (PySequence_Check(plrv))
346 /* composite type as sequence (tuple, list etc) */
347 datum = PLySequence_ToComposite(info, desc, plrv);
348 else if (PyMapping_Check(plrv))
349 /* composite type as mapping (currently only dict) */
350 datum = PLyMapping_ToComposite(info, desc, plrv);
351 else
352 /* returned as smth, must provide method __getattr__(name) */
353 datum = PLyGenericObject_ToComposite(info, desc, plrv);
354
355 return datum;
356 }
357
358 static void
PLy_output_datum_func2(PLyObToDatum * arg,MemoryContext arg_mcxt,HeapTuple typeTup,Oid langid,List * trftypes)359 PLy_output_datum_func2(PLyObToDatum *arg, MemoryContext arg_mcxt, HeapTuple typeTup, Oid langid, List *trftypes)
360 {
361 Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
362 Oid element_type;
363 Oid base_type;
364 Oid funcid;
365 MemoryContext oldcxt;
366
367 oldcxt = MemoryContextSwitchTo(arg_mcxt);
368
369 fmgr_info_cxt(typeStruct->typinput, &arg->typfunc, arg_mcxt);
370 arg->typoid = HeapTupleGetOid(typeTup);
371 arg->typmod = -1;
372 arg->typioparam = getTypeIOParam(typeTup);
373 arg->typbyval = typeStruct->typbyval;
374
375 element_type = get_base_element_type(arg->typoid);
376 base_type = getBaseType(element_type ? element_type : arg->typoid);
377
378 /*
379 * Select a conversion function to convert Python objects to PostgreSQL
380 * datums.
381 */
382
383 if ((funcid = get_transform_tosql(base_type, langid, trftypes)))
384 {
385 arg->func = PLyObject_ToTransform;
386 fmgr_info_cxt(funcid, &arg->typtransform, arg_mcxt);
387 }
388 else if (typeStruct->typtype == TYPTYPE_COMPOSITE)
389 {
390 arg->func = PLyObject_ToComposite;
391 }
392 else
393 switch (base_type)
394 {
395 case BOOLOID:
396 arg->func = PLyObject_ToBool;
397 break;
398 case BYTEAOID:
399 arg->func = PLyObject_ToBytea;
400 break;
401 default:
402 arg->func = PLyObject_ToDatum;
403 break;
404 }
405
406 if (element_type)
407 {
408 char dummy_delim;
409 Oid funcid;
410
411 if (type_is_rowtype(element_type))
412 arg->func = PLyObject_ToComposite;
413
414 arg->elm = palloc0(sizeof(*arg->elm));
415 arg->elm->func = arg->func;
416 arg->elm->typtransform = arg->typtransform;
417 arg->func = PLySequence_ToArray;
418
419 arg->elm->typoid = element_type;
420 arg->elm->typmod = -1;
421 get_type_io_data(element_type, IOFunc_input,
422 &arg->elm->typlen, &arg->elm->typbyval, &arg->elm->typalign, &dummy_delim,
423 &arg->elm->typioparam, &funcid);
424 fmgr_info_cxt(funcid, &arg->elm->typfunc, arg_mcxt);
425 }
426
427 MemoryContextSwitchTo(oldcxt);
428 }
429
430 static void
PLy_input_datum_func2(PLyDatumToOb * arg,MemoryContext arg_mcxt,Oid typeOid,HeapTuple typeTup,Oid langid,List * trftypes)431 PLy_input_datum_func2(PLyDatumToOb *arg, MemoryContext arg_mcxt, Oid typeOid, HeapTuple typeTup, Oid langid, List *trftypes)
432 {
433 Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
434 Oid element_type;
435 Oid base_type;
436 Oid funcid;
437 MemoryContext oldcxt;
438
439 oldcxt = MemoryContextSwitchTo(arg_mcxt);
440
441 /* Get the type's conversion information */
442 fmgr_info_cxt(typeStruct->typoutput, &arg->typfunc, arg_mcxt);
443 arg->typoid = HeapTupleGetOid(typeTup);
444 arg->typmod = -1;
445 arg->typioparam = getTypeIOParam(typeTup);
446 arg->typbyval = typeStruct->typbyval;
447 arg->typlen = typeStruct->typlen;
448 arg->typalign = typeStruct->typalign;
449
450 /* Determine which kind of Python object we will convert to */
451
452 element_type = get_base_element_type(typeOid);
453 base_type = getBaseType(element_type ? element_type : typeOid);
454
455 if ((funcid = get_transform_fromsql(base_type, langid, trftypes)))
456 {
457 arg->func = PLyObject_FromTransform;
458 fmgr_info_cxt(funcid, &arg->typtransform, arg_mcxt);
459 }
460 else
461 switch (base_type)
462 {
463 case BOOLOID:
464 arg->func = PLyBool_FromBool;
465 break;
466 case FLOAT4OID:
467 arg->func = PLyFloat_FromFloat4;
468 break;
469 case FLOAT8OID:
470 arg->func = PLyFloat_FromFloat8;
471 break;
472 case NUMERICOID:
473 arg->func = PLyDecimal_FromNumeric;
474 break;
475 case INT2OID:
476 arg->func = PLyInt_FromInt16;
477 break;
478 case INT4OID:
479 arg->func = PLyInt_FromInt32;
480 break;
481 case INT8OID:
482 arg->func = PLyLong_FromInt64;
483 break;
484 case OIDOID:
485 arg->func = PLyLong_FromOid;
486 break;
487 case BYTEAOID:
488 arg->func = PLyBytes_FromBytea;
489 break;
490 default:
491 arg->func = PLyString_FromDatum;
492 break;
493 }
494
495 if (element_type)
496 {
497 char dummy_delim;
498 Oid funcid;
499
500 arg->elm = palloc0(sizeof(*arg->elm));
501 arg->elm->func = arg->func;
502 arg->elm->typtransform = arg->typtransform;
503 arg->func = PLyList_FromArray;
504 arg->elm->typoid = element_type;
505 arg->elm->typmod = -1;
506 get_type_io_data(element_type, IOFunc_output,
507 &arg->elm->typlen, &arg->elm->typbyval, &arg->elm->typalign, &dummy_delim,
508 &arg->elm->typioparam, &funcid);
509 fmgr_info_cxt(funcid, &arg->elm->typfunc, arg_mcxt);
510 }
511
512 MemoryContextSwitchTo(oldcxt);
513 }
514
515 static PyObject *
PLyBool_FromBool(PLyDatumToOb * arg,Datum d)516 PLyBool_FromBool(PLyDatumToOb *arg, Datum d)
517 {
518 /*
519 * We would like to use Py_RETURN_TRUE and Py_RETURN_FALSE here for
520 * generating SQL from trigger functions, but those are only supported in
521 * Python >= 2.4, and we support older versions.
522 * http://docs.python.org/api/boolObjects.html
523 */
524 if (DatumGetBool(d))
525 return PyBool_FromLong(1);
526 return PyBool_FromLong(0);
527 }
528
529 static PyObject *
PLyFloat_FromFloat4(PLyDatumToOb * arg,Datum d)530 PLyFloat_FromFloat4(PLyDatumToOb *arg, Datum d)
531 {
532 return PyFloat_FromDouble(DatumGetFloat4(d));
533 }
534
535 static PyObject *
PLyFloat_FromFloat8(PLyDatumToOb * arg,Datum d)536 PLyFloat_FromFloat8(PLyDatumToOb *arg, Datum d)
537 {
538 return PyFloat_FromDouble(DatumGetFloat8(d));
539 }
540
541 static PyObject *
PLyDecimal_FromNumeric(PLyDatumToOb * arg,Datum d)542 PLyDecimal_FromNumeric(PLyDatumToOb *arg, Datum d)
543 {
544 static PyObject *decimal_constructor;
545 char *str;
546 PyObject *pyvalue;
547
548 /* Try to import cdecimal. If it doesn't exist, fall back to decimal. */
549 if (!decimal_constructor)
550 {
551 PyObject *decimal_module;
552
553 decimal_module = PyImport_ImportModule("cdecimal");
554 if (!decimal_module)
555 {
556 PyErr_Clear();
557 decimal_module = PyImport_ImportModule("decimal");
558 }
559 if (!decimal_module)
560 PLy_elog(ERROR, "could not import a module for Decimal constructor");
561
562 decimal_constructor = PyObject_GetAttrString(decimal_module, "Decimal");
563 if (!decimal_constructor)
564 PLy_elog(ERROR, "no Decimal attribute in module");
565 }
566
567 str = DatumGetCString(DirectFunctionCall1(numeric_out, d));
568 pyvalue = PyObject_CallFunction(decimal_constructor, "s", str);
569 if (!pyvalue)
570 PLy_elog(ERROR, "conversion from numeric to Decimal failed");
571
572 return pyvalue;
573 }
574
575 static PyObject *
PLyInt_FromInt16(PLyDatumToOb * arg,Datum d)576 PLyInt_FromInt16(PLyDatumToOb *arg, Datum d)
577 {
578 return PyInt_FromLong(DatumGetInt16(d));
579 }
580
581 static PyObject *
PLyInt_FromInt32(PLyDatumToOb * arg,Datum d)582 PLyInt_FromInt32(PLyDatumToOb *arg, Datum d)
583 {
584 return PyInt_FromLong(DatumGetInt32(d));
585 }
586
587 static PyObject *
PLyLong_FromInt64(PLyDatumToOb * arg,Datum d)588 PLyLong_FromInt64(PLyDatumToOb *arg, Datum d)
589 {
590 /* on 32 bit platforms "long" may be too small */
591 if (sizeof(int64) > sizeof(long))
592 return PyLong_FromLongLong(DatumGetInt64(d));
593 else
594 return PyLong_FromLong(DatumGetInt64(d));
595 }
596
597 static PyObject *
PLyLong_FromOid(PLyDatumToOb * arg,Datum d)598 PLyLong_FromOid(PLyDatumToOb *arg, Datum d)
599 {
600 return PyLong_FromUnsignedLong(DatumGetObjectId(d));
601 }
602
603 static PyObject *
PLyBytes_FromBytea(PLyDatumToOb * arg,Datum d)604 PLyBytes_FromBytea(PLyDatumToOb *arg, Datum d)
605 {
606 text *txt = DatumGetByteaP(d);
607 char *str = VARDATA(txt);
608 size_t size = VARSIZE(txt) - VARHDRSZ;
609
610 return PyBytes_FromStringAndSize(str, size);
611 }
612
613 static PyObject *
PLyString_FromDatum(PLyDatumToOb * arg,Datum d)614 PLyString_FromDatum(PLyDatumToOb *arg, Datum d)
615 {
616 char *x = OutputFunctionCall(&arg->typfunc, d);
617 PyObject *r = PyString_FromString(x);
618
619 pfree(x);
620 return r;
621 }
622
623 static PyObject *
PLyObject_FromTransform(PLyDatumToOb * arg,Datum d)624 PLyObject_FromTransform(PLyDatumToOb *arg, Datum d)
625 {
626 return (PyObject *) DatumGetPointer(FunctionCall1(&arg->typtransform, d));
627 }
628
629 static PyObject *
PLyList_FromArray(PLyDatumToOb * arg,Datum d)630 PLyList_FromArray(PLyDatumToOb *arg, Datum d)
631 {
632 ArrayType *array = DatumGetArrayTypeP(d);
633 PLyDatumToOb *elm = arg->elm;
634 PyObject *list;
635 int length;
636 int lbound;
637 int i;
638
639 if (ARR_NDIM(array) == 0)
640 return PyList_New(0);
641
642 if (ARR_NDIM(array) != 1)
643 ereport(ERROR,
644 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
645 errmsg("cannot convert multidimensional array to Python list"),
646 errdetail("PL/Python only supports one-dimensional arrays.")));
647
648 length = ARR_DIMS(array)[0];
649 lbound = ARR_LBOUND(array)[0];
650 list = PyList_New(length);
651 if (list == NULL)
652 PLy_elog(ERROR, "could not create new Python list");
653
654 for (i = 0; i < length; i++)
655 {
656 Datum elem;
657 bool isnull;
658 int offset;
659
660 offset = lbound + i;
661 elem = array_ref(array, 1, &offset, arg->typlen,
662 elm->typlen, elm->typbyval, elm->typalign,
663 &isnull);
664 if (isnull)
665 {
666 Py_INCREF(Py_None);
667 PyList_SET_ITEM(list, i, Py_None);
668 }
669 else
670 PyList_SET_ITEM(list, i, elm->func(elm, elem));
671 }
672
673 return list;
674 }
675
676 /*
677 * Convert a Python object to a PostgreSQL bool datum. This can't go
678 * through the generic conversion function, because Python attaches a
679 * Boolean value to everything, more things than the PostgreSQL bool
680 * type can parse.
681 */
682 static Datum
PLyObject_ToBool(PLyObToDatum * arg,int32 typmod,PyObject * plrv)683 PLyObject_ToBool(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
684 {
685 Datum rv;
686
687 Assert(plrv != Py_None);
688 rv = BoolGetDatum(PyObject_IsTrue(plrv));
689
690 if (get_typtype(arg->typoid) == TYPTYPE_DOMAIN)
691 domain_check(rv, false, arg->typoid, &arg->typfunc.fn_extra, arg->typfunc.fn_mcxt);
692
693 return rv;
694 }
695
696 /*
697 * Convert a Python object to a PostgreSQL bytea datum. This doesn't
698 * go through the generic conversion function to circumvent problems
699 * with embedded nulls. And it's faster this way.
700 */
701 static Datum
PLyObject_ToBytea(PLyObToDatum * arg,int32 typmod,PyObject * plrv)702 PLyObject_ToBytea(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
703 {
704 PyObject *volatile plrv_so = NULL;
705 Datum rv;
706
707 Assert(plrv != Py_None);
708
709 plrv_so = PyObject_Bytes(plrv);
710 if (!plrv_so)
711 PLy_elog(ERROR, "could not create bytes representation of Python object");
712
713 PG_TRY();
714 {
715 char *plrv_sc = PyBytes_AsString(plrv_so);
716 size_t len = PyBytes_Size(plrv_so);
717 size_t size = len + VARHDRSZ;
718 bytea *result = palloc(size);
719
720 SET_VARSIZE(result, size);
721 memcpy(VARDATA(result), plrv_sc, len);
722 rv = PointerGetDatum(result);
723 }
724 PG_CATCH();
725 {
726 Py_XDECREF(plrv_so);
727 PG_RE_THROW();
728 }
729 PG_END_TRY();
730
731 Py_XDECREF(plrv_so);
732
733 if (get_typtype(arg->typoid) == TYPTYPE_DOMAIN)
734 domain_check(rv, false, arg->typoid, &arg->typfunc.fn_extra, arg->typfunc.fn_mcxt);
735
736 return rv;
737 }
738
739
740 /*
741 * Convert a Python object to a composite type. First look up the type's
742 * description, then route the Python object through the conversion function
743 * for obtaining PostgreSQL tuples.
744 */
745 static Datum
PLyObject_ToComposite(PLyObToDatum * arg,int32 typmod,PyObject * plrv)746 PLyObject_ToComposite(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
747 {
748 Datum rv;
749 PLyTypeInfo info;
750 TupleDesc desc;
751 MemoryContext cxt;
752
753 if (typmod != -1)
754 elog(ERROR, "received unnamed record type as input");
755
756 /* Create a dummy PLyTypeInfo */
757 cxt = AllocSetContextCreate(CurrentMemoryContext,
758 "PL/Python temp context",
759 ALLOCSET_DEFAULT_SIZES);
760 MemSet(&info, 0, sizeof(PLyTypeInfo));
761 PLy_typeinfo_init(&info, cxt);
762 /* Mark it as needing output routines lookup */
763 info.is_rowtype = 2;
764
765 desc = lookup_rowtype_tupdesc(arg->typoid, arg->typmod);
766
767 /*
768 * This will set up the dummy PLyTypeInfo's output conversion routines,
769 * since we left is_rowtype as 2. A future optimisation could be caching
770 * that info instead of looking it up every time a tuple is returned from
771 * the function.
772 */
773 rv = PLyObject_ToCompositeDatum(&info, desc, plrv);
774
775 ReleaseTupleDesc(desc);
776
777 MemoryContextDelete(cxt);
778
779 return rv;
780 }
781
782
783 /*
784 * Convert Python object to C string in server encoding.
785 */
786 char *
PLyObject_AsString(PyObject * plrv)787 PLyObject_AsString(PyObject *plrv)
788 {
789 PyObject *plrv_bo;
790 char *plrv_sc;
791 size_t plen;
792 size_t slen;
793
794 if (PyUnicode_Check(plrv))
795 plrv_bo = PLyUnicode_Bytes(plrv);
796 else if (PyFloat_Check(plrv))
797 {
798 /* use repr() for floats, str() is lossy */
799 #if PY_MAJOR_VERSION >= 3
800 PyObject *s = PyObject_Repr(plrv);
801
802 plrv_bo = PLyUnicode_Bytes(s);
803 Py_XDECREF(s);
804 #else
805 plrv_bo = PyObject_Repr(plrv);
806 #endif
807 }
808 else
809 {
810 #if PY_MAJOR_VERSION >= 3
811 PyObject *s = PyObject_Str(plrv);
812
813 plrv_bo = PLyUnicode_Bytes(s);
814 Py_XDECREF(s);
815 #else
816 plrv_bo = PyObject_Str(plrv);
817 #endif
818 }
819 if (!plrv_bo)
820 PLy_elog(ERROR, "could not create string representation of Python object");
821
822 plrv_sc = pstrdup(PyBytes_AsString(plrv_bo));
823 plen = PyBytes_Size(plrv_bo);
824 slen = strlen(plrv_sc);
825
826 Py_XDECREF(plrv_bo);
827
828 if (slen < plen)
829 ereport(ERROR,
830 (errcode(ERRCODE_DATATYPE_MISMATCH),
831 errmsg("could not convert Python object into cstring: Python string representation appears to contain null bytes")));
832 else if (slen > plen)
833 elog(ERROR, "could not convert Python object into cstring: Python string longer than reported length");
834 pg_verifymbstr(plrv_sc, slen, false);
835
836 return plrv_sc;
837 }
838
839
840 /*
841 * Generic conversion function: Convert PyObject to cstring and
842 * cstring into PostgreSQL type.
843 */
844 static Datum
PLyObject_ToDatum(PLyObToDatum * arg,int32 typmod,PyObject * plrv)845 PLyObject_ToDatum(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
846 {
847 Assert(plrv != Py_None);
848
849 return InputFunctionCall(&arg->typfunc,
850 PLyObject_AsString(plrv),
851 arg->typioparam,
852 typmod);
853 }
854
855
856 static Datum
PLyObject_ToTransform(PLyObToDatum * arg,int32 typmod,PyObject * plrv)857 PLyObject_ToTransform(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
858 {
859 return FunctionCall1(&arg->typtransform, PointerGetDatum(plrv));
860 }
861
862
863 static Datum
PLySequence_ToArray(PLyObToDatum * arg,int32 typmod,PyObject * plrv)864 PLySequence_ToArray(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
865 {
866 ArrayType *array;
867 Datum rv;
868 int i;
869 Datum *elems;
870 bool *nulls;
871 int len;
872 int lbs;
873
874 Assert(plrv != Py_None);
875
876 if (!PySequence_Check(plrv))
877 PLy_elog(ERROR, "return value of function with array return type is not a Python sequence");
878
879 len = PySequence_Length(plrv);
880 elems = palloc(sizeof(*elems) * len);
881 nulls = palloc(sizeof(*nulls) * len);
882
883 for (i = 0; i < len; i++)
884 {
885 PyObject *obj = PySequence_GetItem(plrv, i);
886
887 if (obj == Py_None)
888 nulls[i] = true;
889 else
890 {
891 nulls[i] = false;
892 elems[i] = arg->elm->func(arg->elm, -1, obj);
893 }
894 Py_XDECREF(obj);
895 }
896
897 lbs = 1;
898 array = construct_md_array(elems, nulls, 1, &len, &lbs,
899 get_base_element_type(arg->typoid), arg->elm->typlen, arg->elm->typbyval, arg->elm->typalign);
900
901 /*
902 * If the result type is a domain of array, the resulting array must be
903 * checked.
904 */
905 rv = PointerGetDatum(array);
906 if (get_typtype(arg->typoid) == TYPTYPE_DOMAIN)
907 domain_check(rv, false, arg->typoid, &arg->typfunc.fn_extra, arg->typfunc.fn_mcxt);
908 return rv;
909 }
910
911
912 static Datum
PLyString_ToComposite(PLyTypeInfo * info,TupleDesc desc,PyObject * string)913 PLyString_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *string)
914 {
915 Datum result;
916 HeapTuple typeTup;
917 PLyTypeInfo locinfo;
918 PLyExecutionContext *exec_ctx = PLy_current_execution_context();
919 MemoryContext cxt;
920
921 /* Create a dummy PLyTypeInfo */
922 cxt = AllocSetContextCreate(CurrentMemoryContext,
923 "PL/Python temp context",
924 ALLOCSET_DEFAULT_SIZES);
925 MemSet(&locinfo, 0, sizeof(PLyTypeInfo));
926 PLy_typeinfo_init(&locinfo, cxt);
927
928 typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(desc->tdtypeid));
929 if (!HeapTupleIsValid(typeTup))
930 elog(ERROR, "cache lookup failed for type %u", desc->tdtypeid);
931
932 PLy_output_datum_func2(&locinfo.out.d, locinfo.mcxt, typeTup,
933 exec_ctx->curr_proc->langid,
934 exec_ctx->curr_proc->trftypes);
935
936 ReleaseSysCache(typeTup);
937
938 result = PLyObject_ToDatum(&locinfo.out.d, desc->tdtypmod, string);
939
940 MemoryContextDelete(cxt);
941
942 return result;
943 }
944
945
946 static Datum
PLyMapping_ToComposite(PLyTypeInfo * info,TupleDesc desc,PyObject * mapping)947 PLyMapping_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *mapping)
948 {
949 Datum result;
950 HeapTuple tuple;
951 Datum *values;
952 bool *nulls;
953 volatile int i;
954
955 Assert(PyMapping_Check(mapping));
956
957 if (info->is_rowtype == 2)
958 PLy_output_tuple_funcs(info, desc);
959 Assert(info->is_rowtype == 1);
960
961 /* Build tuple */
962 values = palloc(sizeof(Datum) * desc->natts);
963 nulls = palloc(sizeof(bool) * desc->natts);
964 for (i = 0; i < desc->natts; ++i)
965 {
966 char *key;
967 PyObject *volatile value;
968 PLyObToDatum *att;
969
970 if (desc->attrs[i]->attisdropped)
971 {
972 values[i] = (Datum) 0;
973 nulls[i] = true;
974 continue;
975 }
976
977 key = NameStr(desc->attrs[i]->attname);
978 value = NULL;
979 att = &info->out.r.atts[i];
980 PG_TRY();
981 {
982 value = PyMapping_GetItemString(mapping, key);
983 if (value == Py_None)
984 {
985 values[i] = (Datum) NULL;
986 nulls[i] = true;
987 }
988 else if (value)
989 {
990 values[i] = (att->func) (att, -1, value);
991 nulls[i] = false;
992 }
993 else
994 ereport(ERROR,
995 (errcode(ERRCODE_UNDEFINED_COLUMN),
996 errmsg("key \"%s\" not found in mapping", key),
997 errhint("To return null in a column, "
998 "add the value None to the mapping with the key named after the column.")));
999
1000 Py_XDECREF(value);
1001 value = NULL;
1002 }
1003 PG_CATCH();
1004 {
1005 Py_XDECREF(value);
1006 PG_RE_THROW();
1007 }
1008 PG_END_TRY();
1009 }
1010
1011 tuple = heap_form_tuple(desc, values, nulls);
1012 result = heap_copy_tuple_as_datum(tuple, desc);
1013 heap_freetuple(tuple);
1014
1015 pfree(values);
1016 pfree(nulls);
1017
1018 return result;
1019 }
1020
1021
1022 static Datum
PLySequence_ToComposite(PLyTypeInfo * info,TupleDesc desc,PyObject * sequence)1023 PLySequence_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *sequence)
1024 {
1025 Datum result;
1026 HeapTuple tuple;
1027 Datum *values;
1028 bool *nulls;
1029 volatile int idx;
1030 volatile int i;
1031
1032 Assert(PySequence_Check(sequence));
1033
1034 /*
1035 * Check that sequence length is exactly same as PG tuple's. We actually
1036 * can ignore exceeding items or assume missing ones as null but to avoid
1037 * plpython developer's errors we are strict here
1038 */
1039 idx = 0;
1040 for (i = 0; i < desc->natts; i++)
1041 {
1042 if (!desc->attrs[i]->attisdropped)
1043 idx++;
1044 }
1045 if (PySequence_Length(sequence) != idx)
1046 ereport(ERROR,
1047 (errcode(ERRCODE_DATATYPE_MISMATCH),
1048 errmsg("length of returned sequence did not match number of columns in row")));
1049
1050 if (info->is_rowtype == 2)
1051 PLy_output_tuple_funcs(info, desc);
1052 Assert(info->is_rowtype == 1);
1053
1054 /* Build tuple */
1055 values = palloc(sizeof(Datum) * desc->natts);
1056 nulls = palloc(sizeof(bool) * desc->natts);
1057 idx = 0;
1058 for (i = 0; i < desc->natts; ++i)
1059 {
1060 PyObject *volatile value;
1061 PLyObToDatum *att;
1062
1063 if (desc->attrs[i]->attisdropped)
1064 {
1065 values[i] = (Datum) 0;
1066 nulls[i] = true;
1067 continue;
1068 }
1069
1070 value = NULL;
1071 att = &info->out.r.atts[i];
1072 PG_TRY();
1073 {
1074 value = PySequence_GetItem(sequence, idx);
1075 Assert(value);
1076 if (value == Py_None)
1077 {
1078 values[i] = (Datum) NULL;
1079 nulls[i] = true;
1080 }
1081 else if (value)
1082 {
1083 values[i] = (att->func) (att, -1, value);
1084 nulls[i] = false;
1085 }
1086
1087 Py_XDECREF(value);
1088 value = NULL;
1089 }
1090 PG_CATCH();
1091 {
1092 Py_XDECREF(value);
1093 PG_RE_THROW();
1094 }
1095 PG_END_TRY();
1096
1097 idx++;
1098 }
1099
1100 tuple = heap_form_tuple(desc, values, nulls);
1101 result = heap_copy_tuple_as_datum(tuple, desc);
1102 heap_freetuple(tuple);
1103
1104 pfree(values);
1105 pfree(nulls);
1106
1107 return result;
1108 }
1109
1110
1111 static Datum
PLyGenericObject_ToComposite(PLyTypeInfo * info,TupleDesc desc,PyObject * object)1112 PLyGenericObject_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *object)
1113 {
1114 Datum result;
1115 HeapTuple tuple;
1116 Datum *values;
1117 bool *nulls;
1118 volatile int i;
1119
1120 if (info->is_rowtype == 2)
1121 PLy_output_tuple_funcs(info, desc);
1122 Assert(info->is_rowtype == 1);
1123
1124 /* Build tuple */
1125 values = palloc(sizeof(Datum) * desc->natts);
1126 nulls = palloc(sizeof(bool) * desc->natts);
1127 for (i = 0; i < desc->natts; ++i)
1128 {
1129 char *key;
1130 PyObject *volatile value;
1131 PLyObToDatum *att;
1132
1133 if (desc->attrs[i]->attisdropped)
1134 {
1135 values[i] = (Datum) 0;
1136 nulls[i] = true;
1137 continue;
1138 }
1139
1140 key = NameStr(desc->attrs[i]->attname);
1141 value = NULL;
1142 att = &info->out.r.atts[i];
1143 PG_TRY();
1144 {
1145 value = PyObject_GetAttrString(object, key);
1146 if (value == Py_None)
1147 {
1148 values[i] = (Datum) NULL;
1149 nulls[i] = true;
1150 }
1151 else if (value)
1152 {
1153 values[i] = (att->func) (att, -1, value);
1154 nulls[i] = false;
1155 }
1156 else
1157 ereport(ERROR,
1158 (errcode(ERRCODE_UNDEFINED_COLUMN),
1159 errmsg("attribute \"%s\" does not exist in Python object", key),
1160 errhint("To return null in a column, "
1161 "let the returned object have an attribute named "
1162 "after column with value None.")));
1163
1164 Py_XDECREF(value);
1165 value = NULL;
1166 }
1167 PG_CATCH();
1168 {
1169 Py_XDECREF(value);
1170 PG_RE_THROW();
1171 }
1172 PG_END_TRY();
1173 }
1174
1175 tuple = heap_form_tuple(desc, values, nulls);
1176 result = heap_copy_tuple_as_datum(tuple, desc);
1177 heap_freetuple(tuple);
1178
1179 pfree(values);
1180 pfree(nulls);
1181
1182 return result;
1183 }
1184