OSDN Git Service

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