| 1 | /*------------------------------------------------------------------------- |
| 2 | * |
| 3 | * funcapi.c |
| 4 | * Utility and convenience functions for fmgr functions that return |
| 5 | * sets and/or composite types, or deal with VARIADIC inputs. |
| 6 | * |
| 7 | * Copyright (c) 2002-2019, PostgreSQL Global Development Group |
| 8 | * |
| 9 | * IDENTIFICATION |
| 10 | * src/backend/utils/fmgr/funcapi.c |
| 11 | * |
| 12 | *------------------------------------------------------------------------- |
| 13 | */ |
| 14 | #include "postgres.h" |
| 15 | |
| 16 | #include "access/htup_details.h" |
| 17 | #include "access/relation.h" |
| 18 | #include "catalog/namespace.h" |
| 19 | #include "catalog/pg_proc.h" |
| 20 | #include "catalog/pg_type.h" |
| 21 | #include "funcapi.h" |
| 22 | #include "nodes/nodeFuncs.h" |
| 23 | #include "parser/parse_coerce.h" |
| 24 | #include "utils/array.h" |
| 25 | #include "utils/builtins.h" |
| 26 | #include "utils/lsyscache.h" |
| 27 | #include "utils/memutils.h" |
| 28 | #include "utils/regproc.h" |
| 29 | #include "utils/rel.h" |
| 30 | #include "utils/syscache.h" |
| 31 | #include "utils/typcache.h" |
| 32 | |
| 33 | |
| 34 | static void shutdown_MultiFuncCall(Datum arg); |
| 35 | static TypeFuncClass internal_get_result_type(Oid funcid, |
| 36 | Node *call_expr, |
| 37 | ReturnSetInfo *rsinfo, |
| 38 | Oid *resultTypeId, |
| 39 | TupleDesc *resultTupleDesc); |
| 40 | static bool resolve_polymorphic_tupdesc(TupleDesc tupdesc, |
| 41 | oidvector *declared_args, |
| 42 | Node *call_expr); |
| 43 | static TypeFuncClass get_type_func_class(Oid typid, Oid *base_typeid); |
| 44 | |
| 45 | |
| 46 | /* |
| 47 | * init_MultiFuncCall |
| 48 | * Create an empty FuncCallContext data structure |
| 49 | * and do some other basic Multi-function call setup |
| 50 | * and error checking |
| 51 | */ |
| 52 | FuncCallContext * |
| 53 | init_MultiFuncCall(PG_FUNCTION_ARGS) |
| 54 | { |
| 55 | FuncCallContext *retval; |
| 56 | |
| 57 | /* |
| 58 | * Bail if we're called in the wrong context |
| 59 | */ |
| 60 | if (fcinfo->resultinfo == NULL || !IsA(fcinfo->resultinfo, ReturnSetInfo)) |
| 61 | ereport(ERROR, |
| 62 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
| 63 | errmsg("set-valued function called in context that cannot accept a set" ))); |
| 64 | |
| 65 | if (fcinfo->flinfo->fn_extra == NULL) |
| 66 | { |
| 67 | /* |
| 68 | * First call |
| 69 | */ |
| 70 | ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo; |
| 71 | MemoryContext multi_call_ctx; |
| 72 | |
| 73 | /* |
| 74 | * Create a suitably long-lived context to hold cross-call data |
| 75 | */ |
| 76 | multi_call_ctx = AllocSetContextCreate(fcinfo->flinfo->fn_mcxt, |
| 77 | "SRF multi-call context" , |
| 78 | ALLOCSET_SMALL_SIZES); |
| 79 | |
| 80 | /* |
| 81 | * Allocate suitably long-lived space and zero it |
| 82 | */ |
| 83 | retval = (FuncCallContext *) |
| 84 | MemoryContextAllocZero(multi_call_ctx, |
| 85 | sizeof(FuncCallContext)); |
| 86 | |
| 87 | /* |
| 88 | * initialize the elements |
| 89 | */ |
| 90 | retval->call_cntr = 0; |
| 91 | retval->max_calls = 0; |
| 92 | retval->user_fctx = NULL; |
| 93 | retval->attinmeta = NULL; |
| 94 | retval->tuple_desc = NULL; |
| 95 | retval->multi_call_memory_ctx = multi_call_ctx; |
| 96 | |
| 97 | /* |
| 98 | * save the pointer for cross-call use |
| 99 | */ |
| 100 | fcinfo->flinfo->fn_extra = retval; |
| 101 | |
| 102 | /* |
| 103 | * Ensure we will get shut down cleanly if the exprcontext is not run |
| 104 | * to completion. |
| 105 | */ |
| 106 | RegisterExprContextCallback(rsi->econtext, |
| 107 | shutdown_MultiFuncCall, |
| 108 | PointerGetDatum(fcinfo->flinfo)); |
| 109 | } |
| 110 | else |
| 111 | { |
| 112 | /* second and subsequent calls */ |
| 113 | elog(ERROR, "init_MultiFuncCall cannot be called more than once" ); |
| 114 | |
| 115 | /* never reached, but keep compiler happy */ |
| 116 | retval = NULL; |
| 117 | } |
| 118 | |
| 119 | return retval; |
| 120 | } |
| 121 | |
| 122 | /* |
| 123 | * per_MultiFuncCall |
| 124 | * |
| 125 | * Do Multi-function per-call setup |
| 126 | */ |
| 127 | FuncCallContext * |
| 128 | per_MultiFuncCall(PG_FUNCTION_ARGS) |
| 129 | { |
| 130 | FuncCallContext *retval = (FuncCallContext *) fcinfo->flinfo->fn_extra; |
| 131 | |
| 132 | return retval; |
| 133 | } |
| 134 | |
| 135 | /* |
| 136 | * end_MultiFuncCall |
| 137 | * Clean up after init_MultiFuncCall |
| 138 | */ |
| 139 | void |
| 140 | end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx) |
| 141 | { |
| 142 | ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo; |
| 143 | |
| 144 | /* Deregister the shutdown callback */ |
| 145 | UnregisterExprContextCallback(rsi->econtext, |
| 146 | shutdown_MultiFuncCall, |
| 147 | PointerGetDatum(fcinfo->flinfo)); |
| 148 | |
| 149 | /* But use it to do the real work */ |
| 150 | shutdown_MultiFuncCall(PointerGetDatum(fcinfo->flinfo)); |
| 151 | } |
| 152 | |
| 153 | /* |
| 154 | * shutdown_MultiFuncCall |
| 155 | * Shutdown function to clean up after init_MultiFuncCall |
| 156 | */ |
| 157 | static void |
| 158 | shutdown_MultiFuncCall(Datum arg) |
| 159 | { |
| 160 | FmgrInfo *flinfo = (FmgrInfo *) DatumGetPointer(arg); |
| 161 | FuncCallContext *funcctx = (FuncCallContext *) flinfo->fn_extra; |
| 162 | |
| 163 | /* unbind from flinfo */ |
| 164 | flinfo->fn_extra = NULL; |
| 165 | |
| 166 | /* |
| 167 | * Delete context that holds all multi-call data, including the |
| 168 | * FuncCallContext itself |
| 169 | */ |
| 170 | MemoryContextDelete(funcctx->multi_call_memory_ctx); |
| 171 | } |
| 172 | |
| 173 | |
| 174 | /* |
| 175 | * get_call_result_type |
| 176 | * Given a function's call info record, determine the kind of datatype |
| 177 | * it is supposed to return. If resultTypeId isn't NULL, *resultTypeId |
| 178 | * receives the actual datatype OID (this is mainly useful for scalar |
| 179 | * result types). If resultTupleDesc isn't NULL, *resultTupleDesc |
| 180 | * receives a pointer to a TupleDesc when the result is of a composite |
| 181 | * type, or NULL when it's a scalar result. |
| 182 | * |
| 183 | * One hard case that this handles is resolution of actual rowtypes for |
| 184 | * functions returning RECORD (from either the function's OUT parameter |
| 185 | * list, or a ReturnSetInfo context node). TYPEFUNC_RECORD is returned |
| 186 | * only when we couldn't resolve the actual rowtype for lack of information. |
| 187 | * |
| 188 | * The other hard case that this handles is resolution of polymorphism. |
| 189 | * We will never return polymorphic pseudotypes (ANYELEMENT etc), either |
| 190 | * as a scalar result type or as a component of a rowtype. |
| 191 | * |
| 192 | * This function is relatively expensive --- in a function returning set, |
| 193 | * try to call it only the first time through. |
| 194 | */ |
| 195 | TypeFuncClass |
| 196 | get_call_result_type(FunctionCallInfo fcinfo, |
| 197 | Oid *resultTypeId, |
| 198 | TupleDesc *resultTupleDesc) |
| 199 | { |
| 200 | return internal_get_result_type(fcinfo->flinfo->fn_oid, |
| 201 | fcinfo->flinfo->fn_expr, |
| 202 | (ReturnSetInfo *) fcinfo->resultinfo, |
| 203 | resultTypeId, |
| 204 | resultTupleDesc); |
| 205 | } |
| 206 | |
| 207 | /* |
| 208 | * get_expr_result_type |
| 209 | * As above, but work from a calling expression node tree |
| 210 | */ |
| 211 | TypeFuncClass |
| 212 | get_expr_result_type(Node *expr, |
| 213 | Oid *resultTypeId, |
| 214 | TupleDesc *resultTupleDesc) |
| 215 | { |
| 216 | TypeFuncClass result; |
| 217 | |
| 218 | if (expr && IsA(expr, FuncExpr)) |
| 219 | result = internal_get_result_type(((FuncExpr *) expr)->funcid, |
| 220 | expr, |
| 221 | NULL, |
| 222 | resultTypeId, |
| 223 | resultTupleDesc); |
| 224 | else if (expr && IsA(expr, OpExpr)) |
| 225 | result = internal_get_result_type(get_opcode(((OpExpr *) expr)->opno), |
| 226 | expr, |
| 227 | NULL, |
| 228 | resultTypeId, |
| 229 | resultTupleDesc); |
| 230 | else |
| 231 | { |
| 232 | /* handle as a generic expression; no chance to resolve RECORD */ |
| 233 | Oid typid = exprType(expr); |
| 234 | Oid base_typid; |
| 235 | |
| 236 | if (resultTypeId) |
| 237 | *resultTypeId = typid; |
| 238 | if (resultTupleDesc) |
| 239 | *resultTupleDesc = NULL; |
| 240 | result = get_type_func_class(typid, &base_typid); |
| 241 | if ((result == TYPEFUNC_COMPOSITE || |
| 242 | result == TYPEFUNC_COMPOSITE_DOMAIN) && |
| 243 | resultTupleDesc) |
| 244 | *resultTupleDesc = lookup_rowtype_tupdesc_copy(base_typid, -1); |
| 245 | } |
| 246 | |
| 247 | return result; |
| 248 | } |
| 249 | |
| 250 | /* |
| 251 | * get_func_result_type |
| 252 | * As above, but work from a function's OID only |
| 253 | * |
| 254 | * This will not be able to resolve pure-RECORD results nor polymorphism. |
| 255 | */ |
| 256 | TypeFuncClass |
| 257 | get_func_result_type(Oid functionId, |
| 258 | Oid *resultTypeId, |
| 259 | TupleDesc *resultTupleDesc) |
| 260 | { |
| 261 | return internal_get_result_type(functionId, |
| 262 | NULL, |
| 263 | NULL, |
| 264 | resultTypeId, |
| 265 | resultTupleDesc); |
| 266 | } |
| 267 | |
| 268 | /* |
| 269 | * internal_get_result_type -- workhorse code implementing all the above |
| 270 | * |
| 271 | * funcid must always be supplied. call_expr and rsinfo can be NULL if not |
| 272 | * available. We will return TYPEFUNC_RECORD, and store NULL into |
| 273 | * *resultTupleDesc, if we cannot deduce the complete result rowtype from |
| 274 | * the available information. |
| 275 | */ |
| 276 | static TypeFuncClass |
| 277 | internal_get_result_type(Oid funcid, |
| 278 | Node *call_expr, |
| 279 | ReturnSetInfo *rsinfo, |
| 280 | Oid *resultTypeId, |
| 281 | TupleDesc *resultTupleDesc) |
| 282 | { |
| 283 | TypeFuncClass result; |
| 284 | HeapTuple tp; |
| 285 | Form_pg_proc procform; |
| 286 | Oid rettype; |
| 287 | Oid base_rettype; |
| 288 | TupleDesc tupdesc; |
| 289 | |
| 290 | /* First fetch the function's pg_proc row to inspect its rettype */ |
| 291 | tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); |
| 292 | if (!HeapTupleIsValid(tp)) |
| 293 | elog(ERROR, "cache lookup failed for function %u" , funcid); |
| 294 | procform = (Form_pg_proc) GETSTRUCT(tp); |
| 295 | |
| 296 | rettype = procform->prorettype; |
| 297 | |
| 298 | /* Check for OUT parameters defining a RECORD result */ |
| 299 | tupdesc = build_function_result_tupdesc_t(tp); |
| 300 | if (tupdesc) |
| 301 | { |
| 302 | /* |
| 303 | * It has OUT parameters, so it's basically like a regular composite |
| 304 | * type, except we have to be able to resolve any polymorphic OUT |
| 305 | * parameters. |
| 306 | */ |
| 307 | if (resultTypeId) |
| 308 | *resultTypeId = rettype; |
| 309 | |
| 310 | if (resolve_polymorphic_tupdesc(tupdesc, |
| 311 | &procform->proargtypes, |
| 312 | call_expr)) |
| 313 | { |
| 314 | if (tupdesc->tdtypeid == RECORDOID && |
| 315 | tupdesc->tdtypmod < 0) |
| 316 | assign_record_type_typmod(tupdesc); |
| 317 | if (resultTupleDesc) |
| 318 | *resultTupleDesc = tupdesc; |
| 319 | result = TYPEFUNC_COMPOSITE; |
| 320 | } |
| 321 | else |
| 322 | { |
| 323 | if (resultTupleDesc) |
| 324 | *resultTupleDesc = NULL; |
| 325 | result = TYPEFUNC_RECORD; |
| 326 | } |
| 327 | |
| 328 | ReleaseSysCache(tp); |
| 329 | |
| 330 | return result; |
| 331 | } |
| 332 | |
| 333 | /* |
| 334 | * If scalar polymorphic result, try to resolve it. |
| 335 | */ |
| 336 | if (IsPolymorphicType(rettype)) |
| 337 | { |
| 338 | Oid newrettype = exprType(call_expr); |
| 339 | |
| 340 | if (newrettype == InvalidOid) /* this probably should not happen */ |
| 341 | ereport(ERROR, |
| 342 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
| 343 | errmsg("could not determine actual result type for function \"%s\" declared to return type %s" , |
| 344 | NameStr(procform->proname), |
| 345 | format_type_be(rettype)))); |
| 346 | rettype = newrettype; |
| 347 | } |
| 348 | |
| 349 | if (resultTypeId) |
| 350 | *resultTypeId = rettype; |
| 351 | if (resultTupleDesc) |
| 352 | *resultTupleDesc = NULL; /* default result */ |
| 353 | |
| 354 | /* Classify the result type */ |
| 355 | result = get_type_func_class(rettype, &base_rettype); |
| 356 | switch (result) |
| 357 | { |
| 358 | case TYPEFUNC_COMPOSITE: |
| 359 | case TYPEFUNC_COMPOSITE_DOMAIN: |
| 360 | if (resultTupleDesc) |
| 361 | *resultTupleDesc = lookup_rowtype_tupdesc_copy(base_rettype, -1); |
| 362 | /* Named composite types can't have any polymorphic columns */ |
| 363 | break; |
| 364 | case TYPEFUNC_SCALAR: |
| 365 | break; |
| 366 | case TYPEFUNC_RECORD: |
| 367 | /* We must get the tupledesc from call context */ |
| 368 | if (rsinfo && IsA(rsinfo, ReturnSetInfo) && |
| 369 | rsinfo->expectedDesc != NULL) |
| 370 | { |
| 371 | result = TYPEFUNC_COMPOSITE; |
| 372 | if (resultTupleDesc) |
| 373 | *resultTupleDesc = rsinfo->expectedDesc; |
| 374 | /* Assume no polymorphic columns here, either */ |
| 375 | } |
| 376 | break; |
| 377 | default: |
| 378 | break; |
| 379 | } |
| 380 | |
| 381 | ReleaseSysCache(tp); |
| 382 | |
| 383 | return result; |
| 384 | } |
| 385 | |
| 386 | /* |
| 387 | * get_expr_result_tupdesc |
| 388 | * Get a tupdesc describing the result of a composite-valued expression |
| 389 | * |
| 390 | * If expression is not composite or rowtype can't be determined, returns NULL |
| 391 | * if noError is true, else throws error. |
| 392 | * |
| 393 | * This is a simpler version of get_expr_result_type() for use when the caller |
| 394 | * is only interested in determinate rowtype results. |
| 395 | */ |
| 396 | TupleDesc |
| 397 | get_expr_result_tupdesc(Node *expr, bool noError) |
| 398 | { |
| 399 | TupleDesc tupleDesc; |
| 400 | TypeFuncClass functypclass; |
| 401 | |
| 402 | functypclass = get_expr_result_type(expr, NULL, &tupleDesc); |
| 403 | |
| 404 | if (functypclass == TYPEFUNC_COMPOSITE || |
| 405 | functypclass == TYPEFUNC_COMPOSITE_DOMAIN) |
| 406 | return tupleDesc; |
| 407 | |
| 408 | if (!noError) |
| 409 | { |
| 410 | Oid exprTypeId = exprType(expr); |
| 411 | |
| 412 | if (exprTypeId != RECORDOID) |
| 413 | ereport(ERROR, |
| 414 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
| 415 | errmsg("type %s is not composite" , |
| 416 | format_type_be(exprTypeId)))); |
| 417 | else |
| 418 | ereport(ERROR, |
| 419 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
| 420 | errmsg("record type has not been registered" ))); |
| 421 | } |
| 422 | |
| 423 | return NULL; |
| 424 | } |
| 425 | |
| 426 | /* |
| 427 | * Given the result tuple descriptor for a function with OUT parameters, |
| 428 | * replace any polymorphic columns (ANYELEMENT etc) with correct data types |
| 429 | * deduced from the input arguments. Returns true if able to deduce all types, |
| 430 | * false if not. |
| 431 | */ |
| 432 | static bool |
| 433 | resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, |
| 434 | Node *call_expr) |
| 435 | { |
| 436 | int natts = tupdesc->natts; |
| 437 | int nargs = declared_args->dim1; |
| 438 | bool have_anyelement_result = false; |
| 439 | bool have_anyarray_result = false; |
| 440 | bool have_anyrange_result = false; |
| 441 | bool have_anynonarray = false; |
| 442 | bool have_anyenum = false; |
| 443 | Oid anyelement_type = InvalidOid; |
| 444 | Oid anyarray_type = InvalidOid; |
| 445 | Oid anyrange_type = InvalidOid; |
| 446 | Oid anycollation = InvalidOid; |
| 447 | int i; |
| 448 | |
| 449 | /* See if there are any polymorphic outputs; quick out if not */ |
| 450 | for (i = 0; i < natts; i++) |
| 451 | { |
| 452 | switch (TupleDescAttr(tupdesc, i)->atttypid) |
| 453 | { |
| 454 | case ANYELEMENTOID: |
| 455 | have_anyelement_result = true; |
| 456 | break; |
| 457 | case ANYARRAYOID: |
| 458 | have_anyarray_result = true; |
| 459 | break; |
| 460 | case ANYNONARRAYOID: |
| 461 | have_anyelement_result = true; |
| 462 | have_anynonarray = true; |
| 463 | break; |
| 464 | case ANYENUMOID: |
| 465 | have_anyelement_result = true; |
| 466 | have_anyenum = true; |
| 467 | break; |
| 468 | case ANYRANGEOID: |
| 469 | have_anyrange_result = true; |
| 470 | break; |
| 471 | default: |
| 472 | break; |
| 473 | } |
| 474 | } |
| 475 | if (!have_anyelement_result && !have_anyarray_result && |
| 476 | !have_anyrange_result) |
| 477 | return true; |
| 478 | |
| 479 | /* |
| 480 | * Otherwise, extract actual datatype(s) from input arguments. (We assume |
| 481 | * the parser already validated consistency of the arguments.) |
| 482 | */ |
| 483 | if (!call_expr) |
| 484 | return false; /* no hope */ |
| 485 | |
| 486 | for (i = 0; i < nargs; i++) |
| 487 | { |
| 488 | switch (declared_args->values[i]) |
| 489 | { |
| 490 | case ANYELEMENTOID: |
| 491 | case ANYNONARRAYOID: |
| 492 | case ANYENUMOID: |
| 493 | if (!OidIsValid(anyelement_type)) |
| 494 | anyelement_type = get_call_expr_argtype(call_expr, i); |
| 495 | break; |
| 496 | case ANYARRAYOID: |
| 497 | if (!OidIsValid(anyarray_type)) |
| 498 | anyarray_type = get_call_expr_argtype(call_expr, i); |
| 499 | break; |
| 500 | case ANYRANGEOID: |
| 501 | if (!OidIsValid(anyrange_type)) |
| 502 | anyrange_type = get_call_expr_argtype(call_expr, i); |
| 503 | break; |
| 504 | default: |
| 505 | break; |
| 506 | } |
| 507 | } |
| 508 | |
| 509 | /* If nothing found, parser messed up */ |
| 510 | if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) && |
| 511 | !OidIsValid(anyrange_type)) |
| 512 | return false; |
| 513 | |
| 514 | /* If needed, deduce one polymorphic type from others */ |
| 515 | if (have_anyelement_result && !OidIsValid(anyelement_type)) |
| 516 | { |
| 517 | if (OidIsValid(anyarray_type)) |
| 518 | anyelement_type = resolve_generic_type(ANYELEMENTOID, |
| 519 | anyarray_type, |
| 520 | ANYARRAYOID); |
| 521 | if (OidIsValid(anyrange_type)) |
| 522 | { |
| 523 | Oid subtype = resolve_generic_type(ANYELEMENTOID, |
| 524 | anyrange_type, |
| 525 | ANYRANGEOID); |
| 526 | |
| 527 | /* check for inconsistent array and range results */ |
| 528 | if (OidIsValid(anyelement_type) && anyelement_type != subtype) |
| 529 | return false; |
| 530 | anyelement_type = subtype; |
| 531 | } |
| 532 | } |
| 533 | |
| 534 | if (have_anyarray_result && !OidIsValid(anyarray_type)) |
| 535 | anyarray_type = resolve_generic_type(ANYARRAYOID, |
| 536 | anyelement_type, |
| 537 | ANYELEMENTOID); |
| 538 | |
| 539 | /* |
| 540 | * We can't deduce a range type from other polymorphic inputs, because |
| 541 | * there may be multiple range types for the same subtype. |
| 542 | */ |
| 543 | if (have_anyrange_result && !OidIsValid(anyrange_type)) |
| 544 | return false; |
| 545 | |
| 546 | /* Enforce ANYNONARRAY if needed */ |
| 547 | if (have_anynonarray && type_is_array(anyelement_type)) |
| 548 | return false; |
| 549 | |
| 550 | /* Enforce ANYENUM if needed */ |
| 551 | if (have_anyenum && !type_is_enum(anyelement_type)) |
| 552 | return false; |
| 553 | |
| 554 | /* |
| 555 | * Identify the collation to use for polymorphic OUT parameters. (It'll |
| 556 | * necessarily be the same for both anyelement and anyarray.) Note that |
| 557 | * range types are not collatable, so any possible internal collation of a |
| 558 | * range type is not considered here. |
| 559 | */ |
| 560 | if (OidIsValid(anyelement_type)) |
| 561 | anycollation = get_typcollation(anyelement_type); |
| 562 | else if (OidIsValid(anyarray_type)) |
| 563 | anycollation = get_typcollation(anyarray_type); |
| 564 | |
| 565 | if (OidIsValid(anycollation)) |
| 566 | { |
| 567 | /* |
| 568 | * The types are collatable, so consider whether to use a nondefault |
| 569 | * collation. We do so if we can identify the input collation used |
| 570 | * for the function. |
| 571 | */ |
| 572 | Oid inputcollation = exprInputCollation(call_expr); |
| 573 | |
| 574 | if (OidIsValid(inputcollation)) |
| 575 | anycollation = inputcollation; |
| 576 | } |
| 577 | |
| 578 | /* And finally replace the tuple column types as needed */ |
| 579 | for (i = 0; i < natts; i++) |
| 580 | { |
| 581 | Form_pg_attribute att = TupleDescAttr(tupdesc, i); |
| 582 | |
| 583 | switch (att->atttypid) |
| 584 | { |
| 585 | case ANYELEMENTOID: |
| 586 | case ANYNONARRAYOID: |
| 587 | case ANYENUMOID: |
| 588 | TupleDescInitEntry(tupdesc, i + 1, |
| 589 | NameStr(att->attname), |
| 590 | anyelement_type, |
| 591 | -1, |
| 592 | 0); |
| 593 | TupleDescInitEntryCollation(tupdesc, i + 1, anycollation); |
| 594 | break; |
| 595 | case ANYARRAYOID: |
| 596 | TupleDescInitEntry(tupdesc, i + 1, |
| 597 | NameStr(att->attname), |
| 598 | anyarray_type, |
| 599 | -1, |
| 600 | 0); |
| 601 | TupleDescInitEntryCollation(tupdesc, i + 1, anycollation); |
| 602 | break; |
| 603 | case ANYRANGEOID: |
| 604 | TupleDescInitEntry(tupdesc, i + 1, |
| 605 | NameStr(att->attname), |
| 606 | anyrange_type, |
| 607 | -1, |
| 608 | 0); |
| 609 | /* no collation should be attached to a range type */ |
| 610 | break; |
| 611 | default: |
| 612 | break; |
| 613 | } |
| 614 | } |
| 615 | |
| 616 | return true; |
| 617 | } |
| 618 | |
| 619 | /* |
| 620 | * Given the declared argument types and modes for a function, replace any |
| 621 | * polymorphic types (ANYELEMENT etc) with correct data types deduced from the |
| 622 | * input arguments. Returns true if able to deduce all types, false if not. |
| 623 | * This is the same logic as resolve_polymorphic_tupdesc, but with a different |
| 624 | * argument representation. |
| 625 | * |
| 626 | * argmodes may be NULL, in which case all arguments are assumed to be IN mode. |
| 627 | */ |
| 628 | bool |
| 629 | resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes, |
| 630 | Node *call_expr) |
| 631 | { |
| 632 | bool have_anyelement_result = false; |
| 633 | bool have_anyarray_result = false; |
| 634 | bool have_anyrange_result = false; |
| 635 | Oid anyelement_type = InvalidOid; |
| 636 | Oid anyarray_type = InvalidOid; |
| 637 | Oid anyrange_type = InvalidOid; |
| 638 | int inargno; |
| 639 | int i; |
| 640 | |
| 641 | /* First pass: resolve polymorphic inputs, check for outputs */ |
| 642 | inargno = 0; |
| 643 | for (i = 0; i < numargs; i++) |
| 644 | { |
| 645 | char argmode = argmodes ? argmodes[i] : PROARGMODE_IN; |
| 646 | |
| 647 | switch (argtypes[i]) |
| 648 | { |
| 649 | case ANYELEMENTOID: |
| 650 | case ANYNONARRAYOID: |
| 651 | case ANYENUMOID: |
| 652 | if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE) |
| 653 | have_anyelement_result = true; |
| 654 | else |
| 655 | { |
| 656 | if (!OidIsValid(anyelement_type)) |
| 657 | { |
| 658 | anyelement_type = get_call_expr_argtype(call_expr, |
| 659 | inargno); |
| 660 | if (!OidIsValid(anyelement_type)) |
| 661 | return false; |
| 662 | } |
| 663 | argtypes[i] = anyelement_type; |
| 664 | } |
| 665 | break; |
| 666 | case ANYARRAYOID: |
| 667 | if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE) |
| 668 | have_anyarray_result = true; |
| 669 | else |
| 670 | { |
| 671 | if (!OidIsValid(anyarray_type)) |
| 672 | { |
| 673 | anyarray_type = get_call_expr_argtype(call_expr, |
| 674 | inargno); |
| 675 | if (!OidIsValid(anyarray_type)) |
| 676 | return false; |
| 677 | } |
| 678 | argtypes[i] = anyarray_type; |
| 679 | } |
| 680 | break; |
| 681 | case ANYRANGEOID: |
| 682 | if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE) |
| 683 | have_anyrange_result = true; |
| 684 | else |
| 685 | { |
| 686 | if (!OidIsValid(anyrange_type)) |
| 687 | { |
| 688 | anyrange_type = get_call_expr_argtype(call_expr, |
| 689 | inargno); |
| 690 | if (!OidIsValid(anyrange_type)) |
| 691 | return false; |
| 692 | } |
| 693 | argtypes[i] = anyrange_type; |
| 694 | } |
| 695 | break; |
| 696 | default: |
| 697 | break; |
| 698 | } |
| 699 | if (argmode != PROARGMODE_OUT && argmode != PROARGMODE_TABLE) |
| 700 | inargno++; |
| 701 | } |
| 702 | |
| 703 | /* Done? */ |
| 704 | if (!have_anyelement_result && !have_anyarray_result && |
| 705 | !have_anyrange_result) |
| 706 | return true; |
| 707 | |
| 708 | /* If no input polymorphics, parser messed up */ |
| 709 | if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) && |
| 710 | !OidIsValid(anyrange_type)) |
| 711 | return false; |
| 712 | |
| 713 | /* If needed, deduce one polymorphic type from others */ |
| 714 | if (have_anyelement_result && !OidIsValid(anyelement_type)) |
| 715 | { |
| 716 | if (OidIsValid(anyarray_type)) |
| 717 | anyelement_type = resolve_generic_type(ANYELEMENTOID, |
| 718 | anyarray_type, |
| 719 | ANYARRAYOID); |
| 720 | if (OidIsValid(anyrange_type)) |
| 721 | { |
| 722 | Oid subtype = resolve_generic_type(ANYELEMENTOID, |
| 723 | anyrange_type, |
| 724 | ANYRANGEOID); |
| 725 | |
| 726 | /* check for inconsistent array and range results */ |
| 727 | if (OidIsValid(anyelement_type) && anyelement_type != subtype) |
| 728 | return false; |
| 729 | anyelement_type = subtype; |
| 730 | } |
| 731 | } |
| 732 | |
| 733 | if (have_anyarray_result && !OidIsValid(anyarray_type)) |
| 734 | anyarray_type = resolve_generic_type(ANYARRAYOID, |
| 735 | anyelement_type, |
| 736 | ANYELEMENTOID); |
| 737 | |
| 738 | /* |
| 739 | * We can't deduce a range type from other polymorphic inputs, because |
| 740 | * there may be multiple range types for the same subtype. |
| 741 | */ |
| 742 | if (have_anyrange_result && !OidIsValid(anyrange_type)) |
| 743 | return false; |
| 744 | |
| 745 | /* XXX do we need to enforce ANYNONARRAY or ANYENUM here? I think not */ |
| 746 | |
| 747 | /* And finally replace the output column types as needed */ |
| 748 | for (i = 0; i < numargs; i++) |
| 749 | { |
| 750 | switch (argtypes[i]) |
| 751 | { |
| 752 | case ANYELEMENTOID: |
| 753 | case ANYNONARRAYOID: |
| 754 | case ANYENUMOID: |
| 755 | argtypes[i] = anyelement_type; |
| 756 | break; |
| 757 | case ANYARRAYOID: |
| 758 | argtypes[i] = anyarray_type; |
| 759 | break; |
| 760 | case ANYRANGEOID: |
| 761 | argtypes[i] = anyrange_type; |
| 762 | break; |
| 763 | default: |
| 764 | break; |
| 765 | } |
| 766 | } |
| 767 | |
| 768 | return true; |
| 769 | } |
| 770 | |
| 771 | /* |
| 772 | * get_type_func_class |
| 773 | * Given the type OID, obtain its TYPEFUNC classification. |
| 774 | * Also, if it's a domain, return the base type OID. |
| 775 | * |
| 776 | * This is intended to centralize a bunch of formerly ad-hoc code for |
| 777 | * classifying types. The categories used here are useful for deciding |
| 778 | * how to handle functions returning the datatype. |
| 779 | */ |
| 780 | static TypeFuncClass |
| 781 | get_type_func_class(Oid typid, Oid *base_typeid) |
| 782 | { |
| 783 | *base_typeid = typid; |
| 784 | |
| 785 | switch (get_typtype(typid)) |
| 786 | { |
| 787 | case TYPTYPE_COMPOSITE: |
| 788 | return TYPEFUNC_COMPOSITE; |
| 789 | case TYPTYPE_BASE: |
| 790 | case TYPTYPE_ENUM: |
| 791 | case TYPTYPE_RANGE: |
| 792 | return TYPEFUNC_SCALAR; |
| 793 | case TYPTYPE_DOMAIN: |
| 794 | *base_typeid = typid = getBaseType(typid); |
| 795 | if (get_typtype(typid) == TYPTYPE_COMPOSITE) |
| 796 | return TYPEFUNC_COMPOSITE_DOMAIN; |
| 797 | else /* domain base type can't be a pseudotype */ |
| 798 | return TYPEFUNC_SCALAR; |
| 799 | case TYPTYPE_PSEUDO: |
| 800 | if (typid == RECORDOID) |
| 801 | return TYPEFUNC_RECORD; |
| 802 | |
| 803 | /* |
| 804 | * We treat VOID and CSTRING as legitimate scalar datatypes, |
| 805 | * mostly for the convenience of the JDBC driver (which wants to |
| 806 | * be able to do "SELECT * FROM foo()" for all legitimately |
| 807 | * user-callable functions). |
| 808 | */ |
| 809 | if (typid == VOIDOID || typid == CSTRINGOID) |
| 810 | return TYPEFUNC_SCALAR; |
| 811 | return TYPEFUNC_OTHER; |
| 812 | } |
| 813 | /* shouldn't get here, probably */ |
| 814 | return TYPEFUNC_OTHER; |
| 815 | } |
| 816 | |
| 817 | |
| 818 | /* |
| 819 | * get_func_arg_info |
| 820 | * |
| 821 | * Fetch info about the argument types, names, and IN/OUT modes from the |
| 822 | * pg_proc tuple. Return value is the total number of arguments. |
| 823 | * Other results are palloc'd. *p_argtypes is always filled in, but |
| 824 | * *p_argnames and *p_argmodes will be set NULL in the default cases |
| 825 | * (no names, and all IN arguments, respectively). |
| 826 | * |
| 827 | * Note that this function simply fetches what is in the pg_proc tuple; |
| 828 | * it doesn't do any interpretation of polymorphic types. |
| 829 | */ |
| 830 | int |
| 831 | get_func_arg_info(HeapTuple procTup, |
| 832 | Oid **p_argtypes, char ***p_argnames, char **p_argmodes) |
| 833 | { |
| 834 | Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup); |
| 835 | Datum proallargtypes; |
| 836 | Datum proargmodes; |
| 837 | Datum proargnames; |
| 838 | bool isNull; |
| 839 | ArrayType *arr; |
| 840 | int numargs; |
| 841 | Datum *elems; |
| 842 | int nelems; |
| 843 | int i; |
| 844 | |
| 845 | /* First discover the total number of parameters and get their types */ |
| 846 | proallargtypes = SysCacheGetAttr(PROCOID, procTup, |
| 847 | Anum_pg_proc_proallargtypes, |
| 848 | &isNull); |
| 849 | if (!isNull) |
| 850 | { |
| 851 | /* |
| 852 | * We expect the arrays to be 1-D arrays of the right types; verify |
| 853 | * that. For the OID and char arrays, we don't need to use |
| 854 | * deconstruct_array() since the array data is just going to look like |
| 855 | * a C array of values. |
| 856 | */ |
| 857 | arr = DatumGetArrayTypeP(proallargtypes); /* ensure not toasted */ |
| 858 | numargs = ARR_DIMS(arr)[0]; |
| 859 | if (ARR_NDIM(arr) != 1 || |
| 860 | numargs < 0 || |
| 861 | ARR_HASNULL(arr) || |
| 862 | ARR_ELEMTYPE(arr) != OIDOID) |
| 863 | elog(ERROR, "proallargtypes is not a 1-D Oid array" ); |
| 864 | Assert(numargs >= procStruct->pronargs); |
| 865 | *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid)); |
| 866 | memcpy(*p_argtypes, ARR_DATA_PTR(arr), |
| 867 | numargs * sizeof(Oid)); |
| 868 | } |
| 869 | else |
| 870 | { |
| 871 | /* If no proallargtypes, use proargtypes */ |
| 872 | numargs = procStruct->proargtypes.dim1; |
| 873 | Assert(numargs == procStruct->pronargs); |
| 874 | *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid)); |
| 875 | memcpy(*p_argtypes, procStruct->proargtypes.values, |
| 876 | numargs * sizeof(Oid)); |
| 877 | } |
| 878 | |
| 879 | /* Get argument names, if available */ |
| 880 | proargnames = SysCacheGetAttr(PROCOID, procTup, |
| 881 | Anum_pg_proc_proargnames, |
| 882 | &isNull); |
| 883 | if (isNull) |
| 884 | *p_argnames = NULL; |
| 885 | else |
| 886 | { |
| 887 | deconstruct_array(DatumGetArrayTypeP(proargnames), |
| 888 | TEXTOID, -1, false, 'i', |
| 889 | &elems, NULL, &nelems); |
| 890 | if (nelems != numargs) /* should not happen */ |
| 891 | elog(ERROR, "proargnames must have the same number of elements as the function has arguments" ); |
| 892 | *p_argnames = (char **) palloc(sizeof(char *) * numargs); |
| 893 | for (i = 0; i < numargs; i++) |
| 894 | (*p_argnames)[i] = TextDatumGetCString(elems[i]); |
| 895 | } |
| 896 | |
| 897 | /* Get argument modes, if available */ |
| 898 | proargmodes = SysCacheGetAttr(PROCOID, procTup, |
| 899 | Anum_pg_proc_proargmodes, |
| 900 | &isNull); |
| 901 | if (isNull) |
| 902 | *p_argmodes = NULL; |
| 903 | else |
| 904 | { |
| 905 | arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */ |
| 906 | if (ARR_NDIM(arr) != 1 || |
| 907 | ARR_DIMS(arr)[0] != numargs || |
| 908 | ARR_HASNULL(arr) || |
| 909 | ARR_ELEMTYPE(arr) != CHAROID) |
| 910 | elog(ERROR, "proargmodes is not a 1-D char array" ); |
| 911 | *p_argmodes = (char *) palloc(numargs * sizeof(char)); |
| 912 | memcpy(*p_argmodes, ARR_DATA_PTR(arr), |
| 913 | numargs * sizeof(char)); |
| 914 | } |
| 915 | |
| 916 | return numargs; |
| 917 | } |
| 918 | |
| 919 | /* |
| 920 | * get_func_trftypes |
| 921 | * |
| 922 | * Returns the number of transformed types used by function. |
| 923 | */ |
| 924 | int |
| 925 | get_func_trftypes(HeapTuple procTup, |
| 926 | Oid **p_trftypes) |
| 927 | { |
| 928 | Datum protrftypes; |
| 929 | ArrayType *arr; |
| 930 | int nelems; |
| 931 | bool isNull; |
| 932 | |
| 933 | protrftypes = SysCacheGetAttr(PROCOID, procTup, |
| 934 | Anum_pg_proc_protrftypes, |
| 935 | &isNull); |
| 936 | if (!isNull) |
| 937 | { |
| 938 | /* |
| 939 | * We expect the arrays to be 1-D arrays of the right types; verify |
| 940 | * that. For the OID and char arrays, we don't need to use |
| 941 | * deconstruct_array() since the array data is just going to look like |
| 942 | * a C array of values. |
| 943 | */ |
| 944 | arr = DatumGetArrayTypeP(protrftypes); /* ensure not toasted */ |
| 945 | nelems = ARR_DIMS(arr)[0]; |
| 946 | if (ARR_NDIM(arr) != 1 || |
| 947 | nelems < 0 || |
| 948 | ARR_HASNULL(arr) || |
| 949 | ARR_ELEMTYPE(arr) != OIDOID) |
| 950 | elog(ERROR, "protrftypes is not a 1-D Oid array" ); |
| 951 | Assert(nelems >= ((Form_pg_proc) GETSTRUCT(procTup))->pronargs); |
| 952 | *p_trftypes = (Oid *) palloc(nelems * sizeof(Oid)); |
| 953 | memcpy(*p_trftypes, ARR_DATA_PTR(arr), |
| 954 | nelems * sizeof(Oid)); |
| 955 | |
| 956 | return nelems; |
| 957 | } |
| 958 | else |
| 959 | return 0; |
| 960 | } |
| 961 | |
| 962 | /* |
| 963 | * get_func_input_arg_names |
| 964 | * |
| 965 | * Extract the names of input arguments only, given a function's |
| 966 | * proargnames and proargmodes entries in Datum form. |
| 967 | * |
| 968 | * Returns the number of input arguments, which is the length of the |
| 969 | * palloc'd array returned to *arg_names. Entries for unnamed args |
| 970 | * are set to NULL. You don't get anything if proargnames is NULL. |
| 971 | */ |
| 972 | int |
| 973 | get_func_input_arg_names(Datum proargnames, Datum proargmodes, |
| 974 | char ***arg_names) |
| 975 | { |
| 976 | ArrayType *arr; |
| 977 | int numargs; |
| 978 | Datum *argnames; |
| 979 | char *argmodes; |
| 980 | char **inargnames; |
| 981 | int numinargs; |
| 982 | int i; |
| 983 | |
| 984 | /* Do nothing if null proargnames */ |
| 985 | if (proargnames == PointerGetDatum(NULL)) |
| 986 | { |
| 987 | *arg_names = NULL; |
| 988 | return 0; |
| 989 | } |
| 990 | |
| 991 | /* |
| 992 | * We expect the arrays to be 1-D arrays of the right types; verify that. |
| 993 | * For proargmodes, we don't need to use deconstruct_array() since the |
| 994 | * array data is just going to look like a C array of values. |
| 995 | */ |
| 996 | arr = DatumGetArrayTypeP(proargnames); /* ensure not toasted */ |
| 997 | if (ARR_NDIM(arr) != 1 || |
| 998 | ARR_HASNULL(arr) || |
| 999 | ARR_ELEMTYPE(arr) != TEXTOID) |
| 1000 | elog(ERROR, "proargnames is not a 1-D text array" ); |
| 1001 | deconstruct_array(arr, TEXTOID, -1, false, 'i', |
| 1002 | &argnames, NULL, &numargs); |
| 1003 | if (proargmodes != PointerGetDatum(NULL)) |
| 1004 | { |
| 1005 | arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */ |
| 1006 | if (ARR_NDIM(arr) != 1 || |
| 1007 | ARR_DIMS(arr)[0] != numargs || |
| 1008 | ARR_HASNULL(arr) || |
| 1009 | ARR_ELEMTYPE(arr) != CHAROID) |
| 1010 | elog(ERROR, "proargmodes is not a 1-D char array" ); |
| 1011 | argmodes = (char *) ARR_DATA_PTR(arr); |
| 1012 | } |
| 1013 | else |
| 1014 | argmodes = NULL; |
| 1015 | |
| 1016 | /* zero elements probably shouldn't happen, but handle it gracefully */ |
| 1017 | if (numargs <= 0) |
| 1018 | { |
| 1019 | *arg_names = NULL; |
| 1020 | return 0; |
| 1021 | } |
| 1022 | |
| 1023 | /* extract input-argument names */ |
| 1024 | inargnames = (char **) palloc(numargs * sizeof(char *)); |
| 1025 | numinargs = 0; |
| 1026 | for (i = 0; i < numargs; i++) |
| 1027 | { |
| 1028 | if (argmodes == NULL || |
| 1029 | argmodes[i] == PROARGMODE_IN || |
| 1030 | argmodes[i] == PROARGMODE_INOUT || |
| 1031 | argmodes[i] == PROARGMODE_VARIADIC) |
| 1032 | { |
| 1033 | char *pname = TextDatumGetCString(argnames[i]); |
| 1034 | |
| 1035 | if (pname[0] != '\0') |
| 1036 | inargnames[numinargs] = pname; |
| 1037 | else |
| 1038 | inargnames[numinargs] = NULL; |
| 1039 | numinargs++; |
| 1040 | } |
| 1041 | } |
| 1042 | |
| 1043 | *arg_names = inargnames; |
| 1044 | return numinargs; |
| 1045 | } |
| 1046 | |
| 1047 | |
| 1048 | /* |
| 1049 | * get_func_result_name |
| 1050 | * |
| 1051 | * If the function has exactly one output parameter, and that parameter |
| 1052 | * is named, return the name (as a palloc'd string). Else return NULL. |
| 1053 | * |
| 1054 | * This is used to determine the default output column name for functions |
| 1055 | * returning scalar types. |
| 1056 | */ |
| 1057 | char * |
| 1058 | get_func_result_name(Oid functionId) |
| 1059 | { |
| 1060 | char *result; |
| 1061 | HeapTuple procTuple; |
| 1062 | Datum proargmodes; |
| 1063 | Datum proargnames; |
| 1064 | bool isnull; |
| 1065 | ArrayType *arr; |
| 1066 | int numargs; |
| 1067 | char *argmodes; |
| 1068 | Datum *argnames; |
| 1069 | int numoutargs; |
| 1070 | int nargnames; |
| 1071 | int i; |
| 1072 | |
| 1073 | /* First fetch the function's pg_proc row */ |
| 1074 | procTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionId)); |
| 1075 | if (!HeapTupleIsValid(procTuple)) |
| 1076 | elog(ERROR, "cache lookup failed for function %u" , functionId); |
| 1077 | |
| 1078 | /* If there are no named OUT parameters, return NULL */ |
| 1079 | if (heap_attisnull(procTuple, Anum_pg_proc_proargmodes, NULL) || |
| 1080 | heap_attisnull(procTuple, Anum_pg_proc_proargnames, NULL)) |
| 1081 | result = NULL; |
| 1082 | else |
| 1083 | { |
| 1084 | /* Get the data out of the tuple */ |
| 1085 | proargmodes = SysCacheGetAttr(PROCOID, procTuple, |
| 1086 | Anum_pg_proc_proargmodes, |
| 1087 | &isnull); |
| 1088 | Assert(!isnull); |
| 1089 | proargnames = SysCacheGetAttr(PROCOID, procTuple, |
| 1090 | Anum_pg_proc_proargnames, |
| 1091 | &isnull); |
| 1092 | Assert(!isnull); |
| 1093 | |
| 1094 | /* |
| 1095 | * We expect the arrays to be 1-D arrays of the right types; verify |
| 1096 | * that. For the char array, we don't need to use deconstruct_array() |
| 1097 | * since the array data is just going to look like a C array of |
| 1098 | * values. |
| 1099 | */ |
| 1100 | arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */ |
| 1101 | numargs = ARR_DIMS(arr)[0]; |
| 1102 | if (ARR_NDIM(arr) != 1 || |
| 1103 | numargs < 0 || |
| 1104 | ARR_HASNULL(arr) || |
| 1105 | ARR_ELEMTYPE(arr) != CHAROID) |
| 1106 | elog(ERROR, "proargmodes is not a 1-D char array" ); |
| 1107 | argmodes = (char *) ARR_DATA_PTR(arr); |
| 1108 | arr = DatumGetArrayTypeP(proargnames); /* ensure not toasted */ |
| 1109 | if (ARR_NDIM(arr) != 1 || |
| 1110 | ARR_DIMS(arr)[0] != numargs || |
| 1111 | ARR_HASNULL(arr) || |
| 1112 | ARR_ELEMTYPE(arr) != TEXTOID) |
| 1113 | elog(ERROR, "proargnames is not a 1-D text array" ); |
| 1114 | deconstruct_array(arr, TEXTOID, -1, false, 'i', |
| 1115 | &argnames, NULL, &nargnames); |
| 1116 | Assert(nargnames == numargs); |
| 1117 | |
| 1118 | /* scan for output argument(s) */ |
| 1119 | result = NULL; |
| 1120 | numoutargs = 0; |
| 1121 | for (i = 0; i < numargs; i++) |
| 1122 | { |
| 1123 | if (argmodes[i] == PROARGMODE_IN || |
| 1124 | argmodes[i] == PROARGMODE_VARIADIC) |
| 1125 | continue; |
| 1126 | Assert(argmodes[i] == PROARGMODE_OUT || |
| 1127 | argmodes[i] == PROARGMODE_INOUT || |
| 1128 | argmodes[i] == PROARGMODE_TABLE); |
| 1129 | if (++numoutargs > 1) |
| 1130 | { |
| 1131 | /* multiple out args, so forget it */ |
| 1132 | result = NULL; |
| 1133 | break; |
| 1134 | } |
| 1135 | result = TextDatumGetCString(argnames[i]); |
| 1136 | if (result == NULL || result[0] == '\0') |
| 1137 | { |
| 1138 | /* Parameter is not named, so forget it */ |
| 1139 | result = NULL; |
| 1140 | break; |
| 1141 | } |
| 1142 | } |
| 1143 | } |
| 1144 | |
| 1145 | ReleaseSysCache(procTuple); |
| 1146 | |
| 1147 | return result; |
| 1148 | } |
| 1149 | |
| 1150 | |
| 1151 | /* |
| 1152 | * build_function_result_tupdesc_t |
| 1153 | * |
| 1154 | * Given a pg_proc row for a function, return a tuple descriptor for the |
| 1155 | * result rowtype, or NULL if the function does not have OUT parameters. |
| 1156 | * |
| 1157 | * Note that this does not handle resolution of polymorphic types; |
| 1158 | * that is deliberate. |
| 1159 | */ |
| 1160 | TupleDesc |
| 1161 | build_function_result_tupdesc_t(HeapTuple procTuple) |
| 1162 | { |
| 1163 | Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(procTuple); |
| 1164 | Datum proallargtypes; |
| 1165 | Datum proargmodes; |
| 1166 | Datum proargnames; |
| 1167 | bool isnull; |
| 1168 | |
| 1169 | /* Return NULL if the function isn't declared to return RECORD */ |
| 1170 | if (procform->prorettype != RECORDOID) |
| 1171 | return NULL; |
| 1172 | |
| 1173 | /* If there are no OUT parameters, return NULL */ |
| 1174 | if (heap_attisnull(procTuple, Anum_pg_proc_proallargtypes, NULL) || |
| 1175 | heap_attisnull(procTuple, Anum_pg_proc_proargmodes, NULL)) |
| 1176 | return NULL; |
| 1177 | |
| 1178 | /* Get the data out of the tuple */ |
| 1179 | proallargtypes = SysCacheGetAttr(PROCOID, procTuple, |
| 1180 | Anum_pg_proc_proallargtypes, |
| 1181 | &isnull); |
| 1182 | Assert(!isnull); |
| 1183 | proargmodes = SysCacheGetAttr(PROCOID, procTuple, |
| 1184 | Anum_pg_proc_proargmodes, |
| 1185 | &isnull); |
| 1186 | Assert(!isnull); |
| 1187 | proargnames = SysCacheGetAttr(PROCOID, procTuple, |
| 1188 | Anum_pg_proc_proargnames, |
| 1189 | &isnull); |
| 1190 | if (isnull) |
| 1191 | proargnames = PointerGetDatum(NULL); /* just to be sure */ |
| 1192 | |
| 1193 | return build_function_result_tupdesc_d(procform->prokind, |
| 1194 | proallargtypes, |
| 1195 | proargmodes, |
| 1196 | proargnames); |
| 1197 | } |
| 1198 | |
| 1199 | /* |
| 1200 | * build_function_result_tupdesc_d |
| 1201 | * |
| 1202 | * Build a RECORD function's tupledesc from the pg_proc proallargtypes, |
| 1203 | * proargmodes, and proargnames arrays. This is split out for the |
| 1204 | * convenience of ProcedureCreate, which needs to be able to compute the |
| 1205 | * tupledesc before actually creating the function. |
| 1206 | * |
| 1207 | * For functions (but not for procedures), returns NULL if there are not at |
| 1208 | * least two OUT or INOUT arguments. |
| 1209 | */ |
| 1210 | TupleDesc |
| 1211 | build_function_result_tupdesc_d(char prokind, |
| 1212 | Datum proallargtypes, |
| 1213 | Datum proargmodes, |
| 1214 | Datum proargnames) |
| 1215 | { |
| 1216 | TupleDesc desc; |
| 1217 | ArrayType *arr; |
| 1218 | int numargs; |
| 1219 | Oid *argtypes; |
| 1220 | char *argmodes; |
| 1221 | Datum *argnames = NULL; |
| 1222 | Oid *outargtypes; |
| 1223 | char **outargnames; |
| 1224 | int numoutargs; |
| 1225 | int nargnames; |
| 1226 | int i; |
| 1227 | |
| 1228 | /* Can't have output args if columns are null */ |
| 1229 | if (proallargtypes == PointerGetDatum(NULL) || |
| 1230 | proargmodes == PointerGetDatum(NULL)) |
| 1231 | return NULL; |
| 1232 | |
| 1233 | /* |
| 1234 | * We expect the arrays to be 1-D arrays of the right types; verify that. |
| 1235 | * For the OID and char arrays, we don't need to use deconstruct_array() |
| 1236 | * since the array data is just going to look like a C array of values. |
| 1237 | */ |
| 1238 | arr = DatumGetArrayTypeP(proallargtypes); /* ensure not toasted */ |
| 1239 | numargs = ARR_DIMS(arr)[0]; |
| 1240 | if (ARR_NDIM(arr) != 1 || |
| 1241 | numargs < 0 || |
| 1242 | ARR_HASNULL(arr) || |
| 1243 | ARR_ELEMTYPE(arr) != OIDOID) |
| 1244 | elog(ERROR, "proallargtypes is not a 1-D Oid array" ); |
| 1245 | argtypes = (Oid *) ARR_DATA_PTR(arr); |
| 1246 | arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */ |
| 1247 | if (ARR_NDIM(arr) != 1 || |
| 1248 | ARR_DIMS(arr)[0] != numargs || |
| 1249 | ARR_HASNULL(arr) || |
| 1250 | ARR_ELEMTYPE(arr) != CHAROID) |
| 1251 | elog(ERROR, "proargmodes is not a 1-D char array" ); |
| 1252 | argmodes = (char *) ARR_DATA_PTR(arr); |
| 1253 | if (proargnames != PointerGetDatum(NULL)) |
| 1254 | { |
| 1255 | arr = DatumGetArrayTypeP(proargnames); /* ensure not toasted */ |
| 1256 | if (ARR_NDIM(arr) != 1 || |
| 1257 | ARR_DIMS(arr)[0] != numargs || |
| 1258 | ARR_HASNULL(arr) || |
| 1259 | ARR_ELEMTYPE(arr) != TEXTOID) |
| 1260 | elog(ERROR, "proargnames is not a 1-D text array" ); |
| 1261 | deconstruct_array(arr, TEXTOID, -1, false, 'i', |
| 1262 | &argnames, NULL, &nargnames); |
| 1263 | Assert(nargnames == numargs); |
| 1264 | } |
| 1265 | |
| 1266 | /* zero elements probably shouldn't happen, but handle it gracefully */ |
| 1267 | if (numargs <= 0) |
| 1268 | return NULL; |
| 1269 | |
| 1270 | /* extract output-argument types and names */ |
| 1271 | outargtypes = (Oid *) palloc(numargs * sizeof(Oid)); |
| 1272 | outargnames = (char **) palloc(numargs * sizeof(char *)); |
| 1273 | numoutargs = 0; |
| 1274 | for (i = 0; i < numargs; i++) |
| 1275 | { |
| 1276 | char *pname; |
| 1277 | |
| 1278 | if (argmodes[i] == PROARGMODE_IN || |
| 1279 | argmodes[i] == PROARGMODE_VARIADIC) |
| 1280 | continue; |
| 1281 | Assert(argmodes[i] == PROARGMODE_OUT || |
| 1282 | argmodes[i] == PROARGMODE_INOUT || |
| 1283 | argmodes[i] == PROARGMODE_TABLE); |
| 1284 | outargtypes[numoutargs] = argtypes[i]; |
| 1285 | if (argnames) |
| 1286 | pname = TextDatumGetCString(argnames[i]); |
| 1287 | else |
| 1288 | pname = NULL; |
| 1289 | if (pname == NULL || pname[0] == '\0') |
| 1290 | { |
| 1291 | /* Parameter is not named, so gin up a column name */ |
| 1292 | pname = psprintf("column%d" , numoutargs + 1); |
| 1293 | } |
| 1294 | outargnames[numoutargs] = pname; |
| 1295 | numoutargs++; |
| 1296 | } |
| 1297 | |
| 1298 | /* |
| 1299 | * If there is no output argument, or only one, the function does not |
| 1300 | * return tuples. |
| 1301 | */ |
| 1302 | if (numoutargs < 2 && prokind != PROKIND_PROCEDURE) |
| 1303 | return NULL; |
| 1304 | |
| 1305 | desc = CreateTemplateTupleDesc(numoutargs); |
| 1306 | for (i = 0; i < numoutargs; i++) |
| 1307 | { |
| 1308 | TupleDescInitEntry(desc, i + 1, |
| 1309 | outargnames[i], |
| 1310 | outargtypes[i], |
| 1311 | -1, |
| 1312 | 0); |
| 1313 | } |
| 1314 | |
| 1315 | return desc; |
| 1316 | } |
| 1317 | |
| 1318 | |
| 1319 | /* |
| 1320 | * RelationNameGetTupleDesc |
| 1321 | * |
| 1322 | * Given a (possibly qualified) relation name, build a TupleDesc. |
| 1323 | * |
| 1324 | * Note: while this works as advertised, it's seldom the best way to |
| 1325 | * build a tupdesc for a function's result type. It's kept around |
| 1326 | * only for backwards compatibility with existing user-written code. |
| 1327 | */ |
| 1328 | TupleDesc |
| 1329 | RelationNameGetTupleDesc(const char *relname) |
| 1330 | { |
| 1331 | RangeVar *relvar; |
| 1332 | Relation rel; |
| 1333 | TupleDesc tupdesc; |
| 1334 | List *relname_list; |
| 1335 | |
| 1336 | /* Open relation and copy the tuple description */ |
| 1337 | relname_list = stringToQualifiedNameList(relname); |
| 1338 | relvar = makeRangeVarFromNameList(relname_list); |
| 1339 | rel = relation_openrv(relvar, AccessShareLock); |
| 1340 | tupdesc = CreateTupleDescCopy(RelationGetDescr(rel)); |
| 1341 | relation_close(rel, AccessShareLock); |
| 1342 | |
| 1343 | return tupdesc; |
| 1344 | } |
| 1345 | |
| 1346 | /* |
| 1347 | * TypeGetTupleDesc |
| 1348 | * |
| 1349 | * Given a type Oid, build a TupleDesc. (In most cases you should be |
| 1350 | * using get_call_result_type or one of its siblings instead of this |
| 1351 | * routine, so that you can handle OUT parameters, RECORD result type, |
| 1352 | * and polymorphic results.) |
| 1353 | * |
| 1354 | * If the type is composite, *and* a colaliases List is provided, *and* |
| 1355 | * the List is of natts length, use the aliases instead of the relation |
| 1356 | * attnames. (NB: this usage is deprecated since it may result in |
| 1357 | * creation of unnecessary transient record types.) |
| 1358 | * |
| 1359 | * If the type is a base type, a single item alias List is required. |
| 1360 | */ |
| 1361 | TupleDesc |
| 1362 | TypeGetTupleDesc(Oid typeoid, List *colaliases) |
| 1363 | { |
| 1364 | Oid base_typeoid; |
| 1365 | TypeFuncClass functypclass = get_type_func_class(typeoid, &base_typeoid); |
| 1366 | TupleDesc tupdesc = NULL; |
| 1367 | |
| 1368 | /* |
| 1369 | * Build a suitable tupledesc representing the output rows. We |
| 1370 | * intentionally do not support TYPEFUNC_COMPOSITE_DOMAIN here, as it's |
| 1371 | * unlikely that legacy callers of this obsolete function would be |
| 1372 | * prepared to apply domain constraints. |
| 1373 | */ |
| 1374 | if (functypclass == TYPEFUNC_COMPOSITE) |
| 1375 | { |
| 1376 | /* Composite data type, e.g. a table's row type */ |
| 1377 | tupdesc = lookup_rowtype_tupdesc_copy(base_typeoid, -1); |
| 1378 | |
| 1379 | if (colaliases != NIL) |
| 1380 | { |
| 1381 | int natts = tupdesc->natts; |
| 1382 | int varattno; |
| 1383 | |
| 1384 | /* does the list length match the number of attributes? */ |
| 1385 | if (list_length(colaliases) != natts) |
| 1386 | ereport(ERROR, |
| 1387 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
| 1388 | errmsg("number of aliases does not match number of columns" ))); |
| 1389 | |
| 1390 | /* OK, use the aliases instead */ |
| 1391 | for (varattno = 0; varattno < natts; varattno++) |
| 1392 | { |
| 1393 | char *label = strVal(list_nth(colaliases, varattno)); |
| 1394 | Form_pg_attribute attr = TupleDescAttr(tupdesc, varattno); |
| 1395 | |
| 1396 | if (label != NULL) |
| 1397 | namestrcpy(&(attr->attname), label); |
| 1398 | } |
| 1399 | |
| 1400 | /* The tuple type is now an anonymous record type */ |
| 1401 | tupdesc->tdtypeid = RECORDOID; |
| 1402 | tupdesc->tdtypmod = -1; |
| 1403 | } |
| 1404 | } |
| 1405 | else if (functypclass == TYPEFUNC_SCALAR) |
| 1406 | { |
| 1407 | /* Base data type, i.e. scalar */ |
| 1408 | char *attname; |
| 1409 | |
| 1410 | /* the alias list is required for base types */ |
| 1411 | if (colaliases == NIL) |
| 1412 | ereport(ERROR, |
| 1413 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
| 1414 | errmsg("no column alias was provided" ))); |
| 1415 | |
| 1416 | /* the alias list length must be 1 */ |
| 1417 | if (list_length(colaliases) != 1) |
| 1418 | ereport(ERROR, |
| 1419 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
| 1420 | errmsg("number of aliases does not match number of columns" ))); |
| 1421 | |
| 1422 | /* OK, get the column alias */ |
| 1423 | attname = strVal(linitial(colaliases)); |
| 1424 | |
| 1425 | tupdesc = CreateTemplateTupleDesc(1); |
| 1426 | TupleDescInitEntry(tupdesc, |
| 1427 | (AttrNumber) 1, |
| 1428 | attname, |
| 1429 | typeoid, |
| 1430 | -1, |
| 1431 | 0); |
| 1432 | } |
| 1433 | else if (functypclass == TYPEFUNC_RECORD) |
| 1434 | { |
| 1435 | /* XXX can't support this because typmod wasn't passed in ... */ |
| 1436 | ereport(ERROR, |
| 1437 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
| 1438 | errmsg("could not determine row description for function returning record" ))); |
| 1439 | } |
| 1440 | else |
| 1441 | { |
| 1442 | /* crummy error message, but parser should have caught this */ |
| 1443 | elog(ERROR, "function in FROM has unsupported return type" ); |
| 1444 | } |
| 1445 | |
| 1446 | return tupdesc; |
| 1447 | } |
| 1448 | |
| 1449 | /* |
| 1450 | * extract_variadic_args |
| 1451 | * |
| 1452 | * Extract a set of argument values, types and NULL markers for a given |
| 1453 | * input function which makes use of a VARIADIC input whose argument list |
| 1454 | * depends on the caller context. When doing a VARIADIC call, the caller |
| 1455 | * has provided one argument made of an array of values, so deconstruct the |
| 1456 | * array data before using it for the next processing. If no VARIADIC call |
| 1457 | * is used, just fill in the status data based on all the arguments given |
| 1458 | * by the caller. |
| 1459 | * |
| 1460 | * This function returns the number of arguments generated, or -1 in the |
| 1461 | * case of "VARIADIC NULL". |
| 1462 | */ |
| 1463 | int |
| 1464 | (FunctionCallInfo fcinfo, int variadic_start, |
| 1465 | bool convert_unknown, Datum **args, Oid **types, |
| 1466 | bool **nulls) |
| 1467 | { |
| 1468 | bool variadic = get_fn_expr_variadic(fcinfo->flinfo); |
| 1469 | Datum *args_res; |
| 1470 | bool *nulls_res; |
| 1471 | Oid *types_res; |
| 1472 | int nargs, |
| 1473 | i; |
| 1474 | |
| 1475 | *args = NULL; |
| 1476 | *types = NULL; |
| 1477 | *nulls = NULL; |
| 1478 | |
| 1479 | if (variadic) |
| 1480 | { |
| 1481 | ArrayType *array_in; |
| 1482 | Oid element_type; |
| 1483 | bool typbyval; |
| 1484 | char typalign; |
| 1485 | int16 typlen; |
| 1486 | |
| 1487 | Assert(PG_NARGS() == variadic_start + 1); |
| 1488 | |
| 1489 | if (PG_ARGISNULL(variadic_start)) |
| 1490 | return -1; |
| 1491 | |
| 1492 | array_in = PG_GETARG_ARRAYTYPE_P(variadic_start); |
| 1493 | element_type = ARR_ELEMTYPE(array_in); |
| 1494 | |
| 1495 | get_typlenbyvalalign(element_type, |
| 1496 | &typlen, &typbyval, &typalign); |
| 1497 | deconstruct_array(array_in, element_type, typlen, typbyval, |
| 1498 | typalign, &args_res, &nulls_res, |
| 1499 | &nargs); |
| 1500 | |
| 1501 | /* All the elements of the array have the same type */ |
| 1502 | types_res = (Oid *) palloc0(nargs * sizeof(Oid)); |
| 1503 | for (i = 0; i < nargs; i++) |
| 1504 | types_res[i] = element_type; |
| 1505 | } |
| 1506 | else |
| 1507 | { |
| 1508 | nargs = PG_NARGS() - variadic_start; |
| 1509 | Assert(nargs > 0); |
| 1510 | nulls_res = (bool *) palloc0(nargs * sizeof(bool)); |
| 1511 | args_res = (Datum *) palloc0(nargs * sizeof(Datum)); |
| 1512 | types_res = (Oid *) palloc0(nargs * sizeof(Oid)); |
| 1513 | |
| 1514 | for (i = 0; i < nargs; i++) |
| 1515 | { |
| 1516 | nulls_res[i] = PG_ARGISNULL(i + variadic_start); |
| 1517 | types_res[i] = get_fn_expr_argtype(fcinfo->flinfo, |
| 1518 | i + variadic_start); |
| 1519 | |
| 1520 | /* |
| 1521 | * Turn a constant (more or less literal) value that's of unknown |
| 1522 | * type into text if required. Unknowns come in as a cstring |
| 1523 | * pointer. Note: for functions declared as taking type "any", the |
| 1524 | * parser will not do any type conversion on unknown-type literals |
| 1525 | * (that is, undecorated strings or NULLs). |
| 1526 | */ |
| 1527 | if (convert_unknown && |
| 1528 | types_res[i] == UNKNOWNOID && |
| 1529 | get_fn_expr_arg_stable(fcinfo->flinfo, i + variadic_start)) |
| 1530 | { |
| 1531 | types_res[i] = TEXTOID; |
| 1532 | |
| 1533 | if (PG_ARGISNULL(i + variadic_start)) |
| 1534 | args_res[i] = (Datum) 0; |
| 1535 | else |
| 1536 | args_res[i] = |
| 1537 | CStringGetTextDatum(PG_GETARG_POINTER(i + variadic_start)); |
| 1538 | } |
| 1539 | else |
| 1540 | { |
| 1541 | /* no conversion needed, just take the datum as given */ |
| 1542 | args_res[i] = PG_GETARG_DATUM(i + variadic_start); |
| 1543 | } |
| 1544 | |
| 1545 | if (!OidIsValid(types_res[i]) || |
| 1546 | (convert_unknown && types_res[i] == UNKNOWNOID)) |
| 1547 | ereport(ERROR, |
| 1548 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 1549 | errmsg("could not determine data type for argument %d" , |
| 1550 | i + 1))); |
| 1551 | } |
| 1552 | } |
| 1553 | |
| 1554 | /* Fill in results */ |
| 1555 | *args = args_res; |
| 1556 | *nulls = nulls_res; |
| 1557 | *types = types_res; |
| 1558 | |
| 1559 | return nargs; |
| 1560 | } |
| 1561 | |