]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/ruleutils.c
7ab3985f3e72cb984d81f737ee00f0386d6c5d41
[postgresql] / src / backend / utils / adt / ruleutils.c
1 /**********************************************************************
2  * ruleutils.c  - Functions to convert stored expressions/querytrees
3  *                              back to source text
4  *
5  * IDENTIFICATION
6  *        $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.66 2000/10/05 21:52:08 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 "commands/view.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/parse_expr.h"
53 #include "parser/parsetree.h"
54 #include "rewrite/rewriteManip.h"
55 #include "utils/lsyscache.h"
56
57
58 /* ----------
59  * Local data types
60  * ----------
61  */
62 typedef struct
63 {
64         StringInfo      buf;                    /* output buffer to append to */
65         List       *rangetables;        /* List of List of RangeTblEntry */
66         bool            varprefix;              /* TRUE to print prefixes on Vars */
67 } deparse_context;
68
69
70 /* ----------
71  * Global data
72  * ----------
73  */
74 static char *rulename = NULL;
75 static void *plan_getrule = NULL;
76 static char *query_getrule = "SELECT * FROM pg_rewrite WHERE rulename = $1";
77 static void *plan_getview = NULL;
78 static char *query_getview = "SELECT * FROM pg_rewrite WHERE rulename = $1";
79 static void *plan_getam = NULL;
80 static char *query_getam = "SELECT * FROM pg_am WHERE oid = $1";
81 static void *plan_getopclass = NULL;
82 static char *query_getopclass = "SELECT * FROM pg_opclass WHERE oid = $1";
83
84
85 /* ----------
86  * Local functions
87  *
88  * Most of these functions used to use fixed-size buffers to build their
89  * results.  Now, they take an (already initialized) StringInfo object
90  * as a parameter, and append their text output to its contents.
91  * ----------
92  */
93 static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc);
94 static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc);
95 static void get_query_def(Query *query, StringInfo buf, List *parentrtables);
96 static void get_select_query_def(Query *query, deparse_context *context);
97 static void get_insert_query_def(Query *query, deparse_context *context);
98 static void get_update_query_def(Query *query, deparse_context *context);
99 static void get_delete_query_def(Query *query, deparse_context *context);
100 static void get_basic_select_query(Query *query, deparse_context *context);
101 static void get_setop_query(Node *setOp, Query *query,
102                                                         deparse_context *context, bool toplevel);
103 static bool simple_distinct(List *distinctClause, List *targetList);
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 void get_from_clause(Query *query, deparse_context *context);
111 static void get_from_clause_item(Node *jtnode, Query *query,
112                                                                  deparse_context *context);
113 static bool tleIsArrayAssign(TargetEntry *tle);
114 static char *quote_identifier(char *ident);
115 static char *get_relation_name(Oid relid);
116 static char *get_relid_attribute_name(Oid relid, AttrNumber attnum);
117
118 #define only_marker(rte)  ((rte)->inh ? "" : "ONLY ")
119
120
121 /* ----------
122  * get_ruledef                  - Do it all and return a text
123  *                                that could be used as a statement
124  *                                to recreate the rule
125  * ----------
126  */
127 Datum
128 pg_get_ruledef(PG_FUNCTION_ARGS)
129 {
130         Name            rname = PG_GETARG_NAME(0);
131         text       *ruledef;
132         Datum           args[1];
133         char            nulls[2];
134         int                     spirc;
135         HeapTuple       ruletup;
136         TupleDesc       rulettc;
137         StringInfoData buf;
138         int                     len;
139
140         /* ----------
141          * We need the rules name somewhere deep down: rulename is global
142          * ----------
143          */
144         rulename = pstrdup(NameStr(*rname));
145
146         /* ----------
147          * Connect to SPI manager
148          * ----------
149          */
150         if (SPI_connect() != SPI_OK_CONNECT)
151                 elog(ERROR, "get_ruledef: cannot connect to SPI manager");
152
153         /* ----------
154          * On the first call prepare the plan to lookup pg_proc.
155          * We read pg_proc over the SPI manager instead of using
156          * the syscache to be checked for read access on pg_proc.
157          * ----------
158          */
159         if (plan_getrule == NULL)
160         {
161                 Oid                     argtypes[1];
162                 void       *plan;
163
164                 argtypes[0] = NAMEOID;
165                 plan = SPI_prepare(query_getrule, 1, argtypes);
166                 if (plan == NULL)
167                         elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getrule);
168                 plan_getrule = SPI_saveplan(plan);
169         }
170
171         /* ----------
172          * Get the pg_rewrite tuple for this rule
173          * ----------
174          */
175         args[0] = PointerGetDatum(rulename);
176         nulls[0] = (rulename == NULL) ? 'n' : ' ';
177         nulls[1] = '\0';
178         spirc = SPI_execp(plan_getrule, args, nulls, 1);
179         if (spirc != SPI_OK_SELECT)
180                 elog(ERROR, "failed to get pg_rewrite tuple for %s", rulename);
181         if (SPI_processed != 1)
182         {
183                 if (SPI_finish() != SPI_OK_FINISH)
184                         elog(ERROR, "get_ruledef: SPI_finish() failed");
185                 ruledef = palloc(VARHDRSZ + 1);
186                 VARATT_SIZEP(ruledef) = VARHDRSZ + 1;
187                 VARDATA(ruledef)[0] = '-';
188                 PG_RETURN_TEXT_P(ruledef);
189         }
190
191         ruletup = SPI_tuptable->vals[0];
192         rulettc = SPI_tuptable->tupdesc;
193
194         /* ----------
195          * Get the rules definition and put it into executors memory
196          * ----------
197          */
198         initStringInfo(&buf);
199         make_ruledef(&buf, ruletup, rulettc);
200         len = buf.len + VARHDRSZ;
201         ruledef = SPI_palloc(len);
202         VARATT_SIZEP(ruledef) = len;
203         memcpy(VARDATA(ruledef), buf.data, buf.len);
204         pfree(buf.data);
205
206         /* ----------
207          * Disconnect from SPI manager
208          * ----------
209          */
210         if (SPI_finish() != SPI_OK_FINISH)
211                 elog(ERROR, "get_ruledef: SPI_finish() failed");
212
213         /* ----------
214          * Easy - isn't it?
215          * ----------
216          */
217         PG_RETURN_TEXT_P(ruledef);
218 }
219
220
221 /* ----------
222  * get_viewdef                  - Mainly the same thing, but we
223  *                                only return the SELECT part of a view
224  * ----------
225  */
226 Datum
227 pg_get_viewdef(PG_FUNCTION_ARGS)
228 {
229         Name            vname = PG_GETARG_NAME(0);
230         text       *ruledef;
231         Datum           args[1];
232         char            nulls[1];
233         int                     spirc;
234         HeapTuple       ruletup;
235         TupleDesc       rulettc;
236         StringInfoData buf;
237         int                     len;
238         char       *name;
239
240         /* ----------
241          * We need the view name somewhere deep down
242          * ----------
243          */
244         rulename = pstrdup(NameStr(*vname));
245
246         /* ----------
247          * Connect to SPI manager
248          * ----------
249          */
250         if (SPI_connect() != SPI_OK_CONNECT)
251                 elog(ERROR, "get_viewdef: cannot connect to SPI manager");
252
253         /* ----------
254          * On the first call prepare the plan to lookup pg_proc.
255          * We read pg_proc over the SPI manager instead of using
256          * the syscache to be checked for read access on pg_proc.
257          * ----------
258          */
259         if (plan_getview == NULL)
260         {
261                 Oid                     argtypes[1];
262                 void       *plan;
263
264                 argtypes[0] = NAMEOID;
265                 plan = SPI_prepare(query_getview, 1, argtypes);
266                 if (plan == NULL)
267                         elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getview);
268                 plan_getview = SPI_saveplan(plan);
269         }
270
271         /* ----------
272          * Get the pg_rewrite tuple for this rule: rulename is actually viewname here
273          * ----------
274          */
275         name = MakeRetrieveViewRuleName(rulename);
276         args[0] = PointerGetDatum(name);
277         nulls[0] = ' ';
278         spirc = SPI_execp(plan_getview, args, nulls, 1);
279         if (spirc != SPI_OK_SELECT)
280                 elog(ERROR, "failed to get pg_rewrite tuple for view %s", rulename);
281         initStringInfo(&buf);
282         if (SPI_processed != 1)
283                 appendStringInfo(&buf, "Not a view");
284         else
285         {
286                 /* ----------
287                  * Get the rules definition and put it into executors memory
288                  * ----------
289                  */
290                 ruletup = SPI_tuptable->vals[0];
291                 rulettc = SPI_tuptable->tupdesc;
292                 make_viewdef(&buf, ruletup, rulettc);
293         }
294         len = buf.len + VARHDRSZ;
295         ruledef = SPI_palloc(len);
296         VARATT_SIZEP(ruledef) = len;
297         memcpy(VARDATA(ruledef), buf.data, buf.len);
298         pfree(buf.data);
299         pfree(name);
300
301         /* ----------
302          * Disconnect from SPI manager
303          * ----------
304          */
305         if (SPI_finish() != SPI_OK_FINISH)
306                 elog(ERROR, "get_viewdef: SPI_finish() failed");
307
308         /* ----------
309          * Easy - isn't it?
310          * ----------
311          */
312         PG_RETURN_TEXT_P(ruledef);
313 }
314
315
316 /* ----------
317  * get_indexdef                 - Get the definition of an index
318  * ----------
319  */
320 Datum
321 pg_get_indexdef(PG_FUNCTION_ARGS)
322 {
323         Oid                     indexrelid = PG_GETARG_OID(0);
324         text       *indexdef;
325         HeapTuple       ht_idx;
326         HeapTuple       ht_idxrel;
327         HeapTuple       ht_indrel;
328         HeapTuple       spi_tup;
329         TupleDesc       spi_ttc;
330         int                     spi_fno;
331         Form_pg_index idxrec;
332         Form_pg_class idxrelrec;
333         Form_pg_class indrelrec;
334         Datum           spi_args[1];
335         char            spi_nulls[2];
336         int                     spirc;
337         int                     len;
338         int                     keyno;
339         StringInfoData buf;
340         StringInfoData keybuf;
341         char       *sep;
342
343         /* ----------
344          * Connect to SPI manager
345          * ----------
346          */
347         if (SPI_connect() != SPI_OK_CONNECT)
348                 elog(ERROR, "get_indexdef: cannot connect to SPI manager");
349
350         /* ----------
351          * On the first call prepare the plans to lookup pg_am
352          * and pg_opclass.
353          * ----------
354          */
355         if (plan_getam == NULL)
356         {
357                 Oid                     argtypes[1];
358                 void       *plan;
359
360                 argtypes[0] = OIDOID;
361                 plan = SPI_prepare(query_getam, 1, argtypes);
362                 if (plan == NULL)
363                         elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getam);
364                 plan_getam = SPI_saveplan(plan);
365
366                 argtypes[0] = OIDOID;
367                 plan = SPI_prepare(query_getopclass, 1, argtypes);
368                 if (plan == NULL)
369                         elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getopclass);
370                 plan_getopclass = SPI_saveplan(plan);
371         }
372
373         /* ----------
374          * Fetch the pg_index tuple by the Oid of the index
375          * ----------
376          */
377         ht_idx = SearchSysCacheTuple(INDEXRELID,
378                                                                  ObjectIdGetDatum(indexrelid), 0, 0, 0);
379         if (!HeapTupleIsValid(ht_idx))
380                 elog(ERROR, "syscache lookup for index %u failed", indexrelid);
381         idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
382
383         /* ----------
384          * Fetch the pg_class tuple of the index relation
385          * ----------
386          */
387         ht_idxrel = SearchSysCacheTuple(RELOID,
388                                                   ObjectIdGetDatum(idxrec->indexrelid), 0, 0, 0);
389         if (!HeapTupleIsValid(ht_idxrel))
390                 elog(ERROR, "syscache lookup for relid %u failed", idxrec->indexrelid);
391         idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
392
393         /* ----------
394          * Fetch the pg_class tuple of the indexed relation
395          * ----------
396          */
397         ht_indrel = SearchSysCacheTuple(RELOID,
398                                                         ObjectIdGetDatum(idxrec->indrelid), 0, 0, 0);
399         if (!HeapTupleIsValid(ht_indrel))
400                 elog(ERROR, "syscache lookup for relid %u failed", idxrec->indrelid);
401         indrelrec = (Form_pg_class) GETSTRUCT(ht_indrel);
402
403         /* ----------
404          * Get the am name for the index relation
405          * ----------
406          */
407         spi_args[0] = ObjectIdGetDatum(idxrelrec->relam);
408         spi_nulls[0] = ' ';
409         spi_nulls[1] = '\0';
410         spirc = SPI_execp(plan_getam, spi_args, spi_nulls, 1);
411         if (spirc != SPI_OK_SELECT)
412                 elog(ERROR, "failed to get pg_am tuple for index %s",
413                          NameStr(idxrelrec->relname));
414         if (SPI_processed != 1)
415                 elog(ERROR, "failed to get pg_am tuple for index %s",
416                          NameStr(idxrelrec->relname));
417         spi_tup = SPI_tuptable->vals[0];
418         spi_ttc = SPI_tuptable->tupdesc;
419         spi_fno = SPI_fnumber(spi_ttc, "amname");
420
421         /* ----------
422          * Start the index definition
423          * ----------
424          */
425         initStringInfo(&buf);
426         appendStringInfo(&buf, "CREATE %sINDEX %s ON %s USING %s (",
427                                          idxrec->indisunique ? "UNIQUE " : "",
428                                   quote_identifier(pstrdup(NameStr(idxrelrec->relname))),
429                                   quote_identifier(pstrdup(NameStr(indrelrec->relname))),
430                                          quote_identifier(SPI_getvalue(spi_tup, spi_ttc,
431                                                                                                    spi_fno)));
432
433         /* ----------
434          * Collect the indexed attributes
435          * ----------
436          */
437         initStringInfo(&keybuf);
438         sep = "";
439         for (keyno = 0; keyno < INDEX_MAX_KEYS; keyno++)
440         {
441                 if (idxrec->indkey[keyno] == InvalidAttrNumber)
442                         break;
443
444                 appendStringInfo(&keybuf, sep);
445                 sep = ", ";
446
447                 /* ----------
448                  * Add the indexed field name
449                  * ----------
450                  */
451                 appendStringInfo(&keybuf, "%s",
452                                 quote_identifier(get_relid_attribute_name(idxrec->indrelid,
453                                                                                                 idxrec->indkey[keyno])));
454
455                 /* ----------
456                  * If not a functional index, add the operator class name
457                  * ----------
458                  */
459                 if (idxrec->indproc == InvalidOid)
460                 {
461                         spi_args[0] = ObjectIdGetDatum(idxrec->indclass[keyno]);
462                         spi_nulls[0] = ' ';
463                         spi_nulls[1] = '\0';
464                         spirc = SPI_execp(plan_getopclass, spi_args, spi_nulls, 1);
465                         if (spirc != SPI_OK_SELECT)
466                                 elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[keyno]);
467                         if (SPI_processed != 1)
468                                 elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[keyno]);
469                         spi_tup = SPI_tuptable->vals[0];
470                         spi_ttc = SPI_tuptable->tupdesc;
471                         spi_fno = SPI_fnumber(spi_ttc, "opcname");
472                         appendStringInfo(&keybuf, " %s",
473                                                   quote_identifier(SPI_getvalue(spi_tup, spi_ttc,
474                                                                                                                 spi_fno)));
475                 }
476         }
477
478         /* ----------
479          * For functional index say 'func (attrs) opclass'
480          * ----------
481          */
482         if (idxrec->indproc != InvalidOid)
483         {
484                 HeapTuple       proctup;
485                 Form_pg_proc procStruct;
486
487                 proctup = SearchSysCacheTuple(PROCOID,
488                                                          ObjectIdGetDatum(idxrec->indproc), 0, 0, 0);
489                 if (!HeapTupleIsValid(proctup))
490                         elog(ERROR, "cache lookup for proc %u failed", idxrec->indproc);
491
492                 procStruct = (Form_pg_proc) GETSTRUCT(proctup);
493                 appendStringInfo(&buf, "%s(%s) ",
494                                  quote_identifier(pstrdup(NameStr(procStruct->proname))),
495                                                  keybuf.data);
496
497                 spi_args[0] = ObjectIdGetDatum(idxrec->indclass[0]);
498                 spi_nulls[0] = ' ';
499                 spi_nulls[1] = '\0';
500                 spirc = SPI_execp(plan_getopclass, spi_args, spi_nulls, 1);
501                 if (spirc != SPI_OK_SELECT)
502                         elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[0]);
503                 if (SPI_processed != 1)
504                         elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[0]);
505                 spi_tup = SPI_tuptable->vals[0];
506                 spi_ttc = SPI_tuptable->tupdesc;
507                 spi_fno = SPI_fnumber(spi_ttc, "opcname");
508                 appendStringInfo(&buf, "%s",
509                                                  quote_identifier(SPI_getvalue(spi_tup, spi_ttc,
510                                                                                                            spi_fno)));
511         }
512         else
513                 /* ----------
514                  * For the others say 'attr opclass [, ...]'
515                  * ----------
516                  */
517                 appendStringInfo(&buf, "%s", keybuf.data);
518
519         /* ----------
520          * Finish
521          * ----------
522          */
523         appendStringInfo(&buf, ")");
524
525         /* ----------
526          * Create the result in upper executor memory
527          * ----------
528          */
529         len = buf.len + VARHDRSZ;
530         indexdef = SPI_palloc(len);
531         VARATT_SIZEP(indexdef) = len;
532         memcpy(VARDATA(indexdef), buf.data, buf.len);
533         pfree(buf.data);
534         pfree(keybuf.data);
535
536         /* ----------
537          * Disconnect from SPI manager
538          * ----------
539          */
540         if (SPI_finish() != SPI_OK_FINISH)
541                 elog(ERROR, "get_viewdef: SPI_finish() failed");
542
543         PG_RETURN_TEXT_P(indexdef);
544 }
545
546
547 /* ----------
548  * get_userbyid                 - Get a user name by usesysid and
549  *                                fallback to 'unknown (UID=n)'
550  * ----------
551  */
552 Datum
553 pg_get_userbyid(PG_FUNCTION_ARGS)
554 {
555         int32           uid = PG_GETARG_INT32(0);
556         Name            result;
557         HeapTuple       usertup;
558         Form_pg_shadow user_rec;
559
560         /* ----------
561          * Allocate space for the result
562          * ----------
563          */
564         result = (Name) 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),
573                                                                   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(NameStr(*result), "unknown (UID=%d)", uid);
581
582         PG_RETURN_NAME(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_relid_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 = makeList1(query->rtable);
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 ||
818                 strcmp(ev_qual, "<>") != 0)
819         {
820                 appendStringInfo(buf, "Not a view");
821                 return;
822         }
823
824         get_query_def(query, buf, NIL);
825         appendStringInfo(buf, ";");
826 }
827
828
829 /* ----------
830  * get_query_def                        - Parse back one action from
831  *                                        the parsetree in the actions
832  *                                        list
833  * ----------
834  */
835 static void
836 get_query_def(Query *query, StringInfo buf, List *parentrtables)
837 {
838         deparse_context context;
839
840         context.buf = buf;
841         context.rangetables = lcons(query->rtable, parentrtables);
842         context.varprefix = (parentrtables != NIL ||
843                                                  length(query->rtable) != 1);
844
845         switch (query->commandType)
846         {
847                 case CMD_SELECT:
848                         get_select_query_def(query, &context);
849                         break;
850
851                 case CMD_UPDATE:
852                         get_update_query_def(query, &context);
853                         break;
854
855                 case CMD_INSERT:
856                         get_insert_query_def(query, &context);
857                         break;
858
859                 case CMD_DELETE:
860                         get_delete_query_def(query, &context);
861                         break;
862
863                 case CMD_NOTHING:
864                         appendStringInfo(buf, "NOTHING");
865                         break;
866
867                 default:
868                         elog(ERROR, "get_ruledef of %s: query command type %d not implemented yet",
869                                  rulename, query->commandType);
870                         break;
871         }
872 }
873
874
875 /* ----------
876  * get_select_query_def                 - Parse back a SELECT parsetree
877  * ----------
878  */
879 static void
880 get_select_query_def(Query *query, deparse_context *context)
881 {
882         StringInfo      buf = context->buf;
883         bool            shortform_orderby;
884         char       *sep;
885         List       *l;
886
887         /* ----------
888          * If the Query node has a setOperations tree, then it's the top
889          * level of a UNION/INTERSECT/EXCEPT query; only the ORDER BY field
890          * is interesting in the top query itself.
891          * ----------
892          */
893         if (query->setOperations)
894         {
895                 get_setop_query(query->setOperations, query, context, true);
896                 /* ORDER BY clauses must be simple in this case */
897                 shortform_orderby = true;
898         }
899         else
900         {
901                 get_basic_select_query(query, context);
902                 shortform_orderby = false;
903         }
904
905         /* Add the ORDER BY clause if given */
906         if (query->sortClause != NIL)
907         {
908                 appendStringInfo(buf, " ORDER BY ");
909                 sep = "";
910                 foreach(l, query->sortClause)
911                 {
912                         SortClause *srt = (SortClause *) lfirst(l);
913                         TargetEntry *sorttle;
914                         char       *opname;
915
916                         sorttle = get_sortgroupclause_tle(srt,
917                                                                                           query->targetList);
918                         appendStringInfo(buf, sep);
919                         if (shortform_orderby)
920                                 appendStringInfo(buf, "%d", sorttle->resdom->resno);
921                         else
922                                 get_rule_expr(sorttle->expr, context);
923                         opname = get_opname(srt->sortop);
924                         if (strcmp(opname, "<") != 0)
925                         {
926                                 if (strcmp(opname, ">") == 0)
927                                         appendStringInfo(buf, " DESC");
928                                 else
929                                         appendStringInfo(buf, " USING %s", opname);
930                         }
931                         sep = ", ";
932                 }
933         }
934 }
935
936 static void
937 get_basic_select_query(Query *query, deparse_context *context)
938 {
939         StringInfo      buf = context->buf;
940         char       *sep;
941         List       *l;
942
943         /* ----------
944          * Build up the query string - first we say SELECT
945          * ----------
946          */
947         appendStringInfo(buf, "SELECT");
948
949         /* Add the DISTINCT clause if given */
950         if (query->distinctClause != NIL)
951         {
952                 if (simple_distinct(query->distinctClause, query->targetList))
953                 {
954                         appendStringInfo(buf, " DISTINCT");
955                 }
956                 else
957                 {
958                         appendStringInfo(buf, " DISTINCT ON (");
959                         sep = "";
960                         foreach(l, query->distinctClause)
961                         {
962                                 SortClause *srt = (SortClause *) lfirst(l);
963                                 Node       *sortexpr;
964
965                                 sortexpr = get_sortgroupclause_expr(srt,
966                                                                                                         query->targetList);
967                                 appendStringInfo(buf, sep);
968                                 get_rule_expr(sortexpr, context);
969                                 sep = ", ";
970                         }
971                         appendStringInfo(buf, ")");
972                 }
973         }
974
975         /* Then we tell what to select (the targetlist) */
976         sep = " ";
977         foreach(l, query->targetList)
978         {
979                 TargetEntry *tle = (TargetEntry *) lfirst(l);
980                 bool            tell_as = false;
981
982                 if (tle->resdom->resjunk)
983                         continue;                       /* ignore junk entries */
984
985                 appendStringInfo(buf, sep);
986                 sep = ", ";
987
988                 /* Do NOT use get_tle_expr here; see its comments! */
989                 get_rule_expr(tle->expr, context);
990
991                 /* Check if we must say AS ... */
992                 if (!IsA(tle->expr, Var))
993                         tell_as = (strcmp(tle->resdom->resname, "?column?") != 0);
994                 else
995                 {
996                         Var                *var = (Var *) (tle->expr);
997                         RangeTblEntry *rte;
998                         char       *attname;
999
1000                         rte = get_rte_for_var(var, context);
1001                         attname = get_rte_attribute_name(rte, var->varattno);
1002                         tell_as = (strcmp(attname, tle->resdom->resname) != 0);
1003                 }
1004
1005                 /* and do if so */
1006                 if (tell_as)
1007                         appendStringInfo(buf, " AS %s",
1008                                                          quote_identifier(tle->resdom->resname));
1009         }
1010
1011         /* Add the FROM clause if needed */
1012         get_from_clause(query, context);
1013
1014         /* Add the WHERE clause if given */
1015         if (query->jointree->quals != NULL)
1016         {
1017                 appendStringInfo(buf, " WHERE ");
1018                 get_rule_expr(query->jointree->quals, context);
1019         }
1020
1021         /* Add the GROUP BY clause if given */
1022         if (query->groupClause != NULL)
1023         {
1024                 appendStringInfo(buf, " GROUP BY ");
1025                 sep = "";
1026                 foreach(l, query->groupClause)
1027                 {
1028                         GroupClause *grp = (GroupClause *) lfirst(l);
1029                         Node       *groupexpr;
1030
1031                         groupexpr = get_sortgroupclause_expr(grp,
1032                                                                                                  query->targetList);
1033                         appendStringInfo(buf, sep);
1034                         get_rule_expr(groupexpr, context);
1035                         sep = ", ";
1036                 }
1037         }
1038
1039         /* Add the HAVING clause if given */
1040         if (query->havingQual != NULL)
1041         {
1042                 appendStringInfo(buf, " HAVING ");
1043                 get_rule_expr(query->havingQual, context);
1044         }
1045 }
1046
1047 static void
1048 get_setop_query(Node *setOp, Query *query, deparse_context *context,
1049                                 bool toplevel)
1050 {
1051         StringInfo      buf = context->buf;
1052
1053         if (IsA(setOp, RangeTblRef))
1054         {
1055                 RangeTblRef *rtr = (RangeTblRef *) setOp;
1056                 RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
1057                 Query  *subquery = rte->subquery;
1058
1059                 Assert(subquery != NULL);
1060                 get_query_def(subquery, buf, context->rangetables);
1061         }
1062         else if (IsA(setOp, SetOperationStmt))
1063         {
1064                 SetOperationStmt *op = (SetOperationStmt *) setOp;
1065
1066                 /* Must suppress parens at top level of a setop tree because
1067                  * of grammar limitations...
1068                  */
1069                 if (! toplevel)
1070                         appendStringInfo(buf, "(");
1071                 get_setop_query(op->larg, query, context, false);
1072                 switch (op->op)
1073                 {
1074                         case SETOP_UNION:
1075                                 appendStringInfo(buf, " UNION ");
1076                                 break;
1077                         case SETOP_INTERSECT:
1078                                 appendStringInfo(buf, " INTERSECT ");
1079                                 break;
1080                         case SETOP_EXCEPT:
1081                                 appendStringInfo(buf, " EXCEPT ");
1082                                 break;
1083                         default:
1084                                 elog(ERROR, "get_setop_query: unexpected set op %d",
1085                                          (int) op->op);
1086                 }
1087                 if (op->all)
1088                         appendStringInfo(buf, "ALL ");
1089                 get_setop_query(op->rarg, query, context, false);
1090                 if (! toplevel)
1091                         appendStringInfo(buf, ")");
1092         }
1093         else
1094         {
1095                 elog(ERROR, "get_setop_query: unexpected node %d",
1096                          (int) nodeTag(setOp));
1097         }
1098 }
1099
1100 /*
1101  * Detect whether a DISTINCT list can be represented as just DISTINCT
1102  * or needs DISTINCT ON.  It's simple if it contains exactly the nonjunk
1103  * targetlist items.
1104  */
1105 static bool
1106 simple_distinct(List *distinctClause, List *targetList)
1107 {
1108         while (targetList)
1109         {
1110                 TargetEntry *tle = (TargetEntry *) lfirst(targetList);
1111
1112                 if (! tle->resdom->resjunk)
1113                 {
1114                         if (distinctClause == NIL)
1115                                 return false;
1116                         if (((SortClause *) lfirst(distinctClause))->tleSortGroupRef !=
1117                                 tle->resdom->ressortgroupref)
1118                                 return false;
1119                         distinctClause = lnext(distinctClause);
1120                 }
1121                 targetList = lnext(targetList);
1122         }
1123         if (distinctClause != NIL)
1124                 return false;
1125         return true;
1126 }
1127
1128
1129 /* ----------
1130  * get_insert_query_def                 - Parse back an INSERT parsetree
1131  * ----------
1132  */
1133 static void
1134 get_insert_query_def(Query *query, deparse_context *context)
1135 {
1136         StringInfo      buf = context->buf;
1137         RangeTblEntry *select_rte = NULL;
1138         RangeTblEntry *rte;
1139         char       *sep;
1140         List       *l;
1141
1142         /* ----------
1143          * If it's an INSERT ... SELECT there will be a single subquery RTE
1144          * for the SELECT.
1145          * ----------
1146          */
1147         foreach(l, query->rtable)
1148         {
1149                 rte = (RangeTblEntry *) lfirst(l);
1150                 if (rte->subquery == NULL)
1151                         continue;
1152                 if (select_rte)
1153                         elog(ERROR, "get_insert_query_def: too many RTEs in INSERT!");
1154                 select_rte = rte;
1155         }
1156
1157         /* ----------
1158          * Start the query with INSERT INTO relname
1159          * ----------
1160          */
1161         rte = rt_fetch(query->resultRelation, query->rtable);
1162         appendStringInfo(buf, "INSERT INTO %s",
1163                                          quote_identifier(rte->relname));
1164
1165         /* Add the insert-column-names list */
1166         sep = " (";
1167         foreach(l, query->targetList)
1168         {
1169                 TargetEntry *tle = (TargetEntry *) lfirst(l);
1170
1171                 if (tle->resdom->resjunk)
1172                         continue;                       /* ignore junk entries */
1173
1174                 appendStringInfo(buf, sep);
1175                 sep = ", ";
1176                 appendStringInfo(buf, "%s", quote_identifier(tle->resdom->resname));
1177         }
1178         appendStringInfo(buf, ") ");
1179
1180         /* Add the VALUES or the SELECT */
1181         if (select_rte == NULL)
1182         {
1183                 appendStringInfo(buf, "VALUES (");
1184                 sep = "";
1185                 foreach(l, query->targetList)
1186                 {
1187                         TargetEntry *tle = (TargetEntry *) lfirst(l);
1188
1189                         if (tle->resdom->resjunk)
1190                                 continue;               /* ignore junk entries */
1191
1192                         appendStringInfo(buf, sep);
1193                         sep = ", ";
1194                         get_tle_expr(tle, context);
1195                 }
1196                 appendStringInfoChar(buf, ')');
1197         }
1198         else
1199         {
1200                 get_query_def(select_rte->subquery, buf, NIL);
1201         }
1202 }
1203
1204
1205 /* ----------
1206  * get_update_query_def                 - Parse back an UPDATE parsetree
1207  * ----------
1208  */
1209 static void
1210 get_update_query_def(Query *query, deparse_context *context)
1211 {
1212         StringInfo      buf = context->buf;
1213         char       *sep;
1214         RangeTblEntry *rte;
1215         List       *l;
1216
1217         /* ----------
1218          * Start the query with UPDATE relname SET
1219          * ----------
1220          */
1221         rte = rt_fetch(query->resultRelation, query->rtable);
1222         appendStringInfo(buf, "UPDATE %s%s SET ",
1223                                          only_marker(rte),
1224                                          quote_identifier(rte->relname));
1225
1226         /* Add the comma separated list of 'attname = value' */
1227         sep = "";
1228         foreach(l, query->targetList)
1229         {
1230                 TargetEntry *tle = (TargetEntry *) lfirst(l);
1231
1232                 if (tle->resdom->resjunk)
1233                         continue;                       /* ignore junk entries */
1234
1235                 appendStringInfo(buf, sep);
1236                 sep = ", ";
1237                 /*
1238                  * If the update expression is an array assignment, we mustn't
1239                  * put out "attname =" here; it will come out of the display
1240                  * of the ArrayRef node instead.
1241                  */
1242                 if (! tleIsArrayAssign(tle))
1243                         appendStringInfo(buf, "%s = ",
1244                                                          quote_identifier(tle->resdom->resname));
1245                 get_tle_expr(tle, context);
1246         }
1247
1248         /* Add the FROM clause if needed */
1249         get_from_clause(query, context);
1250
1251         /* Finally add a WHERE clause if given */
1252         if (query->jointree->quals != NULL)
1253         {
1254                 appendStringInfo(buf, " WHERE ");
1255                 get_rule_expr(query->jointree->quals, context);
1256         }
1257 }
1258
1259
1260 /* ----------
1261  * get_delete_query_def                 - Parse back a DELETE parsetree
1262  * ----------
1263  */
1264 static void
1265 get_delete_query_def(Query *query, deparse_context *context)
1266 {
1267         StringInfo      buf = context->buf;
1268         RangeTblEntry *rte;
1269
1270         /* ----------
1271          * Start the query with DELETE FROM relname
1272          * ----------
1273          */
1274         rte = rt_fetch(query->resultRelation, query->rtable);
1275         appendStringInfo(buf, "DELETE FROM %s%s",
1276                                          only_marker(rte),
1277                                          quote_identifier(rte->relname));
1278
1279         /* Add a WHERE clause if given */
1280         if (query->jointree->quals != NULL)
1281         {
1282                 appendStringInfo(buf, " WHERE ");
1283                 get_rule_expr(query->jointree->quals, context);
1284         }
1285 }
1286
1287 /*
1288  * Find the RTE referenced by a (possibly nonlocal) Var.
1289  */
1290 static RangeTblEntry *
1291 get_rte_for_var(Var *var, deparse_context *context)
1292 {
1293         List       *rtlist = context->rangetables;
1294         int                     sup = var->varlevelsup;
1295
1296         while (sup-- > 0)
1297                 rtlist = lnext(rtlist);
1298
1299         return rt_fetch(var->varno, (List *) lfirst(rtlist));
1300 }
1301
1302
1303 /* ----------
1304  * get_rule_expr                        - Parse back an expression
1305  * ----------
1306  */
1307 static void
1308 get_rule_expr(Node *node, deparse_context *context)
1309 {
1310         StringInfo      buf = context->buf;
1311
1312         if (node == NULL)
1313                 return;
1314
1315         /* ----------
1316          * Each level of get_rule_expr must emit an indivisible term
1317          * (parenthesized if necessary) to ensure result is reparsed into
1318          * the same expression tree.
1319          *
1320          * There might be some work left here to support additional node types.
1321          * Can we ever see Param nodes here?
1322          * ----------
1323          */
1324         switch (nodeTag(node))
1325         {
1326                 case T_Const:
1327                         get_const_expr((Const *) node, context);
1328                         break;
1329
1330                 case T_Var:
1331                         {
1332                                 Var                *var = (Var *) node;
1333                                 RangeTblEntry *rte = get_rte_for_var(var, context);
1334
1335                                 if (context->varprefix)
1336                                 {
1337                                         if (strcmp(rte->eref->relname, "*NEW*") == 0)
1338                                                 appendStringInfo(buf, "new.");
1339                                         else if (strcmp(rte->eref->relname, "*OLD*") == 0)
1340                                                 appendStringInfo(buf, "old.");
1341                                         else
1342                                                 appendStringInfo(buf, "%s.",
1343                                                                         quote_identifier(rte->eref->relname));
1344                                 }
1345                                 appendStringInfo(buf, "%s",
1346                                                   quote_identifier(get_rte_attribute_name(rte,
1347                                                                                                                 var->varattno)));
1348                         }
1349                         break;
1350
1351                 case T_Expr:
1352                         {
1353                                 Expr       *expr = (Expr *) node;
1354                                 List       *args = expr->args;
1355
1356                                 /* ----------
1357                                  * Expr nodes have to be handled a bit detailed
1358                                  * ----------
1359                                  */
1360                                 switch (expr->opType)
1361                                 {
1362                                         case OP_EXPR:
1363                                                 appendStringInfoChar(buf, '(');
1364                                                 if (length(args) == 2)
1365                                                 {
1366                                                         /* binary operator */
1367                                                         get_rule_expr((Node *) lfirst(args), context);
1368                                                         appendStringInfo(buf, " %s ",
1369                                                                 get_opname(((Oper *) expr->oper)->opno));
1370                                                         get_rule_expr((Node *) lsecond(args), context);
1371                                                 }
1372                                                 else
1373                                                 {
1374                                                         /* unary operator --- but which side? */
1375                                                         Oid                     opno = ((Oper *) expr->oper)->opno;
1376                                                         HeapTuple       tp;
1377                                                         Form_pg_operator optup;
1378
1379                                                         tp = SearchSysCacheTuple(OPEROID,
1380                                                                                                   ObjectIdGetDatum(opno),
1381                                                                                                          0, 0, 0);
1382                                                         Assert(HeapTupleIsValid(tp));
1383                                                         optup = (Form_pg_operator) GETSTRUCT(tp);
1384                                                         switch (optup->oprkind)
1385                                                         {
1386                                                                 case 'l':
1387                                                                         appendStringInfo(buf, "%s ",
1388                                                                                                          get_opname(opno));
1389                                                                         get_rule_expr((Node *) lfirst(args),
1390                                                                                                   context);
1391                                                                         break;
1392                                                                 case 'r':
1393                                                                         get_rule_expr((Node *) lfirst(args),
1394                                                                                                   context);
1395                                                                         appendStringInfo(buf, " %s",
1396                                                                                                          get_opname(opno));
1397                                                                         break;
1398                                                                 default:
1399                                                                         elog(ERROR, "get_rule_expr: bogus oprkind");
1400                                                         }
1401                                                 }
1402                                                 appendStringInfoChar(buf, ')');
1403                                                 break;
1404
1405                                         case OR_EXPR:
1406                                                 appendStringInfoChar(buf, '(');
1407                                                 get_rule_expr((Node *) lfirst(args), context);
1408                                                 while ((args = lnext(args)) != NIL)
1409                                                 {
1410                                                         appendStringInfo(buf, " OR ");
1411                                                         get_rule_expr((Node *) lfirst(args), context);
1412                                                 }
1413                                                 appendStringInfoChar(buf, ')');
1414                                                 break;
1415
1416                                         case AND_EXPR:
1417                                                 appendStringInfoChar(buf, '(');
1418                                                 get_rule_expr((Node *) lfirst(args), context);
1419                                                 while ((args = lnext(args)) != NIL)
1420                                                 {
1421                                                         appendStringInfo(buf, " AND ");
1422                                                         get_rule_expr((Node *) lfirst(args), context);
1423                                                 }
1424                                                 appendStringInfoChar(buf, ')');
1425                                                 break;
1426
1427                                         case NOT_EXPR:
1428                                                 appendStringInfo(buf, "(NOT ");
1429                                                 get_rule_expr((Node *) lfirst(args), context);
1430                                                 appendStringInfoChar(buf, ')');
1431                                                 break;
1432
1433                                         case FUNC_EXPR:
1434                                                 get_func_expr((Expr *) node, context);
1435                                                 break;
1436
1437                                         default:
1438                                                 elog(ERROR, "get_rule_expr: expr opType %d not supported",
1439                                                          expr->opType);
1440                                 }
1441                         }
1442                         break;
1443
1444                 case T_Aggref:
1445                         {
1446                                 Aggref     *aggref = (Aggref *) node;
1447
1448                                 appendStringInfo(buf, "%s(%s",
1449                                                                  quote_identifier(aggref->aggname),
1450                                                                  aggref->aggdistinct ? "DISTINCT " : "");
1451                                 if (aggref->aggstar)
1452                                         appendStringInfo(buf, "*");
1453                                 else
1454                                         get_rule_expr(aggref->target, context);
1455                                 appendStringInfoChar(buf, ')');
1456                         }
1457                         break;
1458
1459                 case T_Iter:
1460                         get_rule_expr(((Iter *) node)->iterexpr, context);
1461                         break;
1462
1463                 case T_ArrayRef:
1464                         {
1465                                 ArrayRef   *aref = (ArrayRef *) node;
1466                                 bool            savevarprefix = context->varprefix;
1467                                 List       *lowlist;
1468                                 List       *uplist;
1469
1470                                 /*
1471                                  * If we are doing UPDATE array[n] = expr, we need to
1472                                  * suppress any prefix on the array name.  Currently,
1473                                  * that is the only context in which we will see a non-null
1474                                  * refassgnexpr --- but someday a smarter test may be needed.
1475                                  */
1476                                 if (aref->refassgnexpr)
1477                                         context->varprefix = false;
1478                                 get_rule_expr(aref->refexpr, context);
1479                                 context->varprefix = savevarprefix;
1480                                 lowlist = aref->reflowerindexpr;
1481                                 foreach(uplist, aref->refupperindexpr)
1482                                 {
1483                                         appendStringInfo(buf, "[");
1484                                         if (lowlist)
1485                                         {
1486                                                 get_rule_expr((Node *) lfirst(lowlist), context);
1487                                                 appendStringInfo(buf, ":");
1488                                                 lowlist = lnext(lowlist);
1489                                         }
1490                                         get_rule_expr((Node *) lfirst(uplist), context);
1491                                         appendStringInfo(buf, "]");
1492                                 }
1493                                 if (aref->refassgnexpr)
1494                                 {
1495                                         appendStringInfo(buf, " = ");
1496                                         get_rule_expr(aref->refassgnexpr, context);
1497                                 }
1498                         }
1499                         break;
1500
1501                 case T_FieldSelect:
1502                         {
1503                                 FieldSelect *fselect = (FieldSelect *) node;
1504                                 HeapTuple       typetup;
1505                                 Form_pg_type typeStruct;
1506                                 Oid                     typrelid;
1507                                 char       *fieldname;
1508
1509                                 /* we do NOT parenthesize the arg expression, for now */
1510                                 get_rule_expr(fselect->arg, context);
1511                                 typetup = SearchSysCacheTuple(TYPEOID,
1512                                                                    ObjectIdGetDatum(exprType(fselect->arg)),
1513                                                                                           0, 0, 0);
1514                                 if (!HeapTupleIsValid(typetup))
1515                                         elog(ERROR, "cache lookup of type %u failed",
1516                                                  exprType(fselect->arg));
1517                                 typeStruct = (Form_pg_type) GETSTRUCT(typetup);
1518                                 typrelid = typeStruct->typrelid;
1519                                 if (!OidIsValid(typrelid))
1520                                         elog(ERROR, "Argument type %s of FieldSelect is not a tuple type",
1521                                                  NameStr(typeStruct->typname));
1522                                 fieldname = get_relid_attribute_name(typrelid,
1523                                                                                                          fselect->fieldnum);
1524                                 appendStringInfo(buf, ".%s", quote_identifier(fieldname));
1525                         }
1526                         break;
1527
1528                 case T_RelabelType:
1529                         {
1530                                 RelabelType *relabel = (RelabelType *) node;
1531                                 HeapTuple       typetup;
1532                                 Form_pg_type typeStruct;
1533                                 char       *extval;
1534
1535                                 appendStringInfoChar(buf, '(');
1536                                 get_rule_expr(relabel->arg, context);
1537                                 typetup = SearchSysCacheTuple(TYPEOID,
1538                                                                    ObjectIdGetDatum(relabel->resulttype),
1539                                                                                           0, 0, 0);
1540                                 if (!HeapTupleIsValid(typetup))
1541                                         elog(ERROR, "cache lookup of type %u failed",
1542                                                  relabel->resulttype);
1543                                 typeStruct = (Form_pg_type) GETSTRUCT(typetup);
1544                                 extval = pstrdup(NameStr(typeStruct->typname));
1545                                 appendStringInfo(buf, ")::%s", quote_identifier(extval));
1546                                 pfree(extval);
1547                         }
1548                         break;
1549
1550                 case T_CaseExpr:
1551                         {
1552                                 CaseExpr   *caseexpr = (CaseExpr *) node;
1553                                 List       *temp;
1554
1555                                 appendStringInfo(buf, "CASE");
1556                                 foreach(temp, caseexpr->args)
1557                                 {
1558                                         CaseWhen   *when = (CaseWhen *) lfirst(temp);
1559
1560                                         appendStringInfo(buf, " WHEN ");
1561                                         get_rule_expr(when->expr, context);
1562                                         appendStringInfo(buf, " THEN ");
1563                                         get_rule_expr(when->result, context);
1564                                 }
1565                                 appendStringInfo(buf, " ELSE ");
1566                                 get_rule_expr(caseexpr->defresult, context);
1567                                 appendStringInfo(buf, " END");
1568                         }
1569                         break;
1570
1571                 case T_SubLink:
1572                         get_sublink_expr(node, context);
1573                         break;
1574
1575                 default:
1576                         printf("\n%s\n", nodeToString(node));
1577                         elog(ERROR, "get_ruledef of %s: unknown node type %d in get_rule_expr()",
1578                                  rulename, nodeTag(node));
1579                         break;
1580         }
1581 }
1582
1583
1584 /* ----------
1585  * get_func_expr                        - Parse back a Func node
1586  * ----------
1587  */
1588 static void
1589 get_func_expr(Expr *expr, deparse_context *context)
1590 {
1591         StringInfo      buf = context->buf;
1592         Func       *func = (Func *) (expr->oper);
1593         HeapTuple       proctup;
1594         Form_pg_proc procStruct;
1595         char       *proname;
1596         int32           coercedTypmod;
1597         List       *l;
1598         char       *sep;
1599
1600         /*
1601          * Get the functions pg_proc tuple
1602          */
1603         proctup = SearchSysCacheTuple(PROCOID,
1604                                                                   ObjectIdGetDatum(func->funcid),
1605                                                                   0, 0, 0);
1606         if (!HeapTupleIsValid(proctup))
1607                 elog(ERROR, "cache lookup for proc %u failed", func->funcid);
1608
1609         procStruct = (Form_pg_proc) GETSTRUCT(proctup);
1610         proname = pstrdup(NameStr(procStruct->proname));
1611
1612         /*
1613          * nullvalue() and nonnullvalue() should get turned into special
1614          * syntax
1615          */
1616         if (procStruct->pronargs == 1 && procStruct->proargtypes[0] == InvalidOid)
1617         {
1618                 if (strcmp(proname, "nullvalue") == 0)
1619                 {
1620                         appendStringInfoChar(buf, '(');
1621                         get_rule_expr((Node *) lfirst(expr->args), context);
1622                         appendStringInfo(buf, " ISNULL)");
1623                         return;
1624                 }
1625                 if (strcmp(proname, "nonnullvalue") == 0)
1626                 {
1627                         appendStringInfoChar(buf, '(');
1628                         get_rule_expr((Node *) lfirst(expr->args), context);
1629                         appendStringInfo(buf, " NOTNULL)");
1630                         return;
1631                 }
1632         }
1633
1634         /*
1635          * Check to see if function is a length-coercion function for some
1636          * datatype.  If so, display the operation as a type cast.
1637          */
1638         if (exprIsLengthCoercion((Node *) expr, &coercedTypmod))
1639         {
1640                 Node       *arg = lfirst(expr->args);
1641
1642                 /*
1643                  * Strip off any RelabelType on the input, so we don't print
1644                  * redundancies like x::bpchar::char(8). XXX Are there any cases
1645                  * where this is a bad idea?
1646                  */
1647                 if (IsA(arg, RelabelType))
1648                         arg = ((RelabelType *) arg)->arg;
1649                 appendStringInfoChar(buf, '(');
1650                 get_rule_expr(arg, context);
1651                 appendStringInfo(buf, ")::");
1652
1653                 /*
1654                  * Show typename with appropriate length decoration. Note that
1655                  * since exprIsLengthCoercion succeeded, the function name is the
1656                  * same as its output type name.
1657                  */
1658                 if (strcmp(proname, "bpchar") == 0)
1659                 {
1660                         if (coercedTypmod > (int32) VARHDRSZ)
1661                                 appendStringInfo(buf, "char(%d)", coercedTypmod - VARHDRSZ);
1662                         else
1663                                 appendStringInfo(buf, "char");
1664                 }
1665                 else if (strcmp(proname, "varchar") == 0)
1666                 {
1667                         if (coercedTypmod > (int32) VARHDRSZ)
1668                                 appendStringInfo(buf, "varchar(%d)", coercedTypmod - VARHDRSZ);
1669                         else
1670                                 appendStringInfo(buf, "varchar");
1671                 }
1672                 else if (strcmp(proname, "numeric") == 0)
1673                 {
1674                         if (coercedTypmod >= (int32) VARHDRSZ)
1675                                 appendStringInfo(buf, "numeric(%d,%d)",
1676                                                          ((coercedTypmod - VARHDRSZ) >> 16) & 0xffff,
1677                                                                  (coercedTypmod - VARHDRSZ) & 0xffff);
1678                         else
1679                                 appendStringInfo(buf, "numeric");
1680                 }
1681                 else
1682                         appendStringInfo(buf, "%s", quote_identifier(proname));
1683                 return;
1684         }
1685
1686         /*
1687          * Normal function: display as proname(args)
1688          */
1689         appendStringInfo(buf, "%s(", quote_identifier(proname));
1690         sep = "";
1691         foreach(l, expr->args)
1692         {
1693                 appendStringInfo(buf, sep);
1694                 sep = ", ";
1695                 get_rule_expr((Node *) lfirst(l), context);
1696         }
1697         appendStringInfoChar(buf, ')');
1698 }
1699
1700
1701 /* ----------
1702  * get_tle_expr
1703  *
1704  *              In an INSERT or UPDATE targetlist item, the parser may have inserted
1705  *              a length-coercion function call to coerce the value to the right
1706  *              length for the target column.  We want to suppress the output of
1707  *              that function call, otherwise dump/reload/dump... would blow up the
1708  *              expression by adding more and more layers of length-coercion calls.
1709  *
1710  * As of 7.0, this hack is no longer absolutely essential, because the parser
1711  * is now smart enough not to add a redundant length coercion function call.
1712  * But we still suppress the function call just for neatness of displayed
1713  * rules.
1714  *
1715  * Note that this hack must NOT be applied to SELECT targetlist items;
1716  * any length coercion appearing there is something the user actually wrote.
1717  * ----------
1718  */
1719 static void
1720 get_tle_expr(TargetEntry *tle, deparse_context *context)
1721 {
1722         Expr       *expr = (Expr *) (tle->expr);
1723         int32           coercedTypmod;
1724
1725         /*
1726          * If top level is a length coercion to the correct length, suppress
1727          * it; else dump the expression normally.
1728          */
1729         if (tle->resdom->restypmod >= 0 &&
1730                 exprIsLengthCoercion((Node *) expr, &coercedTypmod) &&
1731                 coercedTypmod == tle->resdom->restypmod)
1732                 get_rule_expr((Node *) lfirst(expr->args), context);
1733         else
1734                 get_rule_expr(tle->expr, context);
1735 }
1736
1737
1738 /* ----------
1739  * get_const_expr
1740  *
1741  *      Make a string representation of a Const
1742  * ----------
1743  */
1744 static void
1745 get_const_expr(Const *constval, deparse_context *context)
1746 {
1747         StringInfo      buf = context->buf;
1748         HeapTuple       typetup;
1749         Form_pg_type typeStruct;
1750         char       *extval;
1751         char       *valptr;
1752
1753         typetup = SearchSysCacheTuple(TYPEOID,
1754                                                                   ObjectIdGetDatum(constval->consttype),
1755                                                                   0, 0, 0);
1756         if (!HeapTupleIsValid(typetup))
1757                 elog(ERROR, "cache lookup of type %u failed", constval->consttype);
1758
1759         typeStruct = (Form_pg_type) GETSTRUCT(typetup);
1760
1761         if (constval->constisnull)
1762         {
1763
1764                 /*
1765                  * Always label the type of a NULL constant.  This not only
1766                  * prevents misdecisions about the type, but it ensures that our
1767                  * output is a valid b_expr.
1768                  */
1769                 extval = pstrdup(NameStr(typeStruct->typname));
1770                 appendStringInfo(buf, "NULL::%s", quote_identifier(extval));
1771                 pfree(extval);
1772                 return;
1773         }
1774
1775         extval = DatumGetCString(OidFunctionCall3(typeStruct->typoutput,
1776                                                          constval->constvalue,
1777                                                          ObjectIdGetDatum(typeStruct->typelem),
1778                                                          Int32GetDatum(-1)));
1779
1780         switch (constval->consttype)
1781         {
1782                 case INT2OID:
1783                 case INT4OID:
1784                 case OIDOID:                    /* int types */
1785                 case FLOAT4OID:
1786                 case FLOAT8OID: /* float types */
1787                         /* These types are printed without quotes */
1788                         appendStringInfo(buf, extval);
1789                         break;
1790                 default:
1791
1792                         /*
1793                          * We must quote any funny characters in the constant's
1794                          * representation. XXX Any MULTIBYTE considerations here?
1795                          */
1796                         appendStringInfoChar(buf, '\'');
1797                         for (valptr = extval; *valptr; valptr++)
1798                         {
1799                                 char            ch = *valptr;
1800
1801                                 if (ch == '\'' || ch == '\\')
1802                                 {
1803                                         appendStringInfoChar(buf, '\\');
1804                                         appendStringInfoChar(buf, ch);
1805                                 }
1806                                 else if (ch >= 0 && ch < ' ')
1807                                         appendStringInfo(buf, "\\%03o", (int) ch);
1808                                 else
1809                                         appendStringInfoChar(buf, ch);
1810                         }
1811                         appendStringInfoChar(buf, '\'');
1812                         break;
1813         }
1814
1815         pfree(extval);
1816
1817         switch (constval->consttype)
1818         {
1819                 case INT4OID:
1820                 case FLOAT8OID:
1821                 case UNKNOWNOID:
1822                         /* These types can be left unlabeled */
1823                         break;
1824                 default:
1825                         extval = pstrdup(NameStr(typeStruct->typname));
1826                         appendStringInfo(buf, "::%s", quote_identifier(extval));
1827                         pfree(extval);
1828                         break;
1829         }
1830 }
1831
1832
1833 /* ----------
1834  * get_sublink_expr                     - Parse back a sublink
1835  * ----------
1836  */
1837 static void
1838 get_sublink_expr(Node *node, deparse_context *context)
1839 {
1840         StringInfo      buf = context->buf;
1841         SubLink    *sublink = (SubLink *) node;
1842         Query      *query = (Query *) (sublink->subselect);
1843         List       *l;
1844         char       *sep;
1845         Oper       *oper;
1846         bool            need_paren;
1847
1848         appendStringInfoChar(buf, '(');
1849
1850         if (sublink->lefthand != NIL)
1851         {
1852                 need_paren = (length(sublink->lefthand) > 1);
1853                 if (need_paren)
1854                         appendStringInfoChar(buf, '(');
1855
1856                 sep = "";
1857                 foreach(l, sublink->lefthand)
1858                 {
1859                         appendStringInfo(buf, sep);
1860                         sep = ", ";
1861                         get_rule_expr((Node *) lfirst(l), context);
1862                 }
1863
1864                 if (need_paren)
1865                         appendStringInfo(buf, ") ");
1866                 else
1867                         appendStringInfoChar(buf, ' ');
1868         }
1869
1870         need_paren = true;
1871
1872         switch (sublink->subLinkType)
1873         {
1874                 case EXISTS_SUBLINK:
1875                         appendStringInfo(buf, "EXISTS ");
1876                         break;
1877
1878                 case ANY_SUBLINK:
1879                         oper = (Oper *) lfirst(sublink->oper);
1880                         appendStringInfo(buf, "%s ANY ", get_opname(oper->opno));
1881                         break;
1882
1883                 case ALL_SUBLINK:
1884                         oper = (Oper *) lfirst(sublink->oper);
1885                         appendStringInfo(buf, "%s ALL ", get_opname(oper->opno));
1886                         break;
1887
1888                 case MULTIEXPR_SUBLINK:
1889                         oper = (Oper *) lfirst(sublink->oper);
1890                         appendStringInfo(buf, "%s ", get_opname(oper->opno));
1891                         break;
1892
1893                 case EXPR_SUBLINK:
1894                         need_paren = false;
1895                         break;
1896
1897                 default:
1898                         elog(ERROR, "get_sublink_expr: unsupported sublink type %d",
1899                                  sublink->subLinkType);
1900                         break;
1901         }
1902
1903         if (need_paren)
1904                 appendStringInfoChar(buf, '(');
1905
1906         get_query_def(query, buf, context->rangetables);
1907
1908         if (need_paren)
1909                 appendStringInfo(buf, "))");
1910         else
1911                 appendStringInfoChar(buf, ')');
1912 }
1913
1914
1915 /* ----------
1916  * get_from_clause                      - Parse back a FROM clause
1917  * ----------
1918  */
1919 static void
1920 get_from_clause(Query *query, deparse_context *context)
1921 {
1922         StringInfo      buf = context->buf;
1923         char       *sep;
1924         List       *l;
1925
1926         /*
1927          * We use the query's jointree as a guide to what to print.  However,
1928          * we must ignore auto-added RTEs that are marked not inFromCl.
1929          * (These can only appear at the top level of the jointree, so it's
1930          * sufficient to check here.)
1931          * Also ignore the rule pseudo-RTEs for NEW and OLD.
1932          */
1933         sep = " FROM ";
1934
1935         foreach(l, query->jointree->fromlist)
1936         {
1937                 Node   *jtnode = (Node *) lfirst(l);
1938
1939                 if (IsA(jtnode, RangeTblRef))
1940                 {
1941                         int                     varno = ((RangeTblRef *) jtnode)->rtindex;
1942                         RangeTblEntry *rte = rt_fetch(varno, query->rtable);
1943
1944                         if (!rte->inFromCl)
1945                                 continue;
1946                         if (strcmp(rte->eref->relname, "*NEW*") == 0)
1947                                 continue;
1948                         if (strcmp(rte->eref->relname, "*OLD*") == 0)
1949                                 continue;
1950                 }
1951
1952                 appendStringInfo(buf, sep);
1953                 get_from_clause_item(jtnode, query, context);
1954                 sep = ", ";
1955         }
1956 }
1957
1958 static void
1959 get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
1960 {
1961         StringInfo      buf = context->buf;
1962
1963         if (IsA(jtnode, RangeTblRef))
1964         {
1965                 int                     varno = ((RangeTblRef *) jtnode)->rtindex;
1966                 RangeTblEntry *rte = rt_fetch(varno, query->rtable);
1967
1968                 if (rte->relname)
1969                 {
1970                         /* Normal relation RTE */
1971                         appendStringInfo(buf, "%s%s",
1972                                                          only_marker(rte),
1973                                                          quote_identifier(rte->relname));
1974                 }
1975                 else
1976                 {
1977                         /* Subquery RTE */
1978                         Assert(rte->subquery != NULL);
1979                         appendStringInfoChar(buf, '(');
1980                         get_query_def(rte->subquery, buf, context->rangetables);
1981                         appendStringInfoChar(buf, ')');
1982                 }
1983                 if (rte->alias != NULL)
1984                 {
1985                         appendStringInfo(buf, " %s",
1986                                                          quote_identifier(rte->alias->relname));
1987                         if (rte->alias->attrs != NIL)
1988                         {
1989                                 List       *col;
1990
1991                                 appendStringInfo(buf, " (");
1992                                 foreach(col, rte->alias->attrs)
1993                                 {
1994                                         if (col != rte->alias->attrs)
1995                                                 appendStringInfo(buf, ", ");
1996                                         appendStringInfo(buf, "%s",
1997                                                                          quote_identifier(strVal(lfirst(col))));
1998                                 }
1999                                 appendStringInfoChar(buf, ')');
2000                         }
2001                 }
2002         }
2003         else if (IsA(jtnode, JoinExpr))
2004         {
2005                 JoinExpr   *j = (JoinExpr *) jtnode;
2006
2007                 appendStringInfoChar(buf, '(');
2008                 get_from_clause_item(j->larg, query, context);
2009                 if (j->isNatural)
2010                         appendStringInfo(buf, " NATURAL");
2011                 switch (j->jointype)
2012                 {
2013                         case JOIN_INNER:
2014                                 if (j->quals)
2015                                         appendStringInfo(buf, " JOIN ");
2016                                 else
2017                                         appendStringInfo(buf, " CROSS JOIN ");
2018                                 break;
2019                         case JOIN_LEFT:
2020                                 appendStringInfo(buf, " LEFT JOIN ");
2021                                 break;
2022                         case JOIN_FULL:
2023                                 appendStringInfo(buf, " FULL JOIN ");
2024                                 break;
2025                         case JOIN_RIGHT:
2026                                 appendStringInfo(buf, " RIGHT JOIN ");
2027                                 break;
2028                         case JOIN_UNION:
2029                                 appendStringInfo(buf, " UNION JOIN ");
2030                                 break;
2031                         default:
2032                                 elog(ERROR, "get_from_clause_item: unknown join type %d",
2033                                          (int) j->jointype);
2034                 }
2035                 get_from_clause_item(j->rarg, query, context);
2036                 if (! j->isNatural)
2037                 {
2038                         if (j->using)
2039                         {
2040                                 List       *col;
2041
2042                                 appendStringInfo(buf, " USING (");
2043                                 foreach(col, j->using)
2044                                 {
2045                                         if (col != j->using)
2046                                                 appendStringInfo(buf, ", ");
2047                                         appendStringInfo(buf, "%s",
2048                                                                          quote_identifier(strVal(lfirst(col))));
2049                                 }
2050                                 appendStringInfoChar(buf, ')');
2051                         }
2052                         else if (j->quals)
2053                         {
2054                                 appendStringInfo(buf, " ON (");
2055                                 get_rule_expr(j->quals, context);
2056                                 appendStringInfoChar(buf, ')');
2057                         }
2058                 }
2059                 appendStringInfoChar(buf, ')');
2060                 /* Yes, it's correct to put alias after the right paren ... */
2061                 if (j->alias != NULL)
2062                 {
2063                         appendStringInfo(buf, " %s",
2064                                                          quote_identifier(j->alias->relname));
2065                         if (j->alias->attrs != NIL)
2066                         {
2067                                 List       *col;
2068
2069                                 appendStringInfo(buf, " (");
2070                                 foreach(col, j->alias->attrs)
2071                                 {
2072                                         if (col != j->alias->attrs)
2073                                                 appendStringInfo(buf, ", ");
2074                                         appendStringInfo(buf, "%s",
2075                                                                          quote_identifier(strVal(lfirst(col))));
2076                                 }
2077                                 appendStringInfoChar(buf, ')');
2078                         }
2079                 }
2080         }
2081         else
2082                 elog(ERROR, "get_from_clause_item: unexpected node type %d",
2083                          nodeTag(jtnode));
2084 }
2085
2086
2087 /* ----------
2088  * tleIsArrayAssign                     - check for array assignment
2089  * ----------
2090  */
2091 static bool
2092 tleIsArrayAssign(TargetEntry *tle)
2093 {
2094         ArrayRef   *aref;
2095
2096         if (tle->expr == NULL || !IsA(tle->expr, ArrayRef))
2097                 return false;
2098         aref = (ArrayRef *) tle->expr;
2099         if (aref->refassgnexpr == NULL)
2100                 return false;
2101         /*
2102          * Currently, it should only be possible to see non-null refassgnexpr
2103          * if we are indeed looking at an "UPDATE array[n] = expr" situation.
2104          * So aref->refexpr ought to match the tle's target.
2105          */
2106         if (aref->refexpr == NULL || !IsA(aref->refexpr, Var) ||
2107                 ((Var *) aref->refexpr)->varattno != tle->resdom->resno)
2108                 elog(NOTICE, "tleIsArrayAssign: I'm confused ...");
2109         return true;
2110 }
2111
2112 /* ----------
2113  * quote_identifier                     - Quote an identifier only if needed
2114  *
2115  * When quotes are needed, we palloc the required space; slightly
2116  * space-wasteful but well worth it for notational simplicity.
2117  * ----------
2118  */
2119 static char *
2120 quote_identifier(char *ident)
2121 {
2122
2123         /*
2124          * Can avoid quoting if ident starts with a lowercase letter and
2125          * contains only lowercase letters, digits, and underscores, *and* is
2126          * not any SQL keyword.  Otherwise, supply quotes.
2127          */
2128         bool            safe;
2129         char       *result;
2130
2131         /*
2132          * would like to use <ctype.h> macros here, but they might yield
2133          * unwanted locale-specific results...
2134          */
2135         safe = (ident[0] >= 'a' && ident[0] <= 'z');
2136         if (safe)
2137         {
2138                 char       *ptr;
2139
2140                 for (ptr = ident + 1; *ptr; ptr++)
2141                 {
2142                         char            ch = *ptr;
2143
2144                         safe = ((ch >= 'a' && ch <= 'z') ||
2145                                         (ch >= '0' && ch <= '9') ||
2146                                         (ch == '_'));
2147                         if (!safe)
2148                                 break;
2149                 }
2150         }
2151
2152         if (safe)
2153         {
2154
2155                 /*
2156                  * Check for keyword.  This test is overly strong, since many of
2157                  * the "keywords" known to the parser are usable as column names,
2158                  * but the parser doesn't provide any easy way to test for whether
2159                  * an identifier is safe or not... so be safe not sorry.
2160                  *
2161                  * Note: ScanKeywordLookup() expects an all-lower-case input, but
2162                  * we've already checked we have that.
2163                  */
2164                 if (ScanKeywordLookup(ident) != NULL)
2165                         safe = false;
2166         }
2167
2168         if (safe)
2169                 return ident;                   /* no change needed */
2170
2171         result = (char *) palloc(strlen(ident) + 2 + 1);
2172         sprintf(result, "\"%s\"", ident);
2173         return result;
2174 }
2175
2176 /* ----------
2177  * get_relation_name                    - Get a relation name by Oid
2178  * ----------
2179  */
2180 static char *
2181 get_relation_name(Oid relid)
2182 {
2183         HeapTuple       classtup;
2184         Form_pg_class classStruct;
2185
2186         classtup = SearchSysCacheTuple(RELOID,
2187                                                                    ObjectIdGetDatum(relid), 0, 0, 0);
2188         if (!HeapTupleIsValid(classtup))
2189                 elog(ERROR, "cache lookup of relation %u failed", relid);
2190
2191         classStruct = (Form_pg_class) GETSTRUCT(classtup);
2192         return pstrdup(NameStr(classStruct->relname));
2193 }
2194
2195
2196 /* ----------
2197  * get_relid_attribute_name
2198  *              Get an attribute name by its relations Oid and its attnum
2199  *
2200  * Same as underlying syscache routine get_attname(), except that error
2201  * is handled by elog() instead of returning NULL.
2202  * ----------
2203  */
2204 static char *
2205 get_relid_attribute_name(Oid relid, AttrNumber attnum)
2206 {
2207         char       *attname;
2208
2209         attname = get_attname(relid, attnum);
2210         if (attname == NULL)
2211                 elog(ERROR, "cache lookup of attribute %d in relation %u failed",
2212                          attnum, relid);
2213         return attname;
2214 }