00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include "postgres.h"
00015
00016 #include "access/htup_details.h"
00017 #include "catalog/namespace.h"
00018 #include "catalog/pg_proc.h"
00019 #include "catalog/pg_type.h"
00020 #include "funcapi.h"
00021 #include "nodes/nodeFuncs.h"
00022 #include "parser/parse_coerce.h"
00023 #include "utils/array.h"
00024 #include "utils/builtins.h"
00025 #include "utils/lsyscache.h"
00026 #include "utils/memutils.h"
00027 #include "utils/rel.h"
00028 #include "utils/syscache.h"
00029 #include "utils/typcache.h"
00030
00031
00032 static void shutdown_MultiFuncCall(Datum arg);
00033 static TypeFuncClass internal_get_result_type(Oid funcid,
00034 Node *call_expr,
00035 ReturnSetInfo *rsinfo,
00036 Oid *resultTypeId,
00037 TupleDesc *resultTupleDesc);
00038 static bool resolve_polymorphic_tupdesc(TupleDesc tupdesc,
00039 oidvector *declared_args,
00040 Node *call_expr);
00041 static TypeFuncClass get_type_func_class(Oid typid);
00042
00043
00044
00045
00046
00047
00048
00049
00050 FuncCallContext *
00051 init_MultiFuncCall(PG_FUNCTION_ARGS)
00052 {
00053 FuncCallContext *retval;
00054
00055
00056
00057
00058 if (fcinfo->resultinfo == NULL || !IsA(fcinfo->resultinfo, ReturnSetInfo))
00059 ereport(ERROR,
00060 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00061 errmsg("set-valued function called in context that cannot accept a set")));
00062
00063 if (fcinfo->flinfo->fn_extra == NULL)
00064 {
00065
00066
00067
00068 ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
00069 MemoryContext multi_call_ctx;
00070
00071
00072
00073
00074 multi_call_ctx = AllocSetContextCreate(fcinfo->flinfo->fn_mcxt,
00075 "SRF multi-call context",
00076 ALLOCSET_SMALL_MINSIZE,
00077 ALLOCSET_SMALL_INITSIZE,
00078 ALLOCSET_SMALL_MAXSIZE);
00079
00080
00081
00082
00083 retval = (FuncCallContext *)
00084 MemoryContextAllocZero(multi_call_ctx,
00085 sizeof(FuncCallContext));
00086
00087
00088
00089
00090 retval->call_cntr = 0;
00091 retval->max_calls = 0;
00092 retval->slot = NULL;
00093 retval->user_fctx = NULL;
00094 retval->attinmeta = NULL;
00095 retval->tuple_desc = NULL;
00096 retval->multi_call_memory_ctx = multi_call_ctx;
00097
00098
00099
00100
00101 fcinfo->flinfo->fn_extra = retval;
00102
00103
00104
00105
00106
00107 RegisterExprContextCallback(rsi->econtext,
00108 shutdown_MultiFuncCall,
00109 PointerGetDatum(fcinfo->flinfo));
00110 }
00111 else
00112 {
00113
00114 elog(ERROR, "init_MultiFuncCall cannot be called more than once");
00115
00116
00117 retval = NULL;
00118 }
00119
00120 return retval;
00121 }
00122
00123
00124
00125
00126
00127
00128 FuncCallContext *
00129 per_MultiFuncCall(PG_FUNCTION_ARGS)
00130 {
00131 FuncCallContext *retval = (FuncCallContext *) fcinfo->flinfo->fn_extra;
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145 if (retval->slot != NULL)
00146 ExecClearTuple(retval->slot);
00147
00148 return retval;
00149 }
00150
00151
00152
00153
00154
00155 void
00156 end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx)
00157 {
00158 ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
00159
00160
00161 UnregisterExprContextCallback(rsi->econtext,
00162 shutdown_MultiFuncCall,
00163 PointerGetDatum(fcinfo->flinfo));
00164
00165
00166 shutdown_MultiFuncCall(PointerGetDatum(fcinfo->flinfo));
00167 }
00168
00169
00170
00171
00172
00173 static void
00174 shutdown_MultiFuncCall(Datum arg)
00175 {
00176 FmgrInfo *flinfo = (FmgrInfo *) DatumGetPointer(arg);
00177 FuncCallContext *funcctx = (FuncCallContext *) flinfo->fn_extra;
00178
00179
00180 flinfo->fn_extra = NULL;
00181
00182
00183
00184
00185
00186 MemoryContextDelete(funcctx->multi_call_memory_ctx);
00187 }
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211 TypeFuncClass
00212 get_call_result_type(FunctionCallInfo fcinfo,
00213 Oid *resultTypeId,
00214 TupleDesc *resultTupleDesc)
00215 {
00216 return internal_get_result_type(fcinfo->flinfo->fn_oid,
00217 fcinfo->flinfo->fn_expr,
00218 (ReturnSetInfo *) fcinfo->resultinfo,
00219 resultTypeId,
00220 resultTupleDesc);
00221 }
00222
00223
00224
00225
00226
00227 TypeFuncClass
00228 get_expr_result_type(Node *expr,
00229 Oid *resultTypeId,
00230 TupleDesc *resultTupleDesc)
00231 {
00232 TypeFuncClass result;
00233
00234 if (expr && IsA(expr, FuncExpr))
00235 result = internal_get_result_type(((FuncExpr *) expr)->funcid,
00236 expr,
00237 NULL,
00238 resultTypeId,
00239 resultTupleDesc);
00240 else if (expr && IsA(expr, OpExpr))
00241 result = internal_get_result_type(get_opcode(((OpExpr *) expr)->opno),
00242 expr,
00243 NULL,
00244 resultTypeId,
00245 resultTupleDesc);
00246 else
00247 {
00248
00249 Oid typid = exprType(expr);
00250
00251 if (resultTypeId)
00252 *resultTypeId = typid;
00253 if (resultTupleDesc)
00254 *resultTupleDesc = NULL;
00255 result = get_type_func_class(typid);
00256 if (result == TYPEFUNC_COMPOSITE && resultTupleDesc)
00257 *resultTupleDesc = lookup_rowtype_tupdesc_copy(typid, -1);
00258 }
00259
00260 return result;
00261 }
00262
00263
00264
00265
00266
00267
00268
00269 TypeFuncClass
00270 get_func_result_type(Oid functionId,
00271 Oid *resultTypeId,
00272 TupleDesc *resultTupleDesc)
00273 {
00274 return internal_get_result_type(functionId,
00275 NULL,
00276 NULL,
00277 resultTypeId,
00278 resultTupleDesc);
00279 }
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289 static TypeFuncClass
00290 internal_get_result_type(Oid funcid,
00291 Node *call_expr,
00292 ReturnSetInfo *rsinfo,
00293 Oid *resultTypeId,
00294 TupleDesc *resultTupleDesc)
00295 {
00296 TypeFuncClass result;
00297 HeapTuple tp;
00298 Form_pg_proc procform;
00299 Oid rettype;
00300 TupleDesc tupdesc;
00301
00302
00303 tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
00304 if (!HeapTupleIsValid(tp))
00305 elog(ERROR, "cache lookup failed for function %u", funcid);
00306 procform = (Form_pg_proc) GETSTRUCT(tp);
00307
00308 rettype = procform->prorettype;
00309
00310
00311 tupdesc = build_function_result_tupdesc_t(tp);
00312 if (tupdesc)
00313 {
00314
00315
00316
00317
00318
00319 if (resultTypeId)
00320 *resultTypeId = rettype;
00321
00322 if (resolve_polymorphic_tupdesc(tupdesc,
00323 &procform->proargtypes,
00324 call_expr))
00325 {
00326 if (tupdesc->tdtypeid == RECORDOID &&
00327 tupdesc->tdtypmod < 0)
00328 assign_record_type_typmod(tupdesc);
00329 if (resultTupleDesc)
00330 *resultTupleDesc = tupdesc;
00331 result = TYPEFUNC_COMPOSITE;
00332 }
00333 else
00334 {
00335 if (resultTupleDesc)
00336 *resultTupleDesc = NULL;
00337 result = TYPEFUNC_RECORD;
00338 }
00339
00340 ReleaseSysCache(tp);
00341
00342 return result;
00343 }
00344
00345
00346
00347
00348 if (IsPolymorphicType(rettype))
00349 {
00350 Oid newrettype = exprType(call_expr);
00351
00352 if (newrettype == InvalidOid)
00353 ereport(ERROR,
00354 (errcode(ERRCODE_DATATYPE_MISMATCH),
00355 errmsg("could not determine actual result type for function \"%s\" declared to return type %s",
00356 NameStr(procform->proname),
00357 format_type_be(rettype))));
00358 rettype = newrettype;
00359 }
00360
00361 if (resultTypeId)
00362 *resultTypeId = rettype;
00363 if (resultTupleDesc)
00364 *resultTupleDesc = NULL;
00365
00366
00367 result = get_type_func_class(rettype);
00368 switch (result)
00369 {
00370 case TYPEFUNC_COMPOSITE:
00371 if (resultTupleDesc)
00372 *resultTupleDesc = lookup_rowtype_tupdesc_copy(rettype, -1);
00373
00374 break;
00375 case TYPEFUNC_SCALAR:
00376 break;
00377 case TYPEFUNC_RECORD:
00378
00379 if (rsinfo && IsA(rsinfo, ReturnSetInfo) &&
00380 rsinfo->expectedDesc != NULL)
00381 {
00382 result = TYPEFUNC_COMPOSITE;
00383 if (resultTupleDesc)
00384 *resultTupleDesc = rsinfo->expectedDesc;
00385
00386 }
00387 break;
00388 default:
00389 break;
00390 }
00391
00392 ReleaseSysCache(tp);
00393
00394 return result;
00395 }
00396
00397
00398
00399
00400
00401
00402
00403 static bool
00404 resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
00405 Node *call_expr)
00406 {
00407 int natts = tupdesc->natts;
00408 int nargs = declared_args->dim1;
00409 bool have_anyelement_result = false;
00410 bool have_anyarray_result = false;
00411 bool have_anyrange_result = false;
00412 bool have_anynonarray = false;
00413 bool have_anyenum = false;
00414 Oid anyelement_type = InvalidOid;
00415 Oid anyarray_type = InvalidOid;
00416 Oid anyrange_type = InvalidOid;
00417 Oid anycollation = InvalidOid;
00418 int i;
00419
00420
00421 for (i = 0; i < natts; i++)
00422 {
00423 switch (tupdesc->attrs[i]->atttypid)
00424 {
00425 case ANYELEMENTOID:
00426 have_anyelement_result = true;
00427 break;
00428 case ANYARRAYOID:
00429 have_anyarray_result = true;
00430 break;
00431 case ANYNONARRAYOID:
00432 have_anyelement_result = true;
00433 have_anynonarray = true;
00434 break;
00435 case ANYENUMOID:
00436 have_anyelement_result = true;
00437 have_anyenum = true;
00438 break;
00439 case ANYRANGEOID:
00440 have_anyrange_result = true;
00441 break;
00442 default:
00443 break;
00444 }
00445 }
00446 if (!have_anyelement_result && !have_anyarray_result &&
00447 !have_anyrange_result)
00448 return true;
00449
00450
00451
00452
00453
00454 if (!call_expr)
00455 return false;
00456
00457 for (i = 0; i < nargs; i++)
00458 {
00459 switch (declared_args->values[i])
00460 {
00461 case ANYELEMENTOID:
00462 case ANYNONARRAYOID:
00463 case ANYENUMOID:
00464 if (!OidIsValid(anyelement_type))
00465 anyelement_type = get_call_expr_argtype(call_expr, i);
00466 break;
00467 case ANYARRAYOID:
00468 if (!OidIsValid(anyarray_type))
00469 anyarray_type = get_call_expr_argtype(call_expr, i);
00470 break;
00471 case ANYRANGEOID:
00472 if (!OidIsValid(anyrange_type))
00473 anyrange_type = get_call_expr_argtype(call_expr, i);
00474 break;
00475 default:
00476 break;
00477 }
00478 }
00479
00480
00481 if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
00482 !OidIsValid(anyrange_type))
00483 return false;
00484
00485
00486 if (have_anyelement_result && !OidIsValid(anyelement_type))
00487 {
00488 if (OidIsValid(anyarray_type))
00489 anyelement_type = resolve_generic_type(ANYELEMENTOID,
00490 anyarray_type,
00491 ANYARRAYOID);
00492 if (OidIsValid(anyrange_type))
00493 {
00494 Oid subtype = resolve_generic_type(ANYELEMENTOID,
00495 anyrange_type,
00496 ANYRANGEOID);
00497
00498
00499 if (OidIsValid(anyelement_type) && anyelement_type != subtype)
00500 return false;
00501 anyelement_type = subtype;
00502 }
00503 }
00504
00505 if (have_anyarray_result && !OidIsValid(anyarray_type))
00506 anyarray_type = resolve_generic_type(ANYARRAYOID,
00507 anyelement_type,
00508 ANYELEMENTOID);
00509
00510
00511
00512
00513
00514 if (have_anyrange_result && !OidIsValid(anyrange_type))
00515 return false;
00516
00517
00518 if (have_anynonarray && type_is_array(anyelement_type))
00519 return false;
00520
00521
00522 if (have_anyenum && !type_is_enum(anyelement_type))
00523 return false;
00524
00525
00526
00527
00528
00529
00530
00531 if (OidIsValid(anyelement_type))
00532 anycollation = get_typcollation(anyelement_type);
00533 else if (OidIsValid(anyarray_type))
00534 anycollation = get_typcollation(anyarray_type);
00535
00536 if (OidIsValid(anycollation))
00537 {
00538
00539
00540
00541
00542
00543 Oid inputcollation = exprInputCollation(call_expr);
00544
00545 if (OidIsValid(inputcollation))
00546 anycollation = inputcollation;
00547 }
00548
00549
00550 for (i = 0; i < natts; i++)
00551 {
00552 switch (tupdesc->attrs[i]->atttypid)
00553 {
00554 case ANYELEMENTOID:
00555 case ANYNONARRAYOID:
00556 case ANYENUMOID:
00557 TupleDescInitEntry(tupdesc, i + 1,
00558 NameStr(tupdesc->attrs[i]->attname),
00559 anyelement_type,
00560 -1,
00561 0);
00562 TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
00563 break;
00564 case ANYARRAYOID:
00565 TupleDescInitEntry(tupdesc, i + 1,
00566 NameStr(tupdesc->attrs[i]->attname),
00567 anyarray_type,
00568 -1,
00569 0);
00570 TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
00571 break;
00572 case ANYRANGEOID:
00573 TupleDescInitEntry(tupdesc, i + 1,
00574 NameStr(tupdesc->attrs[i]->attname),
00575 anyrange_type,
00576 -1,
00577 0);
00578
00579 break;
00580 default:
00581 break;
00582 }
00583 }
00584
00585 return true;
00586 }
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597 bool
00598 resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
00599 Node *call_expr)
00600 {
00601 bool have_anyelement_result = false;
00602 bool have_anyarray_result = false;
00603 bool have_anyrange_result = false;
00604 Oid anyelement_type = InvalidOid;
00605 Oid anyarray_type = InvalidOid;
00606 Oid anyrange_type = InvalidOid;
00607 int inargno;
00608 int i;
00609
00610
00611 inargno = 0;
00612 for (i = 0; i < numargs; i++)
00613 {
00614 char argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
00615
00616 switch (argtypes[i])
00617 {
00618 case ANYELEMENTOID:
00619 case ANYNONARRAYOID:
00620 case ANYENUMOID:
00621 if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
00622 have_anyelement_result = true;
00623 else
00624 {
00625 if (!OidIsValid(anyelement_type))
00626 {
00627 anyelement_type = get_call_expr_argtype(call_expr,
00628 inargno);
00629 if (!OidIsValid(anyelement_type))
00630 return false;
00631 }
00632 argtypes[i] = anyelement_type;
00633 }
00634 break;
00635 case ANYARRAYOID:
00636 if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
00637 have_anyarray_result = true;
00638 else
00639 {
00640 if (!OidIsValid(anyarray_type))
00641 {
00642 anyarray_type = get_call_expr_argtype(call_expr,
00643 inargno);
00644 if (!OidIsValid(anyarray_type))
00645 return false;
00646 }
00647 argtypes[i] = anyarray_type;
00648 }
00649 break;
00650 case ANYRANGEOID:
00651 if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
00652 have_anyrange_result = true;
00653 else
00654 {
00655 if (!OidIsValid(anyrange_type))
00656 {
00657 anyrange_type = get_call_expr_argtype(call_expr,
00658 inargno);
00659 if (!OidIsValid(anyrange_type))
00660 return false;
00661 }
00662 argtypes[i] = anyrange_type;
00663 }
00664 break;
00665 default:
00666 break;
00667 }
00668 if (argmode != PROARGMODE_OUT && argmode != PROARGMODE_TABLE)
00669 inargno++;
00670 }
00671
00672
00673 if (!have_anyelement_result && !have_anyarray_result &&
00674 !have_anyrange_result)
00675 return true;
00676
00677
00678 if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
00679 !OidIsValid(anyrange_type))
00680 return false;
00681
00682
00683 if (have_anyelement_result && !OidIsValid(anyelement_type))
00684 {
00685 if (OidIsValid(anyarray_type))
00686 anyelement_type = resolve_generic_type(ANYELEMENTOID,
00687 anyarray_type,
00688 ANYARRAYOID);
00689 if (OidIsValid(anyrange_type))
00690 {
00691 Oid subtype = resolve_generic_type(ANYELEMENTOID,
00692 anyrange_type,
00693 ANYRANGEOID);
00694
00695
00696 if (OidIsValid(anyelement_type) && anyelement_type != subtype)
00697 return false;
00698 anyelement_type = subtype;
00699 }
00700 }
00701
00702 if (have_anyarray_result && !OidIsValid(anyarray_type))
00703 anyarray_type = resolve_generic_type(ANYARRAYOID,
00704 anyelement_type,
00705 ANYELEMENTOID);
00706
00707
00708
00709
00710
00711 if (have_anyrange_result && !OidIsValid(anyrange_type))
00712 return false;
00713
00714
00715
00716
00717 for (i = 0; i < numargs; i++)
00718 {
00719 switch (argtypes[i])
00720 {
00721 case ANYELEMENTOID:
00722 case ANYNONARRAYOID:
00723 case ANYENUMOID:
00724 argtypes[i] = anyelement_type;
00725 break;
00726 case ANYARRAYOID:
00727 argtypes[i] = anyarray_type;
00728 break;
00729 case ANYRANGEOID:
00730 argtypes[i] = anyrange_type;
00731 break;
00732 default:
00733 break;
00734 }
00735 }
00736
00737 return true;
00738 }
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748 static TypeFuncClass
00749 get_type_func_class(Oid typid)
00750 {
00751 switch (get_typtype(typid))
00752 {
00753 case TYPTYPE_COMPOSITE:
00754 return TYPEFUNC_COMPOSITE;
00755 case TYPTYPE_BASE:
00756 case TYPTYPE_DOMAIN:
00757 case TYPTYPE_ENUM:
00758 case TYPTYPE_RANGE:
00759 return TYPEFUNC_SCALAR;
00760 case TYPTYPE_PSEUDO:
00761 if (typid == RECORDOID)
00762 return TYPEFUNC_RECORD;
00763
00764
00765
00766
00767
00768
00769
00770 if (typid == VOIDOID || typid == CSTRINGOID)
00771 return TYPEFUNC_SCALAR;
00772 return TYPEFUNC_OTHER;
00773 }
00774
00775 return TYPEFUNC_OTHER;
00776 }
00777
00778
00779
00780
00781
00782
00783
00784
00785
00786
00787
00788
00789
00790
00791 int
00792 get_func_arg_info(HeapTuple procTup,
00793 Oid **p_argtypes, char ***p_argnames, char **p_argmodes)
00794 {
00795 Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);
00796 Datum proallargtypes;
00797 Datum proargmodes;
00798 Datum proargnames;
00799 bool isNull;
00800 ArrayType *arr;
00801 int numargs;
00802 Datum *elems;
00803 int nelems;
00804 int i;
00805
00806
00807 proallargtypes = SysCacheGetAttr(PROCOID, procTup,
00808 Anum_pg_proc_proallargtypes,
00809 &isNull);
00810 if (!isNull)
00811 {
00812
00813
00814
00815
00816
00817
00818 arr = DatumGetArrayTypeP(proallargtypes);
00819 numargs = ARR_DIMS(arr)[0];
00820 if (ARR_NDIM(arr) != 1 ||
00821 numargs < 0 ||
00822 ARR_HASNULL(arr) ||
00823 ARR_ELEMTYPE(arr) != OIDOID)
00824 elog(ERROR, "proallargtypes is not a 1-D Oid array");
00825 Assert(numargs >= procStruct->pronargs);
00826 *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid));
00827 memcpy(*p_argtypes, ARR_DATA_PTR(arr),
00828 numargs * sizeof(Oid));
00829 }
00830 else
00831 {
00832
00833 numargs = procStruct->proargtypes.dim1;
00834 Assert(numargs == procStruct->pronargs);
00835 *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid));
00836 memcpy(*p_argtypes, procStruct->proargtypes.values,
00837 numargs * sizeof(Oid));
00838 }
00839
00840
00841 proargnames = SysCacheGetAttr(PROCOID, procTup,
00842 Anum_pg_proc_proargnames,
00843 &isNull);
00844 if (isNull)
00845 *p_argnames = NULL;
00846 else
00847 {
00848 deconstruct_array(DatumGetArrayTypeP(proargnames),
00849 TEXTOID, -1, false, 'i',
00850 &elems, NULL, &nelems);
00851 if (nelems != numargs)
00852 elog(ERROR, "proargnames must have the same number of elements as the function has arguments");
00853 *p_argnames = (char **) palloc(sizeof(char *) * numargs);
00854 for (i = 0; i < numargs; i++)
00855 (*p_argnames)[i] = TextDatumGetCString(elems[i]);
00856 }
00857
00858
00859 proargmodes = SysCacheGetAttr(PROCOID, procTup,
00860 Anum_pg_proc_proargmodes,
00861 &isNull);
00862 if (isNull)
00863 *p_argmodes = NULL;
00864 else
00865 {
00866 arr = DatumGetArrayTypeP(proargmodes);
00867 if (ARR_NDIM(arr) != 1 ||
00868 ARR_DIMS(arr)[0] != numargs ||
00869 ARR_HASNULL(arr) ||
00870 ARR_ELEMTYPE(arr) != CHAROID)
00871 elog(ERROR, "proargmodes is not a 1-D char array");
00872 *p_argmodes = (char *) palloc(numargs * sizeof(char));
00873 memcpy(*p_argmodes, ARR_DATA_PTR(arr),
00874 numargs * sizeof(char));
00875 }
00876
00877 return numargs;
00878 }
00879
00880
00881
00882
00883
00884
00885
00886
00887
00888
00889
00890
00891 int
00892 get_func_input_arg_names(Datum proargnames, Datum proargmodes,
00893 char ***arg_names)
00894 {
00895 ArrayType *arr;
00896 int numargs;
00897 Datum *argnames;
00898 char *argmodes;
00899 char **inargnames;
00900 int numinargs;
00901 int i;
00902
00903
00904 if (proargnames == PointerGetDatum(NULL))
00905 {
00906 *arg_names = NULL;
00907 return 0;
00908 }
00909
00910
00911
00912
00913
00914
00915 arr = DatumGetArrayTypeP(proargnames);
00916 if (ARR_NDIM(arr) != 1 ||
00917 ARR_HASNULL(arr) ||
00918 ARR_ELEMTYPE(arr) != TEXTOID)
00919 elog(ERROR, "proargnames is not a 1-D text array");
00920 deconstruct_array(arr, TEXTOID, -1, false, 'i',
00921 &argnames, NULL, &numargs);
00922 if (proargmodes != PointerGetDatum(NULL))
00923 {
00924 arr = DatumGetArrayTypeP(proargmodes);
00925 if (ARR_NDIM(arr) != 1 ||
00926 ARR_DIMS(arr)[0] != numargs ||
00927 ARR_HASNULL(arr) ||
00928 ARR_ELEMTYPE(arr) != CHAROID)
00929 elog(ERROR, "proargmodes is not a 1-D char array");
00930 argmodes = (char *) ARR_DATA_PTR(arr);
00931 }
00932 else
00933 argmodes = NULL;
00934
00935
00936 if (numargs <= 0)
00937 {
00938 *arg_names = NULL;
00939 return 0;
00940 }
00941
00942
00943 inargnames = (char **) palloc(numargs * sizeof(char *));
00944 numinargs = 0;
00945 for (i = 0; i < numargs; i++)
00946 {
00947 if (argmodes == NULL ||
00948 argmodes[i] == PROARGMODE_IN ||
00949 argmodes[i] == PROARGMODE_INOUT ||
00950 argmodes[i] == PROARGMODE_VARIADIC)
00951 {
00952 char *pname = TextDatumGetCString(argnames[i]);
00953
00954 if (pname[0] != '\0')
00955 inargnames[numinargs] = pname;
00956 else
00957 inargnames[numinargs] = NULL;
00958 numinargs++;
00959 }
00960 }
00961
00962 *arg_names = inargnames;
00963 return numinargs;
00964 }
00965
00966
00967
00968
00969
00970
00971
00972
00973
00974
00975
00976 char *
00977 get_func_result_name(Oid functionId)
00978 {
00979 char *result;
00980 HeapTuple procTuple;
00981 Datum proargmodes;
00982 Datum proargnames;
00983 bool isnull;
00984 ArrayType *arr;
00985 int numargs;
00986 char *argmodes;
00987 Datum *argnames;
00988 int numoutargs;
00989 int nargnames;
00990 int i;
00991
00992
00993 procTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionId));
00994 if (!HeapTupleIsValid(procTuple))
00995 elog(ERROR, "cache lookup failed for function %u", functionId);
00996
00997
00998 if (heap_attisnull(procTuple, Anum_pg_proc_proargmodes) ||
00999 heap_attisnull(procTuple, Anum_pg_proc_proargnames))
01000 result = NULL;
01001 else
01002 {
01003
01004 proargmodes = SysCacheGetAttr(PROCOID, procTuple,
01005 Anum_pg_proc_proargmodes,
01006 &isnull);
01007 Assert(!isnull);
01008 proargnames = SysCacheGetAttr(PROCOID, procTuple,
01009 Anum_pg_proc_proargnames,
01010 &isnull);
01011 Assert(!isnull);
01012
01013
01014
01015
01016
01017
01018
01019 arr = DatumGetArrayTypeP(proargmodes);
01020 numargs = ARR_DIMS(arr)[0];
01021 if (ARR_NDIM(arr) != 1 ||
01022 numargs < 0 ||
01023 ARR_HASNULL(arr) ||
01024 ARR_ELEMTYPE(arr) != CHAROID)
01025 elog(ERROR, "proargmodes is not a 1-D char array");
01026 argmodes = (char *) ARR_DATA_PTR(arr);
01027 arr = DatumGetArrayTypeP(proargnames);
01028 if (ARR_NDIM(arr) != 1 ||
01029 ARR_DIMS(arr)[0] != numargs ||
01030 ARR_HASNULL(arr) ||
01031 ARR_ELEMTYPE(arr) != TEXTOID)
01032 elog(ERROR, "proargnames is not a 1-D text array");
01033 deconstruct_array(arr, TEXTOID, -1, false, 'i',
01034 &argnames, NULL, &nargnames);
01035 Assert(nargnames == numargs);
01036
01037
01038 result = NULL;
01039 numoutargs = 0;
01040 for (i = 0; i < numargs; i++)
01041 {
01042 if (argmodes[i] == PROARGMODE_IN ||
01043 argmodes[i] == PROARGMODE_VARIADIC)
01044 continue;
01045 Assert(argmodes[i] == PROARGMODE_OUT ||
01046 argmodes[i] == PROARGMODE_INOUT ||
01047 argmodes[i] == PROARGMODE_TABLE);
01048 if (++numoutargs > 1)
01049 {
01050
01051 result = NULL;
01052 break;
01053 }
01054 result = TextDatumGetCString(argnames[i]);
01055 if (result == NULL || result[0] == '\0')
01056 {
01057
01058 result = NULL;
01059 break;
01060 }
01061 }
01062 }
01063
01064 ReleaseSysCache(procTuple);
01065
01066 return result;
01067 }
01068
01069
01070
01071
01072
01073
01074
01075
01076
01077
01078
01079 TupleDesc
01080 build_function_result_tupdesc_t(HeapTuple procTuple)
01081 {
01082 Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(procTuple);
01083 Datum proallargtypes;
01084 Datum proargmodes;
01085 Datum proargnames;
01086 bool isnull;
01087
01088
01089 if (procform->prorettype != RECORDOID)
01090 return NULL;
01091
01092
01093 if (heap_attisnull(procTuple, Anum_pg_proc_proallargtypes) ||
01094 heap_attisnull(procTuple, Anum_pg_proc_proargmodes))
01095 return NULL;
01096
01097
01098 proallargtypes = SysCacheGetAttr(PROCOID, procTuple,
01099 Anum_pg_proc_proallargtypes,
01100 &isnull);
01101 Assert(!isnull);
01102 proargmodes = SysCacheGetAttr(PROCOID, procTuple,
01103 Anum_pg_proc_proargmodes,
01104 &isnull);
01105 Assert(!isnull);
01106 proargnames = SysCacheGetAttr(PROCOID, procTuple,
01107 Anum_pg_proc_proargnames,
01108 &isnull);
01109 if (isnull)
01110 proargnames = PointerGetDatum(NULL);
01111
01112 return build_function_result_tupdesc_d(proallargtypes,
01113 proargmodes,
01114 proargnames);
01115 }
01116
01117
01118
01119
01120
01121
01122
01123
01124
01125
01126
01127 TupleDesc
01128 build_function_result_tupdesc_d(Datum proallargtypes,
01129 Datum proargmodes,
01130 Datum proargnames)
01131 {
01132 TupleDesc desc;
01133 ArrayType *arr;
01134 int numargs;
01135 Oid *argtypes;
01136 char *argmodes;
01137 Datum *argnames = NULL;
01138 Oid *outargtypes;
01139 char **outargnames;
01140 int numoutargs;
01141 int nargnames;
01142 int i;
01143
01144
01145 if (proallargtypes == PointerGetDatum(NULL) ||
01146 proargmodes == PointerGetDatum(NULL))
01147 return NULL;
01148
01149
01150
01151
01152
01153
01154 arr = DatumGetArrayTypeP(proallargtypes);
01155 numargs = ARR_DIMS(arr)[0];
01156 if (ARR_NDIM(arr) != 1 ||
01157 numargs < 0 ||
01158 ARR_HASNULL(arr) ||
01159 ARR_ELEMTYPE(arr) != OIDOID)
01160 elog(ERROR, "proallargtypes is not a 1-D Oid array");
01161 argtypes = (Oid *) ARR_DATA_PTR(arr);
01162 arr = DatumGetArrayTypeP(proargmodes);
01163 if (ARR_NDIM(arr) != 1 ||
01164 ARR_DIMS(arr)[0] != numargs ||
01165 ARR_HASNULL(arr) ||
01166 ARR_ELEMTYPE(arr) != CHAROID)
01167 elog(ERROR, "proargmodes is not a 1-D char array");
01168 argmodes = (char *) ARR_DATA_PTR(arr);
01169 if (proargnames != PointerGetDatum(NULL))
01170 {
01171 arr = DatumGetArrayTypeP(proargnames);
01172 if (ARR_NDIM(arr) != 1 ||
01173 ARR_DIMS(arr)[0] != numargs ||
01174 ARR_HASNULL(arr) ||
01175 ARR_ELEMTYPE(arr) != TEXTOID)
01176 elog(ERROR, "proargnames is not a 1-D text array");
01177 deconstruct_array(arr, TEXTOID, -1, false, 'i',
01178 &argnames, NULL, &nargnames);
01179 Assert(nargnames == numargs);
01180 }
01181
01182
01183 if (numargs <= 0)
01184 return NULL;
01185
01186
01187 outargtypes = (Oid *) palloc(numargs * sizeof(Oid));
01188 outargnames = (char **) palloc(numargs * sizeof(char *));
01189 numoutargs = 0;
01190 for (i = 0; i < numargs; i++)
01191 {
01192 char *pname;
01193
01194 if (argmodes[i] == PROARGMODE_IN ||
01195 argmodes[i] == PROARGMODE_VARIADIC)
01196 continue;
01197 Assert(argmodes[i] == PROARGMODE_OUT ||
01198 argmodes[i] == PROARGMODE_INOUT ||
01199 argmodes[i] == PROARGMODE_TABLE);
01200 outargtypes[numoutargs] = argtypes[i];
01201 if (argnames)
01202 pname = TextDatumGetCString(argnames[i]);
01203 else
01204 pname = NULL;
01205 if (pname == NULL || pname[0] == '\0')
01206 {
01207
01208 pname = (char *) palloc(32);
01209 snprintf(pname, 32, "column%d", numoutargs + 1);
01210 }
01211 outargnames[numoutargs] = pname;
01212 numoutargs++;
01213 }
01214
01215
01216
01217
01218
01219 if (numoutargs < 2)
01220 return NULL;
01221
01222 desc = CreateTemplateTupleDesc(numoutargs, false);
01223 for (i = 0; i < numoutargs; i++)
01224 {
01225 TupleDescInitEntry(desc, i + 1,
01226 outargnames[i],
01227 outargtypes[i],
01228 -1,
01229 0);
01230 }
01231
01232 return desc;
01233 }
01234
01235
01236
01237
01238
01239
01240
01241
01242
01243
01244
01245 TupleDesc
01246 RelationNameGetTupleDesc(const char *relname)
01247 {
01248 RangeVar *relvar;
01249 Relation rel;
01250 TupleDesc tupdesc;
01251 List *relname_list;
01252
01253
01254 relname_list = stringToQualifiedNameList(relname);
01255 relvar = makeRangeVarFromNameList(relname_list);
01256 rel = relation_openrv(relvar, AccessShareLock);
01257 tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
01258 relation_close(rel, AccessShareLock);
01259
01260 return tupdesc;
01261 }
01262
01263
01264
01265
01266
01267
01268
01269
01270
01271
01272
01273
01274
01275
01276
01277
01278 TupleDesc
01279 TypeGetTupleDesc(Oid typeoid, List *colaliases)
01280 {
01281 TypeFuncClass functypclass = get_type_func_class(typeoid);
01282 TupleDesc tupdesc = NULL;
01283
01284
01285
01286
01287 if (functypclass == TYPEFUNC_COMPOSITE)
01288 {
01289
01290 tupdesc = lookup_rowtype_tupdesc_copy(typeoid, -1);
01291
01292 if (colaliases != NIL)
01293 {
01294 int natts = tupdesc->natts;
01295 int varattno;
01296
01297
01298 if (list_length(colaliases) != natts)
01299 ereport(ERROR,
01300 (errcode(ERRCODE_DATATYPE_MISMATCH),
01301 errmsg("number of aliases does not match number of columns")));
01302
01303
01304 for (varattno = 0; varattno < natts; varattno++)
01305 {
01306 char *label = strVal(list_nth(colaliases, varattno));
01307
01308 if (label != NULL)
01309 namestrcpy(&(tupdesc->attrs[varattno]->attname), label);
01310 }
01311
01312
01313 tupdesc->tdtypeid = RECORDOID;
01314 tupdesc->tdtypmod = -1;
01315 }
01316 }
01317 else if (functypclass == TYPEFUNC_SCALAR)
01318 {
01319
01320 char *attname;
01321
01322
01323 if (colaliases == NIL)
01324 ereport(ERROR,
01325 (errcode(ERRCODE_DATATYPE_MISMATCH),
01326 errmsg("no column alias was provided")));
01327
01328
01329 if (list_length(colaliases) != 1)
01330 ereport(ERROR,
01331 (errcode(ERRCODE_DATATYPE_MISMATCH),
01332 errmsg("number of aliases does not match number of columns")));
01333
01334
01335 attname = strVal(linitial(colaliases));
01336
01337 tupdesc = CreateTemplateTupleDesc(1, false);
01338 TupleDescInitEntry(tupdesc,
01339 (AttrNumber) 1,
01340 attname,
01341 typeoid,
01342 -1,
01343 0);
01344 }
01345 else if (functypclass == TYPEFUNC_RECORD)
01346 {
01347
01348 ereport(ERROR,
01349 (errcode(ERRCODE_DATATYPE_MISMATCH),
01350 errmsg("could not determine row description for function returning record")));
01351 }
01352 else
01353 {
01354
01355 elog(ERROR, "function in FROM has unsupported return type");
01356 }
01357
01358 return tupdesc;
01359 }