00001
00002
00003
00004
00005
00006
00007
00008
00009 #include "postgres.h"
00010
00011 #include <tcl.h>
00012
00013 #include <unistd.h>
00014 #include <fcntl.h>
00015
00016
00017 #ifndef CONST84
00018 #define CONST84
00019 #endif
00020
00021
00022 #ifndef CONST86
00023 #define CONST86
00024 #endif
00025
00026 #include "access/htup_details.h"
00027 #include "access/xact.h"
00028 #include "catalog/pg_proc.h"
00029 #include "catalog/pg_type.h"
00030 #include "commands/trigger.h"
00031 #include "executor/spi.h"
00032 #include "fmgr.h"
00033 #include "miscadmin.h"
00034 #include "nodes/makefuncs.h"
00035 #include "parser/parse_type.h"
00036 #include "tcop/tcopprot.h"
00037 #include "utils/builtins.h"
00038 #include "utils/lsyscache.h"
00039 #include "utils/memutils.h"
00040 #include "utils/rel.h"
00041 #include "utils/syscache.h"
00042 #include "utils/typcache.h"
00043
00044
00045 #define HAVE_TCL_VERSION(maj,min) \
00046 ((TCL_MAJOR_VERSION > maj) || \
00047 (TCL_MAJOR_VERSION == maj && TCL_MINOR_VERSION >= min))
00048
00049
00050 #if !HAVE_TCL_VERSION(8,0)
00051 #define Tcl_GetStringResult(interp) ((interp)->result)
00052 #endif
00053
00054
00055 #undef TEXTDOMAIN
00056 #define TEXTDOMAIN PG_TEXTDOMAIN("pltcl")
00057
00058 #if defined(UNICODE_CONVERSION) && HAVE_TCL_VERSION(8,1)
00059
00060 #include "mb/pg_wchar.h"
00061
00062 static unsigned char *
00063 utf_u2e(unsigned char *src)
00064 {
00065 return pg_do_encoding_conversion(src, strlen(src), PG_UTF8, GetDatabaseEncoding());
00066 }
00067
00068 static unsigned char *
00069 utf_e2u(unsigned char *src)
00070 {
00071 return pg_do_encoding_conversion(src, strlen(src), GetDatabaseEncoding(), PG_UTF8);
00072 }
00073
00074 #define PLTCL_UTF
00075 #define UTF_BEGIN do { \
00076 unsigned char *_pltcl_utf_src; \
00077 unsigned char *_pltcl_utf_dst
00078 #define UTF_END if (_pltcl_utf_src!=_pltcl_utf_dst) \
00079 pfree(_pltcl_utf_dst); } while (0)
00080 #define UTF_U2E(x) (_pltcl_utf_dst=utf_u2e(_pltcl_utf_src=(x)))
00081 #define UTF_E2U(x) (_pltcl_utf_dst=utf_e2u(_pltcl_utf_src=(x)))
00082 #else
00083
00084 #define UTF_BEGIN
00085 #define UTF_END
00086 #define UTF_U2E(x) (x)
00087 #define UTF_E2U(x) (x)
00088 #endif
00089
00090 PG_MODULE_MAGIC;
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103 typedef struct pltcl_interp_desc
00104 {
00105 Oid user_id;
00106 Tcl_Interp *interp;
00107 Tcl_HashTable query_hash;
00108 } pltcl_interp_desc;
00109
00110
00111
00112
00113
00114 typedef struct pltcl_proc_desc
00115 {
00116 char *user_proname;
00117 char *internal_proname;
00118 TransactionId fn_xmin;
00119 ItemPointerData fn_tid;
00120 bool fn_readonly;
00121 bool lanpltrusted;
00122 pltcl_interp_desc *interp_desc;
00123 FmgrInfo result_in_func;
00124 Oid result_typioparam;
00125 int nargs;
00126 FmgrInfo arg_out_func[FUNC_MAX_ARGS];
00127 bool arg_is_rowtype[FUNC_MAX_ARGS];
00128 } pltcl_proc_desc;
00129
00130
00131
00132
00133
00134 typedef struct pltcl_query_desc
00135 {
00136 char qname[20];
00137 SPIPlanPtr plan;
00138 int nargs;
00139 Oid *argtypes;
00140 FmgrInfo *arginfuncs;
00141 Oid *argtypioparams;
00142 } pltcl_query_desc;
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156 typedef struct pltcl_proc_key
00157 {
00158 Oid proc_id;
00159
00160
00161
00162
00163
00164 Oid is_trigger;
00165 Oid user_id;
00166 } pltcl_proc_key;
00167
00168 typedef struct pltcl_proc_ptr
00169 {
00170 pltcl_proc_key proc_key;
00171 pltcl_proc_desc *proc_ptr;
00172 } pltcl_proc_ptr;
00173
00174
00175
00176
00177
00178 static bool pltcl_pm_init_done = false;
00179 static Tcl_Interp *pltcl_hold_interp = NULL;
00180 static HTAB *pltcl_interp_htab = NULL;
00181 static HTAB *pltcl_proc_htab = NULL;
00182
00183
00184 static FunctionCallInfo pltcl_current_fcinfo = NULL;
00185 static pltcl_proc_desc *pltcl_current_prodesc = NULL;
00186
00187
00188
00189
00190 Datum pltcl_call_handler(PG_FUNCTION_ARGS);
00191 Datum pltclu_call_handler(PG_FUNCTION_ARGS);
00192 void _PG_init(void);
00193
00194 static void pltcl_init_interp(pltcl_interp_desc *interp_desc, bool pltrusted);
00195 static pltcl_interp_desc *pltcl_fetch_interp(bool pltrusted);
00196 static void pltcl_init_load_unknown(Tcl_Interp *interp);
00197
00198 static Datum pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted);
00199
00200 static Datum pltcl_func_handler(PG_FUNCTION_ARGS, bool pltrusted);
00201
00202 static HeapTuple pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted);
00203
00204 static void throw_tcl_error(Tcl_Interp *interp, const char *proname);
00205
00206 static pltcl_proc_desc *compile_pltcl_function(Oid fn_oid, Oid tgreloid,
00207 bool pltrusted);
00208
00209 static int pltcl_elog(ClientData cdata, Tcl_Interp *interp,
00210 int argc, CONST84 char *argv[]);
00211 static int pltcl_quote(ClientData cdata, Tcl_Interp *interp,
00212 int argc, CONST84 char *argv[]);
00213 static int pltcl_argisnull(ClientData cdata, Tcl_Interp *interp,
00214 int argc, CONST84 char *argv[]);
00215 static int pltcl_returnnull(ClientData cdata, Tcl_Interp *interp,
00216 int argc, CONST84 char *argv[]);
00217
00218 static int pltcl_SPI_execute(ClientData cdata, Tcl_Interp *interp,
00219 int argc, CONST84 char *argv[]);
00220 static int pltcl_process_SPI_result(Tcl_Interp *interp,
00221 CONST84 char *arrayname,
00222 CONST84 char *loop_body,
00223 int spi_rc,
00224 SPITupleTable *tuptable,
00225 int ntuples);
00226 static int pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
00227 int argc, CONST84 char *argv[]);
00228 static int pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp,
00229 int argc, CONST84 char *argv[]);
00230 static int pltcl_SPI_lastoid(ClientData cdata, Tcl_Interp *interp,
00231 int argc, CONST84 char *argv[]);
00232
00233 static void pltcl_set_tuple_values(Tcl_Interp *interp, CONST84 char *arrayname,
00234 int tupno, HeapTuple tuple, TupleDesc tupdesc);
00235 static void pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc,
00236 Tcl_DString *retval);
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251 #if HAVE_TCL_VERSION(8,4)
00252
00253 static ClientData
00254 pltcl_InitNotifier(void)
00255 {
00256 static int fakeThreadKey;
00257
00258 return (ClientData) &(fakeThreadKey);
00259 }
00260
00261 static void
00262 pltcl_FinalizeNotifier(ClientData clientData)
00263 {
00264 }
00265
00266 static void
00267 pltcl_SetTimer(CONST86 Tcl_Time *timePtr)
00268 {
00269 }
00270
00271 static void
00272 pltcl_AlertNotifier(ClientData clientData)
00273 {
00274 }
00275
00276 static void
00277 pltcl_CreateFileHandler(int fd, int mask,
00278 Tcl_FileProc *proc, ClientData clientData)
00279 {
00280 }
00281
00282 static void
00283 pltcl_DeleteFileHandler(int fd)
00284 {
00285 }
00286
00287 static void
00288 pltcl_ServiceModeHook(int mode)
00289 {
00290 }
00291
00292 static int
00293 pltcl_WaitForEvent(CONST86 Tcl_Time *timePtr)
00294 {
00295 return 0;
00296 }
00297 #endif
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311 static void
00312 perm_fmgr_info(Oid functionId, FmgrInfo *finfo)
00313 {
00314 fmgr_info_cxt(functionId, finfo, TopMemoryContext);
00315 }
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325 void
00326 _PG_init(void)
00327 {
00328 HASHCTL hash_ctl;
00329
00330
00331 if (pltcl_pm_init_done)
00332 return;
00333
00334 pg_bindtextdomain(TEXTDOMAIN);
00335
00336 #ifdef WIN32
00337
00338 Tcl_FindExecutable("");
00339 #endif
00340
00341 #if HAVE_TCL_VERSION(8,4)
00342
00343
00344
00345
00346 {
00347 Tcl_NotifierProcs notifier;
00348
00349 notifier.setTimerProc = pltcl_SetTimer;
00350 notifier.waitForEventProc = pltcl_WaitForEvent;
00351 notifier.createFileHandlerProc = pltcl_CreateFileHandler;
00352 notifier.deleteFileHandlerProc = pltcl_DeleteFileHandler;
00353 notifier.initNotifierProc = pltcl_InitNotifier;
00354 notifier.finalizeNotifierProc = pltcl_FinalizeNotifier;
00355 notifier.alertNotifierProc = pltcl_AlertNotifier;
00356 notifier.serviceModeHookProc = pltcl_ServiceModeHook;
00357 Tcl_SetNotifier(¬ifier);
00358 }
00359 #endif
00360
00361
00362
00363
00364
00365 if ((pltcl_hold_interp = Tcl_CreateInterp()) == NULL)
00366 elog(ERROR, "could not create master Tcl interpreter");
00367 if (Tcl_Init(pltcl_hold_interp) == TCL_ERROR)
00368 elog(ERROR, "could not initialize master Tcl interpreter");
00369
00370
00371
00372
00373 memset(&hash_ctl, 0, sizeof(hash_ctl));
00374 hash_ctl.keysize = sizeof(Oid);
00375 hash_ctl.entrysize = sizeof(pltcl_interp_desc);
00376 hash_ctl.hash = oid_hash;
00377 pltcl_interp_htab = hash_create("PL/Tcl interpreters",
00378 8,
00379 &hash_ctl,
00380 HASH_ELEM | HASH_FUNCTION);
00381
00382
00383
00384
00385 memset(&hash_ctl, 0, sizeof(hash_ctl));
00386 hash_ctl.keysize = sizeof(pltcl_proc_key);
00387 hash_ctl.entrysize = sizeof(pltcl_proc_ptr);
00388 hash_ctl.hash = tag_hash;
00389 pltcl_proc_htab = hash_create("PL/Tcl functions",
00390 100,
00391 &hash_ctl,
00392 HASH_ELEM | HASH_FUNCTION);
00393
00394 pltcl_pm_init_done = true;
00395 }
00396
00397
00398
00399
00400 static void
00401 pltcl_init_interp(pltcl_interp_desc *interp_desc, bool pltrusted)
00402 {
00403 Tcl_Interp *interp;
00404 char interpname[32];
00405
00406
00407
00408
00409
00410
00411 snprintf(interpname, sizeof(interpname), "slave_%u", interp_desc->user_id);
00412 if ((interp = Tcl_CreateSlave(pltcl_hold_interp, interpname,
00413 pltrusted ? 1 : 0)) == NULL)
00414 elog(ERROR, "could not create slave Tcl interpreter");
00415 interp_desc->interp = interp;
00416
00417
00418
00419
00420 Tcl_InitHashTable(&interp_desc->query_hash, TCL_STRING_KEYS);
00421
00422
00423
00424
00425 Tcl_CreateCommand(interp, "elog",
00426 pltcl_elog, NULL, NULL);
00427 Tcl_CreateCommand(interp, "quote",
00428 pltcl_quote, NULL, NULL);
00429 Tcl_CreateCommand(interp, "argisnull",
00430 pltcl_argisnull, NULL, NULL);
00431 Tcl_CreateCommand(interp, "return_null",
00432 pltcl_returnnull, NULL, NULL);
00433
00434 Tcl_CreateCommand(interp, "spi_exec",
00435 pltcl_SPI_execute, NULL, NULL);
00436 Tcl_CreateCommand(interp, "spi_prepare",
00437 pltcl_SPI_prepare, NULL, NULL);
00438 Tcl_CreateCommand(interp, "spi_execp",
00439 pltcl_SPI_execute_plan, NULL, NULL);
00440 Tcl_CreateCommand(interp, "spi_lastoid",
00441 pltcl_SPI_lastoid, NULL, NULL);
00442
00443
00444
00445
00446 pltcl_init_load_unknown(interp);
00447 }
00448
00449
00450
00451
00452
00453
00454
00455 static pltcl_interp_desc *
00456 pltcl_fetch_interp(bool pltrusted)
00457 {
00458 Oid user_id;
00459 pltcl_interp_desc *interp_desc;
00460 bool found;
00461
00462
00463 if (pltrusted)
00464 user_id = GetUserId();
00465 else
00466 user_id = InvalidOid;
00467
00468 interp_desc = hash_search(pltcl_interp_htab, &user_id,
00469 HASH_ENTER,
00470 &found);
00471 if (!found)
00472 pltcl_init_interp(interp_desc, pltrusted);
00473
00474 return interp_desc;
00475 }
00476
00477
00478
00479
00480
00481 static void
00482 pltcl_init_load_unknown(Tcl_Interp *interp)
00483 {
00484 Relation pmrel;
00485 char *pmrelname,
00486 *nspname;
00487 char *buf;
00488 int buflen;
00489 int spi_rc;
00490 int tcl_rc;
00491 Tcl_DString unknown_src;
00492 char *part;
00493 int i;
00494 int fno;
00495
00496
00497
00498
00499
00500
00501
00502
00503 pmrel = relation_openrv_extended(makeRangeVar(NULL, "pltcl_modules", -1),
00504 AccessShareLock, true);
00505 if (pmrel == NULL)
00506 return;
00507
00508 if (!(pmrel->rd_rel->relkind == RELKIND_RELATION ||
00509 pmrel->rd_rel->relkind == RELKIND_MATVIEW ||
00510 pmrel->rd_rel->relkind == RELKIND_VIEW))
00511 {
00512 relation_close(pmrel, AccessShareLock);
00513 return;
00514 }
00515
00516 if (!superuser_arg(pmrel->rd_rel->relowner))
00517 {
00518 relation_close(pmrel, AccessShareLock);
00519 return;
00520 }
00521
00522 nspname = get_namespace_name(RelationGetNamespace(pmrel));
00523 if (!nspname)
00524 elog(ERROR, "cache lookup failed for namespace %u",
00525 RelationGetNamespace(pmrel));
00526 pmrelname = quote_qualified_identifier(nspname,
00527 RelationGetRelationName(pmrel));
00528
00529
00530
00531
00532
00533 buflen = strlen(pmrelname) + 100;
00534 buf = (char *) palloc(buflen);
00535 snprintf(buf, buflen,
00536 "select modsrc from %s where modname = 'unknown' order by modseq",
00537 pmrelname);
00538
00539 spi_rc = SPI_execute(buf, false, 0);
00540 if (spi_rc != SPI_OK_SELECT)
00541 elog(ERROR, "select from pltcl_modules failed");
00542
00543 pfree(buf);
00544
00545
00546
00547
00548 if (SPI_processed == 0)
00549 {
00550 SPI_freetuptable(SPI_tuptable);
00551 elog(WARNING, "module \"unknown\" not found in pltcl_modules");
00552 relation_close(pmrel, AccessShareLock);
00553 return;
00554 }
00555
00556
00557
00558
00559
00560
00561 fno = SPI_fnumber(SPI_tuptable->tupdesc, "modsrc");
00562
00563 Tcl_DStringInit(&unknown_src);
00564
00565 for (i = 0; i < SPI_processed; i++)
00566 {
00567 part = SPI_getvalue(SPI_tuptable->vals[i],
00568 SPI_tuptable->tupdesc, fno);
00569 if (part != NULL)
00570 {
00571 UTF_BEGIN;
00572 Tcl_DStringAppend(&unknown_src, UTF_E2U(part), -1);
00573 UTF_END;
00574 pfree(part);
00575 }
00576 }
00577 tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&unknown_src));
00578
00579 Tcl_DStringFree(&unknown_src);
00580 SPI_freetuptable(SPI_tuptable);
00581
00582 if (tcl_rc != TCL_OK)
00583 {
00584 UTF_BEGIN;
00585 elog(ERROR, "could not load module \"unknown\": %s",
00586 UTF_U2E(Tcl_GetStringResult(interp)));
00587 UTF_END;
00588 }
00589
00590 relation_close(pmrel, AccessShareLock);
00591 }
00592
00593
00594
00595
00596
00597
00598
00599
00600
00601 PG_FUNCTION_INFO_V1(pltcl_call_handler);
00602
00603
00604 Datum
00605 pltcl_call_handler(PG_FUNCTION_ARGS)
00606 {
00607 return pltcl_handler(fcinfo, true);
00608 }
00609
00610
00611
00612
00613 PG_FUNCTION_INFO_V1(pltclu_call_handler);
00614
00615
00616 Datum
00617 pltclu_call_handler(PG_FUNCTION_ARGS)
00618 {
00619 return pltcl_handler(fcinfo, false);
00620 }
00621
00622
00623 static Datum
00624 pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted)
00625 {
00626 Datum retval;
00627 FunctionCallInfo save_fcinfo;
00628 pltcl_proc_desc *save_prodesc;
00629
00630
00631
00632
00633 save_fcinfo = pltcl_current_fcinfo;
00634 save_prodesc = pltcl_current_prodesc;
00635
00636 PG_TRY();
00637 {
00638
00639
00640
00641
00642 if (CALLED_AS_TRIGGER(fcinfo))
00643 {
00644 pltcl_current_fcinfo = NULL;
00645 retval = PointerGetDatum(pltcl_trigger_handler(fcinfo, pltrusted));
00646 }
00647 else
00648 {
00649 pltcl_current_fcinfo = fcinfo;
00650 retval = pltcl_func_handler(fcinfo, pltrusted);
00651 }
00652 }
00653 PG_CATCH();
00654 {
00655 pltcl_current_fcinfo = save_fcinfo;
00656 pltcl_current_prodesc = save_prodesc;
00657 PG_RE_THROW();
00658 }
00659 PG_END_TRY();
00660
00661 pltcl_current_fcinfo = save_fcinfo;
00662 pltcl_current_prodesc = save_prodesc;
00663
00664 return retval;
00665 }
00666
00667
00668
00669
00670
00671 static Datum
00672 pltcl_func_handler(PG_FUNCTION_ARGS, bool pltrusted)
00673 {
00674 pltcl_proc_desc *prodesc;
00675 Tcl_Interp *volatile interp;
00676 Tcl_DString tcl_cmd;
00677 Tcl_DString list_tmp;
00678 int i;
00679 int tcl_rc;
00680 Datum retval;
00681
00682
00683 if (SPI_connect() != SPI_OK_CONNECT)
00684 elog(ERROR, "could not connect to SPI manager");
00685
00686
00687 prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid,
00688 pltrusted);
00689
00690 pltcl_current_prodesc = prodesc;
00691
00692 interp = prodesc->interp_desc->interp;
00693
00694
00695
00696
00697
00698 Tcl_DStringInit(&tcl_cmd);
00699 Tcl_DStringInit(&list_tmp);
00700 Tcl_DStringAppendElement(&tcl_cmd, prodesc->internal_proname);
00701
00702
00703
00704
00705 PG_TRY();
00706 {
00707 for (i = 0; i < prodesc->nargs; i++)
00708 {
00709 if (prodesc->arg_is_rowtype[i])
00710 {
00711
00712
00713
00714 if (fcinfo->argnull[i])
00715 Tcl_DStringAppendElement(&tcl_cmd, "");
00716 else
00717 {
00718 HeapTupleHeader td;
00719 Oid tupType;
00720 int32 tupTypmod;
00721 TupleDesc tupdesc;
00722 HeapTupleData tmptup;
00723
00724 td = DatumGetHeapTupleHeader(fcinfo->arg[i]);
00725
00726 tupType = HeapTupleHeaderGetTypeId(td);
00727 tupTypmod = HeapTupleHeaderGetTypMod(td);
00728 tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
00729
00730 tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
00731 tmptup.t_data = td;
00732
00733 Tcl_DStringSetLength(&list_tmp, 0);
00734 pltcl_build_tuple_argument(&tmptup, tupdesc, &list_tmp);
00735 Tcl_DStringAppendElement(&tcl_cmd,
00736 Tcl_DStringValue(&list_tmp));
00737 ReleaseTupleDesc(tupdesc);
00738 }
00739 }
00740 else
00741 {
00742
00743
00744
00745
00746 if (fcinfo->argnull[i])
00747 Tcl_DStringAppendElement(&tcl_cmd, "");
00748 else
00749 {
00750 char *tmp;
00751
00752 tmp = OutputFunctionCall(&prodesc->arg_out_func[i],
00753 fcinfo->arg[i]);
00754 UTF_BEGIN;
00755 Tcl_DStringAppendElement(&tcl_cmd, UTF_E2U(tmp));
00756 UTF_END;
00757 pfree(tmp);
00758 }
00759 }
00760 }
00761 }
00762 PG_CATCH();
00763 {
00764 Tcl_DStringFree(&tcl_cmd);
00765 Tcl_DStringFree(&list_tmp);
00766 PG_RE_THROW();
00767 }
00768 PG_END_TRY();
00769 Tcl_DStringFree(&list_tmp);
00770
00771
00772
00773
00774
00775
00776 tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd));
00777 Tcl_DStringFree(&tcl_cmd);
00778
00779
00780
00781
00782 if (tcl_rc != TCL_OK)
00783 throw_tcl_error(interp, prodesc->user_proname);
00784
00785
00786
00787
00788
00789
00790
00791
00792
00793
00794 if (SPI_finish() != SPI_OK_FINISH)
00795 elog(ERROR, "SPI_finish() failed");
00796
00797 if (fcinfo->isnull)
00798 retval = InputFunctionCall(&prodesc->result_in_func,
00799 NULL,
00800 prodesc->result_typioparam,
00801 -1);
00802 else
00803 {
00804 UTF_BEGIN;
00805 retval = InputFunctionCall(&prodesc->result_in_func,
00806 UTF_U2E((char *) Tcl_GetStringResult(interp)),
00807 prodesc->result_typioparam,
00808 -1);
00809 UTF_END;
00810 }
00811
00812 return retval;
00813 }
00814
00815
00816
00817
00818
00819 static HeapTuple
00820 pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
00821 {
00822 pltcl_proc_desc *prodesc;
00823 Tcl_Interp *volatile interp;
00824 TriggerData *trigdata = (TriggerData *) fcinfo->context;
00825 char *stroid;
00826 TupleDesc tupdesc;
00827 volatile HeapTuple rettup;
00828 Tcl_DString tcl_cmd;
00829 Tcl_DString tcl_trigtup;
00830 Tcl_DString tcl_newtup;
00831 int tcl_rc;
00832 int i;
00833 int *modattrs;
00834 Datum *modvalues;
00835 char *modnulls;
00836 int ret_numvals;
00837 CONST84 char *result;
00838 CONST84 char **ret_values;
00839
00840
00841 if (SPI_connect() != SPI_OK_CONNECT)
00842 elog(ERROR, "could not connect to SPI manager");
00843
00844
00845 prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid,
00846 RelationGetRelid(trigdata->tg_relation),
00847 pltrusted);
00848
00849 pltcl_current_prodesc = prodesc;
00850
00851 interp = prodesc->interp_desc->interp;
00852
00853 tupdesc = trigdata->tg_relation->rd_att;
00854
00855
00856
00857
00858
00859 Tcl_DStringInit(&tcl_cmd);
00860 Tcl_DStringInit(&tcl_trigtup);
00861 Tcl_DStringInit(&tcl_newtup);
00862 PG_TRY();
00863 {
00864
00865 Tcl_DStringAppendElement(&tcl_cmd, prodesc->internal_proname);
00866
00867
00868 Tcl_DStringAppendElement(&tcl_cmd, trigdata->tg_trigger->tgname);
00869
00870
00871 stroid = DatumGetCString(DirectFunctionCall1(oidout,
00872 ObjectIdGetDatum(trigdata->tg_relation->rd_id)));
00873 Tcl_DStringAppendElement(&tcl_cmd, stroid);
00874 pfree(stroid);
00875
00876
00877 stroid = SPI_getrelname(trigdata->tg_relation);
00878 Tcl_DStringAppendElement(&tcl_cmd, stroid);
00879 pfree(stroid);
00880
00881
00882 stroid = SPI_getnspname(trigdata->tg_relation);
00883 Tcl_DStringAppendElement(&tcl_cmd, stroid);
00884 pfree(stroid);
00885
00886
00887 Tcl_DStringAppendElement(&tcl_trigtup, "");
00888 for (i = 0; i < tupdesc->natts; i++)
00889 {
00890 if (tupdesc->attrs[i]->attisdropped)
00891 Tcl_DStringAppendElement(&tcl_trigtup, "");
00892 else
00893 Tcl_DStringAppendElement(&tcl_trigtup,
00894 NameStr(tupdesc->attrs[i]->attname));
00895 }
00896 Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
00897 Tcl_DStringFree(&tcl_trigtup);
00898 Tcl_DStringInit(&tcl_trigtup);
00899
00900
00901 if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
00902 Tcl_DStringAppendElement(&tcl_cmd, "BEFORE");
00903 else if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
00904 Tcl_DStringAppendElement(&tcl_cmd, "AFTER");
00905 else if (TRIGGER_FIRED_INSTEAD(trigdata->tg_event))
00906 Tcl_DStringAppendElement(&tcl_cmd, "INSTEAD OF");
00907 else
00908 elog(ERROR, "unrecognized WHEN tg_event: %u", trigdata->tg_event);
00909
00910
00911 if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
00912 {
00913 Tcl_DStringAppendElement(&tcl_cmd, "ROW");
00914
00915
00916 pltcl_build_tuple_argument(trigdata->tg_trigtuple,
00917 tupdesc, &tcl_trigtup);
00918
00919
00920
00921
00922
00923 if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
00924 {
00925 Tcl_DStringAppendElement(&tcl_cmd, "INSERT");
00926
00927 Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
00928 Tcl_DStringAppendElement(&tcl_cmd, "");
00929
00930 rettup = trigdata->tg_trigtuple;
00931 }
00932 else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
00933 {
00934 Tcl_DStringAppendElement(&tcl_cmd, "DELETE");
00935
00936 Tcl_DStringAppendElement(&tcl_cmd, "");
00937 Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
00938
00939 rettup = trigdata->tg_trigtuple;
00940 }
00941 else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
00942 {
00943 Tcl_DStringAppendElement(&tcl_cmd, "UPDATE");
00944
00945 pltcl_build_tuple_argument(trigdata->tg_newtuple,
00946 tupdesc, &tcl_newtup);
00947
00948 Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_newtup));
00949 Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
00950
00951 rettup = trigdata->tg_newtuple;
00952 }
00953 else
00954 elog(ERROR, "unrecognized OP tg_event: %u", trigdata->tg_event);
00955 }
00956 else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
00957 {
00958 Tcl_DStringAppendElement(&tcl_cmd, "STATEMENT");
00959
00960 if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
00961 Tcl_DStringAppendElement(&tcl_cmd, "INSERT");
00962 else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
00963 Tcl_DStringAppendElement(&tcl_cmd, "DELETE");
00964 else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
00965 Tcl_DStringAppendElement(&tcl_cmd, "UPDATE");
00966 else if (TRIGGER_FIRED_BY_TRUNCATE(trigdata->tg_event))
00967 Tcl_DStringAppendElement(&tcl_cmd, "TRUNCATE");
00968 else
00969 elog(ERROR, "unrecognized OP tg_event: %u", trigdata->tg_event);
00970
00971 Tcl_DStringAppendElement(&tcl_cmd, "");
00972 Tcl_DStringAppendElement(&tcl_cmd, "");
00973
00974 rettup = (HeapTuple) NULL;
00975 }
00976 else
00977 elog(ERROR, "unrecognized LEVEL tg_event: %u", trigdata->tg_event);
00978
00979
00980 for (i = 0; i < trigdata->tg_trigger->tgnargs; i++)
00981 Tcl_DStringAppendElement(&tcl_cmd, trigdata->tg_trigger->tgargs[i]);
00982
00983 }
00984 PG_CATCH();
00985 {
00986 Tcl_DStringFree(&tcl_cmd);
00987 Tcl_DStringFree(&tcl_trigtup);
00988 Tcl_DStringFree(&tcl_newtup);
00989 PG_RE_THROW();
00990 }
00991 PG_END_TRY();
00992 Tcl_DStringFree(&tcl_trigtup);
00993 Tcl_DStringFree(&tcl_newtup);
00994
00995
00996
00997
00998
00999
01000 tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd));
01001 Tcl_DStringFree(&tcl_cmd);
01002
01003
01004
01005
01006 if (tcl_rc != TCL_OK)
01007 throw_tcl_error(interp, prodesc->user_proname);
01008
01009
01010
01011
01012
01013
01014 if (SPI_finish() != SPI_OK_FINISH)
01015 elog(ERROR, "SPI_finish() failed");
01016
01017 result = Tcl_GetStringResult(interp);
01018
01019 if (strcmp(result, "OK") == 0)
01020 return rettup;
01021 if (strcmp(result, "SKIP") == 0)
01022 return (HeapTuple) NULL;
01023
01024
01025
01026
01027
01028 if (Tcl_SplitList(interp, result,
01029 &ret_numvals, &ret_values) != TCL_OK)
01030 {
01031 UTF_BEGIN;
01032 elog(ERROR, "could not split return value from trigger: %s",
01033 UTF_U2E(Tcl_GetStringResult(interp)));
01034 UTF_END;
01035 }
01036
01037
01038 PG_TRY();
01039 {
01040 if (ret_numvals % 2 != 0)
01041 elog(ERROR, "invalid return list from trigger - must have even # of elements");
01042
01043 modattrs = (int *) palloc(tupdesc->natts * sizeof(int));
01044 modvalues = (Datum *) palloc(tupdesc->natts * sizeof(Datum));
01045 for (i = 0; i < tupdesc->natts; i++)
01046 {
01047 modattrs[i] = i + 1;
01048 modvalues[i] = (Datum) NULL;
01049 }
01050
01051 modnulls = palloc(tupdesc->natts);
01052 memset(modnulls, 'n', tupdesc->natts);
01053
01054 for (i = 0; i < ret_numvals; i += 2)
01055 {
01056 CONST84 char *ret_name = ret_values[i];
01057 CONST84 char *ret_value = ret_values[i + 1];
01058 int attnum;
01059 HeapTuple typeTup;
01060 Oid typinput;
01061 Oid typioparam;
01062 FmgrInfo finfo;
01063
01064
01065
01066
01067 if (strcmp(ret_name, ".tupno") == 0)
01068 continue;
01069
01070
01071
01072
01073 attnum = SPI_fnumber(tupdesc, ret_name);
01074 if (attnum == SPI_ERROR_NOATTRIBUTE)
01075 elog(ERROR, "invalid attribute \"%s\"", ret_name);
01076 if (attnum <= 0)
01077 elog(ERROR, "cannot set system attribute \"%s\"", ret_name);
01078
01079
01080
01081
01082 if (tupdesc->attrs[attnum - 1]->attisdropped)
01083 continue;
01084
01085
01086
01087
01088
01089 typeTup = SearchSysCache1(TYPEOID,
01090 ObjectIdGetDatum(tupdesc->attrs[attnum - 1]->atttypid));
01091 if (!HeapTupleIsValid(typeTup))
01092 elog(ERROR, "cache lookup failed for type %u",
01093 tupdesc->attrs[attnum - 1]->atttypid);
01094 typinput = ((Form_pg_type) GETSTRUCT(typeTup))->typinput;
01095 typioparam = getTypeIOParam(typeTup);
01096 ReleaseSysCache(typeTup);
01097
01098
01099
01100
01101 modnulls[attnum - 1] = ' ';
01102 fmgr_info(typinput, &finfo);
01103 UTF_BEGIN;
01104 modvalues[attnum - 1] = InputFunctionCall(&finfo,
01105 (char *) UTF_U2E(ret_value),
01106 typioparam,
01107 tupdesc->attrs[attnum - 1]->atttypmod);
01108 UTF_END;
01109 }
01110
01111 rettup = SPI_modifytuple(trigdata->tg_relation, rettup, tupdesc->natts,
01112 modattrs, modvalues, modnulls);
01113
01114 pfree(modattrs);
01115 pfree(modvalues);
01116 pfree(modnulls);
01117
01118 if (rettup == NULL)
01119 elog(ERROR, "SPI_modifytuple() failed - RC = %d", SPI_result);
01120
01121 }
01122 PG_CATCH();
01123 {
01124 ckfree((char *) ret_values);
01125 PG_RE_THROW();
01126 }
01127 PG_END_TRY();
01128 ckfree((char *) ret_values);
01129
01130 return rettup;
01131 }
01132
01133
01134
01135
01136
01137 static void
01138 throw_tcl_error(Tcl_Interp *interp, const char *proname)
01139 {
01140
01141
01142
01143
01144
01145
01146
01147 char *emsg;
01148 char *econtext;
01149
01150 UTF_BEGIN;
01151 emsg = pstrdup(UTF_U2E(Tcl_GetStringResult(interp)));
01152 UTF_END;
01153 UTF_BEGIN;
01154 econtext = UTF_U2E((char *) Tcl_GetVar(interp, "errorInfo",
01155 TCL_GLOBAL_ONLY));
01156 ereport(ERROR,
01157 (errmsg("%s", emsg),
01158 errcontext("%s\nin PL/Tcl function \"%s\"",
01159 econtext, proname)));
01160 UTF_END;
01161 }
01162
01163
01164
01165
01166
01167
01168
01169
01170 static pltcl_proc_desc *
01171 compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool pltrusted)
01172 {
01173 HeapTuple procTup;
01174 Form_pg_proc procStruct;
01175 pltcl_proc_key proc_key;
01176 pltcl_proc_ptr *proc_ptr;
01177 bool found;
01178 pltcl_proc_desc *prodesc;
01179
01180
01181 procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fn_oid));
01182 if (!HeapTupleIsValid(procTup))
01183 elog(ERROR, "cache lookup failed for function %u", fn_oid);
01184 procStruct = (Form_pg_proc) GETSTRUCT(procTup);
01185
01186
01187 proc_key.proc_id = fn_oid;
01188 proc_key.is_trigger = OidIsValid(tgreloid);
01189 proc_key.user_id = pltrusted ? GetUserId() : InvalidOid;
01190
01191 proc_ptr = hash_search(pltcl_proc_htab, &proc_key,
01192 HASH_ENTER,
01193 &found);
01194 if (!found)
01195 proc_ptr->proc_ptr = NULL;
01196
01197 prodesc = proc_ptr->proc_ptr;
01198
01199
01200
01201
01202
01203
01204 if (prodesc != NULL)
01205 {
01206 bool uptodate;
01207
01208 uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
01209 ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self));
01210
01211 if (!uptodate)
01212 {
01213 proc_ptr->proc_ptr = NULL;
01214 prodesc = NULL;
01215 }
01216 }
01217
01218
01219
01220
01221
01222
01223
01224
01225
01226 if (prodesc == NULL)
01227 {
01228 bool is_trigger = OidIsValid(tgreloid);
01229 char internal_proname[128];
01230 HeapTuple typeTup;
01231 Form_pg_type typeStruct;
01232 Tcl_DString proc_internal_def;
01233 Tcl_DString proc_internal_body;
01234 char proc_internal_args[33 * FUNC_MAX_ARGS];
01235 Datum prosrcdatum;
01236 bool isnull;
01237 char *proc_source;
01238 char buf[32];
01239 Tcl_Interp *interp;
01240 int i;
01241 int tcl_rc;
01242
01243
01244
01245
01246
01247
01248 if (!is_trigger)
01249 snprintf(internal_proname, sizeof(internal_proname),
01250 "__PLTcl_proc_%u", fn_oid);
01251 else
01252 snprintf(internal_proname, sizeof(internal_proname),
01253 "__PLTcl_proc_%u_trigger", fn_oid);
01254
01255
01256
01257
01258 prodesc = (pltcl_proc_desc *) malloc(sizeof(pltcl_proc_desc));
01259 if (prodesc == NULL)
01260 ereport(ERROR,
01261 (errcode(ERRCODE_OUT_OF_MEMORY),
01262 errmsg("out of memory")));
01263 MemSet(prodesc, 0, sizeof(pltcl_proc_desc));
01264 prodesc->user_proname = strdup(NameStr(procStruct->proname));
01265 prodesc->internal_proname = strdup(internal_proname);
01266 if (prodesc->user_proname == NULL || prodesc->internal_proname == NULL)
01267 ereport(ERROR,
01268 (errcode(ERRCODE_OUT_OF_MEMORY),
01269 errmsg("out of memory")));
01270 prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
01271 prodesc->fn_tid = procTup->t_self;
01272
01273
01274 prodesc->fn_readonly =
01275 (procStruct->provolatile != PROVOLATILE_VOLATILE);
01276
01277 prodesc->lanpltrusted = pltrusted;
01278
01279
01280
01281
01282 prodesc->interp_desc = pltcl_fetch_interp(prodesc->lanpltrusted);
01283 interp = prodesc->interp_desc->interp;
01284
01285
01286
01287
01288
01289 if (!is_trigger)
01290 {
01291 typeTup =
01292 SearchSysCache1(TYPEOID,
01293 ObjectIdGetDatum(procStruct->prorettype));
01294 if (!HeapTupleIsValid(typeTup))
01295 {
01296 free(prodesc->user_proname);
01297 free(prodesc->internal_proname);
01298 free(prodesc);
01299 elog(ERROR, "cache lookup failed for type %u",
01300 procStruct->prorettype);
01301 }
01302 typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
01303
01304
01305 if (typeStruct->typtype == TYPTYPE_PSEUDO)
01306 {
01307 if (procStruct->prorettype == VOIDOID)
01308 ;
01309 else if (procStruct->prorettype == TRIGGEROID)
01310 {
01311 free(prodesc->user_proname);
01312 free(prodesc->internal_proname);
01313 free(prodesc);
01314 ereport(ERROR,
01315 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01316 errmsg("trigger functions can only be called as triggers")));
01317 }
01318 else
01319 {
01320 free(prodesc->user_proname);
01321 free(prodesc->internal_proname);
01322 free(prodesc);
01323 ereport(ERROR,
01324 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01325 errmsg("PL/Tcl functions cannot return type %s",
01326 format_type_be(procStruct->prorettype))));
01327 }
01328 }
01329
01330 if (typeStruct->typtype == TYPTYPE_COMPOSITE)
01331 {
01332 free(prodesc->user_proname);
01333 free(prodesc->internal_proname);
01334 free(prodesc);
01335 ereport(ERROR,
01336 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01337 errmsg("PL/Tcl functions cannot return composite types")));
01338 }
01339
01340 perm_fmgr_info(typeStruct->typinput, &(prodesc->result_in_func));
01341 prodesc->result_typioparam = getTypeIOParam(typeTup);
01342
01343 ReleaseSysCache(typeTup);
01344 }
01345
01346
01347
01348
01349
01350 if (!is_trigger)
01351 {
01352 prodesc->nargs = procStruct->pronargs;
01353 proc_internal_args[0] = '\0';
01354 for (i = 0; i < prodesc->nargs; i++)
01355 {
01356 typeTup = SearchSysCache1(TYPEOID,
01357 ObjectIdGetDatum(procStruct->proargtypes.values[i]));
01358 if (!HeapTupleIsValid(typeTup))
01359 {
01360 free(prodesc->user_proname);
01361 free(prodesc->internal_proname);
01362 free(prodesc);
01363 elog(ERROR, "cache lookup failed for type %u",
01364 procStruct->proargtypes.values[i]);
01365 }
01366 typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
01367
01368
01369 if (typeStruct->typtype == TYPTYPE_PSEUDO)
01370 {
01371 free(prodesc->user_proname);
01372 free(prodesc->internal_proname);
01373 free(prodesc);
01374 ereport(ERROR,
01375 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01376 errmsg("PL/Tcl functions cannot accept type %s",
01377 format_type_be(procStruct->proargtypes.values[i]))));
01378 }
01379
01380 if (typeStruct->typtype == TYPTYPE_COMPOSITE)
01381 {
01382 prodesc->arg_is_rowtype[i] = true;
01383 snprintf(buf, sizeof(buf), "__PLTcl_Tup_%d", i + 1);
01384 }
01385 else
01386 {
01387 prodesc->arg_is_rowtype[i] = false;
01388 perm_fmgr_info(typeStruct->typoutput,
01389 &(prodesc->arg_out_func[i]));
01390 snprintf(buf, sizeof(buf), "%d", i + 1);
01391 }
01392
01393 if (i > 0)
01394 strcat(proc_internal_args, " ");
01395 strcat(proc_internal_args, buf);
01396
01397 ReleaseSysCache(typeTup);
01398 }
01399 }
01400 else
01401 {
01402
01403 strcpy(proc_internal_args,
01404 "TG_name TG_relid TG_table_name TG_table_schema TG_relatts TG_when TG_level TG_op __PLTcl_Tup_NEW __PLTcl_Tup_OLD args");
01405 }
01406
01407
01408
01409
01410
01411 Tcl_DStringInit(&proc_internal_def);
01412 Tcl_DStringInit(&proc_internal_body);
01413 Tcl_DStringAppendElement(&proc_internal_def, "proc");
01414 Tcl_DStringAppendElement(&proc_internal_def, internal_proname);
01415 Tcl_DStringAppendElement(&proc_internal_def, proc_internal_args);
01416
01417
01418
01419
01420
01421
01422 Tcl_DStringAppend(&proc_internal_body, "upvar #0 ", -1);
01423 Tcl_DStringAppend(&proc_internal_body, internal_proname, -1);
01424 Tcl_DStringAppend(&proc_internal_body, " GD\n", -1);
01425 if (!is_trigger)
01426 {
01427 for (i = 0; i < prodesc->nargs; i++)
01428 {
01429 if (prodesc->arg_is_rowtype[i])
01430 {
01431 snprintf(buf, sizeof(buf),
01432 "array set %d $__PLTcl_Tup_%d\n",
01433 i + 1, i + 1);
01434 Tcl_DStringAppend(&proc_internal_body, buf, -1);
01435 }
01436 }
01437 }
01438 else
01439 {
01440 Tcl_DStringAppend(&proc_internal_body,
01441 "array set NEW $__PLTcl_Tup_NEW\n", -1);
01442 Tcl_DStringAppend(&proc_internal_body,
01443 "array set OLD $__PLTcl_Tup_OLD\n", -1);
01444
01445 Tcl_DStringAppend(&proc_internal_body,
01446 "set i 0\n"
01447 "set v 0\n"
01448 "foreach v $args {\n"
01449 " incr i\n"
01450 " set $i $v\n"
01451 "}\n"
01452 "unset i v\n\n", -1);
01453 }
01454
01455
01456
01457
01458 prosrcdatum = SysCacheGetAttr(PROCOID, procTup,
01459 Anum_pg_proc_prosrc, &isnull);
01460 if (isnull)
01461 elog(ERROR, "null prosrc");
01462 proc_source = TextDatumGetCString(prosrcdatum);
01463 UTF_BEGIN;
01464 Tcl_DStringAppend(&proc_internal_body, UTF_E2U(proc_source), -1);
01465 UTF_END;
01466 pfree(proc_source);
01467 Tcl_DStringAppendElement(&proc_internal_def,
01468 Tcl_DStringValue(&proc_internal_body));
01469 Tcl_DStringFree(&proc_internal_body);
01470
01471
01472
01473
01474 tcl_rc = Tcl_GlobalEval(interp,
01475 Tcl_DStringValue(&proc_internal_def));
01476 Tcl_DStringFree(&proc_internal_def);
01477 if (tcl_rc != TCL_OK)
01478 {
01479 free(prodesc->user_proname);
01480 free(prodesc->internal_proname);
01481 free(prodesc);
01482 UTF_BEGIN;
01483 elog(ERROR, "could not create internal procedure \"%s\": %s",
01484 internal_proname, UTF_U2E(Tcl_GetStringResult(interp)));
01485 UTF_END;
01486 }
01487
01488
01489
01490
01491
01492
01493
01494 proc_ptr->proc_ptr = prodesc;
01495 }
01496
01497 ReleaseSysCache(procTup);
01498
01499 return prodesc;
01500 }
01501
01502
01503
01504
01505
01506 static int
01507 pltcl_elog(ClientData cdata, Tcl_Interp *interp,
01508 int argc, CONST84 char *argv[])
01509 {
01510 volatile int level;
01511 MemoryContext oldcontext;
01512
01513 if (argc != 3)
01514 {
01515 Tcl_SetResult(interp, "syntax error - 'elog level msg'", TCL_STATIC);
01516 return TCL_ERROR;
01517 }
01518
01519 if (strcmp(argv[1], "DEBUG") == 0)
01520 level = DEBUG2;
01521 else if (strcmp(argv[1], "LOG") == 0)
01522 level = LOG;
01523 else if (strcmp(argv[1], "INFO") == 0)
01524 level = INFO;
01525 else if (strcmp(argv[1], "NOTICE") == 0)
01526 level = NOTICE;
01527 else if (strcmp(argv[1], "WARNING") == 0)
01528 level = WARNING;
01529 else if (strcmp(argv[1], "ERROR") == 0)
01530 level = ERROR;
01531 else if (strcmp(argv[1], "FATAL") == 0)
01532 level = FATAL;
01533 else
01534 {
01535 Tcl_AppendResult(interp, "Unknown elog level '", argv[1],
01536 "'", NULL);
01537 return TCL_ERROR;
01538 }
01539
01540 if (level == ERROR)
01541 {
01542
01543
01544
01545
01546
01547 Tcl_SetResult(interp, (char *) argv[2], TCL_VOLATILE);
01548 return TCL_ERROR;
01549 }
01550
01551
01552
01553
01554
01555
01556
01557
01558
01559
01560 oldcontext = CurrentMemoryContext;
01561 PG_TRY();
01562 {
01563 UTF_BEGIN;
01564 elog(level, "%s", UTF_U2E(argv[2]));
01565 UTF_END;
01566 }
01567 PG_CATCH();
01568 {
01569 ErrorData *edata;
01570
01571
01572 MemoryContextSwitchTo(oldcontext);
01573 edata = CopyErrorData();
01574 FlushErrorState();
01575
01576
01577 UTF_BEGIN;
01578 Tcl_SetResult(interp, UTF_E2U(edata->message), TCL_VOLATILE);
01579 UTF_END;
01580 FreeErrorData(edata);
01581
01582 return TCL_ERROR;
01583 }
01584 PG_END_TRY();
01585
01586 return TCL_OK;
01587 }
01588
01589
01590
01591
01592
01593
01594 static int
01595 pltcl_quote(ClientData cdata, Tcl_Interp *interp,
01596 int argc, CONST84 char *argv[])
01597 {
01598 char *tmp;
01599 const char *cp1;
01600 char *cp2;
01601
01602
01603
01604
01605 if (argc != 2)
01606 {
01607 Tcl_SetResult(interp, "syntax error - 'quote string'", TCL_STATIC);
01608 return TCL_ERROR;
01609 }
01610
01611
01612
01613
01614
01615 tmp = palloc(strlen(argv[1]) * 2 + 1);
01616 cp1 = argv[1];
01617 cp2 = tmp;
01618
01619
01620
01621
01622 while (*cp1)
01623 {
01624 if (*cp1 == '\'')
01625 *cp2++ = '\'';
01626 else
01627 {
01628 if (*cp1 == '\\')
01629 *cp2++ = '\\';
01630 }
01631 *cp2++ = *cp1++;
01632 }
01633
01634
01635
01636
01637 *cp2 = '\0';
01638 Tcl_SetResult(interp, tmp, TCL_VOLATILE);
01639 pfree(tmp);
01640 return TCL_OK;
01641 }
01642
01643
01644
01645
01646
01647 static int
01648 pltcl_argisnull(ClientData cdata, Tcl_Interp *interp,
01649 int argc, CONST84 char *argv[])
01650 {
01651 int argno;
01652 FunctionCallInfo fcinfo = pltcl_current_fcinfo;
01653
01654
01655
01656
01657 if (argc != 2)
01658 {
01659 Tcl_SetResult(interp, "syntax error - 'argisnull argno'",
01660 TCL_STATIC);
01661 return TCL_ERROR;
01662 }
01663
01664
01665
01666
01667 if (fcinfo == NULL)
01668 {
01669 Tcl_SetResult(interp, "argisnull cannot be used in triggers",
01670 TCL_STATIC);
01671 return TCL_ERROR;
01672 }
01673
01674
01675
01676
01677 if (Tcl_GetInt(interp, argv[1], &argno) != TCL_OK)
01678 return TCL_ERROR;
01679
01680
01681
01682
01683 argno--;
01684 if (argno < 0 || argno >= fcinfo->nargs)
01685 {
01686 Tcl_SetResult(interp, "argno out of range", TCL_STATIC);
01687 return TCL_ERROR;
01688 }
01689
01690
01691
01692
01693 if (PG_ARGISNULL(argno))
01694 Tcl_SetResult(interp, "1", TCL_STATIC);
01695 else
01696 Tcl_SetResult(interp, "0", TCL_STATIC);
01697
01698 return TCL_OK;
01699 }
01700
01701
01702
01703
01704
01705 static int
01706 pltcl_returnnull(ClientData cdata, Tcl_Interp *interp,
01707 int argc, CONST84 char *argv[])
01708 {
01709 FunctionCallInfo fcinfo = pltcl_current_fcinfo;
01710
01711
01712
01713
01714 if (argc != 1)
01715 {
01716 Tcl_SetResult(interp, "syntax error - 'return_null'", TCL_STATIC);
01717 return TCL_ERROR;
01718 }
01719
01720
01721
01722
01723 if (fcinfo == NULL)
01724 {
01725 Tcl_SetResult(interp, "return_null cannot be used in triggers",
01726 TCL_STATIC);
01727 return TCL_ERROR;
01728 }
01729
01730
01731
01732
01733
01734 fcinfo->isnull = true;
01735
01736 return TCL_RETURN;
01737 }
01738
01739
01740
01741
01742
01743
01744
01745
01746
01747
01748
01749
01750
01751
01752
01753
01754
01755
01756
01757
01758
01759
01760
01761
01762
01763
01764 static void
01765 pltcl_subtrans_begin(MemoryContext oldcontext, ResourceOwner oldowner)
01766 {
01767 BeginInternalSubTransaction(NULL);
01768
01769
01770 MemoryContextSwitchTo(oldcontext);
01771 }
01772
01773 static void
01774 pltcl_subtrans_commit(MemoryContext oldcontext, ResourceOwner oldowner)
01775 {
01776
01777 ReleaseCurrentSubTransaction();
01778 MemoryContextSwitchTo(oldcontext);
01779 CurrentResourceOwner = oldowner;
01780
01781
01782
01783
01784
01785 SPI_restore_connection();
01786 }
01787
01788 static void
01789 pltcl_subtrans_abort(Tcl_Interp *interp,
01790 MemoryContext oldcontext, ResourceOwner oldowner)
01791 {
01792 ErrorData *edata;
01793
01794
01795 MemoryContextSwitchTo(oldcontext);
01796 edata = CopyErrorData();
01797 FlushErrorState();
01798
01799
01800 RollbackAndReleaseCurrentSubTransaction();
01801 MemoryContextSwitchTo(oldcontext);
01802 CurrentResourceOwner = oldowner;
01803
01804
01805
01806
01807
01808
01809 SPI_restore_connection();
01810
01811
01812 UTF_BEGIN;
01813 Tcl_SetResult(interp, UTF_E2U(edata->message), TCL_VOLATILE);
01814 UTF_END;
01815 FreeErrorData(edata);
01816 }
01817
01818
01819
01820
01821
01822
01823 static int
01824 pltcl_SPI_execute(ClientData cdata, Tcl_Interp *interp,
01825 int argc, CONST84 char *argv[])
01826 {
01827 int my_rc;
01828 int spi_rc;
01829 int query_idx;
01830 int i;
01831 int count = 0;
01832 CONST84 char *volatile arrayname = NULL;
01833 CONST84 char *volatile loop_body = NULL;
01834 MemoryContext oldcontext = CurrentMemoryContext;
01835 ResourceOwner oldowner = CurrentResourceOwner;
01836
01837 char *usage = "syntax error - 'SPI_exec "
01838 "?-count n? "
01839 "?-array name? query ?loop body?";
01840
01841
01842
01843
01844 if (argc < 2)
01845 {
01846 Tcl_SetResult(interp, usage, TCL_STATIC);
01847 return TCL_ERROR;
01848 }
01849
01850 i = 1;
01851 while (i < argc)
01852 {
01853 if (strcmp(argv[i], "-array") == 0)
01854 {
01855 if (++i >= argc)
01856 {
01857 Tcl_SetResult(interp, usage, TCL_STATIC);
01858 return TCL_ERROR;
01859 }
01860 arrayname = argv[i++];
01861 continue;
01862 }
01863
01864 if (strcmp(argv[i], "-count") == 0)
01865 {
01866 if (++i >= argc)
01867 {
01868 Tcl_SetResult(interp, usage, TCL_STATIC);
01869 return TCL_ERROR;
01870 }
01871 if (Tcl_GetInt(interp, argv[i++], &count) != TCL_OK)
01872 return TCL_ERROR;
01873 continue;
01874 }
01875
01876 break;
01877 }
01878
01879 query_idx = i;
01880 if (query_idx >= argc || query_idx + 2 < argc)
01881 {
01882 Tcl_SetResult(interp, usage, TCL_STATIC);
01883 return TCL_ERROR;
01884 }
01885 if (query_idx + 1 < argc)
01886 loop_body = argv[query_idx + 1];
01887
01888
01889
01890
01891
01892
01893 pltcl_subtrans_begin(oldcontext, oldowner);
01894
01895 PG_TRY();
01896 {
01897 UTF_BEGIN;
01898 spi_rc = SPI_execute(UTF_U2E(argv[query_idx]),
01899 pltcl_current_prodesc->fn_readonly, count);
01900 UTF_END;
01901
01902 my_rc = pltcl_process_SPI_result(interp,
01903 arrayname,
01904 loop_body,
01905 spi_rc,
01906 SPI_tuptable,
01907 SPI_processed);
01908
01909 pltcl_subtrans_commit(oldcontext, oldowner);
01910 }
01911 PG_CATCH();
01912 {
01913 pltcl_subtrans_abort(interp, oldcontext, oldowner);
01914 return TCL_ERROR;
01915 }
01916 PG_END_TRY();
01917
01918 return my_rc;
01919 }
01920
01921
01922
01923
01924
01925
01926 static int
01927 pltcl_process_SPI_result(Tcl_Interp *interp,
01928 CONST84 char *arrayname,
01929 CONST84 char *loop_body,
01930 int spi_rc,
01931 SPITupleTable *tuptable,
01932 int ntuples)
01933 {
01934 int my_rc = TCL_OK;
01935 char buf[64];
01936 int i;
01937 int loop_rc;
01938 HeapTuple *tuples;
01939 TupleDesc tupdesc;
01940
01941 switch (spi_rc)
01942 {
01943 case SPI_OK_SELINTO:
01944 case SPI_OK_INSERT:
01945 case SPI_OK_DELETE:
01946 case SPI_OK_UPDATE:
01947 snprintf(buf, sizeof(buf), "%d", ntuples);
01948 Tcl_SetResult(interp, buf, TCL_VOLATILE);
01949 break;
01950
01951 case SPI_OK_UTILITY:
01952 case SPI_OK_REWRITTEN:
01953 if (tuptable == NULL)
01954 {
01955 Tcl_SetResult(interp, "0", TCL_STATIC);
01956 break;
01957 }
01958
01959
01960 case SPI_OK_SELECT:
01961 case SPI_OK_INSERT_RETURNING:
01962 case SPI_OK_DELETE_RETURNING:
01963 case SPI_OK_UPDATE_RETURNING:
01964
01965
01966
01967
01968 tuples = tuptable->vals;
01969 tupdesc = tuptable->tupdesc;
01970
01971 if (loop_body == NULL)
01972 {
01973
01974
01975
01976
01977 if (ntuples > 0)
01978 pltcl_set_tuple_values(interp, arrayname, 0,
01979 tuples[0], tupdesc);
01980 }
01981 else
01982 {
01983
01984
01985
01986
01987 for (i = 0; i < ntuples; i++)
01988 {
01989 pltcl_set_tuple_values(interp, arrayname, i,
01990 tuples[i], tupdesc);
01991
01992 loop_rc = Tcl_Eval(interp, loop_body);
01993
01994 if (loop_rc == TCL_OK)
01995 continue;
01996 if (loop_rc == TCL_CONTINUE)
01997 continue;
01998 if (loop_rc == TCL_RETURN)
01999 {
02000 my_rc = TCL_RETURN;
02001 break;
02002 }
02003 if (loop_rc == TCL_BREAK)
02004 break;
02005 my_rc = TCL_ERROR;
02006 break;
02007 }
02008 }
02009
02010 if (my_rc == TCL_OK)
02011 {
02012 snprintf(buf, sizeof(buf), "%d", ntuples);
02013 Tcl_SetResult(interp, buf, TCL_VOLATILE);
02014 }
02015 break;
02016
02017 default:
02018 Tcl_AppendResult(interp, "pltcl: SPI_execute failed: ",
02019 SPI_result_code_string(spi_rc), NULL);
02020 my_rc = TCL_ERROR;
02021 break;
02022 }
02023
02024 SPI_freetuptable(tuptable);
02025
02026 return my_rc;
02027 }
02028
02029
02030
02031
02032
02033
02034
02035
02036
02037
02038 static int
02039 pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
02040 int argc, CONST84 char *argv[])
02041 {
02042 int nargs;
02043 CONST84 char **args;
02044 pltcl_query_desc *qdesc;
02045 int i;
02046 Tcl_HashEntry *hashent;
02047 int hashnew;
02048 Tcl_HashTable *query_hash;
02049 MemoryContext oldcontext = CurrentMemoryContext;
02050 ResourceOwner oldowner = CurrentResourceOwner;
02051
02052
02053
02054
02055 if (argc != 3)
02056 {
02057 Tcl_SetResult(interp, "syntax error - 'SPI_prepare query argtypes'",
02058 TCL_STATIC);
02059 return TCL_ERROR;
02060 }
02061
02062
02063
02064
02065 if (Tcl_SplitList(interp, argv[2], &nargs, &args) != TCL_OK)
02066 return TCL_ERROR;
02067
02068
02069
02070
02071 qdesc = (pltcl_query_desc *) malloc(sizeof(pltcl_query_desc));
02072 snprintf(qdesc->qname, sizeof(qdesc->qname), "%p", qdesc);
02073 qdesc->nargs = nargs;
02074 qdesc->argtypes = (Oid *) malloc(nargs * sizeof(Oid));
02075 qdesc->arginfuncs = (FmgrInfo *) malloc(nargs * sizeof(FmgrInfo));
02076 qdesc->argtypioparams = (Oid *) malloc(nargs * sizeof(Oid));
02077
02078
02079
02080
02081
02082
02083 pltcl_subtrans_begin(oldcontext, oldowner);
02084
02085 PG_TRY();
02086 {
02087
02088
02089
02090
02091
02092 for (i = 0; i < nargs; i++)
02093 {
02094 Oid typId,
02095 typInput,
02096 typIOParam;
02097 int32 typmod;
02098
02099 parseTypeString(args[i], &typId, &typmod);
02100
02101 getTypeInputInfo(typId, &typInput, &typIOParam);
02102
02103 qdesc->argtypes[i] = typId;
02104 perm_fmgr_info(typInput, &(qdesc->arginfuncs[i]));
02105 qdesc->argtypioparams[i] = typIOParam;
02106 }
02107
02108
02109
02110
02111 UTF_BEGIN;
02112 qdesc->plan = SPI_prepare(UTF_U2E(argv[1]), nargs, qdesc->argtypes);
02113 UTF_END;
02114
02115 if (qdesc->plan == NULL)
02116 elog(ERROR, "SPI_prepare() failed");
02117
02118
02119
02120
02121
02122 if (SPI_keepplan(qdesc->plan))
02123 elog(ERROR, "SPI_keepplan() failed");
02124
02125 pltcl_subtrans_commit(oldcontext, oldowner);
02126 }
02127 PG_CATCH();
02128 {
02129 pltcl_subtrans_abort(interp, oldcontext, oldowner);
02130
02131 free(qdesc->argtypes);
02132 free(qdesc->arginfuncs);
02133 free(qdesc->argtypioparams);
02134 free(qdesc);
02135 ckfree((char *) args);
02136
02137 return TCL_ERROR;
02138 }
02139 PG_END_TRY();
02140
02141
02142
02143
02144
02145 query_hash = &pltcl_current_prodesc->interp_desc->query_hash;
02146
02147 hashent = Tcl_CreateHashEntry(query_hash, qdesc->qname, &hashnew);
02148 Tcl_SetHashValue(hashent, (ClientData) qdesc);
02149
02150 ckfree((char *) args);
02151
02152
02153 Tcl_SetResult(interp, qdesc->qname, TCL_VOLATILE);
02154 return TCL_OK;
02155 }
02156
02157
02158
02159
02160
02161 static int
02162 pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp,
02163 int argc, CONST84 char *argv[])
02164 {
02165 int my_rc;
02166 int spi_rc;
02167 int i;
02168 int j;
02169 Tcl_HashEntry *hashent;
02170 pltcl_query_desc *qdesc;
02171 const char *volatile nulls = NULL;
02172 CONST84 char *volatile arrayname = NULL;
02173 CONST84 char *volatile loop_body = NULL;
02174 int count = 0;
02175 int callnargs;
02176 CONST84 char **callargs = NULL;
02177 Datum *argvalues;
02178 MemoryContext oldcontext = CurrentMemoryContext;
02179 ResourceOwner oldowner = CurrentResourceOwner;
02180 Tcl_HashTable *query_hash;
02181
02182 char *usage = "syntax error - 'SPI_execp "
02183 "?-nulls string? ?-count n? "
02184 "?-array name? query ?args? ?loop body?";
02185
02186
02187
02188
02189 i = 1;
02190 while (i < argc)
02191 {
02192 if (strcmp(argv[i], "-array") == 0)
02193 {
02194 if (++i >= argc)
02195 {
02196 Tcl_SetResult(interp, usage, TCL_STATIC);
02197 return TCL_ERROR;
02198 }
02199 arrayname = argv[i++];
02200 continue;
02201 }
02202 if (strcmp(argv[i], "-nulls") == 0)
02203 {
02204 if (++i >= argc)
02205 {
02206 Tcl_SetResult(interp, usage, TCL_STATIC);
02207 return TCL_ERROR;
02208 }
02209 nulls = argv[i++];
02210 continue;
02211 }
02212 if (strcmp(argv[i], "-count") == 0)
02213 {
02214 if (++i >= argc)
02215 {
02216 Tcl_SetResult(interp, usage, TCL_STATIC);
02217 return TCL_ERROR;
02218 }
02219 if (Tcl_GetInt(interp, argv[i++], &count) != TCL_OK)
02220 return TCL_ERROR;
02221 continue;
02222 }
02223
02224 break;
02225 }
02226
02227
02228
02229
02230 if (i >= argc)
02231 {
02232 Tcl_SetResult(interp, usage, TCL_STATIC);
02233 return TCL_ERROR;
02234 }
02235
02236 query_hash = &pltcl_current_prodesc->interp_desc->query_hash;
02237
02238 hashent = Tcl_FindHashEntry(query_hash, argv[i]);
02239 if (hashent == NULL)
02240 {
02241 Tcl_AppendResult(interp, "invalid queryid '", argv[i], "'", NULL);
02242 return TCL_ERROR;
02243 }
02244 qdesc = (pltcl_query_desc *) Tcl_GetHashValue(hashent);
02245 i++;
02246
02247
02248
02249
02250 if (nulls != NULL)
02251 {
02252 if (strlen(nulls) != qdesc->nargs)
02253 {
02254 Tcl_SetResult(interp,
02255 "length of nulls string doesn't match # of arguments",
02256 TCL_STATIC);
02257 return TCL_ERROR;
02258 }
02259 }
02260
02261
02262
02263
02264
02265 if (qdesc->nargs > 0)
02266 {
02267 if (i >= argc)
02268 {
02269 Tcl_SetResult(interp, "missing argument list", TCL_STATIC);
02270 return TCL_ERROR;
02271 }
02272
02273
02274
02275
02276 if (Tcl_SplitList(interp, argv[i++], &callnargs, &callargs) != TCL_OK)
02277 return TCL_ERROR;
02278
02279
02280
02281
02282 if (callnargs != qdesc->nargs)
02283 {
02284 Tcl_SetResult(interp,
02285 "argument list length doesn't match # of arguments for query",
02286 TCL_STATIC);
02287 ckfree((char *) callargs);
02288 return TCL_ERROR;
02289 }
02290 }
02291 else
02292 callnargs = 0;
02293
02294
02295
02296
02297 if (i < argc)
02298 loop_body = argv[i++];
02299
02300 if (i != argc)
02301 {
02302 Tcl_SetResult(interp, usage, TCL_STATIC);
02303 return TCL_ERROR;
02304 }
02305
02306
02307
02308
02309
02310
02311 pltcl_subtrans_begin(oldcontext, oldowner);
02312
02313 PG_TRY();
02314 {
02315
02316
02317
02318
02319 argvalues = (Datum *) palloc(callnargs * sizeof(Datum));
02320
02321 for (j = 0; j < callnargs; j++)
02322 {
02323 if (nulls && nulls[j] == 'n')
02324 {
02325 argvalues[j] = InputFunctionCall(&qdesc->arginfuncs[j],
02326 NULL,
02327 qdesc->argtypioparams[j],
02328 -1);
02329 }
02330 else
02331 {
02332 UTF_BEGIN;
02333 argvalues[j] = InputFunctionCall(&qdesc->arginfuncs[j],
02334 (char *) UTF_U2E(callargs[j]),
02335 qdesc->argtypioparams[j],
02336 -1);
02337 UTF_END;
02338 }
02339 }
02340
02341 if (callargs)
02342 ckfree((char *) callargs);
02343 callargs = NULL;
02344
02345
02346
02347
02348 spi_rc = SPI_execute_plan(qdesc->plan, argvalues, nulls,
02349 pltcl_current_prodesc->fn_readonly, count);
02350
02351 my_rc = pltcl_process_SPI_result(interp,
02352 arrayname,
02353 loop_body,
02354 spi_rc,
02355 SPI_tuptable,
02356 SPI_processed);
02357
02358 pltcl_subtrans_commit(oldcontext, oldowner);
02359 }
02360 PG_CATCH();
02361 {
02362 pltcl_subtrans_abort(interp, oldcontext, oldowner);
02363
02364 if (callargs)
02365 ckfree((char *) callargs);
02366
02367 return TCL_ERROR;
02368 }
02369 PG_END_TRY();
02370
02371 return my_rc;
02372 }
02373
02374
02375
02376
02377
02378
02379 static int
02380 pltcl_SPI_lastoid(ClientData cdata, Tcl_Interp *interp,
02381 int argc, CONST84 char *argv[])
02382 {
02383 char buf[64];
02384
02385 snprintf(buf, sizeof(buf), "%u", SPI_lastoid);
02386 Tcl_SetResult(interp, buf, TCL_VOLATILE);
02387 return TCL_OK;
02388 }
02389
02390
02391
02392
02393
02394
02395 static void
02396 pltcl_set_tuple_values(Tcl_Interp *interp, CONST84 char *arrayname,
02397 int tupno, HeapTuple tuple, TupleDesc tupdesc)
02398 {
02399 int i;
02400 char *outputstr;
02401 char buf[64];
02402 Datum attr;
02403 bool isnull;
02404
02405 CONST84 char *attname;
02406 HeapTuple typeTup;
02407 Oid typoutput;
02408
02409 CONST84 char **arrptr;
02410 CONST84 char **nameptr;
02411 CONST84 char *nullname = NULL;
02412
02413
02414
02415
02416
02417 if (arrayname == NULL)
02418 {
02419 arrptr = &attname;
02420 nameptr = &nullname;
02421 }
02422 else
02423 {
02424 arrptr = &arrayname;
02425 nameptr = &attname;
02426 snprintf(buf, sizeof(buf), "%d", tupno);
02427 Tcl_SetVar2(interp, arrayname, ".tupno", buf, 0);
02428 }
02429
02430 for (i = 0; i < tupdesc->natts; i++)
02431 {
02432
02433 if (tupdesc->attrs[i]->attisdropped)
02434 continue;
02435
02436
02437
02438
02439 attname = NameStr(tupdesc->attrs[i]->attname);
02440
02441
02442
02443
02444 attr = heap_getattr(tuple, i + 1, tupdesc, &isnull);
02445
02446
02447
02448
02449
02450 typeTup = SearchSysCache1(TYPEOID,
02451 ObjectIdGetDatum(tupdesc->attrs[i]->atttypid));
02452 if (!HeapTupleIsValid(typeTup))
02453 elog(ERROR, "cache lookup failed for type %u",
02454 tupdesc->attrs[i]->atttypid);
02455
02456 typoutput = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput;
02457 ReleaseSysCache(typeTup);
02458
02459
02460
02461
02462
02463
02464
02465
02466
02467 if (!isnull && OidIsValid(typoutput))
02468 {
02469 outputstr = OidOutputFunctionCall(typoutput, attr);
02470 UTF_BEGIN;
02471 Tcl_SetVar2(interp, *arrptr, *nameptr, UTF_E2U(outputstr), 0);
02472 UTF_END;
02473 pfree(outputstr);
02474 }
02475 else
02476 Tcl_UnsetVar2(interp, *arrptr, *nameptr, 0);
02477 }
02478 }
02479
02480
02481
02482
02483
02484
02485 static void
02486 pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc,
02487 Tcl_DString *retval)
02488 {
02489 int i;
02490 char *outputstr;
02491 Datum attr;
02492 bool isnull;
02493
02494 char *attname;
02495 HeapTuple typeTup;
02496 Oid typoutput;
02497
02498 for (i = 0; i < tupdesc->natts; i++)
02499 {
02500
02501 if (tupdesc->attrs[i]->attisdropped)
02502 continue;
02503
02504
02505
02506
02507 attname = NameStr(tupdesc->attrs[i]->attname);
02508
02509
02510
02511
02512 attr = heap_getattr(tuple, i + 1, tupdesc, &isnull);
02513
02514
02515
02516
02517
02518 typeTup = SearchSysCache1(TYPEOID,
02519 ObjectIdGetDatum(tupdesc->attrs[i]->atttypid));
02520 if (!HeapTupleIsValid(typeTup))
02521 elog(ERROR, "cache lookup failed for type %u",
02522 tupdesc->attrs[i]->atttypid);
02523
02524 typoutput = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput;
02525 ReleaseSysCache(typeTup);
02526
02527
02528
02529
02530
02531
02532
02533
02534
02535 if (!isnull && OidIsValid(typoutput))
02536 {
02537 outputstr = OidOutputFunctionCall(typoutput, attr);
02538 Tcl_DStringAppendElement(retval, attname);
02539 UTF_BEGIN;
02540 Tcl_DStringAppendElement(retval, UTF_E2U(outputstr));
02541 UTF_END;
02542 pfree(outputstr);
02543 }
02544 }
02545 }