~ubuntu-branches/ubuntu/natty/postgresql-8.4/natty

« back to all changes in this revision

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

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2010-10-05 20:41:08 UTC
  • mfrom: (1.2.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20101005204108-e32jzj8hiexjffss
Tags: 8.4.5-0ubuntu10.10
* New upstream security/bug fix update: (LP: #655293)
  - Use a separate interpreter for each calling SQL userid in PL/Perl
    and PL/Tcl.
    This change prevents security problems that can be caused by
    subverting Perl or Tcl code that will be executed later in the same
    session under another SQL user identity (for example, within a
    SECURITY DEFINER function). Most scripting languages offer numerous
    ways that that might be done, such as redefining standard functions
    or operators called by the target function. Without this change,
    any SQL user with Perl or Tcl language usage rights can do
    essentially anything with the SQL privileges of the target
    function's owner.
    The cost of this change is that intentional communication among
    Perl and Tcl functions becomes more difficult. To provide an escape
    hatch, PL/PerlU and PL/TclU functions continue to use only one
    interpreter per session. This is not considered a security issue
    since all such functions execute at the trust level of a database
    superuser already.
    It is likely that third-party procedural languages that claim to
    offer trusted execution have similar security issues. We advise
    contacting the authors of any PL you are depending on for
    security-critical purposes.
    Our thanks to Tim Bunce for pointing out this issue
    (CVE-2010-3433).
  - Prevent possible crashes in pg_get_expr() by disallowing it from
    being called with an argument that is not one of the system catalog
    columns it's intended to be used with.
  - Fix incorrect placement of placeholder evaluation.
    This bug could result in query outputs being non-null when they
    should be null, in cases where the inner side of an outer join is a
    sub-select with non-strict expressions in its output list.
  - Fix possible duplicate scans of UNION ALL member relations.
  - Fix "cannot handle unplanned sub-select" error.
    This occurred when a sub-select contains a join alias reference
    that expands into an expression containing another sub-select.
  - Fix mishandling of whole-row Vars that reference a view or
    sub-select and appear within a nested sub-select.
  - Fix mishandling of cross-type IN comparisons.
    This could result in failures if the planner tried to implement an
    IN join with a sort-then-unique-then-plain-join plan.
  - Fix computation of "ANALYZE" statistics for tsvector columns.
    The original coding could produce incorrect statistics, leading to
    poor plan choices later.
  - Improve planner's estimate of memory used by array_agg(),
    string_agg(), and similar aggregate functions.
    The previous drastic underestimate could lead to out-of-memory
    failures due to inappropriate choice of a hash-aggregation plan.
  - Fix failure to mark cached plans as transient.
    If a plan is prepared while "CREATE INDEX CONCURRENTLY" is in
    progress for one of the referenced tables, it is supposed to be
    re-planned once the index is ready for use. This was not happening
    reliably.
  - Reduce PANIC to ERROR in some occasionally-reported btree failure
    cases, and provide additional detail in the resulting error
    messages.
    This should improve the system's robustness with corrupted indexes.
  - Fix incorrect search logic for partial-match queries with GIN
    indexes.
    Cases involving AND/OR combination of several GIN index conditions
    didn't always give the right answer, and were sometimes much slower
    than necessary.
  - Prevent show_session_authorization() from crashing within
    autovacuum processes.
  - Defend against functions returning setof record where not all the
    returned rows are actually of the same rowtype.
  - Fix possible corruption of pending trigger event lists during
    subtransaction rollback.
    This could lead to a crash or incorrect firing of triggers.
  - Fix possible failure when hashing a pass-by-reference function
    result.
  - Improve merge join's handling of NULLs in the join columns.
    A merge join can now stop entirely upon reaching the first NULL, if
    the sort order is such that NULLs sort high.
  - Take care to fsync the contents of lockfiles (both "postmaster.pid"
    and the socket lockfile) while writing them.
    This omission could result in corrupted lockfile contents if the
    machine crashes shortly after postmaster start. That could in turn
    prevent subsequent attempts to start the postmaster from
    succeeding, until the lockfile is manually removed.
  - Avoid recursion while assigning XIDs to heavily-nested
    subtransactions.
    The original coding could result in a crash if there was limited
    stack space.
  - Avoid holding open old WAL segments in the walwriter process.
    The previous coding would prevent removal of no-longer-needed
    segments.
  - Fix log_line_prefix's %i escape, which could produce junk early in
    backend startup.
  - Prevent misinterpretation of partially-specified relation options
    for TOAST tables.
    In particular, fillfactor would be read as zero if any other
    reloption had been set for the table, leading to serious bloat.
  - Fix inheritance count tracking in "ALTER TABLE ... ADD CONSTRAINT"
  - Fix possible data corruption in "ALTER TABLE ... SET TABLESPACE"
    when archiving is enabled.
  - Allow "CREATE DATABASE" and "ALTER DATABASE ... SET TABLESPACE" to
    be interrupted by query-cancel.
  - Improve "CREATE INDEX"'s checking of whether proposed index
    expressions are immutable.
  - Fix "REASSIGN OWNED" to handle operator classes and families.
  - Fix possible core dump when comparing two empty tsquery values.
  - Fix LIKE's handling of patterns containing % followed by _.
    We've fixed this before, but there were still some
    incorrectly-handled cases.
  - Re-allow input of Julian dates prior to 0001-01-01 AD.
    Input such as 'J100000'::date worked before 8.4, but was
    unintentionally broken by added error-checking.
  - Fix PL/pgSQL to throw an error, not crash, if a cursor is closed
    within a FOR loop that is iterating over that cursor.
  - In PL/Python, defend against null pointer results from
    PyCObject_AsVoidPtr and PyCObject_FromVoidPtr.
  - In libpq, fix full SSL certificate verification for the case where
    both host and hostaddr are specified.
  - Make psql recognize "DISCARD ALL" as a command that should not be
    encased in a transaction block in autocommit-off mode.
  - Fix some issues in pg_dump's handling of SQL/MED objects.
    Notably, pg_dump would always fail if run by a non-superuser, which
    was not intended.
  - Improve pg_dump and pg_restore's handling of non-seekable archive
    files.
    This is important for proper functioning of parallel restore.
  - Improve parallel pg_restore's ability to cope with selective
    restore (-L option).
    The original code tended to fail if the -L file commanded a
    non-default restore ordering.
  - Fix ecpg to process data from RETURNING clauses correctly.
  - Fix some memory leaks in ecpg.
  - Improve "contrib/dblink"'s handling of tables containing dropped
    columns.
  - Fix connection leak after "duplicate connection name" errors in
    "contrib/dblink".
  - Fix "contrib/dblink" to handle connection names longer than 62
    bytes correctly.
  - Add hstore(text, text) function to "contrib/hstore".
    This function is the recommended substitute for the now-deprecated
    => operator. It was back-patched so that future-proofed code can be
    used with older server versions. Note that the patch will be
    effective only after "contrib/hstore" is installed or reinstalled
    in a particular database. Users might prefer to execute the "CREATE
    FUNCTION" command by hand, instead.
  - Update build infrastructure and documentation to reflect the source
    code repository's move from CVS to Git.
* debian/postgresql-8.4.preinst: Add missing debhelper token.
* debian/control: Bump Standards-Version to 3.9.1 (no changes necessary).

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
#endif
20
20
 
21
21
#include "access/xact.h"
22
 
#include "catalog/pg_language.h"
23
22
#include "catalog/pg_proc.h"
24
23
#include "catalog/pg_type.h"
25
24
#include "commands/trigger.h"
83
82
 
84
83
PG_MODULE_MAGIC;
85
84
 
 
85
 
 
86
/**********************************************************************
 
87
 * Information associated with a Tcl interpreter.  We have one interpreter
 
88
 * that is used for all pltclu (untrusted) functions.  For pltcl (trusted)
 
89
 * functions, there is a separate interpreter for each effective SQL userid.
 
90
 * (This is needed to ensure that an unprivileged user can't inject Tcl code
 
91
 * that'll be executed with the privileges of some other SQL user.)
 
92
 *
 
93
 * The pltcl_interp_desc structs are kept in a Postgres hash table indexed
 
94
 * by userid OID, with OID 0 used for the single untrusted interpreter.
 
95
 **********************************************************************/
 
96
typedef struct pltcl_interp_desc
 
97
{
 
98
        Oid                     user_id;                                /* Hash key (must be first!) */
 
99
        Tcl_Interp *interp;                                     /* The interpreter */
 
100
        Tcl_HashTable query_hash;                       /* pltcl_query_desc structs */
 
101
} pltcl_interp_desc;
 
102
 
 
103
 
86
104
/**********************************************************************
87
105
 * The information we cache about loaded procedures
88
106
 **********************************************************************/
94
112
        ItemPointerData fn_tid;
95
113
        bool            fn_readonly;
96
114
        bool            lanpltrusted;
 
115
        pltcl_interp_desc *interp_desc;
97
116
        FmgrInfo        result_in_func;
98
117
        Oid                     result_typioparam;
99
118
        int                     nargs;
117
136
 
118
137
 
119
138
/**********************************************************************
 
139
 * For speedy lookup, we maintain a hash table mapping from
 
140
 * function OID + trigger OID + user OID to pltcl_proc_desc pointers.
 
141
 * The reason the pltcl_proc_desc struct isn't directly part of the hash
 
142
 * entry is to simplify recovery from errors during compile_pltcl_function.
 
143
 *
 
144
 * Note: if the same function is called by multiple userIDs within a session,
 
145
 * there will be a separate pltcl_proc_desc entry for each userID in the case
 
146
 * of pltcl functions, but only one entry for pltclu functions, because we
 
147
 * set user_id = 0 for that case.
 
148
 **********************************************************************/
 
149
typedef struct pltcl_proc_key
 
150
{
 
151
        Oid                     proc_id;                                /* Function OID */
 
152
        Oid                     trig_id;                                /* Trigger OID, or 0 if not trigger */
 
153
        Oid                     user_id;                                /* User calling the function, or 0 */
 
154
} pltcl_proc_key;
 
155
 
 
156
typedef struct pltcl_proc_ptr
 
157
{
 
158
        pltcl_proc_key proc_key;                        /* Hash key (must be first!) */
 
159
        pltcl_proc_desc *proc_ptr;
 
160
} pltcl_proc_ptr;
 
161
 
 
162
 
 
163
/**********************************************************************
120
164
 * Global data
121
165
 **********************************************************************/
122
166
static bool pltcl_pm_init_done = false;
123
 
static bool pltcl_be_norm_init_done = false;
124
 
static bool pltcl_be_safe_init_done = false;
125
167
static Tcl_Interp *pltcl_hold_interp = NULL;
126
 
static Tcl_Interp *pltcl_norm_interp = NULL;
127
 
static Tcl_Interp *pltcl_safe_interp = NULL;
128
 
static Tcl_HashTable *pltcl_proc_hash = NULL;
129
 
static Tcl_HashTable *pltcl_norm_query_hash = NULL;
130
 
static Tcl_HashTable *pltcl_safe_query_hash = NULL;
 
168
static HTAB *pltcl_interp_htab = NULL;
 
169
static HTAB *pltcl_proc_htab = NULL;
131
170
 
132
 
/* these are saved and restored by pltcl_call_handler */
 
171
/* these are saved and restored by pltcl_handler */
133
172
static FunctionCallInfo pltcl_current_fcinfo = NULL;
134
173
static pltcl_proc_desc *pltcl_current_prodesc = NULL;
135
174
 
140
179
Datum           pltclu_call_handler(PG_FUNCTION_ARGS);
141
180
void            _PG_init(void);
142
181
 
143
 
static void pltcl_init_interp(Tcl_Interp *interp);
144
 
static Tcl_Interp *pltcl_fetch_interp(bool pltrusted);
 
182
static void pltcl_init_interp(pltcl_interp_desc *interp_desc, bool pltrusted);
 
183
static pltcl_interp_desc *pltcl_fetch_interp(bool pltrusted);
145
184
static void pltcl_init_load_unknown(Tcl_Interp *interp);
146
185
 
147
 
static Datum pltcl_func_handler(PG_FUNCTION_ARGS);
148
 
 
149
 
static HeapTuple pltcl_trigger_handler(PG_FUNCTION_ARGS);
 
186
static Datum pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted);
 
187
 
 
188
static Datum pltcl_func_handler(PG_FUNCTION_ARGS, bool pltrusted);
 
189
 
 
190
static HeapTuple pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted);
150
191
 
151
192
static void throw_tcl_error(Tcl_Interp *interp, const char *proname);
152
193
 
153
 
static pltcl_proc_desc *compile_pltcl_function(Oid fn_oid, Oid tgreloid);
 
194
static pltcl_proc_desc *compile_pltcl_function(Oid fn_oid, Oid tgreloid,
 
195
                                                                                           bool pltrusted);
154
196
 
155
197
static int pltcl_elog(ClientData cdata, Tcl_Interp *interp,
156
198
                   int argc, CONST84 char *argv[]);
264
306
 * _PG_init()                   - library load-time initialization
265
307
 *
266
308
 * DO NOT make this static nor change its name!
 
309
 *
 
310
 * The work done here must be safe to do in the postmaster process,
 
311
 * in case the pltcl library is preloaded in the postmaster.
267
312
 */
268
313
void
269
314
_PG_init(void)
270
315
{
 
316
        HASHCTL         hash_ctl;
 
317
 
271
318
        /* Be sure we do initialization only once (should be redundant now) */
272
319
        if (pltcl_pm_init_done)
273
320
                return;
304
351
         * stdout and stderr on DeleteInterp
305
352
         ************************************************************/
306
353
        if ((pltcl_hold_interp = Tcl_CreateInterp()) == NULL)
307
 
                elog(ERROR, "could not create \"hold\" interpreter");
 
354
                elog(ERROR, "could not create master Tcl interpreter");
308
355
        if (Tcl_Init(pltcl_hold_interp) == TCL_ERROR)
309
 
                elog(ERROR, "could not initialize \"hold\" interpreter");
310
 
 
311
 
        /************************************************************
312
 
         * Create the two slave interpreters.  Note: Tcl automatically does
313
 
         * Tcl_Init on the normal slave, and it's not wanted for the safe slave.
314
 
         ************************************************************/
315
 
        if ((pltcl_norm_interp =
316
 
                 Tcl_CreateSlave(pltcl_hold_interp, "norm", 0)) == NULL)
317
 
                elog(ERROR, "could not create \"normal\" interpreter");
318
 
        pltcl_init_interp(pltcl_norm_interp);
319
 
 
320
 
        if ((pltcl_safe_interp =
321
 
                 Tcl_CreateSlave(pltcl_hold_interp, "safe", 1)) == NULL)
322
 
                elog(ERROR, "could not create \"safe\" interpreter");
323
 
        pltcl_init_interp(pltcl_safe_interp);
324
 
 
325
 
        /************************************************************
326
 
         * Initialize the proc and query hash tables
327
 
         ************************************************************/
328
 
        pltcl_proc_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable));
329
 
        pltcl_norm_query_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable));
330
 
        pltcl_safe_query_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable));
331
 
        Tcl_InitHashTable(pltcl_proc_hash, TCL_STRING_KEYS);
332
 
        Tcl_InitHashTable(pltcl_norm_query_hash, TCL_STRING_KEYS);
333
 
        Tcl_InitHashTable(pltcl_safe_query_hash, TCL_STRING_KEYS);
 
356
                elog(ERROR, "could not initialize master Tcl interpreter");
 
357
 
 
358
        /************************************************************
 
359
         * Create the hash table for working interpreters
 
360
         ************************************************************/
 
361
        memset(&hash_ctl, 0, sizeof(hash_ctl));
 
362
        hash_ctl.keysize = sizeof(Oid);
 
363
        hash_ctl.entrysize = sizeof(pltcl_interp_desc);
 
364
        hash_ctl.hash = oid_hash;
 
365
        pltcl_interp_htab = hash_create("PL/Tcl interpreters",
 
366
                                                                        8,
 
367
                                                                        &hash_ctl,
 
368
                                                                        HASH_ELEM | HASH_FUNCTION);
 
369
 
 
370
        /************************************************************
 
371
         * Create the hash table for function lookup
 
372
         ************************************************************/
 
373
        memset(&hash_ctl, 0, sizeof(hash_ctl));
 
374
        hash_ctl.keysize = sizeof(pltcl_proc_key);
 
375
        hash_ctl.entrysize = sizeof(pltcl_proc_ptr);
 
376
        hash_ctl.hash = tag_hash;
 
377
        pltcl_proc_htab = hash_create("PL/Tcl functions",
 
378
                                                                  100,
 
379
                                                                  &hash_ctl,
 
380
                                                                  HASH_ELEM | HASH_FUNCTION);
334
381
 
335
382
        pltcl_pm_init_done = true;
336
383
}
337
384
 
338
385
/**********************************************************************
339
 
 * pltcl_init_interp() - initialize a Tcl interpreter
340
 
 *
341
 
 * The work done here must be safe to do in the postmaster process,
342
 
 * in case the pltcl library is preloaded in the postmaster.  Note
343
 
 * that this is applied separately to the "normal" and "safe" interpreters.
 
386
 * pltcl_init_interp() - initialize a new Tcl interpreter
344
387
 **********************************************************************/
345
388
static void
346
 
pltcl_init_interp(Tcl_Interp *interp)
 
389
pltcl_init_interp(pltcl_interp_desc *interp_desc, bool pltrusted)
347
390
{
 
391
        Tcl_Interp *interp;
 
392
        char            interpname[32];
 
393
 
 
394
        /************************************************************
 
395
         * Create the Tcl interpreter as a slave of pltcl_hold_interp.
 
396
         * Note: Tcl automatically does Tcl_Init in the untrusted case,
 
397
         * and it's not wanted in the trusted case.
 
398
         ************************************************************/
 
399
        snprintf(interpname, sizeof(interpname), "slave_%u", interp_desc->user_id);
 
400
        if ((interp = Tcl_CreateSlave(pltcl_hold_interp, interpname,
 
401
                                                                  pltrusted ? 1 : 0)) == NULL)
 
402
                elog(ERROR, "could not create slave Tcl interpreter");
 
403
        interp_desc->interp = interp;
 
404
 
 
405
        /************************************************************
 
406
         * Initialize the query hash table associated with interpreter
 
407
         ************************************************************/
 
408
        Tcl_InitHashTable(&interp_desc->query_hash, TCL_STRING_KEYS);
 
409
 
348
410
        /************************************************************
349
411
         * Install the commands for SPI support in the interpreter
350
412
         ************************************************************/
365
427
                                          pltcl_SPI_execute_plan, NULL, NULL);
366
428
        Tcl_CreateCommand(interp, "spi_lastoid",
367
429
                                          pltcl_SPI_lastoid, NULL, NULL);
 
430
 
 
431
        /************************************************************
 
432
         * Try to load the unknown procedure from pltcl_modules
 
433
         ************************************************************/
 
434
        pltcl_init_load_unknown(interp);
368
435
}
369
436
 
370
437
/**********************************************************************
371
438
 * pltcl_fetch_interp() - fetch the Tcl interpreter to use for a function
372
439
 *
373
440
 * This also takes care of any on-first-use initialization required.
374
 
 * The initialization work done here can't be done in the postmaster, and
375
 
 * hence is not safe to do at library load time, because it may invoke
376
 
 * arbitrary user-defined code.
377
441
 * Note: we assume caller has already connected to SPI.
378
442
 **********************************************************************/
379
 
static Tcl_Interp *
 
443
static pltcl_interp_desc *
380
444
pltcl_fetch_interp(bool pltrusted)
381
445
{
382
 
        Tcl_Interp *interp;
 
446
        Oid                     user_id;
 
447
        pltcl_interp_desc *interp_desc;
 
448
        bool            found;
383
449
 
384
 
        /* On first use, we try to load the unknown procedure from pltcl_modules */
 
450
        /* Find or create the interpreter hashtable entry for this userid */
385
451
        if (pltrusted)
386
 
        {
387
 
                interp = pltcl_safe_interp;
388
 
                if (!pltcl_be_safe_init_done)
389
 
                {
390
 
                        pltcl_init_load_unknown(interp);
391
 
                        pltcl_be_safe_init_done = true;
392
 
                }
393
 
        }
 
452
                user_id = GetUserId();
394
453
        else
395
 
        {
396
 
                interp = pltcl_norm_interp;
397
 
                if (!pltcl_be_norm_init_done)
398
 
                {
399
 
                        pltcl_init_load_unknown(interp);
400
 
                        pltcl_be_norm_init_done = true;
401
 
                }
402
 
        }
403
 
 
404
 
        return interp;
 
454
                user_id = InvalidOid;
 
455
 
 
456
        interp_desc = hash_search(pltcl_interp_htab, &user_id,
 
457
                                                          HASH_ENTER,
 
458
                                                          &found);
 
459
        if (!found)
 
460
                pltcl_init_interp(interp_desc, pltrusted);
 
461
 
 
462
        return interp_desc;
405
463
}
406
464
 
407
465
/**********************************************************************
533
591
Datum
534
592
pltcl_call_handler(PG_FUNCTION_ARGS)
535
593
{
 
594
        return pltcl_handler(fcinfo, true);
 
595
}
 
596
 
 
597
/*
 
598
 * Alternative handler for unsafe functions
 
599
 */
 
600
PG_FUNCTION_INFO_V1(pltclu_call_handler);
 
601
 
 
602
/* keep non-static */
 
603
Datum
 
604
pltclu_call_handler(PG_FUNCTION_ARGS)
 
605
{
 
606
        return pltcl_handler(fcinfo, false);
 
607
}
 
608
 
 
609
 
 
610
static Datum
 
611
pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted)
 
612
{
536
613
        Datum           retval;
537
614
        FunctionCallInfo save_fcinfo;
538
615
        pltcl_proc_desc *save_prodesc;
552
629
                if (CALLED_AS_TRIGGER(fcinfo))
553
630
                {
554
631
                        pltcl_current_fcinfo = NULL;
555
 
                        retval = PointerGetDatum(pltcl_trigger_handler(fcinfo));
 
632
                        retval = PointerGetDatum(pltcl_trigger_handler(fcinfo, pltrusted));
556
633
                }
557
634
                else
558
635
                {
559
636
                        pltcl_current_fcinfo = fcinfo;
560
 
                        retval = pltcl_func_handler(fcinfo);
 
637
                        retval = pltcl_func_handler(fcinfo, pltrusted);
561
638
                }
562
639
        }
563
640
        PG_CATCH();
575
652
}
576
653
 
577
654
 
578
 
/*
579
 
 * Alternative handler for unsafe functions
580
 
 */
581
 
PG_FUNCTION_INFO_V1(pltclu_call_handler);
582
 
 
583
 
/* keep non-static */
584
 
Datum
585
 
pltclu_call_handler(PG_FUNCTION_ARGS)
586
 
{
587
 
        return pltcl_call_handler(fcinfo);
588
 
}
589
 
 
590
655
/**********************************************************************
591
656
 * pltcl_func_handler()         - Handler for regular function calls
592
657
 **********************************************************************/
593
658
static Datum
594
 
pltcl_func_handler(PG_FUNCTION_ARGS)
 
659
pltcl_func_handler(PG_FUNCTION_ARGS, bool pltrusted)
595
660
{
596
661
        pltcl_proc_desc *prodesc;
597
662
        Tcl_Interp *volatile interp;
606
671
                elog(ERROR, "could not connect to SPI manager");
607
672
 
608
673
        /* Find or compile the function */
609
 
        prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid);
 
674
        prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid,
 
675
                                                                         pltrusted);
610
676
 
611
677
        pltcl_current_prodesc = prodesc;
612
678
 
613
 
        interp = pltcl_fetch_interp(prodesc->lanpltrusted);
 
679
        interp = prodesc->interp_desc->interp;
614
680
 
615
681
        /************************************************************
616
682
         * Create the tcl command to call the internal
738
804
 * pltcl_trigger_handler()      - Handler for trigger calls
739
805
 **********************************************************************/
740
806
static HeapTuple
741
 
pltcl_trigger_handler(PG_FUNCTION_ARGS)
 
807
pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
742
808
{
743
809
        pltcl_proc_desc *prodesc;
744
810
        Tcl_Interp *volatile interp;
764
830
 
765
831
        /* Find or compile the function */
766
832
        prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid,
767
 
                                                                         RelationGetRelid(trigdata->tg_relation));
 
833
                                                                         RelationGetRelid(trigdata->tg_relation),
 
834
                                                                         pltrusted);
768
835
 
769
836
        pltcl_current_prodesc = prodesc;
770
837
 
771
 
        interp = pltcl_fetch_interp(prodesc->lanpltrusted);
 
838
        interp = prodesc->interp_desc->interp;
772
839
 
773
840
        tupdesc = trigdata->tg_relation->rd_att;
774
841
 
1087
1154
 * (InvalidOid) when compiling a plain function.
1088
1155
 **********************************************************************/
1089
1156
static pltcl_proc_desc *
1090
 
compile_pltcl_function(Oid fn_oid, Oid tgreloid)
 
1157
compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool pltrusted)
1091
1158
{
1092
 
        bool            is_trigger = OidIsValid(tgreloid);
1093
1159
        HeapTuple       procTup;
1094
1160
        Form_pg_proc procStruct;
1095
 
        char            internal_proname[128];
1096
 
        Tcl_HashEntry *hashent;
1097
 
        pltcl_proc_desc *prodesc = NULL;
1098
 
        Tcl_Interp *interp;
1099
 
        int                     i;
1100
 
        int                     hashnew;
1101
 
        int                     tcl_rc;
 
1161
        pltcl_proc_key proc_key;
 
1162
        pltcl_proc_ptr *proc_ptr;
 
1163
        bool            found;
 
1164
        pltcl_proc_desc *prodesc;
1102
1165
 
1103
1166
        /* We'll need the pg_proc tuple in any case... */
1104
1167
        procTup = SearchSysCache(PROCOID,
1108
1171
                elog(ERROR, "cache lookup failed for function %u", fn_oid);
1109
1172
        procStruct = (Form_pg_proc) GETSTRUCT(procTup);
1110
1173
 
1111
 
        /************************************************************
1112
 
         * Build our internal proc name from the functions Oid
1113
 
         ************************************************************/
1114
 
        if (!is_trigger)
1115
 
                snprintf(internal_proname, sizeof(internal_proname),
1116
 
                                 "__PLTcl_proc_%u", fn_oid);
1117
 
        else
1118
 
                snprintf(internal_proname, sizeof(internal_proname),
1119
 
                                 "__PLTcl_proc_%u_trigger_%u", fn_oid, tgreloid);
1120
 
 
1121
 
        /************************************************************
1122
 
         * Lookup the internal proc name in the hashtable
1123
 
         ************************************************************/
1124
 
        hashent = Tcl_FindHashEntry(pltcl_proc_hash, internal_proname);
 
1174
        /* Try to find function in pltcl_proc_htab */
 
1175
        proc_key.proc_id = fn_oid;
 
1176
        proc_key.trig_id = tgreloid;
 
1177
        proc_key.user_id = pltrusted ? GetUserId() : InvalidOid;
 
1178
 
 
1179
        proc_ptr = hash_search(pltcl_proc_htab, &proc_key,
 
1180
                                                   HASH_ENTER,
 
1181
                                                   &found);
 
1182
        if (!found)
 
1183
                proc_ptr->proc_ptr = NULL;
 
1184
 
 
1185
        prodesc = proc_ptr->proc_ptr;
1125
1186
 
1126
1187
        /************************************************************
1127
1188
         * If it's present, must check whether it's still up to date.
1128
1189
         * This is needed because CREATE OR REPLACE FUNCTION can modify the
1129
1190
         * function's pg_proc entry without changing its OID.
1130
1191
         ************************************************************/
1131
 
        if (hashent != NULL)
 
1192
        if (prodesc != NULL)
1132
1193
        {
1133
1194
                bool            uptodate;
1134
1195
 
1135
 
                prodesc = (pltcl_proc_desc *) Tcl_GetHashValue(hashent);
1136
 
 
1137
1196
                uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
1138
1197
                                        ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self));
1139
1198
 
1140
1199
                if (!uptodate)
1141
1200
                {
1142
 
                        Tcl_DeleteHashEntry(hashent);
1143
 
                        hashent = NULL;
 
1201
                        proc_ptr->proc_ptr = NULL;
 
1202
                        prodesc = NULL;
1144
1203
                }
1145
1204
        }
1146
1205
 
1152
1211
         *
1153
1212
         * Then we load the procedure into the Tcl interpreter.
1154
1213
         ************************************************************/
1155
 
        if (hashent == NULL)
 
1214
        if (prodesc == NULL)
1156
1215
        {
1157
 
                HeapTuple       langTup;
 
1216
                bool            is_trigger = OidIsValid(tgreloid);
 
1217
                char            internal_proname[128];
1158
1218
                HeapTuple       typeTup;
1159
 
                Form_pg_language langStruct;
1160
1219
                Form_pg_type typeStruct;
1161
1220
                Tcl_DString proc_internal_def;
1162
1221
                Tcl_DString proc_internal_body;
1165
1224
                bool            isnull;
1166
1225
                char       *proc_source;
1167
1226
                char            buf[32];
 
1227
                Tcl_Interp *interp;
 
1228
                int                     i;
 
1229
                int                     tcl_rc;
 
1230
 
 
1231
                /************************************************************
 
1232
                 * Build our internal proc name from the functions Oid + trigger Oid
 
1233
                 ************************************************************/
 
1234
                if (!is_trigger)
 
1235
                        snprintf(internal_proname, sizeof(internal_proname),
 
1236
                                         "__PLTcl_proc_%u", fn_oid);
 
1237
                else
 
1238
                        snprintf(internal_proname, sizeof(internal_proname),
 
1239
                                         "__PLTcl_proc_%u_trigger_%u", fn_oid, tgreloid);
1168
1240
 
1169
1241
                /************************************************************
1170
1242
                 * Allocate a new procedure description block
1177
1249
                MemSet(prodesc, 0, sizeof(pltcl_proc_desc));
1178
1250
                prodesc->user_proname = strdup(NameStr(procStruct->proname));
1179
1251
                prodesc->internal_proname = strdup(internal_proname);
 
1252
                if (prodesc->user_proname == NULL || prodesc->internal_proname == NULL)
 
1253
                        ereport(ERROR,
 
1254
                                        (errcode(ERRCODE_OUT_OF_MEMORY),
 
1255
                                         errmsg("out of memory")));
1180
1256
                prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
1181
1257
                prodesc->fn_tid = procTup->t_self;
1182
1258
 
1183
1259
                /* Remember if function is STABLE/IMMUTABLE */
1184
1260
                prodesc->fn_readonly =
1185
1261
                        (procStruct->provolatile != PROVOLATILE_VOLATILE);
 
1262
                /* And whether it is trusted */
 
1263
                prodesc->lanpltrusted = pltrusted;
1186
1264
 
1187
1265
                /************************************************************
1188
 
                 * Lookup the pg_language tuple by Oid
 
1266
                 * Identify the interpreter to use for the function
1189
1267
                 ************************************************************/
1190
 
                langTup = SearchSysCache(LANGOID,
1191
 
                                                                 ObjectIdGetDatum(procStruct->prolang),
1192
 
                                                                 0, 0, 0);
1193
 
                if (!HeapTupleIsValid(langTup))
1194
 
                {
1195
 
                        free(prodesc->user_proname);
1196
 
                        free(prodesc->internal_proname);
1197
 
                        free(prodesc);
1198
 
                        elog(ERROR, "cache lookup failed for language %u",
1199
 
                                 procStruct->prolang);
1200
 
                }
1201
 
                langStruct = (Form_pg_language) GETSTRUCT(langTup);
1202
 
                prodesc->lanpltrusted = langStruct->lanpltrusted;
1203
 
                ReleaseSysCache(langTup);
1204
 
 
1205
 
                interp = pltcl_fetch_interp(prodesc->lanpltrusted);
 
1268
                prodesc->interp_desc = pltcl_fetch_interp(prodesc->lanpltrusted);
 
1269
                interp = prodesc->interp_desc->interp;
1206
1270
 
1207
1271
                /************************************************************
1208
1272
                 * Get the required information for input conversion of the
1409
1473
                }
1410
1474
 
1411
1475
                /************************************************************
1412
 
                 * Add the proc description block to the hashtable
 
1476
                 * Add the proc description block to the hashtable.  Note we do not
 
1477
                 * attempt to free any previously existing prodesc block.  This is
 
1478
                 * annoying, but necessary since there could be active calls using
 
1479
                 * the old prodesc.
1413
1480
                 ************************************************************/
1414
 
                hashent = Tcl_CreateHashEntry(pltcl_proc_hash,
1415
 
                                                                          prodesc->internal_proname, &hashnew);
1416
 
                Tcl_SetHashValue(hashent, (ClientData) prodesc);
 
1481
                proc_ptr->proc_ptr = prodesc;
1417
1482
        }
1418
1483
 
1419
1484
        ReleaseSysCache(procTup);
2069
2134
         * Insert a hashtable entry for the plan and return
2070
2135
         * the key to the caller
2071
2136
         ************************************************************/
2072
 
        if (interp == pltcl_norm_interp)
2073
 
                query_hash = pltcl_norm_query_hash;
2074
 
        else
2075
 
                query_hash = pltcl_safe_query_hash;
 
2137
        query_hash = &pltcl_current_prodesc->interp_desc->query_hash;
2076
2138
 
2077
2139
        hashent = Tcl_CreateHashEntry(query_hash, qdesc->qname, &hashnew);
2078
2140
        Tcl_SetHashValue(hashent, (ClientData) qdesc);
2163
2225
                return TCL_ERROR;
2164
2226
        }
2165
2227
 
2166
 
        if (interp == pltcl_norm_interp)
2167
 
                query_hash = pltcl_norm_query_hash;
2168
 
        else
2169
 
                query_hash = pltcl_safe_query_hash;
 
2228
        query_hash = &pltcl_current_prodesc->interp_desc->query_hash;
2170
2229
 
2171
2230
        hashent = Tcl_FindHashEntry(query_hash, argv[i]);
2172
2231
        if (hashent == NULL)