~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

Viewing changes to src/pl/tcl/pltcl.c

  • Committer: alvherre
  • Date: 2005-12-16 21:24:52 UTC
  • Revision ID: svn-v4:db760fc0-0f08-0410-9d63-cc6633f64896:trunk:1
Initial import of the REL8_0_3 sources from the Pgsql CVS repository.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**********************************************************************
 
2
 * pltcl.c              - PostgreSQL support for Tcl as
 
3
 *                                procedural language (PL)
 
4
 *
 
5
 *        This software is copyrighted by Jan Wieck - Hamburg.
 
6
 *
 
7
 *        The author hereby grants permission  to  use,  copy,  modify,
 
8
 *        distribute,  and      license this software and its documentation
 
9
 *        for any purpose, provided that existing copyright notices are
 
10
 *        retained      in      all  copies  and  that  this notice is included
 
11
 *        verbatim in any distributions. No written agreement, license,
 
12
 *        or  royalty  fee      is required for any of the authorized uses.
 
13
 *        Modifications to this software may be  copyrighted  by  their
 
14
 *        author  and  need  not  follow  the licensing terms described
 
15
 *        here, provided that the new terms are  clearly  indicated  on
 
16
 *        the first page of each file where they apply.
 
17
 *
 
18
 *        IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY
 
19
 *        PARTY  FOR  DIRECT,   INDIRECT,       SPECIAL,   INCIDENTAL,   OR
 
20
 *        CONSEQUENTIAL   DAMAGES  ARISING      OUT  OF  THE  USE  OF  THIS
 
21
 *        SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN
 
22
 *        IF  THE  AUTHOR  HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
 
23
 *        DAMAGE.
 
24
 *
 
25
 *        THE  AUTHOR  AND      DISTRIBUTORS  SPECIFICALLY       DISCLAIM       ANY
 
26
 *        WARRANTIES,  INCLUDING,  BUT  NOT  LIMITED  TO,  THE  IMPLIED
 
27
 *        WARRANTIES  OF  MERCHANTABILITY,      FITNESS  FOR  A  PARTICULAR
 
28
 *        PURPOSE,      AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON
 
29
 *        AN "AS IS" BASIS, AND THE AUTHOR      AND  DISTRIBUTORS  HAVE  NO
 
30
 *        OBLIGATION   TO       PROVIDE   MAINTENANCE,   SUPPORT,  UPDATES,
 
31
 *        ENHANCEMENTS, OR MODIFICATIONS.
 
32
 *
 
33
 * IDENTIFICATION
 
34
 *        $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.94 2004-11-21 21:17:05 tgl Exp $
 
35
 *
 
36
 **********************************************************************/
 
37
 
 
38
#include "postgres.h"
 
39
 
 
40
#include <tcl.h>
 
41
 
 
42
#include <unistd.h>
 
43
#include <fcntl.h>
 
44
 
 
45
/* Hack to deal with Tcl 8.4 const-ification without losing compatibility */
 
46
#ifndef CONST84
 
47
#define CONST84
 
48
#endif
 
49
 
 
50
#include "access/heapam.h"
 
51
#include "catalog/pg_language.h"
 
52
#include "catalog/pg_proc.h"
 
53
#include "commands/trigger.h"
 
54
#include "executor/spi.h"
 
55
#include "fmgr.h"
 
56
#include "nodes/makefuncs.h"
 
57
#include "parser/parse_type.h"
 
58
#include "tcop/tcopprot.h"
 
59
#include "utils/builtins.h"
 
60
#include "utils/lsyscache.h"
 
61
#include "utils/syscache.h"
 
62
#include "utils/typcache.h"
 
63
 
 
64
 
 
65
#if defined(UNICODE_CONVERSION) && TCL_MAJOR_VERSION == 8 \
 
66
        && TCL_MINOR_VERSION > 0
 
67
 
 
68
#include "mb/pg_wchar.h"
 
69
 
 
70
static unsigned char *
 
71
utf_u2e(unsigned char *src)
 
72
{
 
73
        return pg_do_encoding_conversion(src, strlen(src), PG_UTF8, GetDatabaseEncoding());
 
74
}
 
75
 
 
76
static unsigned char *
 
77
utf_e2u(unsigned char *src)
 
78
{
 
79
        return pg_do_encoding_conversion(src, strlen(src), GetDatabaseEncoding(), PG_UTF8);
 
80
}
 
81
 
 
82
#define PLTCL_UTF
 
83
#define UTF_BEGIN        do { \
 
84
                                        unsigned char *_pltcl_utf_src; \
 
85
                                        unsigned char *_pltcl_utf_dst
 
86
#define UTF_END          if (_pltcl_utf_src!=_pltcl_utf_dst) \
 
87
                                        pfree(_pltcl_utf_dst); } while (0)
 
88
#define UTF_U2E(x)       (_pltcl_utf_dst=utf_u2e(_pltcl_utf_src=(x)))
 
89
#define UTF_E2U(x)       (_pltcl_utf_dst=utf_e2u(_pltcl_utf_src=(x)))
 
90
 
 
91
#else                                                   /* !PLTCL_UTF */
 
92
 
 
93
#define  UTF_BEGIN
 
94
#define  UTF_END
 
95
#define  UTF_U2E(x)  (x)
 
96
#define  UTF_E2U(x)  (x)
 
97
 
 
98
#endif   /* PLTCL_UTF */
 
99
 
 
100
 
 
101
/**********************************************************************
 
102
 * The information we cache about loaded procedures
 
103
 **********************************************************************/
 
104
typedef struct pltcl_proc_desc
 
105
{
 
106
        char       *proname;
 
107
        TransactionId fn_xmin;
 
108
        CommandId       fn_cmin;
 
109
        bool            fn_readonly;
 
110
        bool            lanpltrusted;
 
111
        FmgrInfo        result_in_func;
 
112
        Oid                     result_typioparam;
 
113
        int                     nargs;
 
114
        FmgrInfo        arg_out_func[FUNC_MAX_ARGS];
 
115
        Oid                     arg_typioparam[FUNC_MAX_ARGS];
 
116
        bool            arg_is_rowtype[FUNC_MAX_ARGS];
 
117
} pltcl_proc_desc;
 
118
 
 
119
 
 
120
/**********************************************************************
 
121
 * The information we cache about prepared and saved plans
 
122
 **********************************************************************/
 
123
typedef struct pltcl_query_desc
 
124
{
 
125
        char            qname[20];
 
126
        void       *plan;
 
127
        int                     nargs;
 
128
        Oid                *argtypes;
 
129
        FmgrInfo   *arginfuncs;
 
130
        Oid                *argtypioparams;
 
131
} pltcl_query_desc;
 
132
 
 
133
 
 
134
/**********************************************************************
 
135
 * Global data
 
136
 **********************************************************************/
 
137
static bool pltcl_pm_init_done = false;
 
138
static bool pltcl_be_init_done = false;
 
139
static Tcl_Interp *pltcl_hold_interp = NULL;
 
140
static Tcl_Interp *pltcl_norm_interp = NULL;
 
141
static Tcl_Interp *pltcl_safe_interp = NULL;
 
142
static Tcl_HashTable *pltcl_proc_hash = NULL;
 
143
static Tcl_HashTable *pltcl_norm_query_hash = NULL;
 
144
static Tcl_HashTable *pltcl_safe_query_hash = NULL;
 
145
 
 
146
/* these are saved and restored by pltcl_call_handler */
 
147
static FunctionCallInfo pltcl_current_fcinfo = NULL;
 
148
static pltcl_proc_desc *pltcl_current_prodesc = NULL;
 
149
 
 
150
/**********************************************************************
 
151
 * Forward declarations
 
152
 **********************************************************************/
 
153
static void pltcl_init_all(void);
 
154
static void pltcl_init_interp(Tcl_Interp *interp);
 
155
 
 
156
static void pltcl_init_load_unknown(Tcl_Interp *interp);
 
157
 
 
158
Datum           pltcl_call_handler(PG_FUNCTION_ARGS);
 
159
Datum           pltclu_call_handler(PG_FUNCTION_ARGS);
 
160
void            pltcl_init(void);
 
161
 
 
162
static Datum pltcl_func_handler(PG_FUNCTION_ARGS);
 
163
 
 
164
static HeapTuple pltcl_trigger_handler(PG_FUNCTION_ARGS);
 
165
 
 
166
static pltcl_proc_desc *compile_pltcl_function(Oid fn_oid, Oid tgreloid);
 
167
 
 
168
static int pltcl_elog(ClientData cdata, Tcl_Interp *interp,
 
169
                   int argc, CONST84 char *argv[]);
 
170
static int pltcl_quote(ClientData cdata, Tcl_Interp *interp,
 
171
                        int argc, CONST84 char *argv[]);
 
172
static int pltcl_argisnull(ClientData cdata, Tcl_Interp *interp,
 
173
                                int argc, CONST84 char *argv[]);
 
174
static int pltcl_returnnull(ClientData cdata, Tcl_Interp *interp,
 
175
                                 int argc, CONST84 char *argv[]);
 
176
 
 
177
static int pltcl_SPI_execute(ClientData cdata, Tcl_Interp *interp,
 
178
                           int argc, CONST84 char *argv[]);
 
179
static int pltcl_process_SPI_result(Tcl_Interp *interp,
 
180
                                                                        CONST84 char *arrayname,
 
181
                                                                        CONST84 char *loop_body,
 
182
                                                                        int spi_rc,
 
183
                                                                        SPITupleTable *tuptable,
 
184
                                                                        int ntuples);
 
185
static int pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
 
186
                                  int argc, CONST84 char *argv[]);
 
187
static int pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp,
 
188
                                int argc, CONST84 char *argv[]);
 
189
static int pltcl_SPI_lastoid(ClientData cdata, Tcl_Interp *interp,
 
190
                                  int argc, CONST84 char *argv[]);
 
191
 
 
192
static void pltcl_set_tuple_values(Tcl_Interp *interp, CONST84 char *arrayname,
 
193
                                           int tupno, HeapTuple tuple, TupleDesc tupdesc);
 
194
static void pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc,
 
195
                                                   Tcl_DString *retval);
 
196
 
 
197
 
 
198
/*
 
199
 * This routine is a crock, and so is everyplace that calls it.  The problem
 
200
 * is that the cached form of pltcl functions/queries is allocated permanently
 
201
 * (mostly via malloc()) and never released until backend exit.  Subsidiary
 
202
 * data structures such as fmgr info records therefore must live forever
 
203
 * as well.  A better implementation would store all this stuff in a per-
 
204
 * function memory context that could be reclaimed at need.  In the meantime,
 
205
 * fmgr_info_cxt must be called specifying TopMemoryContext so that whatever
 
206
 * it might allocate, and whatever the eventual function might allocate using
 
207
 * fn_mcxt, will live forever too.
 
208
 */
 
209
static void
 
210
perm_fmgr_info(Oid functionId, FmgrInfo *finfo)
 
211
{
 
212
        fmgr_info_cxt(functionId, finfo, TopMemoryContext);
 
213
}
 
214
 
 
215
/**********************************************************************
 
216
 * pltcl_init()         - Initialize all that's safe to do in the postmaster
 
217
 *
 
218
 * DO NOT make this static --- it has to be callable by preload
 
219
 **********************************************************************/
 
220
void
 
221
pltcl_init(void)
 
222
{
 
223
        /************************************************************
 
224
         * Do initialization only once
 
225
         ************************************************************/
 
226
        if (pltcl_pm_init_done)
 
227
                return;
 
228
 
 
229
#ifdef WIN32
 
230
        /* Required on win32 to prevent error loading init.tcl */
 
231
        Tcl_FindExecutable("");
 
232
#endif
 
233
 
 
234
        /************************************************************
 
235
         * Create the dummy hold interpreter to prevent close of
 
236
         * stdout and stderr on DeleteInterp
 
237
         ************************************************************/
 
238
        if ((pltcl_hold_interp = Tcl_CreateInterp()) == NULL)
 
239
                elog(ERROR, "could not create \"hold\" interpreter");
 
240
 
 
241
        /************************************************************
 
242
         * Create the two interpreters
 
243
         ************************************************************/
 
244
        if ((pltcl_norm_interp =
 
245
                 Tcl_CreateSlave(pltcl_hold_interp, "norm", 0)) == NULL)
 
246
                elog(ERROR, "could not create \"normal\" interpreter");
 
247
        pltcl_init_interp(pltcl_norm_interp);
 
248
 
 
249
        if ((pltcl_safe_interp =
 
250
                 Tcl_CreateSlave(pltcl_hold_interp, "safe", 1)) == NULL)
 
251
                elog(ERROR, "could not create \"safe\" interpreter");
 
252
        pltcl_init_interp(pltcl_safe_interp);
 
253
 
 
254
        /************************************************************
 
255
         * Initialize the proc and query hash tables
 
256
         ************************************************************/
 
257
        pltcl_proc_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable));
 
258
        pltcl_norm_query_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable));
 
259
        pltcl_safe_query_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable));
 
260
        Tcl_InitHashTable(pltcl_proc_hash, TCL_STRING_KEYS);
 
261
        Tcl_InitHashTable(pltcl_norm_query_hash, TCL_STRING_KEYS);
 
262
        Tcl_InitHashTable(pltcl_safe_query_hash, TCL_STRING_KEYS);
 
263
 
 
264
        pltcl_pm_init_done = true;
 
265
}
 
266
 
 
267
/**********************************************************************
 
268
 * pltcl_init_all()             - Initialize all
 
269
 **********************************************************************/
 
270
static void
 
271
pltcl_init_all(void)
 
272
{
 
273
        /************************************************************
 
274
         * Execute postmaster-startup safe initialization
 
275
         ************************************************************/
 
276
        if (!pltcl_pm_init_done)
 
277
                pltcl_init();
 
278
 
 
279
        /************************************************************
 
280
         * Any other initialization that must be done each time a new
 
281
         * backend starts:
 
282
         * - Try to load the unknown procedure from pltcl_modules
 
283
         ************************************************************/
 
284
        if (!pltcl_be_init_done)
 
285
        {
 
286
                if (SPI_connect() != SPI_OK_CONNECT)
 
287
                        elog(ERROR, "SPI_connect failed");
 
288
                pltcl_init_load_unknown(pltcl_norm_interp);
 
289
                pltcl_init_load_unknown(pltcl_safe_interp);
 
290
                if (SPI_finish() != SPI_OK_FINISH)
 
291
                        elog(ERROR, "SPI_finish failed");
 
292
                pltcl_be_init_done = true;
 
293
        }
 
294
}
 
295
 
 
296
 
 
297
/**********************************************************************
 
298
 * pltcl_init_interp() - initialize a Tcl interpreter
 
299
 **********************************************************************/
 
300
static void
 
301
pltcl_init_interp(Tcl_Interp *interp)
 
302
{
 
303
        /************************************************************
 
304
         * Install the commands for SPI support in the interpreter
 
305
         ************************************************************/
 
306
        Tcl_CreateCommand(interp, "elog",
 
307
                                          pltcl_elog, NULL, NULL);
 
308
        Tcl_CreateCommand(interp, "quote",
 
309
                                          pltcl_quote, NULL, NULL);
 
310
        Tcl_CreateCommand(interp, "argisnull",
 
311
                                          pltcl_argisnull, NULL, NULL);
 
312
        Tcl_CreateCommand(interp, "return_null",
 
313
                                          pltcl_returnnull, NULL, NULL);
 
314
 
 
315
        Tcl_CreateCommand(interp, "spi_exec",
 
316
                                          pltcl_SPI_execute, NULL, NULL);
 
317
        Tcl_CreateCommand(interp, "spi_prepare",
 
318
                                          pltcl_SPI_prepare, NULL, NULL);
 
319
        Tcl_CreateCommand(interp, "spi_execp",
 
320
                                          pltcl_SPI_execute_plan, NULL, NULL);
 
321
        Tcl_CreateCommand(interp, "spi_lastoid",
 
322
                                          pltcl_SPI_lastoid, NULL, NULL);
 
323
}
 
324
 
 
325
 
 
326
/**********************************************************************
 
327
 * pltcl_init_load_unknown()    - Load the unknown procedure from
 
328
 *                                table pltcl_modules (if it exists)
 
329
 **********************************************************************/
 
330
static void
 
331
pltcl_init_load_unknown(Tcl_Interp *interp)
 
332
{
 
333
        int                     spi_rc;
 
334
        int                     tcl_rc;
 
335
        Tcl_DString unknown_src;
 
336
        char       *part;
 
337
        int                     i;
 
338
        int                     fno;
 
339
 
 
340
        /************************************************************
 
341
         * Check if table pltcl_modules exists
 
342
         ************************************************************/
 
343
        spi_rc = SPI_execute("select 1 from pg_catalog.pg_class "
 
344
                                                 "where relname = 'pltcl_modules'",
 
345
                                                 false, 1);
 
346
        SPI_freetuptable(SPI_tuptable);
 
347
        if (spi_rc != SPI_OK_SELECT)
 
348
                elog(ERROR, "select from pg_class failed");
 
349
        if (SPI_processed == 0)
 
350
                return;
 
351
 
 
352
        /************************************************************
 
353
         * Read all the row's from it where modname = 'unknown' in
 
354
         * the order of modseq
 
355
         ************************************************************/
 
356
        Tcl_DStringInit(&unknown_src);
 
357
 
 
358
        spi_rc = SPI_execute("select modseq, modsrc from pltcl_modules "
 
359
                                                 "where modname = 'unknown' "
 
360
                                                 "order by modseq",
 
361
                                                 false, 0);
 
362
        if (spi_rc != SPI_OK_SELECT)
 
363
                elog(ERROR, "select from pltcl_modules failed");
 
364
 
 
365
        /************************************************************
 
366
         * If there's nothing, module unknown doesn't exist
 
367
         ************************************************************/
 
368
        if (SPI_processed == 0)
 
369
        {
 
370
                Tcl_DStringFree(&unknown_src);
 
371
                SPI_freetuptable(SPI_tuptable);
 
372
                elog(WARNING, "module \"unknown\" not found in pltcl_modules");
 
373
                return;
 
374
        }
 
375
 
 
376
        /************************************************************
 
377
         * There is a module named unknown. Resemble the
 
378
         * source from the modsrc attributes and evaluate
 
379
         * it in the Tcl interpreter
 
380
         ************************************************************/
 
381
        fno = SPI_fnumber(SPI_tuptable->tupdesc, "modsrc");
 
382
 
 
383
        for (i = 0; i < SPI_processed; i++)
 
384
        {
 
385
                part = SPI_getvalue(SPI_tuptable->vals[i],
 
386
                                                        SPI_tuptable->tupdesc, fno);
 
387
                if (part != NULL)
 
388
                {
 
389
                        UTF_BEGIN;
 
390
                        Tcl_DStringAppend(&unknown_src, UTF_E2U(part), -1);
 
391
                        UTF_END;
 
392
                        pfree(part);
 
393
                }
 
394
        }
 
395
        tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&unknown_src));
 
396
        Tcl_DStringFree(&unknown_src);
 
397
        SPI_freetuptable(SPI_tuptable);
 
398
}
 
399
 
 
400
 
 
401
/**********************************************************************
 
402
 * pltcl_call_handler           - This is the only visible function
 
403
 *                                of the PL interpreter. The PostgreSQL
 
404
 *                                function manager and trigger manager
 
405
 *                                call this function for execution of
 
406
 *                                PL/Tcl procedures.
 
407
 **********************************************************************/
 
408
PG_FUNCTION_INFO_V1(pltcl_call_handler);
 
409
 
 
410
/* keep non-static */
 
411
Datum
 
412
pltcl_call_handler(PG_FUNCTION_ARGS)
 
413
{
 
414
        Datum           retval;
 
415
        FunctionCallInfo save_fcinfo;
 
416
        pltcl_proc_desc *save_prodesc;
 
417
 
 
418
        /*
 
419
         * Initialize interpreters if first time through
 
420
         */
 
421
        pltcl_init_all();
 
422
 
 
423
        /*
 
424
         * Ensure that static pointers are saved/restored properly
 
425
         */
 
426
        save_fcinfo = pltcl_current_fcinfo;
 
427
        save_prodesc = pltcl_current_prodesc;
 
428
 
 
429
        PG_TRY();
 
430
        {
 
431
                /*
 
432
                 * Determine if called as function or trigger and
 
433
                 * call appropriate subhandler
 
434
                 */
 
435
                if (CALLED_AS_TRIGGER(fcinfo))
 
436
                {
 
437
                        pltcl_current_fcinfo = NULL;
 
438
                        retval = PointerGetDatum(pltcl_trigger_handler(fcinfo));
 
439
                }
 
440
                else
 
441
                {
 
442
                        pltcl_current_fcinfo = fcinfo;
 
443
                        retval = pltcl_func_handler(fcinfo);
 
444
                }
 
445
        }
 
446
        PG_CATCH();
 
447
        {
 
448
                pltcl_current_fcinfo = save_fcinfo;
 
449
                pltcl_current_prodesc = save_prodesc;
 
450
                PG_RE_THROW();
 
451
        }
 
452
        PG_END_TRY();
 
453
 
 
454
        pltcl_current_fcinfo = save_fcinfo;
 
455
        pltcl_current_prodesc = save_prodesc;
 
456
 
 
457
        return retval;
 
458
}
 
459
 
 
460
 
 
461
/*
 
462
 * Alternate handler for unsafe functions
 
463
 */
 
464
PG_FUNCTION_INFO_V1(pltclu_call_handler);
 
465
 
 
466
/* keep non-static */
 
467
Datum
 
468
pltclu_call_handler(PG_FUNCTION_ARGS)
 
469
{
 
470
        return pltcl_call_handler(fcinfo);
 
471
}
 
472
 
 
473
/**********************************************************************
 
474
 * pltcl_func_handler()         - Handler for regular function calls
 
475
 **********************************************************************/
 
476
static Datum
 
477
pltcl_func_handler(PG_FUNCTION_ARGS)
 
478
{
 
479
        pltcl_proc_desc *prodesc;
 
480
        Tcl_Interp *volatile interp;
 
481
        Tcl_DString tcl_cmd;
 
482
        Tcl_DString list_tmp;
 
483
        int                     i;
 
484
        int                     tcl_rc;
 
485
        Datum           retval;
 
486
 
 
487
        /* Connect to SPI manager */
 
488
        if (SPI_connect() != SPI_OK_CONNECT)
 
489
                elog(ERROR, "could not connect to SPI manager");
 
490
 
 
491
        /* Find or compile the function */
 
492
        prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid);
 
493
 
 
494
        pltcl_current_prodesc = prodesc;
 
495
 
 
496
        if (prodesc->lanpltrusted)
 
497
                interp = pltcl_safe_interp;
 
498
        else
 
499
                interp = pltcl_norm_interp;
 
500
 
 
501
        /************************************************************
 
502
         * Create the tcl command to call the internal
 
503
         * proc in the Tcl interpreter
 
504
         ************************************************************/
 
505
        Tcl_DStringInit(&tcl_cmd);
 
506
        Tcl_DStringInit(&list_tmp);
 
507
        Tcl_DStringAppendElement(&tcl_cmd, prodesc->proname);
 
508
 
 
509
        /************************************************************
 
510
         * Add all call arguments to the command
 
511
         ************************************************************/
 
512
        PG_TRY();
 
513
        {
 
514
                for (i = 0; i < prodesc->nargs; i++)
 
515
                {
 
516
                        if (prodesc->arg_is_rowtype[i])
 
517
                        {
 
518
                                /**************************************************
 
519
                                 * For tuple values, add a list for 'array set ...'
 
520
                                 **************************************************/
 
521
                                if (fcinfo->argnull[i])
 
522
                                        Tcl_DStringAppendElement(&tcl_cmd, "");
 
523
                                else
 
524
                                {
 
525
                                        HeapTupleHeader td;
 
526
                                        Oid                     tupType;
 
527
                                        int32           tupTypmod;
 
528
                                        TupleDesc       tupdesc;
 
529
                                        HeapTupleData tmptup;
 
530
 
 
531
                                        td = DatumGetHeapTupleHeader(fcinfo->arg[i]);
 
532
                                        /* Extract rowtype info and find a tupdesc */
 
533
                                        tupType = HeapTupleHeaderGetTypeId(td);
 
534
                                        tupTypmod = HeapTupleHeaderGetTypMod(td);
 
535
                                        tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
 
536
                                        /* Build a temporary HeapTuple control structure */
 
537
                                        tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
 
538
                                        tmptup.t_data = td;
 
539
 
 
540
                                        Tcl_DStringSetLength(&list_tmp, 0);
 
541
                                        pltcl_build_tuple_argument(&tmptup, tupdesc, &list_tmp);
 
542
                                        Tcl_DStringAppendElement(&tcl_cmd,
 
543
                                                                                         Tcl_DStringValue(&list_tmp));
 
544
                                }
 
545
                        }
 
546
                        else
 
547
                        {
 
548
                                /**************************************************
 
549
                                 * Single values are added as string element
 
550
                                 * of their external representation
 
551
                                 **************************************************/
 
552
                                if (fcinfo->argnull[i])
 
553
                                        Tcl_DStringAppendElement(&tcl_cmd, "");
 
554
                                else
 
555
                                {
 
556
                                        char       *tmp;
 
557
 
 
558
                                        tmp = DatumGetCString(FunctionCall3(&prodesc->arg_out_func[i],
 
559
                                                                                                                fcinfo->arg[i],
 
560
                                                        ObjectIdGetDatum(prodesc->arg_typioparam[i]),
 
561
                                                                                                         Int32GetDatum(-1)));
 
562
                                        UTF_BEGIN;
 
563
                                        Tcl_DStringAppendElement(&tcl_cmd, UTF_E2U(tmp));
 
564
                                        UTF_END;
 
565
                                        pfree(tmp);
 
566
                                }
 
567
                        }
 
568
                }
 
569
        }
 
570
        PG_CATCH();
 
571
        {
 
572
                Tcl_DStringFree(&tcl_cmd);
 
573
                Tcl_DStringFree(&list_tmp);
 
574
                PG_RE_THROW();
 
575
        }
 
576
        PG_END_TRY();
 
577
        Tcl_DStringFree(&list_tmp);
 
578
 
 
579
        /************************************************************
 
580
         * Call the Tcl function
 
581
         *
 
582
         * We assume no PG error can be thrown directly from this call.
 
583
         ************************************************************/
 
584
        tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd));
 
585
        Tcl_DStringFree(&tcl_cmd);
 
586
 
 
587
        /************************************************************
 
588
         * Check for errors reported by Tcl.
 
589
         ************************************************************/
 
590
        if (tcl_rc != TCL_OK)
 
591
        {
 
592
                UTF_BEGIN;
 
593
                ereport(ERROR,
 
594
                                (errmsg("%s", interp->result),
 
595
                                 errcontext("%s",
 
596
                                                        UTF_U2E(Tcl_GetVar(interp, "errorInfo",
 
597
                                                                                           TCL_GLOBAL_ONLY)))));
 
598
                UTF_END;
 
599
        }
 
600
 
 
601
        /************************************************************
 
602
         * Disconnect from SPI manager and then create the return
 
603
         * value datum (if the input function does a palloc for it
 
604
         * this must not be allocated in the SPI memory context
 
605
         * because SPI_finish would free it).  But don't try to call
 
606
         * the result_in_func if we've been told to return a NULL;
 
607
         * the contents of interp->result may not be a valid value of
 
608
         * the result type in that case.
 
609
         ************************************************************/
 
610
        if (SPI_finish() != SPI_OK_FINISH)
 
611
                elog(ERROR, "SPI_finish() failed");
 
612
 
 
613
        if (fcinfo->isnull)
 
614
                retval = (Datum) 0;
 
615
        else
 
616
        {
 
617
                UTF_BEGIN;
 
618
                retval = FunctionCall3(&prodesc->result_in_func,
 
619
                                                           PointerGetDatum(UTF_U2E(interp->result)),
 
620
                                                        ObjectIdGetDatum(prodesc->result_typioparam),
 
621
                                                           Int32GetDatum(-1));
 
622
                UTF_END;
 
623
        }
 
624
 
 
625
        return retval;
 
626
}
 
627
 
 
628
 
 
629
/**********************************************************************
 
630
 * pltcl_trigger_handler()      - Handler for trigger calls
 
631
 **********************************************************************/
 
632
static HeapTuple
 
633
pltcl_trigger_handler(PG_FUNCTION_ARGS)
 
634
{
 
635
        pltcl_proc_desc *prodesc;
 
636
        Tcl_Interp *volatile interp;
 
637
        TriggerData *trigdata = (TriggerData *) fcinfo->context;
 
638
        char       *stroid;
 
639
        TupleDesc       tupdesc;
 
640
        volatile HeapTuple rettup;
 
641
        Tcl_DString tcl_cmd;
 
642
        Tcl_DString tcl_trigtup;
 
643
        Tcl_DString tcl_newtup;
 
644
        int                     tcl_rc;
 
645
        int                     i;
 
646
        int                *modattrs;
 
647
        Datum      *modvalues;
 
648
        char       *modnulls;
 
649
        int                     ret_numvals;
 
650
        CONST84 char **ret_values;
 
651
 
 
652
        /* Connect to SPI manager */
 
653
        if (SPI_connect() != SPI_OK_CONNECT)
 
654
                elog(ERROR, "could not connect to SPI manager");
 
655
 
 
656
        /* Find or compile the function */
 
657
        prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid,
 
658
                                                                RelationGetRelid(trigdata->tg_relation));
 
659
 
 
660
        pltcl_current_prodesc = prodesc;
 
661
 
 
662
        if (prodesc->lanpltrusted)
 
663
                interp = pltcl_safe_interp;
 
664
        else
 
665
                interp = pltcl_norm_interp;
 
666
 
 
667
        tupdesc = trigdata->tg_relation->rd_att;
 
668
 
 
669
        /************************************************************
 
670
         * Create the tcl command to call the internal
 
671
         * proc in the interpreter
 
672
         ************************************************************/
 
673
        Tcl_DStringInit(&tcl_cmd);
 
674
        Tcl_DStringInit(&tcl_trigtup);
 
675
        Tcl_DStringInit(&tcl_newtup);
 
676
        PG_TRY();
 
677
        {
 
678
                /* The procedure name */
 
679
                Tcl_DStringAppendElement(&tcl_cmd, prodesc->proname);
 
680
 
 
681
                /* The trigger name for argument TG_name */
 
682
                Tcl_DStringAppendElement(&tcl_cmd, trigdata->tg_trigger->tgname);
 
683
 
 
684
                /* The oid of the trigger relation for argument TG_relid */
 
685
                stroid = DatumGetCString(DirectFunctionCall1(oidout,
 
686
                                                ObjectIdGetDatum(trigdata->tg_relation->rd_id)));
 
687
                Tcl_DStringAppendElement(&tcl_cmd, stroid);
 
688
                pfree(stroid);
 
689
 
 
690
                /* A list of attribute names for argument TG_relatts */
 
691
                Tcl_DStringAppendElement(&tcl_trigtup, "");
 
692
                for (i = 0; i < tupdesc->natts; i++)
 
693
                {
 
694
                        if (tupdesc->attrs[i]->attisdropped)
 
695
                                Tcl_DStringAppendElement(&tcl_trigtup, "");
 
696
                        else
 
697
                                Tcl_DStringAppendElement(&tcl_trigtup,
 
698
                                                                        NameStr(tupdesc->attrs[i]->attname));
 
699
                }
 
700
                Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
 
701
                Tcl_DStringFree(&tcl_trigtup);
 
702
                Tcl_DStringInit(&tcl_trigtup);
 
703
 
 
704
                /* The when part of the event for TG_when */
 
705
                if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
 
706
                        Tcl_DStringAppendElement(&tcl_cmd, "BEFORE");
 
707
                else if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
 
708
                        Tcl_DStringAppendElement(&tcl_cmd, "AFTER");
 
709
                else
 
710
                        elog(ERROR, "unrecognized WHEN tg_event: %u", trigdata->tg_event);
 
711
 
 
712
                /* The level part of the event for TG_level */
 
713
                if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
 
714
                {
 
715
                        Tcl_DStringAppendElement(&tcl_cmd, "ROW");
 
716
 
 
717
                        /* Build the data list for the trigtuple */
 
718
                        pltcl_build_tuple_argument(trigdata->tg_trigtuple,
 
719
                                                                           tupdesc, &tcl_trigtup);
 
720
 
 
721
                        /*
 
722
                         * Now the command part of the event for TG_op and data for
 
723
                         * NEW and OLD
 
724
                         */
 
725
                        if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
 
726
                        {
 
727
                                Tcl_DStringAppendElement(&tcl_cmd, "INSERT");
 
728
 
 
729
                                Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
 
730
                                Tcl_DStringAppendElement(&tcl_cmd, "");
 
731
 
 
732
                                rettup = trigdata->tg_trigtuple;
 
733
                        }
 
734
                        else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
 
735
                        {
 
736
                                Tcl_DStringAppendElement(&tcl_cmd, "DELETE");
 
737
 
 
738
                                Tcl_DStringAppendElement(&tcl_cmd, "");
 
739
                                Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
 
740
 
 
741
                                rettup = trigdata->tg_trigtuple;
 
742
                        }
 
743
                        else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
 
744
                        {
 
745
                                Tcl_DStringAppendElement(&tcl_cmd, "UPDATE");
 
746
 
 
747
                                pltcl_build_tuple_argument(trigdata->tg_newtuple,
 
748
                                                                                   tupdesc, &tcl_newtup);
 
749
 
 
750
                                Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_newtup));
 
751
                                Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
 
752
 
 
753
                                rettup = trigdata->tg_newtuple;
 
754
                        }
 
755
                        else
 
756
                                elog(ERROR, "unrecognized OP tg_event: %u", trigdata->tg_event);
 
757
                }
 
758
                else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
 
759
                {
 
760
                        Tcl_DStringAppendElement(&tcl_cmd, "STATEMENT");
 
761
 
 
762
                        if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
 
763
                                Tcl_DStringAppendElement(&tcl_cmd, "INSERT");
 
764
                        else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
 
765
                                Tcl_DStringAppendElement(&tcl_cmd, "DELETE");
 
766
                        else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
 
767
                                Tcl_DStringAppendElement(&tcl_cmd, "UPDATE");
 
768
                        else
 
769
                                elog(ERROR, "unrecognized OP tg_event: %u", trigdata->tg_event);
 
770
 
 
771
                        Tcl_DStringAppendElement(&tcl_cmd, "");
 
772
                        Tcl_DStringAppendElement(&tcl_cmd, "");
 
773
 
 
774
                        rettup = (HeapTuple) NULL;
 
775
                }
 
776
                else
 
777
                        elog(ERROR, "unrecognized LEVEL tg_event: %u", trigdata->tg_event);
 
778
 
 
779
                /* Finally append the arguments from CREATE TRIGGER */
 
780
                for (i = 0; i < trigdata->tg_trigger->tgnargs; i++)
 
781
                        Tcl_DStringAppendElement(&tcl_cmd, trigdata->tg_trigger->tgargs[i]);
 
782
 
 
783
        }
 
784
        PG_CATCH();
 
785
        {
 
786
                Tcl_DStringFree(&tcl_cmd);
 
787
                Tcl_DStringFree(&tcl_trigtup);
 
788
                Tcl_DStringFree(&tcl_newtup);
 
789
                PG_RE_THROW();
 
790
        }
 
791
        PG_END_TRY();
 
792
        Tcl_DStringFree(&tcl_trigtup);
 
793
        Tcl_DStringFree(&tcl_newtup);
 
794
 
 
795
        /************************************************************
 
796
         * Call the Tcl function
 
797
         *
 
798
         * We assume no PG error can be thrown directly from this call.
 
799
         ************************************************************/
 
800
        tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd));
 
801
        Tcl_DStringFree(&tcl_cmd);
 
802
 
 
803
        /************************************************************
 
804
         * Check for errors reported by Tcl.
 
805
         ************************************************************/
 
806
        if (tcl_rc != TCL_OK)
 
807
        {
 
808
                UTF_BEGIN;
 
809
                ereport(ERROR,
 
810
                                (errmsg("%s", interp->result),
 
811
                                 errcontext("%s",
 
812
                                                        UTF_U2E(Tcl_GetVar(interp, "errorInfo",
 
813
                                                                                           TCL_GLOBAL_ONLY)))));
 
814
                UTF_END;
 
815
        }
 
816
 
 
817
        /************************************************************
 
818
         * The return value from the procedure might be one of
 
819
         * the magic strings OK or SKIP or a list from array get
 
820
         ************************************************************/
 
821
        if (SPI_finish() != SPI_OK_FINISH)
 
822
                elog(ERROR, "SPI_finish() failed");
 
823
 
 
824
        if (strcmp(interp->result, "OK") == 0)
 
825
                return rettup;
 
826
        if (strcmp(interp->result, "SKIP") == 0)
 
827
                return (HeapTuple) NULL;
 
828
 
 
829
        /************************************************************
 
830
         * Convert the result value from the Tcl interpreter
 
831
         * and setup structures for SPI_modifytuple();
 
832
         ************************************************************/
 
833
        if (Tcl_SplitList(interp, interp->result,
 
834
                                          &ret_numvals, &ret_values) != TCL_OK)
 
835
                elog(ERROR, "could not split return value from trigger: %s",
 
836
                         interp->result);
 
837
 
 
838
        /* Use a TRY to ensure ret_values will get freed */
 
839
        PG_TRY();
 
840
        {
 
841
 
 
842
                if (ret_numvals % 2 != 0)
 
843
                        elog(ERROR, "invalid return list from trigger - must have even # of elements");
 
844
 
 
845
                modattrs = (int *) palloc(tupdesc->natts * sizeof(int));
 
846
                modvalues = (Datum *) palloc(tupdesc->natts * sizeof(Datum));
 
847
                for (i = 0; i < tupdesc->natts; i++)
 
848
                {
 
849
                        modattrs[i] = i + 1;
 
850
                        modvalues[i] = (Datum) NULL;
 
851
                }
 
852
 
 
853
                modnulls = palloc(tupdesc->natts);
 
854
                memset(modnulls, 'n', tupdesc->natts);
 
855
 
 
856
                for (i = 0; i < ret_numvals; i += 2)
 
857
                {
 
858
                        CONST84 char *ret_name = ret_values[i];
 
859
                        CONST84 char *ret_value = ret_values[i + 1];
 
860
                        int                     attnum;
 
861
                        HeapTuple       typeTup;
 
862
                        Oid                     typinput;
 
863
                        Oid                     typioparam;
 
864
                        FmgrInfo        finfo;
 
865
 
 
866
                        /************************************************************
 
867
                         * Ignore ".tupno" pseudo elements (see pltcl_set_tuple_values)
 
868
                         ************************************************************/
 
869
                        if (strcmp(ret_name, ".tupno") == 0)
 
870
                                continue;
 
871
 
 
872
                        /************************************************************
 
873
                         * Get the attribute number
 
874
                         ************************************************************/
 
875
                        attnum = SPI_fnumber(tupdesc, ret_name);
 
876
                        if (attnum == SPI_ERROR_NOATTRIBUTE)
 
877
                                elog(ERROR, "invalid attribute \"%s\"", ret_name);
 
878
                        if (attnum <= 0)
 
879
                                elog(ERROR, "cannot set system attribute \"%s\"", ret_name);
 
880
 
 
881
                        /************************************************************
 
882
                         * Ignore dropped columns
 
883
                         ************************************************************/
 
884
                        if (tupdesc->attrs[attnum - 1]->attisdropped)
 
885
                                continue;
 
886
 
 
887
                        /************************************************************
 
888
                         * Lookup the attribute type in the syscache
 
889
                         * for the input function
 
890
                         ************************************************************/
 
891
                        typeTup = SearchSysCache(TYPEOID,
 
892
                                  ObjectIdGetDatum(tupdesc->attrs[attnum - 1]->atttypid),
 
893
                                                                         0, 0, 0);
 
894
                        if (!HeapTupleIsValid(typeTup))
 
895
                                elog(ERROR, "cache lookup failed for type %u",
 
896
                                         tupdesc->attrs[attnum - 1]->atttypid);
 
897
                        typinput = ((Form_pg_type) GETSTRUCT(typeTup))->typinput;
 
898
                        typioparam = getTypeIOParam(typeTup);
 
899
                        ReleaseSysCache(typeTup);
 
900
 
 
901
                        /************************************************************
 
902
                         * Set the attribute to NOT NULL and convert the contents
 
903
                         ************************************************************/
 
904
                        modnulls[attnum - 1] = ' ';
 
905
                        fmgr_info(typinput, &finfo);
 
906
                        UTF_BEGIN;
 
907
                        modvalues[attnum - 1] =
 
908
                                FunctionCall3(&finfo,
 
909
                                                          CStringGetDatum(UTF_U2E(ret_value)),
 
910
                                                          ObjectIdGetDatum(typioparam),
 
911
                                   Int32GetDatum(tupdesc->attrs[attnum - 1]->atttypmod));
 
912
                        UTF_END;
 
913
                }
 
914
 
 
915
                rettup = SPI_modifytuple(trigdata->tg_relation, rettup, tupdesc->natts,
 
916
                                                                 modattrs, modvalues, modnulls);
 
917
 
 
918
                pfree(modattrs);
 
919
                pfree(modvalues);
 
920
                pfree(modnulls);
 
921
 
 
922
                if (rettup == NULL)
 
923
                        elog(ERROR, "SPI_modifytuple() failed - RC = %d", SPI_result);
 
924
 
 
925
        }
 
926
        PG_CATCH();
 
927
        {
 
928
                ckfree((char *) ret_values);
 
929
                PG_RE_THROW();
 
930
        }
 
931
        PG_END_TRY();
 
932
        ckfree((char *) ret_values);
 
933
 
 
934
        return rettup;
 
935
}
 
936
 
 
937
 
 
938
/**********************************************************************
 
939
 * compile_pltcl_function       - compile (or hopefully just look up) function
 
940
 *
 
941
 * tgreloid is the OID of the relation when compiling a trigger, or zero
 
942
 * (InvalidOid) when compiling a plain function.
 
943
 **********************************************************************/
 
944
static pltcl_proc_desc *
 
945
compile_pltcl_function(Oid fn_oid, Oid tgreloid)
 
946
{
 
947
        bool            is_trigger = OidIsValid(tgreloid);
 
948
        HeapTuple       procTup;
 
949
        Form_pg_proc procStruct;
 
950
        char            internal_proname[128];
 
951
        Tcl_HashEntry *hashent;
 
952
        pltcl_proc_desc *prodesc = NULL;
 
953
        Tcl_Interp *interp;
 
954
        int                     i;
 
955
        int                     hashnew;
 
956
        int                     tcl_rc;
 
957
 
 
958
        /* We'll need the pg_proc tuple in any case... */
 
959
        procTup = SearchSysCache(PROCOID,
 
960
                                                         ObjectIdGetDatum(fn_oid),
 
961
                                                         0, 0, 0);
 
962
        if (!HeapTupleIsValid(procTup))
 
963
                elog(ERROR, "cache lookup failed for function %u", fn_oid);
 
964
        procStruct = (Form_pg_proc) GETSTRUCT(procTup);
 
965
 
 
966
        /************************************************************
 
967
         * Build our internal proc name from the functions Oid
 
968
         ************************************************************/
 
969
        if (!is_trigger)
 
970
                snprintf(internal_proname, sizeof(internal_proname),
 
971
                                 "__PLTcl_proc_%u", fn_oid);
 
972
        else
 
973
                snprintf(internal_proname, sizeof(internal_proname),
 
974
                                 "__PLTcl_proc_%u_trigger_%u", fn_oid, tgreloid);
 
975
 
 
976
        /************************************************************
 
977
         * Lookup the internal proc name in the hashtable
 
978
         ************************************************************/
 
979
        hashent = Tcl_FindHashEntry(pltcl_proc_hash, internal_proname);
 
980
 
 
981
        /************************************************************
 
982
         * If it's present, must check whether it's still up to date.
 
983
         * This is needed because CREATE OR REPLACE FUNCTION can modify the
 
984
         * function's pg_proc entry without changing its OID.
 
985
         ************************************************************/
 
986
        if (hashent != NULL)
 
987
        {
 
988
                bool            uptodate;
 
989
 
 
990
                prodesc = (pltcl_proc_desc *) Tcl_GetHashValue(hashent);
 
991
 
 
992
                uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
 
993
                        prodesc->fn_cmin == HeapTupleHeaderGetCmin(procTup->t_data));
 
994
 
 
995
                if (!uptodate)
 
996
                {
 
997
                        Tcl_DeleteHashEntry(hashent);
 
998
                        hashent = NULL;
 
999
                }
 
1000
        }
 
1001
 
 
1002
        /************************************************************
 
1003
         * If we haven't found it in the hashtable, we analyze
 
1004
         * the functions arguments and returntype and store
 
1005
         * the in-/out-functions in the prodesc block and create
 
1006
         * a new hashtable entry for it.
 
1007
         *
 
1008
         * Then we load the procedure into the Tcl interpreter.
 
1009
         ************************************************************/
 
1010
        if (hashent == NULL)
 
1011
        {
 
1012
                HeapTuple       langTup;
 
1013
                HeapTuple       typeTup;
 
1014
                Form_pg_language langStruct;
 
1015
                Form_pg_type typeStruct;
 
1016
                Tcl_DString proc_internal_def;
 
1017
                Tcl_DString proc_internal_body;
 
1018
                char            proc_internal_args[33 * FUNC_MAX_ARGS];
 
1019
                Datum           prosrcdatum;
 
1020
                bool            isnull;
 
1021
                char       *proc_source;
 
1022
                char            buf[32];
 
1023
 
 
1024
                /************************************************************
 
1025
                 * Allocate a new procedure description block
 
1026
                 ************************************************************/
 
1027
                prodesc = (pltcl_proc_desc *) malloc(sizeof(pltcl_proc_desc));
 
1028
                if (prodesc == NULL)
 
1029
                        ereport(ERROR,
 
1030
                                        (errcode(ERRCODE_OUT_OF_MEMORY),
 
1031
                                         errmsg("out of memory")));
 
1032
                MemSet(prodesc, 0, sizeof(pltcl_proc_desc));
 
1033
                prodesc->proname = strdup(internal_proname);
 
1034
                prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
 
1035
                prodesc->fn_cmin = HeapTupleHeaderGetCmin(procTup->t_data);
 
1036
 
 
1037
                /* Remember if function is STABLE/IMMUTABLE */
 
1038
                prodesc->fn_readonly =
 
1039
                        (procStruct->provolatile != PROVOLATILE_VOLATILE);
 
1040
 
 
1041
                /************************************************************
 
1042
                 * Lookup the pg_language tuple by Oid
 
1043
                 ************************************************************/
 
1044
                langTup = SearchSysCache(LANGOID,
 
1045
                                                                 ObjectIdGetDatum(procStruct->prolang),
 
1046
                                                                 0, 0, 0);
 
1047
                if (!HeapTupleIsValid(langTup))
 
1048
                {
 
1049
                        free(prodesc->proname);
 
1050
                        free(prodesc);
 
1051
                        elog(ERROR, "cache lookup failed for language %u",
 
1052
                                 procStruct->prolang);
 
1053
                }
 
1054
                langStruct = (Form_pg_language) GETSTRUCT(langTup);
 
1055
                prodesc->lanpltrusted = langStruct->lanpltrusted;
 
1056
                ReleaseSysCache(langTup);
 
1057
 
 
1058
                if (prodesc->lanpltrusted)
 
1059
                        interp = pltcl_safe_interp;
 
1060
                else
 
1061
                        interp = pltcl_norm_interp;
 
1062
 
 
1063
                /************************************************************
 
1064
                 * Get the required information for input conversion of the
 
1065
                 * return value.
 
1066
                 ************************************************************/
 
1067
                if (!is_trigger)
 
1068
                {
 
1069
                        typeTup = SearchSysCache(TYPEOID,
 
1070
                                                                ObjectIdGetDatum(procStruct->prorettype),
 
1071
                                                                         0, 0, 0);
 
1072
                        if (!HeapTupleIsValid(typeTup))
 
1073
                        {
 
1074
                                free(prodesc->proname);
 
1075
                                free(prodesc);
 
1076
                                elog(ERROR, "cache lookup failed for type %u",
 
1077
                                         procStruct->prorettype);
 
1078
                        }
 
1079
                        typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
 
1080
 
 
1081
                        /* Disallow pseudotype result, except VOID */
 
1082
                        if (typeStruct->typtype == 'p')
 
1083
                        {
 
1084
                                if (procStruct->prorettype == VOIDOID)
 
1085
                                         /* okay */ ;
 
1086
                                else if (procStruct->prorettype == TRIGGEROID)
 
1087
                                {
 
1088
                                        free(prodesc->proname);
 
1089
                                        free(prodesc);
 
1090
                                        ereport(ERROR,
 
1091
                                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 
1092
                                                         errmsg("trigger functions may only be called as triggers")));
 
1093
                                }
 
1094
                                else
 
1095
                                {
 
1096
                                        free(prodesc->proname);
 
1097
                                        free(prodesc);
 
1098
                                        ereport(ERROR,
 
1099
                                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 
1100
                                                  errmsg("pltcl functions cannot return type %s",
 
1101
                                                           format_type_be(procStruct->prorettype))));
 
1102
                                }
 
1103
                        }
 
1104
 
 
1105
                        if (typeStruct->typtype == 'c')
 
1106
                        {
 
1107
                                free(prodesc->proname);
 
1108
                                free(prodesc);
 
1109
                                ereport(ERROR,
 
1110
                                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 
1111
                                        errmsg("pltcl functions cannot return tuples yet")));
 
1112
                        }
 
1113
 
 
1114
                        perm_fmgr_info(typeStruct->typinput, &(prodesc->result_in_func));
 
1115
                        prodesc->result_typioparam = getTypeIOParam(typeTup);
 
1116
 
 
1117
                        ReleaseSysCache(typeTup);
 
1118
                }
 
1119
 
 
1120
                /************************************************************
 
1121
                 * Get the required information for output conversion
 
1122
                 * of all procedure arguments
 
1123
                 ************************************************************/
 
1124
                if (!is_trigger)
 
1125
                {
 
1126
                        prodesc->nargs = procStruct->pronargs;
 
1127
                        proc_internal_args[0] = '\0';
 
1128
                        for (i = 0; i < prodesc->nargs; i++)
 
1129
                        {
 
1130
                                typeTup = SearchSysCache(TYPEOID,
 
1131
                                                        ObjectIdGetDatum(procStruct->proargtypes[i]),
 
1132
                                                                                 0, 0, 0);
 
1133
                                if (!HeapTupleIsValid(typeTup))
 
1134
                                {
 
1135
                                        free(prodesc->proname);
 
1136
                                        free(prodesc);
 
1137
                                        elog(ERROR, "cache lookup failed for type %u",
 
1138
                                                 procStruct->proargtypes[i]);
 
1139
                                }
 
1140
                                typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
 
1141
 
 
1142
                                /* Disallow pseudotype argument */
 
1143
                                if (typeStruct->typtype == 'p')
 
1144
                                {
 
1145
                                        free(prodesc->proname);
 
1146
                                        free(prodesc);
 
1147
                                        ereport(ERROR,
 
1148
                                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 
1149
                                                         errmsg("pltcl functions cannot take type %s",
 
1150
                                                   format_type_be(procStruct->proargtypes[i]))));
 
1151
                                }
 
1152
 
 
1153
                                if (typeStruct->typtype == 'c')
 
1154
                                {
 
1155
                                        prodesc->arg_is_rowtype[i] = true;
 
1156
                                        snprintf(buf, sizeof(buf), "__PLTcl_Tup_%d", i + 1);
 
1157
                                }
 
1158
                                else
 
1159
                                {
 
1160
                                        prodesc->arg_is_rowtype[i] = false;
 
1161
                                        perm_fmgr_info(typeStruct->typoutput,
 
1162
                                                                   &(prodesc->arg_out_func[i]));
 
1163
                                        prodesc->arg_typioparam[i] = getTypeIOParam(typeTup);
 
1164
                                        snprintf(buf, sizeof(buf), "%d", i + 1);
 
1165
                                }
 
1166
 
 
1167
                                if (i > 0)
 
1168
                                        strcat(proc_internal_args, " ");
 
1169
                                strcat(proc_internal_args, buf);
 
1170
 
 
1171
                                ReleaseSysCache(typeTup);
 
1172
                        }
 
1173
                }
 
1174
                else
 
1175
                {
 
1176
                        /* trigger procedure has fixed args */
 
1177
                        strcpy(proc_internal_args,
 
1178
                                   "TG_name TG_relid TG_relatts TG_when TG_level TG_op __PLTcl_Tup_NEW __PLTcl_Tup_OLD args");
 
1179
                }
 
1180
 
 
1181
                /************************************************************
 
1182
                 * Create the tcl command to define the internal
 
1183
                 * procedure
 
1184
                 ************************************************************/
 
1185
                Tcl_DStringInit(&proc_internal_def);
 
1186
                Tcl_DStringInit(&proc_internal_body);
 
1187
                Tcl_DStringAppendElement(&proc_internal_def, "proc");
 
1188
                Tcl_DStringAppendElement(&proc_internal_def, internal_proname);
 
1189
                Tcl_DStringAppendElement(&proc_internal_def, proc_internal_args);
 
1190
 
 
1191
                /************************************************************
 
1192
                 * prefix procedure body with
 
1193
                 * upvar #0 <internal_procname> GD
 
1194
                 * and with appropriate setting of arguments
 
1195
                 ************************************************************/
 
1196
                Tcl_DStringAppend(&proc_internal_body, "upvar #0 ", -1);
 
1197
                Tcl_DStringAppend(&proc_internal_body, internal_proname, -1);
 
1198
                Tcl_DStringAppend(&proc_internal_body, " GD\n", -1);
 
1199
                if (!is_trigger)
 
1200
                {
 
1201
                        for (i = 0; i < prodesc->nargs; i++)
 
1202
                        {
 
1203
                                if (prodesc->arg_is_rowtype[i])
 
1204
                                {
 
1205
                                        snprintf(buf, sizeof(buf),
 
1206
                                                         "array set %d $__PLTcl_Tup_%d\n",
 
1207
                                                         i + 1, i + 1);
 
1208
                                        Tcl_DStringAppend(&proc_internal_body, buf, -1);
 
1209
                                }
 
1210
                        }
 
1211
                }
 
1212
                else
 
1213
                {
 
1214
                        Tcl_DStringAppend(&proc_internal_body,
 
1215
                                                          "array set NEW $__PLTcl_Tup_NEW\n", -1);
 
1216
                        Tcl_DStringAppend(&proc_internal_body,
 
1217
                                                          "array set OLD $__PLTcl_Tup_OLD\n", -1);
 
1218
 
 
1219
                        Tcl_DStringAppend(&proc_internal_body,
 
1220
                                                          "set i 0\n"
 
1221
                                                          "set v 0\n"
 
1222
                                                          "foreach v $args {\n"
 
1223
                                                          "  incr i\n"
 
1224
                                                          "  set $i $v\n"
 
1225
                                                          "}\n"
 
1226
                                                          "unset i v\n\n", -1);
 
1227
                }
 
1228
 
 
1229
                /************************************************************
 
1230
                 * Add user's function definition to proc body
 
1231
                 ************************************************************/
 
1232
                prosrcdatum = SysCacheGetAttr(PROCOID, procTup,
 
1233
                                                                          Anum_pg_proc_prosrc, &isnull);
 
1234
                if (isnull)
 
1235
                        elog(ERROR, "null prosrc");
 
1236
                proc_source = DatumGetCString(DirectFunctionCall1(textout,
 
1237
                                                                                                                  prosrcdatum));
 
1238
                UTF_BEGIN;
 
1239
                Tcl_DStringAppend(&proc_internal_body, UTF_E2U(proc_source), -1);
 
1240
                UTF_END;
 
1241
                pfree(proc_source);
 
1242
                Tcl_DStringAppendElement(&proc_internal_def,
 
1243
                                                                 Tcl_DStringValue(&proc_internal_body));
 
1244
                Tcl_DStringFree(&proc_internal_body);
 
1245
 
 
1246
                /************************************************************
 
1247
                 * Create the procedure in the interpreter
 
1248
                 ************************************************************/
 
1249
                tcl_rc = Tcl_GlobalEval(interp,
 
1250
                                                                Tcl_DStringValue(&proc_internal_def));
 
1251
                Tcl_DStringFree(&proc_internal_def);
 
1252
                if (tcl_rc != TCL_OK)
 
1253
                {
 
1254
                        free(prodesc->proname);
 
1255
                        free(prodesc);
 
1256
                        elog(ERROR, "could not create internal procedure \"%s\": %s",
 
1257
                                 internal_proname, interp->result);
 
1258
                }
 
1259
 
 
1260
                /************************************************************
 
1261
                 * Add the proc description block to the hashtable
 
1262
                 ************************************************************/
 
1263
                hashent = Tcl_CreateHashEntry(pltcl_proc_hash,
 
1264
                                                                          prodesc->proname, &hashnew);
 
1265
                Tcl_SetHashValue(hashent, (ClientData) prodesc);
 
1266
        }
 
1267
 
 
1268
        ReleaseSysCache(procTup);
 
1269
 
 
1270
        return prodesc;
 
1271
}
 
1272
 
 
1273
 
 
1274
/**********************************************************************
 
1275
 * pltcl_elog()         - elog() support for PLTcl
 
1276
 **********************************************************************/
 
1277
static int
 
1278
pltcl_elog(ClientData cdata, Tcl_Interp *interp,
 
1279
                   int argc, CONST84 char *argv[])
 
1280
{
 
1281
        volatile int level;
 
1282
        MemoryContext oldcontext;
 
1283
 
 
1284
        if (argc != 3)
 
1285
        {
 
1286
                Tcl_SetResult(interp, "syntax error - 'elog level msg'",
 
1287
                                          TCL_VOLATILE);
 
1288
                return TCL_ERROR;
 
1289
        }
 
1290
 
 
1291
        if (strcmp(argv[1], "DEBUG") == 0)
 
1292
                level = DEBUG2;
 
1293
        else if (strcmp(argv[1], "LOG") == 0)
 
1294
                level = LOG;
 
1295
        else if (strcmp(argv[1], "INFO") == 0)
 
1296
                level = INFO;
 
1297
        else if (strcmp(argv[1], "NOTICE") == 0)
 
1298
                level = NOTICE;
 
1299
        else if (strcmp(argv[1], "WARNING") == 0)
 
1300
                level = WARNING;
 
1301
        else if (strcmp(argv[1], "ERROR") == 0)
 
1302
                level = ERROR;
 
1303
        else if (strcmp(argv[1], "FATAL") == 0)
 
1304
                level = FATAL;
 
1305
        else
 
1306
        {
 
1307
                Tcl_AppendResult(interp, "Unknown elog level '", argv[1],
 
1308
                                                 "'", NULL);
 
1309
                return TCL_ERROR;
 
1310
        }
 
1311
 
 
1312
        /************************************************************
 
1313
         * If elog() throws an error, catch it and return the error to the
 
1314
         * Tcl interpreter.  Note we are assuming that elog() can't have any
 
1315
         * internal failures that are so bad as to require a transaction abort.
 
1316
         ************************************************************/
 
1317
        oldcontext = CurrentMemoryContext;
 
1318
        PG_TRY();
 
1319
        {
 
1320
                UTF_BEGIN;
 
1321
                elog(level, "%s", UTF_U2E(argv[2]));
 
1322
                UTF_END;
 
1323
        }
 
1324
        PG_CATCH();
 
1325
        {
 
1326
                ErrorData  *edata;
 
1327
 
 
1328
                /* Must reset elog.c's state */
 
1329
                MemoryContextSwitchTo(oldcontext);
 
1330
                edata = CopyErrorData();
 
1331
                FlushErrorState();
 
1332
 
 
1333
                /* Pass the error message to Tcl */
 
1334
                Tcl_SetResult(interp, edata->message, TCL_VOLATILE);
 
1335
                FreeErrorData(edata);
 
1336
 
 
1337
                return TCL_ERROR;
 
1338
        }
 
1339
        PG_END_TRY();
 
1340
 
 
1341
        return TCL_OK;
 
1342
}
 
1343
 
 
1344
 
 
1345
/**********************************************************************
 
1346
 * pltcl_quote()        - quote literal strings that are to
 
1347
 *                        be used in SPI_execute query strings
 
1348
 **********************************************************************/
 
1349
static int
 
1350
pltcl_quote(ClientData cdata, Tcl_Interp *interp,
 
1351
                        int argc, CONST84 char *argv[])
 
1352
{
 
1353
        char       *tmp;
 
1354
        const char *cp1;
 
1355
        char       *cp2;
 
1356
 
 
1357
        /************************************************************
 
1358
         * Check call syntax
 
1359
         ************************************************************/
 
1360
        if (argc != 2)
 
1361
        {
 
1362
                Tcl_SetResult(interp, "syntax error - 'quote string'", TCL_VOLATILE);
 
1363
                return TCL_ERROR;
 
1364
        }
 
1365
 
 
1366
        /************************************************************
 
1367
         * Allocate space for the maximum the string can
 
1368
         * grow to and initialize pointers
 
1369
         ************************************************************/
 
1370
        tmp = palloc(strlen(argv[1]) * 2 + 1);
 
1371
        cp1 = argv[1];
 
1372
        cp2 = tmp;
 
1373
 
 
1374
        /************************************************************
 
1375
         * Walk through string and double every quote and backslash
 
1376
         ************************************************************/
 
1377
        while (*cp1)
 
1378
        {
 
1379
                if (*cp1 == '\'')
 
1380
                        *cp2++ = '\'';
 
1381
                else
 
1382
                {
 
1383
                        if (*cp1 == '\\')
 
1384
                                *cp2++ = '\\';
 
1385
                }
 
1386
                *cp2++ = *cp1++;
 
1387
        }
 
1388
 
 
1389
        /************************************************************
 
1390
         * Terminate the string and set it as result
 
1391
         ************************************************************/
 
1392
        *cp2 = '\0';
 
1393
        Tcl_SetResult(interp, tmp, TCL_VOLATILE);
 
1394
        pfree(tmp);
 
1395
        return TCL_OK;
 
1396
}
 
1397
 
 
1398
 
 
1399
/**********************************************************************
 
1400
 * pltcl_argisnull()    - determine if a specific argument is NULL
 
1401
 **********************************************************************/
 
1402
static int
 
1403
pltcl_argisnull(ClientData cdata, Tcl_Interp *interp,
 
1404
                                int argc, CONST84 char *argv[])
 
1405
{
 
1406
        int                     argno;
 
1407
        FunctionCallInfo fcinfo = pltcl_current_fcinfo;
 
1408
 
 
1409
        /************************************************************
 
1410
         * Check call syntax
 
1411
         ************************************************************/
 
1412
        if (argc != 2)
 
1413
        {
 
1414
                Tcl_SetResult(interp, "syntax error - 'argisnull argno'", TCL_VOLATILE);
 
1415
                return TCL_ERROR;
 
1416
        }
 
1417
 
 
1418
        /************************************************************
 
1419
         * Check that we're called as a normal function
 
1420
         ************************************************************/
 
1421
        if (fcinfo == NULL)
 
1422
        {
 
1423
                Tcl_SetResult(interp, "argisnull cannot be used in triggers",
 
1424
                                          TCL_VOLATILE);
 
1425
                return TCL_ERROR;
 
1426
        }
 
1427
 
 
1428
        /************************************************************
 
1429
         * Get the argument number
 
1430
         ************************************************************/
 
1431
        if (Tcl_GetInt(interp, argv[1], &argno) != TCL_OK)
 
1432
                return TCL_ERROR;
 
1433
 
 
1434
        /************************************************************
 
1435
         * Check that the argno is valid
 
1436
         ************************************************************/
 
1437
        argno--;
 
1438
        if (argno < 0 || argno >= fcinfo->nargs)
 
1439
        {
 
1440
                Tcl_SetResult(interp, "argno out of range", TCL_VOLATILE);
 
1441
                return TCL_ERROR;
 
1442
        }
 
1443
 
 
1444
        /************************************************************
 
1445
         * Get the requested NULL state
 
1446
         ************************************************************/
 
1447
        if (PG_ARGISNULL(argno))
 
1448
                Tcl_SetResult(interp, "1", TCL_VOLATILE);
 
1449
        else
 
1450
                Tcl_SetResult(interp, "0", TCL_VOLATILE);
 
1451
 
 
1452
        return TCL_OK;
 
1453
}
 
1454
 
 
1455
 
 
1456
/**********************************************************************
 
1457
 * pltcl_returnnull()   - Cause a NULL return from a function
 
1458
 **********************************************************************/
 
1459
static int
 
1460
pltcl_returnnull(ClientData cdata, Tcl_Interp *interp,
 
1461
                                 int argc, CONST84 char *argv[])
 
1462
{
 
1463
        FunctionCallInfo fcinfo = pltcl_current_fcinfo;
 
1464
 
 
1465
        /************************************************************
 
1466
         * Check call syntax
 
1467
         ************************************************************/
 
1468
        if (argc != 1)
 
1469
        {
 
1470
                Tcl_SetResult(interp, "syntax error - 'return_null'", TCL_VOLATILE);
 
1471
                return TCL_ERROR;
 
1472
        }
 
1473
 
 
1474
        /************************************************************
 
1475
         * Check that we're called as a normal function
 
1476
         ************************************************************/
 
1477
        if (fcinfo == NULL)
 
1478
        {
 
1479
                Tcl_SetResult(interp, "return_null cannot be used in triggers",
 
1480
                                          TCL_VOLATILE);
 
1481
                return TCL_ERROR;
 
1482
        }
 
1483
 
 
1484
        /************************************************************
 
1485
         * Set the NULL return flag and cause Tcl to return from the
 
1486
         * procedure.
 
1487
         ************************************************************/
 
1488
        fcinfo->isnull = true;
 
1489
 
 
1490
        return TCL_RETURN;
 
1491
}
 
1492
 
 
1493
 
 
1494
/*----------
 
1495
 * Support for running SPI operations inside subtransactions
 
1496
 *
 
1497
 * Intended usage pattern is:
 
1498
 *
 
1499
 *      MemoryContext oldcontext = CurrentMemoryContext;
 
1500
 *      ResourceOwner oldowner = CurrentResourceOwner;
 
1501
 *
 
1502
 *      ...
 
1503
 *      pltcl_subtrans_begin(oldcontext, oldowner);
 
1504
 *      PG_TRY();
 
1505
 *      {
 
1506
 *              do something risky;
 
1507
 *              pltcl_subtrans_commit(oldcontext, oldowner);
 
1508
 *      }
 
1509
 *      PG_CATCH();
 
1510
 *      {
 
1511
 *              pltcl_subtrans_abort(interp, oldcontext, oldowner);
 
1512
 *              return TCL_ERROR;
 
1513
 *      }
 
1514
 *      PG_END_TRY();
 
1515
 *      return TCL_OK;
 
1516
 *----------
 
1517
 */
 
1518
static void
 
1519
pltcl_subtrans_begin(MemoryContext oldcontext, ResourceOwner oldowner)
 
1520
{
 
1521
        BeginInternalSubTransaction(NULL);
 
1522
 
 
1523
        /* Want to run inside function's memory context */
 
1524
        MemoryContextSwitchTo(oldcontext);
 
1525
}
 
1526
 
 
1527
static void
 
1528
pltcl_subtrans_commit(MemoryContext oldcontext, ResourceOwner oldowner)
 
1529
{
 
1530
        /* Commit the inner transaction, return to outer xact context */
 
1531
        ReleaseCurrentSubTransaction();
 
1532
        MemoryContextSwitchTo(oldcontext);
 
1533
        CurrentResourceOwner = oldowner;
 
1534
 
 
1535
        /*
 
1536
         * AtEOSubXact_SPI() should not have popped any SPI context,
 
1537
         * but just in case it did, make sure we remain connected.
 
1538
         */
 
1539
        SPI_restore_connection();
 
1540
}
 
1541
 
 
1542
static void
 
1543
pltcl_subtrans_abort(Tcl_Interp *interp,
 
1544
                                         MemoryContext oldcontext, ResourceOwner oldowner)
 
1545
{
 
1546
        ErrorData  *edata;
 
1547
 
 
1548
        /* Save error info */
 
1549
        MemoryContextSwitchTo(oldcontext);
 
1550
        edata = CopyErrorData();
 
1551
        FlushErrorState();
 
1552
 
 
1553
        /* Abort the inner transaction */
 
1554
        RollbackAndReleaseCurrentSubTransaction();
 
1555
        MemoryContextSwitchTo(oldcontext);
 
1556
        CurrentResourceOwner = oldowner;
 
1557
 
 
1558
        /*
 
1559
         * If AtEOSubXact_SPI() popped any SPI context of the subxact,
 
1560
         * it will have left us in a disconnected state.  We need this
 
1561
         * hack to return to connected state.
 
1562
         */
 
1563
        SPI_restore_connection();
 
1564
 
 
1565
        /* Pass the error message to Tcl */
 
1566
        Tcl_SetResult(interp, edata->message, TCL_VOLATILE);
 
1567
        FreeErrorData(edata);
 
1568
}
 
1569
 
 
1570
 
 
1571
/**********************************************************************
 
1572
 * pltcl_SPI_execute()          - The builtin SPI_execute command
 
1573
 *                                for the Tcl interpreter
 
1574
 **********************************************************************/
 
1575
static int
 
1576
pltcl_SPI_execute(ClientData cdata, Tcl_Interp *interp,
 
1577
                                  int argc, CONST84 char *argv[])
 
1578
{
 
1579
        int                     my_rc;
 
1580
        int                     spi_rc;
 
1581
        int                     query_idx;
 
1582
        int                     i;
 
1583
        int                     count = 0;
 
1584
        CONST84 char *volatile arrayname = NULL;
 
1585
        CONST84 char *volatile loop_body = NULL;
 
1586
        MemoryContext oldcontext = CurrentMemoryContext;
 
1587
        ResourceOwner oldowner = CurrentResourceOwner;
 
1588
 
 
1589
        char       *usage = "syntax error - 'SPI_exec "
 
1590
        "?-count n? "
 
1591
        "?-array name? query ?loop body?";
 
1592
 
 
1593
        /************************************************************
 
1594
         * Check the call syntax and get the options
 
1595
         ************************************************************/
 
1596
        if (argc < 2)
 
1597
        {
 
1598
                Tcl_SetResult(interp, usage, TCL_VOLATILE);
 
1599
                return TCL_ERROR;
 
1600
        }
 
1601
 
 
1602
        i = 1;
 
1603
        while (i < argc)
 
1604
        {
 
1605
                if (strcmp(argv[i], "-array") == 0)
 
1606
                {
 
1607
                        if (++i >= argc)
 
1608
                        {
 
1609
                                Tcl_SetResult(interp, usage, TCL_VOLATILE);
 
1610
                                return TCL_ERROR;
 
1611
                        }
 
1612
                        arrayname = argv[i++];
 
1613
                        continue;
 
1614
                }
 
1615
 
 
1616
                if (strcmp(argv[i], "-count") == 0)
 
1617
                {
 
1618
                        if (++i >= argc)
 
1619
                        {
 
1620
                                Tcl_SetResult(interp, usage, TCL_VOLATILE);
 
1621
                                return TCL_ERROR;
 
1622
                        }
 
1623
                        if (Tcl_GetInt(interp, argv[i++], &count) != TCL_OK)
 
1624
                                return TCL_ERROR;
 
1625
                        continue;
 
1626
                }
 
1627
 
 
1628
                break;
 
1629
        }
 
1630
 
 
1631
        query_idx = i;
 
1632
        if (query_idx >= argc || query_idx + 2 < argc)
 
1633
        {
 
1634
                Tcl_SetResult(interp, usage, TCL_VOLATILE);
 
1635
                return TCL_ERROR;
 
1636
        }
 
1637
        if (query_idx + 1 < argc)
 
1638
                loop_body = argv[query_idx + 1];
 
1639
 
 
1640
        /************************************************************
 
1641
         * Execute the query inside a sub-transaction, so we can cope with
 
1642
         * errors sanely
 
1643
         ************************************************************/
 
1644
 
 
1645
        pltcl_subtrans_begin(oldcontext, oldowner);
 
1646
 
 
1647
        PG_TRY();
 
1648
        {
 
1649
                UTF_BEGIN;
 
1650
                spi_rc = SPI_execute(UTF_U2E(argv[query_idx]),
 
1651
                                                         pltcl_current_prodesc->fn_readonly, count);
 
1652
                UTF_END;
 
1653
 
 
1654
                my_rc = pltcl_process_SPI_result(interp,
 
1655
                                                                                 arrayname,
 
1656
                                                                                 loop_body,
 
1657
                                                                                 spi_rc,
 
1658
                                                                                 SPI_tuptable,
 
1659
                                                                                 SPI_processed);
 
1660
 
 
1661
                pltcl_subtrans_commit(oldcontext, oldowner);
 
1662
        }
 
1663
        PG_CATCH();
 
1664
        {
 
1665
                pltcl_subtrans_abort(interp, oldcontext, oldowner);
 
1666
                return TCL_ERROR;
 
1667
        }
 
1668
        PG_END_TRY();
 
1669
 
 
1670
        return my_rc;
 
1671
}
 
1672
 
 
1673
/*
 
1674
 * Process the result from SPI_execute or SPI_execute_plan
 
1675
 *
 
1676
 * Shared code between pltcl_SPI_execute and pltcl_SPI_execute_plan
 
1677
 */
 
1678
static int
 
1679
pltcl_process_SPI_result(Tcl_Interp *interp,
 
1680
                                                 CONST84 char *arrayname,
 
1681
                                                 CONST84 char *loop_body,
 
1682
                                                 int spi_rc,
 
1683
                                                 SPITupleTable *tuptable,
 
1684
                                                 int ntuples)
 
1685
{
 
1686
        int                     my_rc = TCL_OK;
 
1687
        char            buf[64];
 
1688
        int                     i;
 
1689
        int                     loop_rc;
 
1690
        HeapTuple  *tuples;
 
1691
        TupleDesc       tupdesc;
 
1692
 
 
1693
        switch (spi_rc)
 
1694
        {
 
1695
                case SPI_OK_UTILITY:
 
1696
                        Tcl_SetResult(interp, "0", TCL_VOLATILE);
 
1697
                        break;
 
1698
 
 
1699
                case SPI_OK_SELINTO:
 
1700
                case SPI_OK_INSERT:
 
1701
                case SPI_OK_DELETE:
 
1702
                case SPI_OK_UPDATE:
 
1703
                        snprintf(buf, sizeof(buf), "%d", ntuples);
 
1704
                        Tcl_SetResult(interp, buf, TCL_VOLATILE);
 
1705
                        break;
 
1706
 
 
1707
                case SPI_OK_SELECT:
 
1708
                        /*
 
1709
                         * Process the tuples we got
 
1710
                         */
 
1711
                        tuples = tuptable->vals;
 
1712
                        tupdesc = tuptable->tupdesc;
 
1713
 
 
1714
                        if (loop_body == NULL)
 
1715
                        {
 
1716
                                /*
 
1717
                                 * If there is no loop body given, just set the variables
 
1718
                                 * from the first tuple (if any)
 
1719
                                 */
 
1720
                                if (ntuples > 0)
 
1721
                                        pltcl_set_tuple_values(interp, arrayname, 0,
 
1722
                                                                                   tuples[0], tupdesc);
 
1723
                        }
 
1724
                        else
 
1725
                        {
 
1726
                                /*
 
1727
                                 * There is a loop body - process all tuples and evaluate
 
1728
                                 * the body on each
 
1729
                                 */
 
1730
                                for (i = 0; i < ntuples; i++)
 
1731
                                {
 
1732
                                        pltcl_set_tuple_values(interp, arrayname, i,
 
1733
                                                                                   tuples[i], tupdesc);
 
1734
 
 
1735
                                        loop_rc = Tcl_Eval(interp, loop_body);
 
1736
 
 
1737
                                        if (loop_rc == TCL_OK)
 
1738
                                                continue;
 
1739
                                        if (loop_rc == TCL_CONTINUE)
 
1740
                                                continue;
 
1741
                                        if (loop_rc == TCL_RETURN)
 
1742
                                        {
 
1743
                                                my_rc = TCL_RETURN;
 
1744
                                                break;
 
1745
                                        }
 
1746
                                        if (loop_rc == TCL_BREAK)
 
1747
                                                break;
 
1748
                                        my_rc = TCL_ERROR;
 
1749
                                        break;
 
1750
                                }
 
1751
                        }
 
1752
 
 
1753
                        if (my_rc == TCL_OK)
 
1754
                        {
 
1755
                                snprintf(buf, sizeof(buf), "%d", ntuples);
 
1756
                                Tcl_SetResult(interp, buf, TCL_VOLATILE);
 
1757
                        }
 
1758
                        break;
 
1759
 
 
1760
                default:
 
1761
                        Tcl_AppendResult(interp, "pltcl: SPI_execute failed: ",
 
1762
                                                         SPI_result_code_string(spi_rc), NULL);
 
1763
                        my_rc = TCL_ERROR;
 
1764
                        break;
 
1765
        }
 
1766
 
 
1767
        SPI_freetuptable(tuptable);
 
1768
 
 
1769
        return my_rc;
 
1770
}
 
1771
 
 
1772
 
 
1773
/**********************************************************************
 
1774
 * pltcl_SPI_prepare()          - Builtin support for prepared plans
 
1775
 *                                The Tcl command SPI_prepare
 
1776
 *                                always saves the plan using
 
1777
 *                                SPI_saveplan and returns a key for
 
1778
 *                                access. There is no chance to prepare
 
1779
 *                                and not save the plan currently.
 
1780
 **********************************************************************/
 
1781
static int
 
1782
pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
 
1783
                                  int argc, CONST84 char *argv[])
 
1784
{
 
1785
        int                     nargs;
 
1786
        CONST84 char **args;
 
1787
        pltcl_query_desc *qdesc;
 
1788
        void       *plan;
 
1789
        int                     i;
 
1790
        HeapTuple       typeTup;
 
1791
        Tcl_HashEntry *hashent;
 
1792
        int                     hashnew;
 
1793
        Tcl_HashTable *query_hash;
 
1794
        MemoryContext oldcontext = CurrentMemoryContext;
 
1795
        ResourceOwner oldowner = CurrentResourceOwner;
 
1796
 
 
1797
        /************************************************************
 
1798
         * Check the call syntax
 
1799
         ************************************************************/
 
1800
        if (argc != 3)
 
1801
        {
 
1802
                Tcl_SetResult(interp, "syntax error - 'SPI_prepare query argtypes'",
 
1803
                                          TCL_VOLATILE);
 
1804
                return TCL_ERROR;
 
1805
        }
 
1806
 
 
1807
        /************************************************************
 
1808
         * Split the argument type list
 
1809
         ************************************************************/
 
1810
        if (Tcl_SplitList(interp, argv[2], &nargs, &args) != TCL_OK)
 
1811
                return TCL_ERROR;
 
1812
 
 
1813
        /************************************************************
 
1814
         * Allocate the new querydesc structure
 
1815
         ************************************************************/
 
1816
        qdesc = (pltcl_query_desc *) malloc(sizeof(pltcl_query_desc));
 
1817
        snprintf(qdesc->qname, sizeof(qdesc->qname), "%lx", (long) qdesc);
 
1818
        qdesc->nargs = nargs;
 
1819
        qdesc->argtypes = (Oid *) malloc(nargs * sizeof(Oid));
 
1820
        qdesc->arginfuncs = (FmgrInfo *) malloc(nargs * sizeof(FmgrInfo));
 
1821
        qdesc->argtypioparams = (Oid *) malloc(nargs * sizeof(Oid));
 
1822
 
 
1823
        /************************************************************
 
1824
         * Execute the prepare inside a sub-transaction, so we can cope with
 
1825
         * errors sanely
 
1826
         ************************************************************/
 
1827
 
 
1828
        pltcl_subtrans_begin(oldcontext, oldowner);
 
1829
 
 
1830
        PG_TRY();
 
1831
        {
 
1832
                /************************************************************
 
1833
                 * Lookup the argument types by name in the system cache
 
1834
                 * and remember the required information for input conversion
 
1835
                 ************************************************************/
 
1836
                for (i = 0; i < nargs; i++)
 
1837
                {
 
1838
                        char       *argcopy;
 
1839
                        List       *names = NIL;
 
1840
                        ListCell   *l;
 
1841
                        TypeName   *typename;
 
1842
 
 
1843
                        /************************************************************
 
1844
                         * Use SplitIdentifierString() on a copy of the type name,
 
1845
                         * turn the resulting pointer list into a TypeName node
 
1846
                         * and call typenameType() to get the pg_type tuple.
 
1847
                         ************************************************************/
 
1848
                        argcopy = pstrdup(args[i]);
 
1849
                        SplitIdentifierString(argcopy, '.', &names);
 
1850
                        typename = makeNode(TypeName);
 
1851
                        foreach(l, names)
 
1852
                                typename->names = lappend(typename->names, makeString(lfirst(l)));
 
1853
 
 
1854
                        typeTup = typenameType(typename);
 
1855
                        qdesc->argtypes[i] = HeapTupleGetOid(typeTup);
 
1856
                        perm_fmgr_info(((Form_pg_type) GETSTRUCT(typeTup))->typinput,
 
1857
                                                   &(qdesc->arginfuncs[i]));
 
1858
                        qdesc->argtypioparams[i] = getTypeIOParam(typeTup);
 
1859
                        ReleaseSysCache(typeTup);
 
1860
 
 
1861
                        list_free(typename->names);
 
1862
                        pfree(typename);
 
1863
                        list_free(names);
 
1864
                        pfree(argcopy);
 
1865
                }
 
1866
 
 
1867
                /************************************************************
 
1868
                 * Prepare the plan and check for errors
 
1869
                 ************************************************************/
 
1870
                UTF_BEGIN;
 
1871
                plan = SPI_prepare(UTF_U2E(argv[1]), nargs, qdesc->argtypes);
 
1872
                UTF_END;
 
1873
 
 
1874
                if (plan == NULL)
 
1875
                        elog(ERROR, "SPI_prepare() failed");
 
1876
 
 
1877
                /************************************************************
 
1878
                 * Save the plan into permanent memory (right now it's in the
 
1879
                 * SPI procCxt, which will go away at function end).
 
1880
                 ************************************************************/
 
1881
                qdesc->plan = SPI_saveplan(plan);
 
1882
                if (qdesc->plan == NULL)
 
1883
                        elog(ERROR, "SPI_saveplan() failed");
 
1884
 
 
1885
                /* Release the procCxt copy to avoid within-function memory leak */
 
1886
                SPI_freeplan(plan);
 
1887
 
 
1888
                pltcl_subtrans_commit(oldcontext, oldowner);
 
1889
        }
 
1890
        PG_CATCH();
 
1891
        {
 
1892
                pltcl_subtrans_abort(interp, oldcontext, oldowner);
 
1893
 
 
1894
                free(qdesc->argtypes);
 
1895
                free(qdesc->arginfuncs);
 
1896
                free(qdesc->argtypioparams);
 
1897
                free(qdesc);
 
1898
                ckfree((char *) args);
 
1899
 
 
1900
                return TCL_ERROR;
 
1901
        }
 
1902
        PG_END_TRY();
 
1903
 
 
1904
        /************************************************************
 
1905
         * Insert a hashtable entry for the plan and return
 
1906
         * the key to the caller
 
1907
         ************************************************************/
 
1908
        if (interp == pltcl_norm_interp)
 
1909
                query_hash = pltcl_norm_query_hash;
 
1910
        else
 
1911
                query_hash = pltcl_safe_query_hash;
 
1912
 
 
1913
        hashent = Tcl_CreateHashEntry(query_hash, qdesc->qname, &hashnew);
 
1914
        Tcl_SetHashValue(hashent, (ClientData) qdesc);
 
1915
 
 
1916
        ckfree((char *) args);
 
1917
 
 
1918
        Tcl_SetResult(interp, qdesc->qname, TCL_VOLATILE);
 
1919
        return TCL_OK;
 
1920
}
 
1921
 
 
1922
 
 
1923
/**********************************************************************
 
1924
 * pltcl_SPI_execute_plan()             - Execute a prepared plan
 
1925
 **********************************************************************/
 
1926
static int
 
1927
pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp,
 
1928
                                           int argc, CONST84 char *argv[])
 
1929
{
 
1930
        int                     my_rc;
 
1931
        int                     spi_rc;
 
1932
        int                     i;
 
1933
        int                     j;
 
1934
        Tcl_HashEntry *hashent;
 
1935
        pltcl_query_desc *qdesc;
 
1936
        const char *volatile nulls = NULL;
 
1937
        CONST84 char *volatile arrayname = NULL;
 
1938
        CONST84 char *volatile loop_body = NULL;
 
1939
        int                     count = 0;
 
1940
        int                     callnargs;
 
1941
        CONST84 char **callargs = NULL;
 
1942
        Datum      *argvalues;
 
1943
        MemoryContext oldcontext = CurrentMemoryContext;
 
1944
        ResourceOwner oldowner = CurrentResourceOwner;
 
1945
        Tcl_HashTable *query_hash;
 
1946
 
 
1947
        char       *usage = "syntax error - 'SPI_execp "
 
1948
        "?-nulls string? ?-count n? "
 
1949
        "?-array name? query ?args? ?loop body?";
 
1950
 
 
1951
        /************************************************************
 
1952
         * Get the options and check syntax
 
1953
         ************************************************************/
 
1954
        i = 1;
 
1955
        while (i < argc)
 
1956
        {
 
1957
                if (strcmp(argv[i], "-array") == 0)
 
1958
                {
 
1959
                        if (++i >= argc)
 
1960
                        {
 
1961
                                Tcl_SetResult(interp, usage, TCL_VOLATILE);
 
1962
                                return TCL_ERROR;
 
1963
                        }
 
1964
                        arrayname = argv[i++];
 
1965
                        continue;
 
1966
                }
 
1967
                if (strcmp(argv[i], "-nulls") == 0)
 
1968
                {
 
1969
                        if (++i >= argc)
 
1970
                        {
 
1971
                                Tcl_SetResult(interp, usage, TCL_VOLATILE);
 
1972
                                return TCL_ERROR;
 
1973
                        }
 
1974
                        nulls = argv[i++];
 
1975
                        continue;
 
1976
                }
 
1977
                if (strcmp(argv[i], "-count") == 0)
 
1978
                {
 
1979
                        if (++i >= argc)
 
1980
                        {
 
1981
                                Tcl_SetResult(interp, usage, TCL_VOLATILE);
 
1982
                                return TCL_ERROR;
 
1983
                        }
 
1984
                        if (Tcl_GetInt(interp, argv[i++], &count) != TCL_OK)
 
1985
                                return TCL_ERROR;
 
1986
                        continue;
 
1987
                }
 
1988
 
 
1989
                break;
 
1990
        }
 
1991
 
 
1992
        /************************************************************
 
1993
         * Get the prepared plan descriptor by its key
 
1994
         ************************************************************/
 
1995
        if (i >= argc)
 
1996
        {
 
1997
                Tcl_SetResult(interp, usage, TCL_VOLATILE);
 
1998
                return TCL_ERROR;
 
1999
        }
 
2000
 
 
2001
        if (interp == pltcl_norm_interp)
 
2002
                query_hash = pltcl_norm_query_hash;
 
2003
        else
 
2004
                query_hash = pltcl_safe_query_hash;
 
2005
 
 
2006
        hashent = Tcl_FindHashEntry(query_hash, argv[i]);
 
2007
        if (hashent == NULL)
 
2008
        {
 
2009
                Tcl_AppendResult(interp, "invalid queryid '", argv[i], "'", NULL);
 
2010
                return TCL_ERROR;
 
2011
        }
 
2012
        qdesc = (pltcl_query_desc *) Tcl_GetHashValue(hashent);
 
2013
        i++;
 
2014
 
 
2015
        /************************************************************
 
2016
         * If a nulls string is given, check for correct length
 
2017
         ************************************************************/
 
2018
        if (nulls != NULL)
 
2019
        {
 
2020
                if (strlen(nulls) != qdesc->nargs)
 
2021
                {
 
2022
                        Tcl_SetResult(interp,
 
2023
                                   "length of nulls string doesn't match # of arguments",
 
2024
                                                  TCL_VOLATILE);
 
2025
                        return TCL_ERROR;
 
2026
                }
 
2027
        }
 
2028
 
 
2029
        /************************************************************
 
2030
         * If there was a argtype list on preparation, we need
 
2031
         * an argument value list now
 
2032
         ************************************************************/
 
2033
        if (qdesc->nargs > 0)
 
2034
        {
 
2035
                if (i >= argc)
 
2036
                {
 
2037
                        Tcl_SetResult(interp, "missing argument list", TCL_VOLATILE);
 
2038
                        return TCL_ERROR;
 
2039
                }
 
2040
 
 
2041
                /************************************************************
 
2042
                 * Split the argument values
 
2043
                 ************************************************************/
 
2044
                if (Tcl_SplitList(interp, argv[i++], &callnargs, &callargs) != TCL_OK)
 
2045
                        return TCL_ERROR;
 
2046
 
 
2047
                /************************************************************
 
2048
                 * Check that the # of arguments matches
 
2049
                 ************************************************************/
 
2050
                if (callnargs != qdesc->nargs)
 
2051
                {
 
2052
                        Tcl_SetResult(interp,
 
2053
                        "argument list length doesn't match # of arguments for query",
 
2054
                                                  TCL_VOLATILE);
 
2055
                        ckfree((char *) callargs);
 
2056
                        return TCL_ERROR;
 
2057
                }
 
2058
        }
 
2059
        else
 
2060
                callnargs = 0;
 
2061
 
 
2062
        /************************************************************
 
2063
         * Get loop body if present
 
2064
         ************************************************************/
 
2065
        if (i < argc)
 
2066
                loop_body = argv[i++];
 
2067
 
 
2068
        if (i != argc)
 
2069
        {
 
2070
                Tcl_SetResult(interp, usage, TCL_VOLATILE);
 
2071
                return TCL_ERROR;
 
2072
        }
 
2073
 
 
2074
        /************************************************************
 
2075
         * Execute the plan inside a sub-transaction, so we can cope with
 
2076
         * errors sanely
 
2077
         ************************************************************/
 
2078
 
 
2079
        pltcl_subtrans_begin(oldcontext, oldowner);
 
2080
 
 
2081
        PG_TRY();
 
2082
        {
 
2083
                /************************************************************
 
2084
                 * Setup the value array for SPI_execute_plan() using
 
2085
                 * the type specific input functions
 
2086
                 ************************************************************/
 
2087
                argvalues = (Datum *) palloc(callnargs * sizeof(Datum));
 
2088
 
 
2089
                for (j = 0; j < callnargs; j++)
 
2090
                {
 
2091
                        if (nulls && nulls[j] == 'n')
 
2092
                        {
 
2093
                                /* don't try to convert the input for a null */
 
2094
                                argvalues[j] = (Datum) 0;
 
2095
                        }
 
2096
                        else
 
2097
                        {
 
2098
                                UTF_BEGIN;
 
2099
                                argvalues[j] =
 
2100
                                        FunctionCall3(&qdesc->arginfuncs[j],
 
2101
                                                                  CStringGetDatum(UTF_U2E(callargs[j])),
 
2102
                                                                  ObjectIdGetDatum(qdesc->argtypioparams[j]),
 
2103
                                                                  Int32GetDatum(-1));
 
2104
                                UTF_END;
 
2105
                        }
 
2106
                }
 
2107
 
 
2108
                if (callargs)
 
2109
                        ckfree((char *) callargs);
 
2110
                callargs = NULL;
 
2111
 
 
2112
                /************************************************************
 
2113
                 * Execute the plan
 
2114
                 ************************************************************/
 
2115
                spi_rc = SPI_execute_plan(qdesc->plan, argvalues, nulls,
 
2116
                                                                  pltcl_current_prodesc->fn_readonly, count);
 
2117
 
 
2118
                my_rc = pltcl_process_SPI_result(interp,
 
2119
                                                                                 arrayname,
 
2120
                                                                                 loop_body,
 
2121
                                                                                 spi_rc,
 
2122
                                                                                 SPI_tuptable,
 
2123
                                                                                 SPI_processed);
 
2124
 
 
2125
                pltcl_subtrans_commit(oldcontext, oldowner);
 
2126
        }
 
2127
        PG_CATCH();
 
2128
        {
 
2129
                pltcl_subtrans_abort(interp, oldcontext, oldowner);
 
2130
 
 
2131
                if (callargs)
 
2132
                        ckfree((char *) callargs);
 
2133
 
 
2134
                return TCL_ERROR;
 
2135
        }
 
2136
        PG_END_TRY();
 
2137
 
 
2138
        return my_rc;
 
2139
}
 
2140
 
 
2141
 
 
2142
/**********************************************************************
 
2143
 * pltcl_SPI_lastoid()  - return the last oid. To
 
2144
 *                be used after insert queries
 
2145
 **********************************************************************/
 
2146
static int
 
2147
pltcl_SPI_lastoid(ClientData cdata, Tcl_Interp *interp,
 
2148
                                  int argc, CONST84 char *argv[])
 
2149
{
 
2150
        char            buf[64];
 
2151
 
 
2152
        snprintf(buf, sizeof(buf), "%u", SPI_lastoid);
 
2153
        Tcl_SetResult(interp, buf, TCL_VOLATILE);
 
2154
        return TCL_OK;
 
2155
}
 
2156
 
 
2157
 
 
2158
/**********************************************************************
 
2159
 * pltcl_set_tuple_values() - Set variables for all attributes
 
2160
 *                                of a given tuple
 
2161
 **********************************************************************/
 
2162
static void
 
2163
pltcl_set_tuple_values(Tcl_Interp *interp, CONST84 char *arrayname,
 
2164
                                           int tupno, HeapTuple tuple, TupleDesc tupdesc)
 
2165
{
 
2166
        int                     i;
 
2167
        char       *outputstr;
 
2168
        char            buf[64];
 
2169
        Datum           attr;
 
2170
        bool            isnull;
 
2171
 
 
2172
        CONST84 char *attname;
 
2173
        HeapTuple       typeTup;
 
2174
        Oid                     typoutput;
 
2175
        Oid                     typioparam;
 
2176
 
 
2177
        CONST84 char **arrptr;
 
2178
        CONST84 char **nameptr;
 
2179
        CONST84 char *nullname = NULL;
 
2180
 
 
2181
        /************************************************************
 
2182
         * Prepare pointers for Tcl_SetVar2() below and in array
 
2183
         * mode set the .tupno element
 
2184
         ************************************************************/
 
2185
        if (arrayname == NULL)
 
2186
        {
 
2187
                arrptr = &attname;
 
2188
                nameptr = &nullname;
 
2189
        }
 
2190
        else
 
2191
        {
 
2192
                arrptr = &arrayname;
 
2193
                nameptr = &attname;
 
2194
                snprintf(buf, sizeof(buf), "%d", tupno);
 
2195
                Tcl_SetVar2(interp, arrayname, ".tupno", buf, 0);
 
2196
        }
 
2197
 
 
2198
        for (i = 0; i < tupdesc->natts; i++)
 
2199
        {
 
2200
                /* ignore dropped attributes */
 
2201
                if (tupdesc->attrs[i]->attisdropped)
 
2202
                        continue;
 
2203
 
 
2204
                /************************************************************
 
2205
                 * Get the attribute name
 
2206
                 ************************************************************/
 
2207
                attname = NameStr(tupdesc->attrs[i]->attname);
 
2208
 
 
2209
                /************************************************************
 
2210
                 * Get the attributes value
 
2211
                 ************************************************************/
 
2212
                attr = heap_getattr(tuple, i + 1, tupdesc, &isnull);
 
2213
 
 
2214
                /************************************************************
 
2215
                 * Lookup the attribute type in the syscache
 
2216
                 * for the output function
 
2217
                 ************************************************************/
 
2218
                typeTup = SearchSysCache(TYPEOID,
 
2219
                                                   ObjectIdGetDatum(tupdesc->attrs[i]->atttypid),
 
2220
                                                                 0, 0, 0);
 
2221
                if (!HeapTupleIsValid(typeTup))
 
2222
                        elog(ERROR, "cache lookup failed for type %u",
 
2223
                                 tupdesc->attrs[i]->atttypid);
 
2224
 
 
2225
                typoutput = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput;
 
2226
                typioparam = getTypeIOParam(typeTup);
 
2227
                ReleaseSysCache(typeTup);
 
2228
 
 
2229
                /************************************************************
 
2230
                 * If there is a value, set the variable
 
2231
                 * If not, unset it
 
2232
                 *
 
2233
                 * Hmmm - Null attributes will cause functions to
 
2234
                 *                crash if they don't expect them - need something
 
2235
                 *                smarter here.
 
2236
                 ************************************************************/
 
2237
                if (!isnull && OidIsValid(typoutput))
 
2238
                {
 
2239
                        outputstr = DatumGetCString(OidFunctionCall3(typoutput,
 
2240
                                                                                                                 attr,
 
2241
                                                                                        ObjectIdGetDatum(typioparam),
 
2242
                                                   Int32GetDatum(tupdesc->attrs[i]->atttypmod)));
 
2243
                        UTF_BEGIN;
 
2244
                        Tcl_SetVar2(interp, *arrptr, *nameptr, UTF_E2U(outputstr), 0);
 
2245
                        UTF_END;
 
2246
                        pfree(outputstr);
 
2247
                }
 
2248
                else
 
2249
                        Tcl_UnsetVar2(interp, *arrptr, *nameptr, 0);
 
2250
        }
 
2251
}
 
2252
 
 
2253
 
 
2254
/**********************************************************************
 
2255
 * pltcl_build_tuple_argument() - Build a string usable for 'array set'
 
2256
 *                                from all attributes of a given tuple
 
2257
 **********************************************************************/
 
2258
static void
 
2259
pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc,
 
2260
                                                   Tcl_DString *retval)
 
2261
{
 
2262
        int                     i;
 
2263
        char       *outputstr;
 
2264
        Datum           attr;
 
2265
        bool            isnull;
 
2266
 
 
2267
        char       *attname;
 
2268
        HeapTuple       typeTup;
 
2269
        Oid                     typoutput;
 
2270
        Oid                     typioparam;
 
2271
 
 
2272
        for (i = 0; i < tupdesc->natts; i++)
 
2273
        {
 
2274
                /* ignore dropped attributes */
 
2275
                if (tupdesc->attrs[i]->attisdropped)
 
2276
                        continue;
 
2277
 
 
2278
                /************************************************************
 
2279
                 * Get the attribute name
 
2280
                 ************************************************************/
 
2281
                attname = NameStr(tupdesc->attrs[i]->attname);
 
2282
 
 
2283
                /************************************************************
 
2284
                 * Get the attributes value
 
2285
                 ************************************************************/
 
2286
                attr = heap_getattr(tuple, i + 1, tupdesc, &isnull);
 
2287
 
 
2288
                /************************************************************
 
2289
                 * Lookup the attribute type in the syscache
 
2290
                 * for the output function
 
2291
                 ************************************************************/
 
2292
                typeTup = SearchSysCache(TYPEOID,
 
2293
                                                   ObjectIdGetDatum(tupdesc->attrs[i]->atttypid),
 
2294
                                                                 0, 0, 0);
 
2295
                if (!HeapTupleIsValid(typeTup))
 
2296
                        elog(ERROR, "cache lookup failed for type %u",
 
2297
                                 tupdesc->attrs[i]->atttypid);
 
2298
 
 
2299
                typoutput = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput;
 
2300
                typioparam = getTypeIOParam(typeTup);
 
2301
                ReleaseSysCache(typeTup);
 
2302
 
 
2303
                /************************************************************
 
2304
                 * If there is a value, append the attribute name and the
 
2305
                 * value to the list
 
2306
                 *
 
2307
                 * Hmmm - Null attributes will cause functions to
 
2308
                 *                crash if they don't expect them - need something
 
2309
                 *                smarter here.
 
2310
                 ************************************************************/
 
2311
                if (!isnull && OidIsValid(typoutput))
 
2312
                {
 
2313
                        outputstr = DatumGetCString(OidFunctionCall3(typoutput,
 
2314
                                                                                                                 attr,
 
2315
                                                                                        ObjectIdGetDatum(typioparam),
 
2316
                                                   Int32GetDatum(tupdesc->attrs[i]->atttypmod)));
 
2317
                        Tcl_DStringAppendElement(retval, attname);
 
2318
                        UTF_BEGIN;
 
2319
                        Tcl_DStringAppendElement(retval, UTF_E2U(outputstr));
 
2320
                        UTF_END;
 
2321
                        pfree(outputstr);
 
2322
                }
 
2323
        }
 
2324
}