]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/ruleutils.c
Create a new expression node type RelabelType, which exists solely to
[postgresql] / src / backend / utils / adt / ruleutils.c
1 /**********************************************************************
2  * get_ruledef.c        - Function to get a rules definition text
3  *                        out of its tuple
4  *
5  * IDENTIFICATION
6  *        $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.42 2000/02/20 21:32:12 tgl Exp $
7  *
8  *        This software is copyrighted by Jan Wieck - Hamburg.
9  *
10  *        The author hereby grants permission  to  use,  copy,  modify,
11  *        distribute,  and      license this software and its documentation
12  *        for any purpose, provided that existing copyright notices are
13  *        retained      in      all  copies  and  that  this notice is included
14  *        verbatim in any distributions. No written agreement, license,
15  *        or  royalty  fee      is required for any of the authorized uses.
16  *        Modifications to this software may be  copyrighted  by  their
17  *        author  and  need  not  follow  the licensing terms described
18  *        here, provided that the new terms are  clearly  indicated  on
19  *        the first page of each file where they apply.
20  *
21  *        IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY
22  *        PARTY  FOR  DIRECT,   INDIRECT,       SPECIAL,   INCIDENTAL,   OR
23  *        CONSEQUENTIAL   DAMAGES  ARISING      OUT  OF  THE  USE  OF  THIS
24  *        SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN
25  *        IF  THE  AUTHOR  HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
26  *        DAMAGE.
27  *
28  *        THE  AUTHOR  AND      DISTRIBUTORS  SPECIFICALLY       DISCLAIM       ANY
29  *        WARRANTIES,  INCLUDING,  BUT  NOT  LIMITED  TO,  THE  IMPLIED
30  *        WARRANTIES  OF  MERCHANTABILITY,      FITNESS  FOR  A  PARTICULAR
31  *        PURPOSE,      AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON
32  *        AN "AS IS" BASIS, AND THE AUTHOR      AND  DISTRIBUTORS  HAVE  NO
33  *        OBLIGATION   TO       PROVIDE   MAINTENANCE,   SUPPORT,  UPDATES,
34  *        ENHANCEMENTS, OR MODIFICATIONS.
35  *
36  **********************************************************************/
37
38 #include <unistd.h>
39 #include <fcntl.h>
40
41 #include "postgres.h"
42
43 #include "catalog/pg_index.h"
44 #include "catalog/pg_operator.h"
45 #include "catalog/pg_shadow.h"
46 #include "catalog/pg_type.h"
47 #include "executor/spi.h"
48 #include "lib/stringinfo.h"
49 #include "optimizer/clauses.h"
50 #include "optimizer/tlist.h"
51 #include "parser/keywords.h"
52 #include "parser/parsetree.h"
53 #include "utils/builtins.h"
54 #include "utils/lsyscache.h"
55
56
57 /* ----------
58  * Local data types
59  * ----------
60  */
61 typedef struct
62 {
63         StringInfo      buf;                    /* output buffer to append to */
64         List       *rangetables;        /* List of List of RangeTblEntry */
65         bool            varprefix;              /* TRUE to print prefixes on Vars */
66 } deparse_context;
67
68 typedef struct {
69         Index           rt_index;
70         int                     levelsup;
71 } check_if_rte_used_context;
72
73
74 /* ----------
75  * Global data
76  * ----------
77  */
78 static char *rulename = NULL;
79 static void *plan_getrule = NULL;
80 static char *query_getrule = "SELECT * FROM pg_rewrite WHERE rulename = $1";
81 static void *plan_getview = NULL;
82 static char *query_getview = "SELECT * FROM pg_rewrite WHERE rulename = $1 or rulename = $2";
83 static void *plan_getam = NULL;
84 static char *query_getam = "SELECT * FROM pg_am WHERE oid = $1";
85 static void *plan_getopclass = NULL;
86 static char *query_getopclass = "SELECT * FROM pg_opclass WHERE oid = $1";
87
88
89 /* ----------
90  * Local functions
91  *
92  * Most of these functions used to use fixed-size buffers to build their
93  * results.  Now, they take an (already initialized) StringInfo object
94  * as a parameter, and append their text output to its contents.
95  * ----------
96  */
97 static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc);
98 static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc);
99 static void get_query_def(Query *query, StringInfo buf, List *parentrtables);
100 static void get_select_query_def(Query *query, deparse_context *context);
101 static void get_insert_query_def(Query *query, deparse_context *context);
102 static void get_update_query_def(Query *query, deparse_context *context);
103 static void get_delete_query_def(Query *query, deparse_context *context);
104 static RangeTblEntry *get_rte_for_var(Var *var, deparse_context *context);
105 static void get_rule_expr(Node *node, deparse_context *context);
106 static void get_func_expr(Expr *expr, deparse_context *context);
107 static void get_tle_expr(TargetEntry *tle, deparse_context *context);
108 static void get_const_expr(Const *constval, deparse_context *context);
109 static void get_sublink_expr(Node *node, deparse_context *context);
110 static char *quote_identifier(char *ident);
111 static char *get_relation_name(Oid relid);
112 static char *get_attribute_name(Oid relid, int2 attnum);
113 static bool check_if_rte_used(Node *node, Index rt_index, int levelsup);
114 static bool check_if_rte_used_walker(Node *node,
115                                                                          check_if_rte_used_context *context);
116
117 #define inherit_marker(rte)  ((rte)->inh ? "*" : "")
118
119
120 /* ----------
121  * get_ruledef                  - Do it all and return a text
122  *                                that could be used as a statement
123  *                                to recreate the rule
124  * ----------
125  */
126 text *
127 pg_get_ruledef(NameData *rname)
128 {
129         text       *ruledef;
130         Datum           args[1];
131         char            nulls[2];
132         int                     spirc;
133         HeapTuple       ruletup;
134         TupleDesc       rulettc;
135         StringInfoData  buf;
136         int                     len;
137
138         /* ----------
139          * We need the rules name somewhere deep down
140          * ----------
141          */
142         rulename = pstrdup(NameStr(*rname));
143
144         /* ----------
145          * Connect to SPI manager
146          * ----------
147          */
148         if (SPI_connect() != SPI_OK_CONNECT)
149                 elog(ERROR, "get_ruledef: cannot connect to SPI manager");
150
151         /* ----------
152          * On the first call prepare the plan to lookup pg_proc.
153          * We read pg_proc over the SPI manager instead of using
154          * the syscache to be checked for read access on pg_proc.
155          * ----------
156          */
157         if (plan_getrule == NULL)
158         {
159                 Oid                     argtypes[1];
160                 void       *plan;
161
162                 argtypes[0] = NAMEOID;
163                 plan = SPI_prepare(query_getrule, 1, argtypes);
164                 if (plan == NULL)
165                         elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getrule);
166                 plan_getrule = SPI_saveplan(plan);
167         }
168
169         /* ----------
170          * Get the pg_rewrite tuple for this rule
171          * ----------
172          */
173         args[0] = PointerGetDatum(rulename);
174         nulls[0] = (rulename == NULL) ? 'n' : ' ';
175         nulls[1] = '\0';
176         spirc = SPI_execp(plan_getrule, args, nulls, 1);
177         if (spirc != SPI_OK_SELECT)
178                 elog(ERROR, "failed to get pg_rewrite tuple for %s", rulename);
179         if (SPI_processed != 1)
180         {
181                 if (SPI_finish() != SPI_OK_FINISH)
182                         elog(ERROR, "get_ruledef: SPI_finish() failed");
183                 ruledef = SPI_palloc(VARHDRSZ + 1);
184                 VARSIZE(ruledef) = VARHDRSZ + 1;
185                 VARDATA(ruledef)[0] = '-';
186                 return ruledef;
187         }
188
189         ruletup = SPI_tuptable->vals[0];
190         rulettc = SPI_tuptable->tupdesc;
191
192         /* ----------
193          * Get the rules definition and put it into executors memory
194          * ----------
195          */
196         initStringInfo(&buf);
197         make_ruledef(&buf, ruletup, rulettc);
198         len = buf.len + VARHDRSZ;
199         ruledef = SPI_palloc(len);
200         VARSIZE(ruledef) = len;
201         memcpy(VARDATA(ruledef), buf.data, buf.len);
202         pfree(buf.data);
203
204         /* ----------
205          * Disconnect from SPI manager
206          * ----------
207          */
208         if (SPI_finish() != SPI_OK_FINISH)
209                 elog(ERROR, "get_ruledef: SPI_finish() failed");
210
211         /* ----------
212          * Easy - isn't it?
213          * ----------
214          */
215         return ruledef;
216 }
217
218
219 /* ----------
220  * get_viewdef                  - Mainly the same thing, but we
221  *                                only return the SELECT part of a view
222  * ----------
223  */
224 text *
225 pg_get_viewdef(NameData *rname)
226 {
227         text       *ruledef;
228         Datum           args[2];
229         char            nulls[3];
230         int                     spirc;
231         HeapTuple       ruletup;
232         TupleDesc       rulettc;
233         StringInfoData  buf;
234         int                     len;
235         char            name1[NAMEDATALEN + 5];
236         char            name2[NAMEDATALEN + 5];
237
238         /* ----------
239          * We need the rules name somewhere deep down
240          * ----------
241          */
242         rulename = pstrdup(NameStr(*rname));
243
244         /* ----------
245          * Connect to SPI manager
246          * ----------
247          */
248         if (SPI_connect() != SPI_OK_CONNECT)
249                 elog(ERROR, "get_viewdef: cannot connect to SPI manager");
250
251         /* ----------
252          * On the first call prepare the plan to lookup pg_proc.
253          * We read pg_proc over the SPI manager instead of using
254          * the syscache to be checked for read access on pg_proc.
255          * ----------
256          */
257         if (plan_getview == NULL)
258         {
259                 Oid                     argtypes[2];
260                 void       *plan;
261
262                 argtypes[0] = NAMEOID;
263                 argtypes[1] = NAMEOID;
264                 plan = SPI_prepare(query_getview, 2, argtypes);
265                 if (plan == NULL)
266                         elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getview);
267                 plan_getview = SPI_saveplan(plan);
268         }
269
270         /* ----------
271          * Get the pg_rewrite tuple for this rule
272          * ----------
273          */
274         sprintf(name1, "_RET%s", rulename);
275         sprintf(name2, "_ret%s", rulename);
276         args[0] = PointerGetDatum(name1);
277         args[1] = PointerGetDatum(name2);
278         nulls[0] = ' ';
279         nulls[1] = ' ';
280         nulls[2] = '\0';
281         spirc = SPI_execp(plan_getview, args, nulls, 1);
282         if (spirc != SPI_OK_SELECT)
283                 elog(ERROR, "failed to get pg_rewrite tuple for view %s", rulename);
284         initStringInfo(&buf);
285         if (SPI_processed != 1)
286                 appendStringInfo(&buf, "Not a view");
287         else
288         {
289                 /* ----------
290                  * Get the rules definition and put it into executors memory
291                  * ----------
292                  */
293                 ruletup = SPI_tuptable->vals[0];
294                 rulettc = SPI_tuptable->tupdesc;
295                 make_viewdef(&buf, ruletup, rulettc);
296         }
297         len = buf.len + VARHDRSZ;
298         ruledef = SPI_palloc(len);
299         VARSIZE(ruledef) = len;
300         memcpy(VARDATA(ruledef), buf.data, buf.len);
301         pfree(buf.data);
302
303         /* ----------
304          * Disconnect from SPI manager
305          * ----------
306          */
307         if (SPI_finish() != SPI_OK_FINISH)
308                 elog(ERROR, "get_viewdef: SPI_finish() failed");
309
310         /* ----------
311          * Easy - isn't it?
312          * ----------
313          */
314         return ruledef;
315 }
316
317
318 /* ----------
319  * get_indexdef                 - Get the definition of an index
320  * ----------
321  */
322 text *
323 pg_get_indexdef(Oid indexrelid)
324 {
325         text       *indexdef;
326         HeapTuple       ht_idx;
327         HeapTuple       ht_idxrel;
328         HeapTuple       ht_indrel;
329         HeapTuple       spi_tup;
330         TupleDesc       spi_ttc;
331         int                     spi_fno;
332         Form_pg_index idxrec;
333         Form_pg_class idxrelrec;
334         Form_pg_class indrelrec;
335         Datum           spi_args[1];
336         char            spi_nulls[2];
337         int                     spirc;
338         int                     len;
339         int                     keyno;
340         StringInfoData  buf;
341         StringInfoData  keybuf;
342         char       *sep;
343
344         /* ----------
345          * Connect to SPI manager
346          * ----------
347          */
348         if (SPI_connect() != SPI_OK_CONNECT)
349                 elog(ERROR, "get_indexdef: cannot connect to SPI manager");
350
351         /* ----------
352          * On the first call prepare the plans to lookup pg_am
353          * and pg_opclass.
354          * ----------
355          */
356         if (plan_getam == NULL)
357         {
358                 Oid                     argtypes[1];
359                 void       *plan;
360
361                 argtypes[0] = OIDOID;
362                 plan = SPI_prepare(query_getam, 1, argtypes);
363                 if (plan == NULL)
364                         elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getam);
365                 plan_getam = SPI_saveplan(plan);
366
367                 argtypes[0] = OIDOID;
368                 plan = SPI_prepare(query_getopclass, 1, argtypes);
369                 if (plan == NULL)
370                         elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getopclass);
371                 plan_getopclass = SPI_saveplan(plan);
372         }
373
374         /* ----------
375          * Fetch the pg_index tuple by the Oid of the index
376          * ----------
377          */
378         ht_idx = SearchSysCacheTuple(INDEXRELID,
379                                                                  ObjectIdGetDatum(indexrelid), 0, 0, 0);
380         if (!HeapTupleIsValid(ht_idx))
381                 elog(ERROR, "syscache lookup for index %u failed", indexrelid);
382         idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
383
384         /* ----------
385          * Fetch the pg_class tuple of the index relation
386          * ----------
387          */
388         ht_idxrel = SearchSysCacheTuple(RELOID,
389                                                   ObjectIdGetDatum(idxrec->indexrelid), 0, 0, 0);
390         if (!HeapTupleIsValid(ht_idxrel))
391                 elog(ERROR, "syscache lookup for relid %u failed", idxrec->indexrelid);
392         idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
393
394         /* ----------
395          * Fetch the pg_class tuple of the indexed relation
396          * ----------
397          */
398         ht_indrel = SearchSysCacheTuple(RELOID,
399                                                         ObjectIdGetDatum(idxrec->indrelid), 0, 0, 0);
400         if (!HeapTupleIsValid(ht_indrel))
401                 elog(ERROR, "syscache lookup for relid %u failed", idxrec->indrelid);
402         indrelrec = (Form_pg_class) GETSTRUCT(ht_indrel);
403
404         /* ----------
405          * Get the am name for the index relation
406          * ----------
407          */
408         spi_args[0] = ObjectIdGetDatum(idxrelrec->relam);
409         spi_nulls[0] = ' ';
410         spi_nulls[1] = '\0';
411         spirc = SPI_execp(plan_getam, spi_args, spi_nulls, 1);
412         if (spirc != SPI_OK_SELECT)
413                 elog(ERROR, "failed to get pg_am tuple for index %s",
414                          NameStr(idxrelrec->relname));
415         if (SPI_processed != 1)
416                 elog(ERROR, "failed to get pg_am tuple for index %s",
417                          NameStr(idxrelrec->relname));
418         spi_tup = SPI_tuptable->vals[0];
419         spi_ttc = SPI_tuptable->tupdesc;
420         spi_fno = SPI_fnumber(spi_ttc, "amname");
421
422         /* ----------
423          * Start the index definition
424          * ----------
425          */
426         initStringInfo(&buf);
427         appendStringInfo(&buf, "CREATE %sINDEX %s ON %s USING %s (",
428                                          idxrec->indisunique ? "UNIQUE " : "",
429                                          quote_identifier(pstrdup(NameStr(idxrelrec->relname))),
430                                          quote_identifier(pstrdup(NameStr(indrelrec->relname))),
431                                          quote_identifier(SPI_getvalue(spi_tup, spi_ttc,
432                                                                                                    spi_fno)));
433
434         /* ----------
435          * Collect the indexed attributes
436          * ----------
437          */
438         initStringInfo(&keybuf);
439         sep = "";
440         for (keyno = 0; keyno < INDEX_MAX_KEYS; keyno++)
441         {
442                 if (idxrec->indkey[keyno] == InvalidAttrNumber)
443                         break;
444
445                 appendStringInfo(&keybuf, sep);
446                 sep = ", ";
447
448                 /* ----------
449                  * Add the indexed field name
450                  * ----------
451                  */
452                 appendStringInfo(&keybuf, "%s",
453                                 quote_identifier(get_attribute_name(idxrec->indrelid,
454                                                                                                         idxrec->indkey[keyno])));
455
456                 /* ----------
457                  * If not a functional index, add the operator class name
458                  * ----------
459                  */
460                 if (idxrec->indproc == InvalidOid)
461                 {
462                         spi_args[0] = ObjectIdGetDatum(idxrec->indclass[keyno]);
463                         spi_nulls[0] = ' ';
464                         spi_nulls[1] = '\0';
465                         spirc = SPI_execp(plan_getopclass, spi_args, spi_nulls, 1);
466                         if (spirc != SPI_OK_SELECT)
467                                 elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[keyno]);
468                         if (SPI_processed != 1)
469                                 elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[keyno]);
470                         spi_tup = SPI_tuptable->vals[0];
471                         spi_ttc = SPI_tuptable->tupdesc;
472                         spi_fno = SPI_fnumber(spi_ttc, "opcname");
473                         appendStringInfo(&keybuf, " %s",
474                                                          quote_identifier(SPI_getvalue(spi_tup, spi_ttc,
475                                                                                                                    spi_fno)));
476                 }
477         }
478
479         /* ----------
480          * For functional index say 'func (attrs) opclass'
481          * ----------
482          */
483         if (idxrec->indproc != InvalidOid)
484         {
485                 HeapTuple       proctup;
486                 Form_pg_proc procStruct;
487
488                 proctup = SearchSysCacheTuple(PROCOID,
489                                                          ObjectIdGetDatum(idxrec->indproc), 0, 0, 0);
490                 if (!HeapTupleIsValid(proctup))
491                         elog(ERROR, "cache lookup for proc %u failed", idxrec->indproc);
492
493                 procStruct = (Form_pg_proc) GETSTRUCT(proctup);
494                 appendStringInfo(&buf, "%s(%s) ",
495                                                  quote_identifier(pstrdup(NameStr(procStruct->proname))),
496                                                  keybuf.data);
497
498                 spi_args[0] = ObjectIdGetDatum(idxrec->indclass[0]);
499                 spi_nulls[0] = ' ';
500                 spi_nulls[1] = '\0';
501                 spirc = SPI_execp(plan_getopclass, spi_args, spi_nulls, 1);
502                 if (spirc != SPI_OK_SELECT)
503                         elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[0]);
504                 if (SPI_processed != 1)
505                         elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[0]);
506                 spi_tup = SPI_tuptable->vals[0];
507                 spi_ttc = SPI_tuptable->tupdesc;
508                 spi_fno = SPI_fnumber(spi_ttc, "opcname");
509                 appendStringInfo(&buf, "%s",
510                                                  quote_identifier(SPI_getvalue(spi_tup, spi_ttc,
511                                                                                                            spi_fno)));
512         }
513         else
514                 /* ----------
515                  * For the others say 'attr opclass [, ...]'
516                  * ----------
517                  */
518                 appendStringInfo(&buf, "%s", keybuf.data);
519
520         /* ----------
521          * Finish
522          * ----------
523          */
524         appendStringInfo(&buf, ")");
525
526         /* ----------
527          * Create the result in upper executor memory
528          * ----------
529          */
530         len = buf.len + VARHDRSZ;
531         indexdef = SPI_palloc(len);
532         VARSIZE(indexdef) = len;
533         memcpy(VARDATA(indexdef), buf.data, buf.len);
534         pfree(buf.data);
535         pfree(keybuf.data);
536
537         /* ----------
538          * Disconnect from SPI manager
539          * ----------
540          */
541         if (SPI_finish() != SPI_OK_FINISH)
542                 elog(ERROR, "get_viewdef: SPI_finish() failed");
543
544         return indexdef;
545 }
546
547
548 /* ----------
549  * get_userbyid                 - Get a user name by usesysid and
550  *                                fallback to 'unknown (UID=n)'
551  * ----------
552  */
553 NameData   *
554 pg_get_userbyid(int32 uid)
555 {
556         HeapTuple       usertup;
557         Form_pg_shadow user_rec;
558         NameData   *result;
559
560         /* ----------
561          * Allocate space for the result
562          * ----------
563          */
564         result = (NameData *) palloc(NAMEDATALEN);
565         memset(NameStr(*result), 0, NAMEDATALEN);
566
567         /* ----------
568          * Get the pg_shadow entry and print the result
569          * ----------
570          */
571         usertup = SearchSysCacheTuple(SHADOWSYSID,
572                                                                   ObjectIdGetDatum(uid), 0, 0, 0);
573         if (HeapTupleIsValid(usertup))
574         {
575                 user_rec = (Form_pg_shadow) GETSTRUCT(usertup);
576                 StrNCpy(NameStr(*result), NameStr(user_rec->usename), NAMEDATALEN);
577         }
578         else
579                 sprintf((char *) result, "unknown (UID=%d)", uid);
580
581         return result;
582 }
583
584 /* ----------
585  * deparse_expression                   - General utility for deparsing expressions
586  *
587  * expr is the node tree to be deparsed.  It must be a transformed expression
588  * tree (ie, not the raw output of gram.y).
589  *
590  * rangetables is a List of Lists of RangeTblEntry nodes: first sublist is for
591  * varlevelsup = 0, next for varlevelsup = 1, etc.  In each sublist the first
592  * item is for varno = 1, next varno = 2, etc.  (Each sublist has the same
593  * format as the rtable list of a parsetree or query.)
594  *
595  * forceprefix is TRUE to force all Vars to be prefixed with their table names.
596  * Otherwise, a prefix is printed only if there's more than one table involved
597  * (and someday the code might try to print one only if there's ambiguity).
598  *
599  * The result is a palloc'd string.
600  * ----------
601  */
602 char *
603 deparse_expression(Node *expr, List *rangetables, bool forceprefix)
604 {
605         StringInfoData  buf;
606         deparse_context context;
607
608         initStringInfo(&buf);
609         context.buf = &buf;
610         context.rangetables = rangetables;
611         context.varprefix = (forceprefix ||
612                                                  length(rangetables) != 1 ||
613                                                  length((List *) lfirst(rangetables)) != 1);
614
615         rulename = "";                          /* in case of errors */
616
617         get_rule_expr(expr, &context);
618
619         return buf.data;
620 }
621
622 /* ----------
623  * make_ruledef                 - reconstruct the CREATE RULE command
624  *                                for a given pg_rewrite tuple
625  * ----------
626  */
627 static void
628 make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
629 {
630         char            ev_type;
631         Oid                     ev_class;
632         int2            ev_attr;
633         bool            is_instead;
634         char       *ev_qual;
635         char       *ev_action;
636         List       *actions = NIL;
637         int                     fno;
638         bool            isnull;
639
640         /* ----------
641          * Get the attribute values from the rules tuple
642          * ----------
643          */
644         fno = SPI_fnumber(rulettc, "ev_type");
645         ev_type = (char) SPI_getbinval(ruletup, rulettc, fno, &isnull);
646
647         fno = SPI_fnumber(rulettc, "ev_class");
648         ev_class = (Oid) SPI_getbinval(ruletup, rulettc, fno, &isnull);
649
650         fno = SPI_fnumber(rulettc, "ev_attr");
651         ev_attr = (int2) SPI_getbinval(ruletup, rulettc, fno, &isnull);
652
653         fno = SPI_fnumber(rulettc, "is_instead");
654         is_instead = (bool) SPI_getbinval(ruletup, rulettc, fno, &isnull);
655
656         fno = SPI_fnumber(rulettc, "ev_qual");
657         ev_qual = SPI_getvalue(ruletup, rulettc, fno);
658
659         fno = SPI_fnumber(rulettc, "ev_action");
660         ev_action = SPI_getvalue(ruletup, rulettc, fno);
661         if (ev_action != NULL)
662                 actions = (List *) stringToNode(ev_action);
663
664
665         /* ----------
666          * Build the rules definition text
667          * ----------
668          */
669         appendStringInfo(buf, "CREATE RULE %s AS ON ",
670                                          quote_identifier(rulename));
671
672         /* The event the rule is fired for */
673         switch (ev_type)
674         {
675                 case '1':
676                         appendStringInfo(buf, "SELECT");
677                         break;
678
679                 case '2':
680                         appendStringInfo(buf, "UPDATE");
681                         break;
682
683                 case '3':
684                         appendStringInfo(buf, "INSERT");
685                         break;
686
687                 case '4':
688                         appendStringInfo(buf, "DELETE");
689                         break;
690
691                 default:
692                         elog(ERROR, "get_ruledef: rule %s has unsupported event type %d",
693                                  rulename, ev_type);
694                         break;
695         }
696
697         /* The relation the rule is fired on */
698         appendStringInfo(buf, " TO %s",
699                                          quote_identifier(get_relation_name(ev_class)));
700         if (ev_attr > 0)
701                 appendStringInfo(buf, ".%s",
702                                                  quote_identifier(get_attribute_name(ev_class,
703                                                                                                                          ev_attr)));
704
705         /* If the rule has an event qualification, add it */
706         if (ev_qual == NULL)
707                 ev_qual = "";
708         if (strlen(ev_qual) > 0 && strcmp(ev_qual, "<>") != 0)
709         {
710                 Node       *qual;
711                 Query      *query;
712                 deparse_context context;
713
714                 appendStringInfo(buf, " WHERE ");
715
716                 qual = stringToNode(ev_qual);
717                 query = (Query *) lfirst(actions);
718
719                 context.buf = buf;
720                 context.rangetables = lcons(query->rtable, NIL);
721                 context.varprefix = (length(query->rtable) != 1);
722
723                 get_rule_expr(qual, &context);
724         }
725
726         appendStringInfo(buf, " DO ");
727
728         /* The INSTEAD keyword (if so) */
729         if (is_instead)
730                 appendStringInfo(buf, "INSTEAD ");
731
732         /* Finally the rules actions */
733         if (length(actions) > 1)
734         {
735                 List       *action;
736                 Query      *query;
737
738                 appendStringInfo(buf, "(");
739                 foreach(action, actions)
740                 {
741                         query = (Query *) lfirst(action);
742                         get_query_def(query, buf, NIL);
743                         appendStringInfo(buf, "; ");
744                 }
745                 appendStringInfo(buf, ");");
746         }
747         else
748         {
749                 if (length(actions) == 0)
750                 {
751                         appendStringInfo(buf, "NOTHING;");
752                 }
753                 else
754                 {
755                         Query      *query;
756
757                         query = (Query *) lfirst(actions);
758                         get_query_def(query, buf, NIL);
759                         appendStringInfo(buf, ";");
760                 }
761         }
762 }
763
764
765 /* ----------
766  * make_viewdef                 - reconstruct the SELECT part of a
767  *                                view rewrite rule
768  * ----------
769  */
770 static void
771 make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
772 {
773         Query      *query;
774         char            ev_type;
775         Oid                     ev_class;
776         int2            ev_attr;
777         bool            is_instead;
778         char       *ev_qual;
779         char       *ev_action;
780         List       *actions = NIL;
781         int                     fno;
782         bool            isnull;
783
784         /* ----------
785          * Get the attribute values from the rules tuple
786          * ----------
787          */
788         fno = SPI_fnumber(rulettc, "ev_type");
789         ev_type = (char) SPI_getbinval(ruletup, rulettc, fno, &isnull);
790
791         fno = SPI_fnumber(rulettc, "ev_class");
792         ev_class = (Oid) SPI_getbinval(ruletup, rulettc, fno, &isnull);
793
794         fno = SPI_fnumber(rulettc, "ev_attr");
795         ev_attr = (int2) SPI_getbinval(ruletup, rulettc, fno, &isnull);
796
797         fno = SPI_fnumber(rulettc, "is_instead");
798         is_instead = (bool) SPI_getbinval(ruletup, rulettc, fno, &isnull);
799
800         fno = SPI_fnumber(rulettc, "ev_qual");
801         ev_qual = SPI_getvalue(ruletup, rulettc, fno);
802
803         fno = SPI_fnumber(rulettc, "ev_action");
804         ev_action = SPI_getvalue(ruletup, rulettc, fno);
805         if (ev_action != NULL)
806                 actions = (List *) stringToNode(ev_action);
807
808         if (length(actions) != 1)
809         {
810                 appendStringInfo(buf, "Not a view");
811                 return;
812         }
813
814         query = (Query *) lfirst(actions);
815
816         if (ev_type != '1' || ev_attr >= 0 || !is_instead || strcmp(ev_qual, "<>"))
817         {
818                 appendStringInfo(buf, "Not a view");
819                 return;
820         }
821
822         get_query_def(query, buf, NIL);
823         appendStringInfo(buf, ";");
824 }
825
826
827 /* ----------
828  * get_query_def                        - Parse back one action from
829  *                                        the parsetree in the actions
830  *                                        list
831  * ----------
832  */
833 static void
834 get_query_def(Query *query, StringInfo buf, List *parentrtables)
835 {
836         deparse_context context;
837
838         context.buf = buf;
839         context.rangetables = lcons(query->rtable, parentrtables);
840         context.varprefix = (parentrtables != NIL ||
841                                                  length(query->rtable) != 1);
842
843         switch (query->commandType)
844         {
845                 case CMD_SELECT:
846                         get_select_query_def(query, &context);
847                         break;
848
849                 case CMD_UPDATE:
850                         get_update_query_def(query, &context);
851                         break;
852
853                 case CMD_INSERT:
854                         get_insert_query_def(query, &context);
855                         break;
856
857                 case CMD_DELETE:
858                         get_delete_query_def(query, &context);
859                         break;
860
861                 case CMD_NOTHING:
862                         appendStringInfo(buf, "NOTHING");
863                         break;
864
865                 default:
866                         elog(ERROR, "get_ruledef of %s: query command type %d not implemented yet",
867                                  rulename, query->commandType);
868                         break;
869         }
870 }
871
872
873 /* ----------
874  * get_select_query_def                 - Parse back a SELECT parsetree
875  * ----------
876  */
877 static void
878 get_select_query_def(Query *query, deparse_context *context)
879 {
880         StringInfo      buf = context->buf;
881         char       *sep;
882         TargetEntry *tle;
883         RangeTblEntry *rte;
884         bool       *rt_used;
885         int                     rt_length;
886         int                     rt_numused = 0;
887         bool            rt_constonly = TRUE;
888         int                     i;
889         List       *l;
890
891         /* ----------
892          * First we need to know which and how many of the
893          * range table entries in the query are used in the target list
894          * or queries qualification
895          * ----------
896          */
897         rt_length = length(query->rtable);
898         rt_used = palloc(sizeof(bool) * rt_length);
899         for (i = 0; i < rt_length; i++)
900         {
901                 if (check_if_rte_used((Node *) (query->targetList), i + 1, 0) ||
902                         check_if_rte_used(query->qual, i + 1, 0) ||
903                         check_if_rte_used(query->havingQual, i + 1, 0))
904                 {
905                         rt_used[i] = TRUE;
906                         rt_numused++;
907                 }
908                 else
909                         rt_used[i] = FALSE;
910         }
911
912         /* ----------
913          * Now check if any of the used rangetable entries is different
914          * from *NEW* and *CURRENT*. If so we must provide the FROM clause
915          * later.
916          * ----------
917          */
918         i = 0;
919         foreach(l, query->rtable)
920         {
921                 if (!rt_used[i++])
922                         continue;
923
924                 rte = (RangeTblEntry *) lfirst(l);
925                 if (!strcmp(rte->ref->relname, "*NEW*"))
926                         continue;
927                 if (!strcmp(rte->ref->relname, "*CURRENT*"))
928                         continue;
929
930                 rt_constonly = FALSE;
931                 break;
932         }
933
934         /* ----------
935          * Build up the query string - first we say SELECT
936          * ----------
937          */
938         appendStringInfo(buf, "SELECT");
939
940         /* Then we tell what to select (the targetlist) */
941         sep = " ";
942         foreach(l, query->targetList)
943         {
944                 bool            tell_as = FALSE;
945
946                 tle = (TargetEntry *) lfirst(l);
947                 appendStringInfo(buf, sep);
948                 sep = ", ";
949
950                 get_tle_expr(tle, context);
951
952                 /* Check if we must say AS ... */
953                 if (! IsA(tle->expr, Var))
954                         tell_as = strcmp(tle->resdom->resname, "?column?");
955                 else
956                 {
957                         Var                *var = (Var *) (tle->expr);
958                         char       *attname;
959
960                         rte = get_rte_for_var(var, context);
961                         attname = get_attribute_name(rte->relid, var->varattno);
962                         if (strcmp(attname, tle->resdom->resname))
963                                 tell_as = TRUE;
964                 }
965
966                 /* and do if so */
967                 if (tell_as)
968                         appendStringInfo(buf, " AS %s",
969                                                          quote_identifier(tle->resdom->resname));
970         }
971
972         /* If we need other tables than *NEW* or *CURRENT* add the FROM clause */
973         if (!rt_constonly && rt_numused > 0)
974         {
975                 sep = " FROM ";
976                 i = 0;
977                 foreach(l, query->rtable)
978                 {
979                         if (rt_used[i++])
980                         {
981                                 rte = (RangeTblEntry *) lfirst(l);
982
983                                 if (!strcmp(rte->ref->relname, "*NEW*"))
984                                         continue;
985
986                                 if (!strcmp(rte->ref->relname, "*CURRENT*"))
987                                         continue;
988
989                                 appendStringInfo(buf, sep);
990                                 sep = ", ";
991                                 appendStringInfo(buf, "%s%s",
992                                                                  quote_identifier(rte->relname),
993                                                                  inherit_marker(rte));
994                                 if (strcmp(rte->relname, rte->ref->relname) != 0)
995                                         appendStringInfo(buf, " %s",
996                                                                          quote_identifier(rte->ref->relname));
997                                 if (rte->ref->attrs != NIL)
998                                 {
999                                         List *col;
1000
1001                                         appendStringInfo(buf, " (");
1002                                         foreach(col, rte->ref->attrs)
1003                                         {
1004                                                 if (col != rte->ref->attrs)
1005                                                         appendStringInfo(buf, ", ");
1006                                                 appendStringInfo(buf, "%s",
1007                                                                                  quote_identifier(strVal(lfirst(col))));
1008                                         }
1009                                         appendStringInfoChar(buf, ')');
1010                                 }
1011                         }
1012                 }
1013         }
1014
1015         /* Add the WHERE clause if given */
1016         if (query->qual != NULL)
1017         {
1018                 appendStringInfo(buf, " WHERE ");
1019                 get_rule_expr(query->qual, context);
1020         }
1021
1022         /* Add the GROUP BY CLAUSE */
1023         if (query->groupClause != NULL)
1024         {
1025                 appendStringInfo(buf, " GROUP BY ");
1026                 sep = "";
1027                 foreach(l, query->groupClause)
1028                 {
1029                         GroupClause *grp = (GroupClause *) lfirst(l);
1030                         Node *groupexpr;
1031
1032                         groupexpr = get_sortgroupclause_expr(grp,
1033                                                                                                  query->targetList);
1034                         appendStringInfo(buf, sep);
1035                         get_rule_expr(groupexpr, context);
1036                         sep = ", ";
1037                 }
1038         }
1039 }
1040
1041
1042 /* ----------
1043  * get_insert_query_def                 - Parse back an INSERT parsetree
1044  * ----------
1045  */
1046 static void
1047 get_insert_query_def(Query *query, deparse_context *context)
1048 {
1049         StringInfo      buf = context->buf;
1050         char       *sep;
1051         TargetEntry *tle;
1052         RangeTblEntry *rte;
1053         bool       *rt_used;
1054         int                     rt_length;
1055         int                     rt_numused = 0;
1056         bool            rt_constonly = TRUE;
1057         int                     i;
1058         List       *l;
1059
1060         /* ----------
1061          * We need to know if other tables than *NEW* or *CURRENT*
1062          * are used in the query. If not, it's an INSERT ... VALUES,
1063          * otherwise an INSERT ... SELECT.
1064          * ----------
1065          */
1066         rt_length = length(query->rtable);
1067         rt_used = palloc(sizeof(bool) * rt_length);
1068         for (i = 0; i < rt_length; i++)
1069         {
1070                 if (check_if_rte_used((Node *) (query->targetList), i + 1, 0) ||
1071                         check_if_rte_used(query->qual, i + 1, 0) ||
1072                         check_if_rte_used(query->havingQual, i + 1, 0))
1073                 {
1074                         rt_used[i] = TRUE;
1075                         rt_numused++;
1076                 }
1077                 else
1078                         rt_used[i] = FALSE;
1079         }
1080
1081         i = 0;
1082         foreach(l, query->rtable)
1083         {
1084                 if (!rt_used[i++])
1085                         continue;
1086
1087                 rte = (RangeTblEntry *) lfirst(l);
1088                 if (!strcmp(rte->ref->relname, "*NEW*"))
1089                         continue;
1090                 if (!strcmp(rte->ref->relname, "*CURRENT*"))
1091                         continue;
1092
1093                 rt_constonly = FALSE;
1094                 break;
1095         }
1096
1097         /* ----------
1098          * Start the query with INSERT INTO relname
1099          * ----------
1100          */
1101         rte = rt_fetch(query->resultRelation, query->rtable);
1102         appendStringInfo(buf, "INSERT INTO %s",
1103                                          quote_identifier(rte->relname));
1104
1105         /* Add the target list */
1106         sep = " (";
1107         foreach(l, query->targetList)
1108         {
1109                 tle = (TargetEntry *) lfirst(l);
1110
1111                 appendStringInfo(buf, sep);
1112                 sep = ", ";
1113                 appendStringInfo(buf, "%s", quote_identifier(tle->resdom->resname));
1114         }
1115         appendStringInfo(buf, ") ");
1116
1117         /* Add the VALUES or the SELECT */
1118         if (rt_constonly && query->qual == NULL)
1119         {
1120                 appendStringInfo(buf, "VALUES (");
1121                 sep = "";
1122                 foreach(l, query->targetList)
1123                 {
1124                         tle = (TargetEntry *) lfirst(l);
1125
1126                         appendStringInfo(buf, sep);
1127                         sep = ", ";
1128                         get_tle_expr(tle, context);
1129                 }
1130                 appendStringInfoChar(buf, ')');
1131         }
1132         else
1133                 get_select_query_def(query, context);
1134 }
1135
1136
1137 /* ----------
1138  * get_update_query_def                 - Parse back an UPDATE parsetree
1139  * ----------
1140  */
1141 static void
1142 get_update_query_def(Query *query, deparse_context *context)
1143 {
1144         StringInfo      buf = context->buf;
1145         char       *sep;
1146         TargetEntry *tle;
1147         RangeTblEntry *rte;
1148         List       *l;
1149
1150         /* ----------
1151          * Start the query with UPDATE relname SET
1152          * ----------
1153          */
1154         rte = rt_fetch(query->resultRelation, query->rtable);
1155         appendStringInfo(buf, "UPDATE %s%s SET ",
1156                                          quote_identifier(rte->relname),
1157                                          inherit_marker(rte));
1158
1159         /* Add the comma separated list of 'attname = value' */
1160         sep = "";
1161         foreach(l, query->targetList)
1162         {
1163                 tle = (TargetEntry *) lfirst(l);
1164
1165                 appendStringInfo(buf, sep);
1166                 sep = ", ";
1167                 appendStringInfo(buf, "%s = ",
1168                                                  quote_identifier(tle->resdom->resname));
1169                 get_tle_expr(tle, context);
1170         }
1171
1172         /* Finally add a WHERE clause if given */
1173         if (query->qual != NULL)
1174         {
1175                 appendStringInfo(buf, " WHERE ");
1176                 get_rule_expr(query->qual, context);
1177         }
1178 }
1179
1180
1181 /* ----------
1182  * get_delete_query_def                 - Parse back a DELETE parsetree
1183  * ----------
1184  */
1185 static void
1186 get_delete_query_def(Query *query, deparse_context *context)
1187 {
1188         StringInfo      buf = context->buf;
1189         RangeTblEntry *rte;
1190
1191         /* ----------
1192          * Start the query with DELETE FROM relname
1193          * ----------
1194          */
1195         rte = rt_fetch(query->resultRelation, query->rtable);
1196         appendStringInfo(buf, "DELETE FROM %s%s",
1197                                          quote_identifier(rte->relname),
1198                                          inherit_marker(rte));
1199
1200         /* Add a WHERE clause if given */
1201         if (query->qual != NULL)
1202         {
1203                 appendStringInfo(buf, " WHERE ");
1204                 get_rule_expr(query->qual, context);
1205         }
1206 }
1207
1208 /*
1209  * Find the RTE referenced by a (possibly nonlocal) Var.
1210  */
1211 static RangeTblEntry *
1212 get_rte_for_var(Var *var, deparse_context *context)
1213 {
1214         List   *rtlist = context->rangetables;
1215         int             sup = var->varlevelsup;
1216
1217         while (sup-- > 0)
1218                 rtlist = lnext(rtlist);
1219
1220         return rt_fetch(var->varno, (List *) lfirst(rtlist));
1221 }
1222
1223
1224 /* ----------
1225  * get_rule_expr                        - Parse back an expression
1226  * ----------
1227  */
1228 static void
1229 get_rule_expr(Node *node, deparse_context *context)
1230 {
1231         StringInfo      buf = context->buf;
1232
1233         if (node == NULL)
1234                 return;
1235
1236         /* ----------
1237          * Each level of get_rule_expr must emit an indivisible term
1238          * (parenthesized if necessary) to ensure result is reparsed into
1239          * the same expression tree.
1240          *
1241          * There might be some work left here to support additional node types.
1242          * Can we ever see Param nodes here?
1243          * ----------
1244          */
1245         switch (nodeTag(node))
1246         {
1247                 case T_Const:
1248                         get_const_expr((Const *) node, context);
1249                         break;
1250
1251                 case T_Var:
1252                         {
1253                                 Var                *var = (Var *) node;
1254                                 RangeTblEntry *rte = get_rte_for_var(var, context);
1255
1256                                 if (context->varprefix)
1257                                 {
1258                                         if (!strcmp(rte->ref->relname, "*NEW*"))
1259                                                 appendStringInfo(buf, "new.");
1260                                         else if (!strcmp(rte->ref->relname, "*CURRENT*"))
1261                                                 appendStringInfo(buf, "old.");
1262                                         else
1263                                                 appendStringInfo(buf, "%s.",
1264                                                                                  quote_identifier(rte->ref->relname));
1265                                 }
1266                                 appendStringInfo(buf, "%s",
1267                                                 quote_identifier(get_attribute_name(rte->relid,
1268                                                                                                                         var->varattno)));
1269                         }
1270                         break;
1271
1272                 case T_Expr:
1273                         {
1274                                 Expr       *expr = (Expr *) node;
1275                                 List       *args = expr->args;
1276
1277                                 /* ----------
1278                                  * Expr nodes have to be handled a bit detailed
1279                                  * ----------
1280                                  */
1281                                 switch (expr->opType)
1282                                 {
1283                                         case OP_EXPR:
1284                                                 appendStringInfoChar(buf, '(');
1285                                                 if (length(args) == 2)
1286                                                 {
1287                                                         /* binary operator */
1288                                                         get_rule_expr((Node *) lfirst(args), context);
1289                                                         appendStringInfo(buf, " %s ",
1290                                                                         get_opname(((Oper *) expr->oper)->opno));
1291                                                         get_rule_expr((Node *) lsecond(args), context);
1292                                                 }
1293                                                 else
1294                                                 {
1295                                                         /* unary operator --- but which side? */
1296                                                         Oid                     opno = ((Oper *) expr->oper)->opno;
1297                                                         HeapTuple       tp;
1298                                                         Form_pg_operator optup;
1299
1300                                                         tp = SearchSysCacheTuple(OPEROID,
1301                                                                                                          ObjectIdGetDatum(opno),
1302                                                                                                          0, 0, 0);
1303                                                         Assert(HeapTupleIsValid(tp));
1304                                                         optup = (Form_pg_operator) GETSTRUCT(tp);
1305                                                         switch (optup->oprkind)
1306                                                         {
1307                                                                 case 'l':
1308                                                                         appendStringInfo(buf, "%s ",
1309                                                                                                          get_opname(opno));
1310                                                                         get_rule_expr((Node *) lfirst(args),
1311                                                                                                   context);
1312                                                                         break;
1313                                                                 case 'r':
1314                                                                         get_rule_expr((Node *) lfirst(args),
1315                                                                                                   context);
1316                                                                         appendStringInfo(buf, " %s",
1317                                                                                                          get_opname(opno));
1318                                                                         break;
1319                                                                 default:
1320                                                                         elog(ERROR, "get_rule_expr: bogus oprkind");
1321                                                         }
1322                                                 }
1323                                                 appendStringInfoChar(buf, ')');
1324                                                 break;
1325
1326                                         case OR_EXPR:
1327                                                 appendStringInfoChar(buf, '(');
1328                                                 get_rule_expr((Node *) lfirst(args), context);
1329                                                 while ((args = lnext(args)) != NIL)
1330                                                 {
1331                                                         appendStringInfo(buf, " OR ");
1332                                                         get_rule_expr((Node *) lfirst(args), context);
1333                                                 }
1334                                                 appendStringInfoChar(buf, ')');
1335                                                 break;
1336
1337                                         case AND_EXPR:
1338                                                 appendStringInfoChar(buf, '(');
1339                                                 get_rule_expr((Node *) lfirst(args), context);
1340                                                 while ((args = lnext(args)) != NIL)
1341                                                 {
1342                                                         appendStringInfo(buf, " AND ");
1343                                                         get_rule_expr((Node *) lfirst(args), context);
1344                                                 }
1345                                                 appendStringInfoChar(buf, ')');
1346                                                 break;
1347
1348                                         case NOT_EXPR:
1349                                                 appendStringInfo(buf, "(NOT ");
1350                                                 get_rule_expr((Node *) lfirst(args), context);
1351                                                 appendStringInfoChar(buf, ')');
1352                                                 break;
1353
1354                                         case FUNC_EXPR:
1355                                                 get_func_expr((Expr *) node, context);
1356                                                 break;
1357
1358                                         default:
1359                                                 elog(ERROR, "get_rule_expr: expr opType %d not supported",
1360                                                          expr->opType);
1361                                 }
1362                         }
1363                         break;
1364
1365                 case T_Aggref:
1366                         {
1367                                 Aggref     *aggref = (Aggref *) node;
1368
1369                                 appendStringInfo(buf, "%s(%s",
1370                                                                  quote_identifier(aggref->aggname),
1371                                                                  aggref->aggdistinct ? "DISTINCT " : "");
1372                                 if (aggref->aggstar)
1373                                         appendStringInfo(buf, "*");
1374                                 else
1375                                         get_rule_expr(aggref->target, context);
1376                                 appendStringInfoChar(buf, ')');
1377                         }
1378                         break;
1379
1380                 case T_Iter:
1381                         get_rule_expr(((Iter *) node)->iterexpr, context);
1382                         break;
1383
1384                 case T_ArrayRef:
1385                         {
1386                                 ArrayRef   *aref = (ArrayRef *) node;
1387                                 List       *lowlist;
1388                                 List       *uplist;
1389
1390                                 get_rule_expr(aref->refexpr, context);
1391                                 lowlist = aref->reflowerindexpr;
1392                                 foreach(uplist, aref->refupperindexpr)
1393                                 {
1394                                         appendStringInfo(buf, "[");
1395                                         if (lowlist)
1396                                         {
1397                                                 get_rule_expr((Node *) lfirst(lowlist), context);
1398                                                 appendStringInfo(buf, ":");
1399                                                 lowlist = lnext(lowlist);
1400                                         }
1401                                         get_rule_expr((Node *) lfirst(uplist), context);
1402                                         appendStringInfo(buf, "]");
1403                                 }
1404                                 /* XXX need to do anything with refassgnexpr? */
1405                         }
1406                         break;
1407
1408                 case T_RelabelType:
1409                         {
1410                                 RelabelType *relabel = (RelabelType *) node;
1411                                 HeapTuple       typetup;
1412                                 Form_pg_type typeStruct;
1413                                 char       *extval;
1414
1415                                 appendStringInfoChar(buf, '(');
1416                                 get_rule_expr(relabel->arg, context);
1417                                 typetup = SearchSysCacheTuple(TYPEOID,
1418                                                                         ObjectIdGetDatum(relabel->resulttype),
1419                                                                                           0, 0, 0);
1420                                 if (!HeapTupleIsValid(typetup))
1421                                         elog(ERROR, "cache lookup of type %u failed",
1422                                                  relabel->resulttype);
1423                                 typeStruct = (Form_pg_type) GETSTRUCT(typetup);
1424                                 extval = pstrdup(NameStr(typeStruct->typname));
1425                                 appendStringInfo(buf, ")::%s", quote_identifier(extval));
1426                                 pfree(extval);
1427                         }
1428                         break;
1429
1430                 case T_CaseExpr:
1431                         {
1432                                 CaseExpr   *caseexpr = (CaseExpr *) node;
1433                                 List       *temp;
1434
1435                                 appendStringInfo(buf, "CASE");
1436                                 foreach(temp, caseexpr->args)
1437                                 {
1438                                         CaseWhen   *when = (CaseWhen *) lfirst(temp);
1439
1440                                         appendStringInfo(buf, " WHEN ");
1441                                         get_rule_expr(when->expr, context);
1442                                         appendStringInfo(buf, " THEN ");
1443                                         get_rule_expr(when->result, context);
1444                                 }
1445                                 appendStringInfo(buf, " ELSE ");
1446                                 get_rule_expr(caseexpr->defresult, context);
1447                                 appendStringInfo(buf, " END");
1448                         }
1449                         break;
1450
1451                 case T_SubLink:
1452                         get_sublink_expr(node, context);
1453                         break;
1454
1455                 default:
1456                         printf("\n%s\n", nodeToString(node));
1457                         elog(ERROR, "get_ruledef of %s: unknown node type %d in get_rule_expr()",
1458                                  rulename, nodeTag(node));
1459                         break;
1460         }
1461 }
1462
1463
1464 /* ----------
1465  * get_func_expr                        - Parse back a Func node
1466  * ----------
1467  */
1468 static void
1469 get_func_expr(Expr *expr, deparse_context *context)
1470 {
1471         StringInfo      buf = context->buf;
1472         HeapTuple       proctup;
1473         Form_pg_proc procStruct;
1474         List       *l;
1475         char       *sep;
1476         Func       *func = (Func *) (expr->oper);
1477         char       *proname;
1478
1479         /* ----------
1480          * Get the functions pg_proc tuple
1481          * ----------
1482          */
1483         proctup = SearchSysCacheTuple(PROCOID,
1484                                                                   ObjectIdGetDatum(func->funcid),
1485                                                                   0, 0, 0);
1486         if (!HeapTupleIsValid(proctup))
1487                 elog(ERROR, "cache lookup for proc %u failed", func->funcid);
1488
1489         procStruct = (Form_pg_proc) GETSTRUCT(proctup);
1490         proname = pstrdup(NameStr(procStruct->proname));
1491
1492         /*
1493          * nullvalue() and nonnullvalue() should get turned into special syntax
1494          */
1495         if (procStruct->pronargs == 1 && procStruct->proargtypes[0] == InvalidOid)
1496         {
1497                 if (!strcmp(proname, "nullvalue"))
1498                 {
1499                         appendStringInfoChar(buf, '(');
1500                         get_rule_expr((Node *) lfirst(expr->args), context);
1501                         appendStringInfo(buf, " ISNULL)");
1502                         return;
1503                 }
1504                 if (!strcmp(proname, "nonnullvalue"))
1505                 {
1506                         appendStringInfoChar(buf, '(');
1507                         get_rule_expr((Node *) lfirst(expr->args), context);
1508                         appendStringInfo(buf, " NOTNULL)");
1509                         return;
1510                 }
1511         }
1512
1513         /* ----------
1514          * Build a string of proname(args)
1515          * ----------
1516          */
1517         appendStringInfo(buf, "%s(", quote_identifier(proname));
1518         sep = "";
1519         foreach(l, expr->args)
1520         {
1521                 appendStringInfo(buf, sep);
1522                 sep = ", ";
1523                 get_rule_expr((Node *) lfirst(l), context);
1524         }
1525         appendStringInfoChar(buf, ')');
1526 }
1527
1528
1529 /* ----------
1530  * get_tle_expr
1531  *
1532  *              A target list expression is a bit different from a normal expression.
1533  *              If the target column has an atttypmod, the parser usually puts a
1534  *              padding-/cut-function call around the expression itself.
1535  *              We must get rid of it, otherwise dump/reload/dump... would blow up
1536  *              the expressions.
1537  * ----------
1538  */
1539 static void
1540 get_tle_expr(TargetEntry *tle, deparse_context *context)
1541 {
1542         Expr       *expr = (Expr *) (tle->expr);
1543         Func       *func;
1544         HeapTuple       tup;
1545         Form_pg_proc procStruct;
1546         Form_pg_type typeStruct;
1547         Const      *second_arg;
1548
1549         /* ----------
1550          * Check if the result has an atttypmod and if the
1551          * expression in the targetlist entry is a function call
1552          * ----------
1553          */
1554         if (tle->resdom->restypmod < 0 ||
1555                 ! IsA(expr, Expr) ||
1556                 expr->opType != FUNC_EXPR)
1557         {
1558                 get_rule_expr(tle->expr, context);
1559                 return;
1560         }
1561
1562         func = (Func *) (expr->oper);
1563
1564         /* ----------
1565          * Get the functions pg_proc tuple
1566          * ----------
1567          */
1568         tup = SearchSysCacheTuple(PROCOID,
1569                                                           ObjectIdGetDatum(func->funcid), 0, 0, 0);
1570         if (!HeapTupleIsValid(tup))
1571                 elog(ERROR, "cache lookup for proc %u failed", func->funcid);
1572         procStruct = (Form_pg_proc) GETSTRUCT(tup);
1573
1574         /* ----------
1575          * It must be a function with two arguments where the first
1576          * is of the same type as the return value and the second is
1577          * an int4.
1578          * ----------
1579          */
1580         if (procStruct->pronargs != 2 ||
1581                 procStruct->prorettype != procStruct->proargtypes[0] ||
1582                 procStruct->proargtypes[1] != INT4OID)
1583         {
1584                 get_rule_expr(tle->expr, context);
1585                 return;
1586         }
1587
1588         /*
1589          * Furthermore, the name of the function must be the same
1590          * as the argument/result type name.
1591          */
1592         tup = SearchSysCacheTuple(TYPEOID,
1593                                                           ObjectIdGetDatum(procStruct->prorettype),
1594                                                           0, 0, 0);
1595         if (!HeapTupleIsValid(tup))
1596                 elog(ERROR, "cache lookup for type %u failed",
1597                          procStruct->prorettype);
1598         typeStruct = (Form_pg_type) GETSTRUCT(tup);
1599         if (strncmp(NameStr(procStruct->proname),
1600                                 NameStr(typeStruct->typname),
1601                                 NAMEDATALEN) != 0)
1602         {
1603                 get_rule_expr(tle->expr, context);
1604                 return;
1605         }
1606
1607         /* ----------
1608          * Finally (to be totally safe) the second argument must be a
1609          * const and match the value in the results atttypmod.
1610          * ----------
1611          */
1612         second_arg = (Const *) lsecond(expr->args);
1613         if (! IsA(second_arg, Const) ||
1614                 DatumGetInt32(second_arg->constvalue) != tle->resdom->restypmod)
1615         {
1616                 get_rule_expr(tle->expr, context);
1617                 return;
1618         }
1619
1620         /* ----------
1621          * Whow - got it. Now get rid of the padding function
1622          * ----------
1623          */
1624         get_rule_expr((Node *) lfirst(expr->args), context);
1625 }
1626
1627
1628 /* ----------
1629  * get_const_expr
1630  *
1631  *      Make a string representation of a Const
1632  * ----------
1633  */
1634 static void
1635 get_const_expr(Const *constval, deparse_context *context)
1636 {
1637         StringInfo      buf = context->buf;
1638         HeapTuple       typetup;
1639         Form_pg_type typeStruct;
1640         FmgrInfo        finfo_output;
1641         char       *extval;
1642         char       *valptr;
1643
1644         typetup = SearchSysCacheTuple(TYPEOID,
1645                                                                   ObjectIdGetDatum(constval->consttype),
1646                                                                   0, 0, 0);
1647         if (!HeapTupleIsValid(typetup))
1648                 elog(ERROR, "cache lookup of type %u failed", constval->consttype);
1649
1650         typeStruct = (Form_pg_type) GETSTRUCT(typetup);
1651
1652         if (constval->constisnull)
1653         {
1654                 /*
1655                  * Always label the type of a NULL constant.  This not only
1656                  * prevents misdecisions about the type, but it ensures that
1657                  * our output is a valid b_expr.
1658                  */
1659                 extval = pstrdup(NameStr(typeStruct->typname));
1660                 appendStringInfo(buf, "NULL::%s", quote_identifier(extval));
1661                 pfree(extval);
1662                 return;
1663         }
1664
1665         fmgr_info(typeStruct->typoutput, &finfo_output);
1666         extval = (char *) (*fmgr_faddr(&finfo_output)) (constval->constvalue,
1667                                                                                                         typeStruct->typelem,
1668                                                                                                         -1);
1669
1670         switch (constval->consttype)
1671         {
1672                 case INT2OID:
1673                 case INT4OID:
1674                 case OIDOID:                    /* int types */
1675                 case FLOAT4OID:
1676                 case FLOAT8OID:                 /* float types */
1677                         /* These types are printed without quotes */
1678                         appendStringInfo(buf, extval);
1679                         break;
1680                 default:
1681                         /*
1682                          * We must quote any funny characters in the constant's
1683                          * representation.
1684                          * XXX Any MULTIBYTE considerations here?
1685                          */
1686                         appendStringInfoChar(buf, '\'');
1687                         for (valptr = extval; *valptr; valptr++)
1688                         {
1689                                 char    ch = *valptr;
1690                                 if (ch == '\'' || ch == '\\')
1691                                 {
1692                                         appendStringInfoChar(buf, '\\');
1693                                         appendStringInfoChar(buf, ch);
1694                                 }
1695                                 else if (ch >= 0 && ch < ' ')
1696                                         appendStringInfo(buf, "\\%03o", (int) ch);
1697                                 else
1698                                         appendStringInfoChar(buf, ch);
1699                         }
1700                         appendStringInfoChar(buf, '\'');
1701                         break;
1702         }
1703
1704         pfree(extval);
1705
1706         switch (constval->consttype)
1707         {
1708                 case INT4OID:
1709                 case FLOAT8OID:
1710                 case UNKNOWNOID:
1711                         /* These types can be left unlabeled */
1712                         break;
1713                 default:
1714                         extval = pstrdup(NameStr(typeStruct->typname));
1715                         appendStringInfo(buf, "::%s", quote_identifier(extval));
1716                         pfree(extval);
1717                         break;
1718         }
1719 }
1720
1721
1722 /* ----------
1723  * get_sublink_expr                     - Parse back a sublink
1724  * ----------
1725  */
1726 static void
1727 get_sublink_expr(Node *node, deparse_context *context)
1728 {
1729         StringInfo      buf = context->buf;
1730         SubLink    *sublink = (SubLink *) node;
1731         Query      *query = (Query *) (sublink->subselect);
1732         List       *l;
1733         char       *sep;
1734         Oper       *oper;
1735         bool            need_paren;
1736
1737         appendStringInfoChar(buf, '(');
1738
1739         if (sublink->lefthand != NIL)
1740         {
1741                 need_paren = (length(sublink->lefthand) > 1);
1742                 if (need_paren)
1743                         appendStringInfoChar(buf, '(');
1744
1745                 sep = "";
1746                 foreach(l, sublink->lefthand)
1747                 {
1748                         appendStringInfo(buf, sep);
1749                         sep = ", ";
1750                         get_rule_expr((Node *) lfirst(l), context);
1751                 }
1752
1753                 if (need_paren)
1754                         appendStringInfo(buf, ") ");
1755                 else
1756                         appendStringInfoChar(buf, ' ');
1757         }
1758
1759         need_paren = true;
1760
1761         switch (sublink->subLinkType)
1762         {
1763                 case EXISTS_SUBLINK:
1764                         appendStringInfo(buf, "EXISTS ");
1765                         break;
1766
1767                 case ANY_SUBLINK:
1768                         oper = (Oper *) lfirst(sublink->oper);
1769                         appendStringInfo(buf, "%s ANY ", get_opname(oper->opno));
1770                         break;
1771
1772                 case ALL_SUBLINK:
1773                         oper = (Oper *) lfirst(sublink->oper);
1774                         appendStringInfo(buf, "%s ALL ", get_opname(oper->opno));
1775                         break;
1776
1777                 case MULTIEXPR_SUBLINK:
1778                         oper = (Oper *) lfirst(sublink->oper);
1779                         appendStringInfo(buf, "%s ", get_opname(oper->opno));
1780                         break;
1781
1782                 case EXPR_SUBLINK:
1783                         need_paren = false;
1784                         break;
1785
1786                 default:
1787                         elog(ERROR, "get_sublink_expr: unsupported sublink type %d",
1788                                  sublink->subLinkType);
1789                         break;
1790         }
1791
1792         if (need_paren)
1793                 appendStringInfoChar(buf, '(');
1794
1795         get_query_def(query, buf, context->rangetables);
1796
1797         if (need_paren)
1798                 appendStringInfo(buf, "))");
1799         else
1800                 appendStringInfoChar(buf, ')');
1801 }
1802
1803 /* ----------
1804  * quote_identifier                     - Quote an identifier only if needed
1805  *
1806  * When quotes are needed, we palloc the required space; slightly
1807  * space-wasteful but well worth it for notational simplicity.
1808  * ----------
1809  */
1810 static char *
1811 quote_identifier(char *ident)
1812 {
1813         /*
1814          * Can avoid quoting if ident starts with a lowercase letter and
1815          * contains only lowercase letters, digits, and underscores,
1816          * *and* is not any SQL keyword.  Otherwise, supply quotes.
1817          */
1818         bool            safe;
1819         char       *result;
1820
1821         /*
1822          * would like to use <ctype.h> macros here, but they might yield
1823          * unwanted locale-specific results...
1824          */
1825         safe = (ident[0] >= 'a' && ident[0] <= 'z');
1826         if (safe)
1827         {
1828                 char       *ptr;
1829
1830                 for (ptr = ident+1; *ptr; ptr++)
1831                 {
1832                         char    ch = *ptr;
1833
1834                         safe = ((ch >= 'a' && ch <= 'z') ||
1835                                         (ch >= '0' && ch <= '9') ||
1836                                         (ch == '_'));
1837                         if (! safe)
1838                                 break;
1839                 }
1840         }
1841
1842         if (safe)
1843         {
1844                 /*
1845                  * Check for keyword.  This test is overly strong, since many of
1846                  * the "keywords" known to the parser are usable as column names,
1847                  * but the parser doesn't provide any easy way to test for whether
1848                  * an identifier is safe or not... so be safe not sorry.
1849                  *
1850                  * Note: ScanKeywordLookup() expects an all-lower-case input, but
1851                  * we've already checked we have that.
1852                  */
1853                 if (ScanKeywordLookup(ident) != NULL)
1854                         safe = false;
1855         }
1856
1857         if (safe)
1858                 return ident;                   /* no change needed */
1859
1860         result = (char *) palloc(strlen(ident) + 2 + 1);
1861         sprintf(result, "\"%s\"", ident);
1862         return result;
1863 }
1864
1865 /* ----------
1866  * get_relation_name                    - Get a relation name by Oid
1867  * ----------
1868  */
1869 static char *
1870 get_relation_name(Oid relid)
1871 {
1872         HeapTuple       classtup;
1873         Form_pg_class classStruct;
1874
1875         classtup = SearchSysCacheTuple(RELOID,
1876                                                                    ObjectIdGetDatum(relid), 0, 0, 0);
1877         if (!HeapTupleIsValid(classtup))
1878                 elog(ERROR, "cache lookup of relation %u failed", relid);
1879
1880         classStruct = (Form_pg_class) GETSTRUCT(classtup);
1881         return pstrdup(NameStr(classStruct->relname));
1882 }
1883
1884
1885 /* ----------
1886  * get_attribute_name                   - Get an attribute name by its
1887  *                                        relations Oid and its attnum
1888  * ----------
1889  */
1890 static char *
1891 get_attribute_name(Oid relid, int2 attnum)
1892 {
1893         HeapTuple       atttup;
1894         Form_pg_attribute attStruct;
1895
1896         atttup = SearchSysCacheTuple(ATTNUM,
1897                                                                  ObjectIdGetDatum(relid), (Datum) attnum,
1898                                                                  0, 0);
1899         if (!HeapTupleIsValid(atttup))
1900                 elog(ERROR, "cache lookup of attribute %d in relation %u failed",
1901                          attnum, relid);
1902
1903         attStruct = (Form_pg_attribute) GETSTRUCT(atttup);
1904         return pstrdup(NameStr(attStruct->attname));
1905 }
1906
1907
1908 /* ----------
1909  * check_if_rte_used
1910  *              Check a targetlist or qual to see if a given rangetable entry
1911  *              is used in it
1912  * ----------
1913  */
1914 static bool
1915 check_if_rte_used(Node *node, Index rt_index, int levelsup)
1916 {
1917         check_if_rte_used_context context;
1918
1919         context.rt_index = rt_index;
1920         context.levelsup = levelsup;
1921         return check_if_rte_used_walker(node, &context);
1922 }
1923
1924 static bool
1925 check_if_rte_used_walker(Node *node,
1926                                                  check_if_rte_used_context *context)
1927 {
1928         if (node == NULL)
1929                 return false;
1930         if (IsA(node, Var))
1931         {
1932                 Var                *var = (Var *) node;
1933
1934                 return var->varno == context->rt_index &&
1935                         var->varlevelsup == context->levelsup;
1936         }
1937         if (IsA(node, SubLink))
1938         {
1939                 SubLink    *sublink = (SubLink *) node;
1940                 Query      *query = (Query *) sublink->subselect;
1941
1942                 /* Recurse into subquery; expression_tree_walker will not */
1943                 if (check_if_rte_used((Node *) (query->targetList),
1944                                                           context->rt_index, context->levelsup + 1) ||
1945                         check_if_rte_used(query->qual,
1946                                                           context->rt_index, context->levelsup + 1) ||
1947                         check_if_rte_used(query->havingQual,
1948                                                           context->rt_index, context->levelsup + 1))
1949                         return true;
1950                 /* fall through to let expression_tree_walker examine lefthand args */
1951         }
1952         return expression_tree_walker(node, check_if_rte_used_walker,
1953                                                                   (void *) context);
1954 }