]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/ruleutils.c
Move some system includes into c.h, and remove duplicates.
[postgresql] / src / backend / utils / adt / ruleutils.c
1 /**********************************************************************
2  * get_ruledef.c        - Function to get a rules definition text
3  *                        out of it's tuple
4  *
5  * IDENTIFICATION
6  *        $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.21 1999/07/17 20:17:59 momjian 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 #include "executor/spi.h"
43 #include "optimizer/clauses.h"
44 #include "utils/lsyscache.h"
45 #include "catalog/pg_shadow.h"
46 #include "catalog/pg_index.h"
47
48 #define BUFSIZE 8192
49
50 /* ----------
51  * Local data types
52  * ----------
53  */
54 typedef struct QryHier
55 {
56         struct QryHier *parent;
57         Query      *query;
58 } QryHier;
59
60
61 /* ----------
62  * Global data
63  * ----------
64  */
65 static char *rulename;
66 static void *plan_getrule = NULL;
67 static char *query_getrule = "SELECT * FROM pg_rewrite WHERE rulename = $1";
68 static void *plan_getview = NULL;
69 static char *query_getview = "SELECT * FROM pg_rewrite WHERE rulename = $1 or rulename = $2";
70 static void *plan_getam = NULL;
71 static char *query_getam = "SELECT * FROM pg_am WHERE oid = $1";
72 static void *plan_getopclass = NULL;
73 static char *query_getopclass = "SELECT * FROM pg_opclass WHERE oid = $1";
74
75
76 /* ----------
77  * Global functions
78  * ----------
79  */
80 text       *pg_get_ruledef(NameData *rname);
81 text       *pg_get_viewdef(NameData *rname);
82 text       *pg_get_indexdef(Oid indexrelid);
83 NameData   *pg_get_userbyid(int4 uid);
84
85
86 /* ----------
87  * Local functions
88  * ----------
89  */
90 static char *make_ruledef(HeapTuple ruletup, TupleDesc rulettc);
91 static char *make_viewdef(HeapTuple ruletup, TupleDesc rulettc);
92 static char *get_query_def(Query *query, QryHier *parentqh);
93 static char *get_select_query_def(Query *query, QryHier *qh);
94 static char *get_insert_query_def(Query *query, QryHier *qh);
95 static char *get_update_query_def(Query *query, QryHier *qh);
96 static char *get_delete_query_def(Query *query, QryHier *qh);
97 static char *get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix);
98 static char *get_func_expr(QryHier *qh, int rt_index, Expr *expr, bool varprefix);
99 static char *get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix);
100 static char *get_const_expr(Const *constval);
101 static char *get_sublink_expr(QryHier *qh, int rt_index, Node *node, bool varprefix);
102 static char *get_relation_name(Oid relid);
103 static char *get_attribute_name(Oid relid, int2 attnum);
104 static bool check_if_rte_used(int rt_index, Node *node, int sup);
105
106
107 /* ----------
108  * get_ruledef                  - Do it all and return a text
109  *                                that could be used as a statement
110  *                                to recreate the rule
111  * ----------
112  */
113 text *
114 pg_get_ruledef(NameData *rname)
115 {
116         text       *ruledef;
117         Datum           args[1];
118         char            nulls[2];
119         int                     spirc;
120         HeapTuple       ruletup;
121         TupleDesc       rulettc;
122         char       *tmp;
123         int                     len;
124
125         /* ----------
126          * We need the rules name somewhere deep down
127          * ----------
128          */
129         rulename = nameout(rname);
130
131         /* ----------
132          * Connect to SPI manager
133          * ----------
134          */
135         if (SPI_connect() != SPI_OK_CONNECT)
136                 elog(ERROR, "get_ruledef: cannot connect to SPI manager");
137
138         /* ----------
139          * On the first call prepare the plan to lookup pg_proc.
140          * We read pg_proc over the SPI manager instead of using
141          * the syscache to be checked for read access on pg_proc.
142          * ----------
143          */
144         if (plan_getrule == NULL)
145         {
146                 Oid                     argtypes[1];
147                 void       *plan;
148
149                 argtypes[0] = NAMEOID;
150                 plan = SPI_prepare(query_getrule, 1, argtypes);
151                 if (plan == NULL)
152                         elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getrule);
153                 plan_getrule = SPI_saveplan(plan);
154         }
155
156         /* ----------
157          * Get the pg_rewrite tuple for this rule
158          * ----------
159          */
160         args[0] = PointerGetDatum(rulename);
161         nulls[0] = (rulename == NULL) ? 'n' : ' ';
162         nulls[1] = '\0';
163         spirc = SPI_execp(plan_getrule, args, nulls, 1);
164         if (spirc != SPI_OK_SELECT)
165                 elog(ERROR, "failed to get pg_rewrite tuple for %s", rulename);
166         if (SPI_processed != 1)
167         {
168                 if (SPI_finish() != SPI_OK_FINISH)
169                         elog(ERROR, "get_ruledef: SPI_finish() failed");
170                 ruledef = SPI_palloc(VARHDRSZ + 1);
171                 VARSIZE(ruledef) = VARHDRSZ + 1;
172                 VARDATA(ruledef)[0] = '-';
173                 return ruledef;
174         }
175
176         ruletup = SPI_tuptable->vals[0];
177         rulettc = SPI_tuptable->tupdesc;
178
179         /* ----------
180          * Get the rules definition and put it into executors memory
181          * ----------
182          */
183         tmp = make_ruledef(ruletup, rulettc);
184         len = strlen(tmp) + VARHDRSZ;
185         ruledef = SPI_palloc(len);
186         VARSIZE(ruledef) = len;
187         memcpy(VARDATA(ruledef), tmp, len - VARHDRSZ);
188
189         /* ----------
190          * Disconnect from SPI manager
191          * ----------
192          */
193         if (SPI_finish() != SPI_OK_FINISH)
194                 elog(ERROR, "get_ruledef: SPI_finish() failed");
195
196         /* ----------
197          * Easy - isn't it?
198          * ----------
199          */
200         return ruledef;
201 }
202
203
204 /* ----------
205  * get_viewdef                  - Mainly the same thing, but we
206  *                                only return the SELECT part of a view
207  * ----------
208  */
209 text *
210 pg_get_viewdef(NameData *rname)
211 {
212         text       *ruledef;
213         Datum           args[2];
214         char            nulls[3];
215         int                     spirc;
216         HeapTuple       ruletup;
217         TupleDesc       rulettc;
218         char       *tmp;
219         int                     len;
220         char            name1[NAMEDATALEN + 5];
221         char            name2[NAMEDATALEN + 5];
222
223         /* ----------
224          * We need the rules name somewhere deep down
225          * ----------
226          */
227         rulename = nameout(rname);
228
229         /* ----------
230          * Connect to SPI manager
231          * ----------
232          */
233         if (SPI_connect() != SPI_OK_CONNECT)
234                 elog(ERROR, "get_viewdef: cannot connect to SPI manager");
235
236         /* ----------
237          * On the first call prepare the plan to lookup pg_proc.
238          * We read pg_proc over the SPI manager instead of using
239          * the syscache to be checked for read access on pg_proc.
240          * ----------
241          */
242         if (plan_getview == NULL)
243         {
244                 Oid                     argtypes[2];
245                 void       *plan;
246
247                 argtypes[0] = NAMEOID;
248                 argtypes[1] = NAMEOID;
249                 plan = SPI_prepare(query_getview, 2, argtypes);
250                 if (plan == NULL)
251                         elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getview);
252                 plan_getview = SPI_saveplan(plan);
253         }
254
255         /* ----------
256          * Get the pg_rewrite tuple for this rule
257          * ----------
258          */
259         sprintf(name1, "_RET%s", rulename);
260         sprintf(name2, "_ret%s", rulename);
261         args[0] = PointerGetDatum(name1);
262         args[1] = PointerGetDatum(name2);
263         nulls[0] = ' ';
264         nulls[1] = ' ';
265         nulls[2] = '\0';
266         spirc = SPI_execp(plan_getview, args, nulls, 1);
267         if (spirc != SPI_OK_SELECT)
268                 elog(ERROR, "failed to get pg_rewrite tuple for view %s", rulename);
269         if (SPI_processed != 1)
270                 tmp = "Not a view";
271         else
272         {
273                 /* ----------
274                  * Get the rules definition and put it into executors memory
275                  * ----------
276                  */
277                 ruletup = SPI_tuptable->vals[0];
278                 rulettc = SPI_tuptable->tupdesc;
279                 tmp = make_viewdef(ruletup, rulettc);
280         }
281         len = strlen(tmp) + VARHDRSZ;
282         ruledef = SPI_palloc(len);
283         VARSIZE(ruledef) = len;
284         memcpy(VARDATA(ruledef), tmp, len - VARHDRSZ);
285
286         /* ----------
287          * Disconnect from SPI manager
288          * ----------
289          */
290         if (SPI_finish() != SPI_OK_FINISH)
291                 elog(ERROR, "get_viewdef: SPI_finish() failed");
292
293         /* ----------
294          * Easy - isn't it?
295          * ----------
296          */
297         return ruledef;
298 }
299
300
301 /* ----------
302  * get_viewdef                  - Mainly the same thing, but we
303  *                                only return the SELECT part of a view
304  * ----------
305  */
306 text *
307 pg_get_indexdef(Oid indexrelid)
308 {
309         text       *indexdef;
310         HeapTuple       ht_idx;
311         HeapTuple       ht_idxrel;
312         HeapTuple       ht_indrel;
313         HeapTuple       spi_tup;
314         TupleDesc       spi_ttc;
315         int                     spi_fno;
316         Form_pg_index idxrec;
317         Form_pg_class idxrelrec;
318         Form_pg_class indrelrec;
319         Datum           spi_args[1];
320         char            spi_nulls[2];
321         int                     spirc;
322         int                     len;
323         int                     keyno;
324         char            buf[BUFSIZE];
325         char            keybuf[BUFSIZE];
326         char       *sep;
327
328         /* ----------
329          * Connect to SPI manager
330          * ----------
331          */
332         if (SPI_connect() != SPI_OK_CONNECT)
333                 elog(ERROR, "get_indexdef: cannot connect to SPI manager");
334
335         /* ----------
336          * On the first call prepare the plans to lookup pg_am
337          * and pg_opclass.
338          * ----------
339          */
340         if (plan_getam == NULL)
341         {
342                 Oid                     argtypes[1];
343                 void       *plan;
344
345                 argtypes[0] = OIDOID;
346                 plan = SPI_prepare(query_getam, 1, argtypes);
347                 if (plan == NULL)
348                         elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getam);
349                 plan_getam = SPI_saveplan(plan);
350
351                 argtypes[0] = OIDOID;
352                 plan = SPI_prepare(query_getopclass, 1, argtypes);
353                 if (plan == NULL)
354                         elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getopclass);
355                 plan_getopclass = SPI_saveplan(plan);
356         }
357
358         /* ----------
359          * Fetch the pg_index tuple by the Oid of the index
360          * ----------
361          */
362         ht_idx = SearchSysCacheTuple(INDEXRELID,
363                                                                  ObjectIdGetDatum(indexrelid), 0, 0, 0);
364         if (!HeapTupleIsValid(ht_idx))
365                 elog(ERROR, "syscache lookup for index %u failed", indexrelid);
366         idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
367
368         /* ----------
369          * Fetch the pg_class tuple of the index relation
370          * ----------
371          */
372         ht_idxrel = SearchSysCacheTuple(RELOID,
373                                                   ObjectIdGetDatum(idxrec->indexrelid), 0, 0, 0);
374         if (!HeapTupleIsValid(ht_idxrel))
375                 elog(ERROR, "syscache lookup for relid %u failed", idxrec->indexrelid);
376         idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
377
378         /* ----------
379          * Fetch the pg_class tuple of the indexed relation
380          * ----------
381          */
382         ht_indrel = SearchSysCacheTuple(RELOID,
383                                                         ObjectIdGetDatum(idxrec->indrelid), 0, 0, 0);
384         if (!HeapTupleIsValid(ht_indrel))
385                 elog(ERROR, "syscache lookup for relid %u failed", idxrec->indrelid);
386         indrelrec = (Form_pg_class) GETSTRUCT(ht_indrel);
387
388         /* ----------
389          * Get the am name for the index relation
390          * ----------
391          */
392         spi_args[0] = ObjectIdGetDatum(idxrelrec->relam);
393         spi_nulls[0] = ' ';
394         spi_nulls[1] = '\0';
395         spirc = SPI_execp(plan_getam, spi_args, spi_nulls, 1);
396         if (spirc != SPI_OK_SELECT)
397                 elog(ERROR, "failed to get pg_am tuple for index %s", nameout(&(idxrelrec->relname)));
398         if (SPI_processed != 1)
399                 elog(ERROR, "failed to get pg_am tuple for index %s", nameout(&(idxrelrec->relname)));
400         spi_tup = SPI_tuptable->vals[0];
401         spi_ttc = SPI_tuptable->tupdesc;
402         spi_fno = SPI_fnumber(spi_ttc, "amname");
403
404         /* ----------
405          * Start the index definition
406          * ----------
407          */
408         sprintf(buf, "CREATE %sINDEX \"%s\" ON \"%s\" USING %s (",
409                         idxrec->indisunique ? "UNIQUE " : "",
410                         nameout(&(idxrelrec->relname)),
411                         nameout(&(indrelrec->relname)),
412                         SPI_getvalue(spi_tup, spi_ttc, spi_fno));
413
414         /* ----------
415          * Collect the indexed attributes
416          * ----------
417          */
418         sep = "";
419         keybuf[0] = '\0';
420         for (keyno = 0; keyno < INDEX_MAX_KEYS; keyno++)
421         {
422                 if (idxrec->indkey[keyno] == InvalidAttrNumber)
423                         break;
424
425                 strcat(keybuf, sep);
426                 sep = ", ";
427
428                 /* ----------
429                  * Add the indexed field name
430                  * ----------
431                  */
432                 strcat(keybuf, "\"");
433                 if (idxrec->indkey[keyno] == ObjectIdAttributeNumber - 1)
434                         strcat(keybuf, "oid");
435                 else
436                         strcat(keybuf, get_attribute_name(idxrec->indrelid,
437                                                                                           idxrec->indkey[keyno]));
438                 strcat(keybuf, "\"");
439
440                 /* ----------
441                  * If not a functional index, add the operator class name
442                  * ----------
443                  */
444                 if (idxrec->indproc == InvalidOid)
445                 {
446                         spi_args[0] = ObjectIdGetDatum(idxrec->indclass[keyno]);
447                         spi_nulls[0] = ' ';
448                         spi_nulls[1] = '\0';
449                         spirc = SPI_execp(plan_getopclass, spi_args, spi_nulls, 1);
450                         if (spirc != SPI_OK_SELECT)
451                                 elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[keyno]);
452                         if (SPI_processed != 1)
453                                 elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[keyno]);
454                         spi_tup = SPI_tuptable->vals[0];
455                         spi_ttc = SPI_tuptable->tupdesc;
456                         spi_fno = SPI_fnumber(spi_ttc, "opcname");
457                         strcat(keybuf, " \"");
458                         strcat(keybuf, SPI_getvalue(spi_tup, spi_ttc, spi_fno));
459                         strcat(keybuf, "\"");
460                 }
461         }
462
463         /* ----------
464          * For functional index say 'func (attrs) opclass'
465          * ----------
466          */
467         if (idxrec->indproc != InvalidOid)
468         {
469                 HeapTuple       proctup;
470                 Form_pg_proc procStruct;
471
472                 proctup = SearchSysCacheTuple(PROOID,
473                                                          ObjectIdGetDatum(idxrec->indproc), 0, 0, 0);
474                 if (!HeapTupleIsValid(proctup))
475                         elog(ERROR, "cache lookup for proc %u failed", idxrec->indproc);
476
477                 procStruct = (Form_pg_proc) GETSTRUCT(proctup);
478                 strcat(buf, "\"");
479                 strcat(buf, nameout(&(procStruct->proname)));
480                 strcat(buf, "\" (");
481                 strcat(buf, keybuf);
482                 strcat(buf, ") ");
483
484                 spi_args[0] = ObjectIdGetDatum(idxrec->indclass[0]);
485                 spi_nulls[0] = ' ';
486                 spi_nulls[1] = '\0';
487                 spirc = SPI_execp(plan_getopclass, spi_args, spi_nulls, 1);
488                 if (spirc != SPI_OK_SELECT)
489                         elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[0]);
490                 if (SPI_processed != 1)
491                         elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[0]);
492                 spi_tup = SPI_tuptable->vals[0];
493                 spi_ttc = SPI_tuptable->tupdesc;
494                 spi_fno = SPI_fnumber(spi_ttc, "opcname");
495                 strcat(buf, "\"");
496                 strcat(buf, SPI_getvalue(spi_tup, spi_ttc, spi_fno));
497                 strcat(buf, "\"");
498         }
499         else
500                 /* ----------
501                  * For the others say 'attr opclass [, ...]'
502                  * ----------
503                  */
504                 strcat(buf, keybuf);
505
506         /* ----------
507          * Finish
508          * ----------
509          */
510         strcat(buf, ")");
511
512         /* ----------
513          * Create the result in upper executor memory
514          * ----------
515          */
516         len = strlen(buf) + VARHDRSZ;
517         indexdef = SPI_palloc(len);
518         VARSIZE(indexdef) = len;
519         memcpy(VARDATA(indexdef), buf, len - VARHDRSZ);
520
521         /* ----------
522          * Disconnect from SPI manager
523          * ----------
524          */
525         if (SPI_finish() != SPI_OK_FINISH)
526                 elog(ERROR, "get_viewdef: SPI_finish() failed");
527
528         return indexdef;
529 }
530
531
532 /* ----------
533  * get_userbyid                 - Get a user name by usesysid and
534  *                                fallback to 'unknown (UID=n)'
535  * ----------
536  */
537 NameData   *
538 pg_get_userbyid(int4 uid)
539 {
540         HeapTuple       usertup;
541         Form_pg_shadow user_rec;
542         NameData   *result;
543
544         /* ----------
545          * Allocate space for the result
546          * ----------
547          */
548         result = (NameData *) palloc(NAMEDATALEN);
549         memset(result->data, 0, NAMEDATALEN);
550
551         /* ----------
552          * Get the pg_shadow entry and print the result
553          * ----------
554          */
555         usertup = SearchSysCacheTuple(USESYSID,
556                                                                   ObjectIdGetDatum(uid), 0, 0, 0);
557         if (HeapTupleIsValid(usertup))
558         {
559                 user_rec = (Form_pg_shadow) GETSTRUCT(usertup);
560                 StrNCpy(result->data, (&(user_rec->usename))->data, NAMEDATALEN);
561         }
562         else
563                 sprintf((char *) result, "unknown (UID=%d)", uid);
564
565         return result;
566 }
567
568
569 /* ----------
570  * make_ruledef                 - reconstruct the CREATE RULE command
571  *                                for a given pg_rewrite tuple
572  * ----------
573  */
574 static char *
575 make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
576 {
577         char       *buf;
578         char            ev_type;
579         Oid                     ev_class;
580         int2            ev_attr;
581         bool            is_instead;
582         char       *ev_qual;
583         char       *ev_action;
584         List       *actions = NIL;
585         int                     fno;
586         bool            isnull;
587
588         /* ----------
589          * Allocate space for the returned rule definition text
590          * ----------
591          */
592         buf = palloc(BUFSIZE);
593
594         /* ----------
595          * Get the attribute values from the rules tuple
596          * ----------
597          */
598         fno = SPI_fnumber(rulettc, "ev_type");
599         ev_type = (char) SPI_getbinval(ruletup, rulettc, fno, &isnull);
600
601         fno = SPI_fnumber(rulettc, "ev_class");
602         ev_class = (Oid) SPI_getbinval(ruletup, rulettc, fno, &isnull);
603
604         fno = SPI_fnumber(rulettc, "ev_attr");
605         ev_attr = (int2) SPI_getbinval(ruletup, rulettc, fno, &isnull);
606
607         fno = SPI_fnumber(rulettc, "is_instead");
608         is_instead = (bool) SPI_getbinval(ruletup, rulettc, fno, &isnull);
609
610         fno = SPI_fnumber(rulettc, "ev_qual");
611         ev_qual = SPI_getvalue(ruletup, rulettc, fno);
612
613         fno = SPI_fnumber(rulettc, "ev_action");
614         ev_action = SPI_getvalue(ruletup, rulettc, fno);
615         if (ev_action != NULL)
616                 actions = (List *) stringToNode(ev_action);
617
618
619         /* ----------
620          * Build the rules definition text
621          * ----------
622          */
623         strcpy(buf, "CREATE RULE \"");
624
625         /* The rule name */
626         strcat(buf, rulename);
627         strcat(buf, "\" AS ON ");
628
629         /* The event the rule is fired for */
630         switch (ev_type)
631         {
632                 case '1':
633                         strcat(buf, "SELECT TO \"");
634                         break;
635
636                 case '2':
637                         strcat(buf, "UPDATE TO \"");
638                         break;
639
640                 case '3':
641                         strcat(buf, "INSERT TO \"");
642                         break;
643
644                 case '4':
645                         strcat(buf, "DELETE TO \"");
646                         break;
647
648                 default:
649                         elog(ERROR, "get_ruledef: rule %s has unsupported event type %d",
650                                  rulename, ev_type);
651                         break;
652         }
653
654         /* The relation the rule is fired on */
655         strcat(buf, get_relation_name(ev_class));
656         strcat(buf, "\"");
657         if (ev_attr > 0)
658         {
659                 strcat(buf, ".\"");
660                 strcat(buf, get_attribute_name(ev_class, ev_attr));
661                 strcat(buf, "\"");
662         }
663
664         /* If the rule has an event qualification, add it */
665         if (ev_qual == NULL)
666                 ev_qual = "";
667         if (strlen(ev_qual) > 0 && strcmp(ev_qual, "<>") != 0)
668         {
669                 Node       *qual;
670                 Query      *query;
671                 QryHier         qh;
672
673                 qual = stringToNode(ev_qual);
674                 query = (Query *) lfirst(actions);
675                 qh.parent = NULL;
676                 qh.query = query;
677
678                 strcat(buf, " WHERE ");
679                 strcat(buf, get_rule_expr(&qh, 0, qual, TRUE));
680         }
681
682         strcat(buf, " DO ");
683
684         /* The INSTEAD keyword (if so) */
685         if (is_instead)
686                 strcat(buf, "INSTEAD ");
687
688         /* Finally the rules actions */
689         if (length(actions) > 1)
690         {
691                 List       *action;
692                 Query      *query;
693
694                 strcat(buf, "(");
695                 foreach(action, actions)
696                 {
697                         query = (Query *) lfirst(action);
698                         strcat(buf, get_query_def(query, NULL));
699                         strcat(buf, "; ");
700                 }
701                 strcat(buf, ");");
702         }
703         else
704         {
705                 if (length(actions) == 0)
706                 {
707                         strcat(buf, "NOTHING;");
708                 }
709                 else
710                 {
711                         Query      *query;
712
713                         query = (Query *) lfirst(actions);
714                         strcat(buf, get_query_def(query, NULL));
715                         strcat(buf, ";");
716                 }
717         }
718
719         /* ----------
720          * That's it
721          * ----------
722          */
723         return buf;
724 }
725
726
727 /* ----------
728  * make_viewdef                 - reconstruct the SELECT part of a
729  *                                view rewrite rule
730  * ----------
731  */
732 static char *
733 make_viewdef(HeapTuple ruletup, TupleDesc rulettc)
734 {
735         char            buf[BUFSIZE];
736         Query      *query;
737         char            ev_type;
738         Oid                     ev_class;
739         int2            ev_attr;
740         bool            is_instead;
741         char       *ev_qual;
742         char       *ev_action;
743         List       *actions = NIL;
744         int                     fno;
745         bool            isnull;
746
747         /* ----------
748          * Get the attribute values from the rules tuple
749          * ----------
750          */
751         fno = SPI_fnumber(rulettc, "ev_type");
752         ev_type = (char) SPI_getbinval(ruletup, rulettc, fno, &isnull);
753
754         fno = SPI_fnumber(rulettc, "ev_class");
755         ev_class = (Oid) SPI_getbinval(ruletup, rulettc, fno, &isnull);
756
757         fno = SPI_fnumber(rulettc, "ev_attr");
758         ev_attr = (int2) SPI_getbinval(ruletup, rulettc, fno, &isnull);
759
760         fno = SPI_fnumber(rulettc, "is_instead");
761         is_instead = (bool) SPI_getbinval(ruletup, rulettc, fno, &isnull);
762
763         fno = SPI_fnumber(rulettc, "ev_qual");
764         ev_qual = SPI_getvalue(ruletup, rulettc, fno);
765
766         fno = SPI_fnumber(rulettc, "ev_action");
767         ev_action = SPI_getvalue(ruletup, rulettc, fno);
768         if (ev_action != NULL)
769                 actions = (List *) stringToNode(ev_action);
770
771         if (length(actions) != 1)
772                 return "Not a view";
773
774         query = (Query *) lfirst(actions);
775
776         if (ev_type != '1' || ev_attr >= 0 || !is_instead || strcmp(ev_qual, "<>"))
777                 return "Not a view";
778
779         strcpy(buf, get_query_def(query, NULL));
780         strcat(buf, ";");
781
782         /* ----------
783          * That's it
784          * ----------
785          */
786         return pstrdup(buf);
787 }
788
789
790 /* ----------
791  * get_query_def                        - Parse back one action from
792  *                                        the parsetree in the actions
793  *                                        list
794  * ----------
795  */
796 static char *
797 get_query_def(Query *query, QryHier *parentqh)
798 {
799         QryHier         qh;
800
801         qh.parent = parentqh;
802         qh.query = query;
803
804         switch (query->commandType)
805         {
806                 case CMD_SELECT:
807                         return get_select_query_def(query, &qh);
808                         break;
809
810                 case CMD_UPDATE:
811                         return get_update_query_def(query, &qh);
812                         break;
813
814                 case CMD_INSERT:
815                         return get_insert_query_def(query, &qh);
816                         break;
817
818                 case CMD_DELETE:
819                         return get_delete_query_def(query, &qh);
820                         break;
821
822                 case CMD_NOTHING:
823                         return "NOTHING";
824                         break;
825
826                 default:
827                         elog(ERROR, "get_ruledef of %s: query command type %d not implemented yet",
828                                  rulename, query->commandType);
829                         break;
830         }
831
832         return NULL;
833 }
834
835
836 /* ----------
837  * get_select_query_def                 - Parse back a SELECT parsetree
838  * ----------
839  */
840 static char *
841 get_select_query_def(Query *query, QryHier *qh)
842 {
843         char            buf[BUFSIZE];
844         char       *sep;
845         TargetEntry *tle;
846         RangeTblEntry *rte;
847         bool       *rt_used;
848         int                     rt_length;
849         int                     rt_numused = 0;
850         bool            rt_constonly = TRUE;
851         int                     i;
852         List       *l;
853
854         /* ----------
855          * First we need need to know which and how many of the
856          * range table entries in the query are used in the target list
857          * or queries qualification
858          * ----------
859          */
860         rt_length = length(query->rtable);
861         rt_used = palloc(sizeof(bool) * rt_length);
862         for (i = 0; i < rt_length; i++)
863         {
864                 if (check_if_rte_used(i + 1, (Node *) (query->targetList), 0))
865                 {
866                         rt_used[i] = TRUE;
867                         rt_numused++;
868                 }
869                 else
870                 {
871                         if (check_if_rte_used(i + 1, (Node *) (query->qual), 0))
872                         {
873                                 rt_used[i] = TRUE;
874                                 rt_numused++;
875                         }
876                         else
877                                 rt_used[i] = FALSE;
878                 }
879         }
880
881         /* ----------
882          * Now check if any of the used rangetable entries is different
883          * from *NEW* and *CURRENT*. If so we must omit the FROM clause
884          * later.
885          * ----------
886          */
887         i = 0;
888         foreach(l, query->rtable)
889         {
890                 if (!rt_used[i++])
891                         continue;
892
893                 rte = (RangeTblEntry *) lfirst(l);
894                 if (!strcmp(rte->refname, "*NEW*"))
895                         continue;
896                 if (!strcmp(rte->refname, "*CURRENT*"))
897                         continue;
898
899                 rt_constonly = FALSE;
900                 break;
901         }
902
903         /* ----------
904          * Build up the query string - first we say SELECT
905          * ----------
906          */
907         strcpy(buf, "SELECT");
908
909         /* Then we tell what to select (the targetlist) */
910         sep = " ";
911         foreach(l, query->targetList)
912         {
913                 bool            tell_as = FALSE;
914
915                 tle = (TargetEntry *) lfirst(l);
916                 strcat(buf, sep);
917                 sep = ", ";
918
919                 strcat(buf, get_tle_expr(qh, 0, tle, (rt_numused > 1)));
920
921                 /* Check if we must say AS ... */
922                 if (nodeTag(tle->expr) != T_Var)
923                         tell_as = strcmp(tle->resdom->resname, "?column?");
924                 else
925                 {
926                         Var                *var = (Var *) (tle->expr);
927                         char       *attname;
928
929                         rte = (RangeTblEntry *) nth(var->varno - 1, query->rtable);
930                         attname = get_attribute_name(rte->relid, var->varattno);
931                         if (strcmp(attname, tle->resdom->resname))
932                                 tell_as = TRUE;
933                 }
934
935                 /* and do if so */
936                 if (tell_as)
937                 {
938                         strcat(buf, " AS \"");
939                         strcat(buf, tle->resdom->resname);
940                         strcat(buf, "\"");
941                 }
942         }
943
944         /* If we need other tables that *NEW* or *CURRENT* add the FROM clause */
945         if (!rt_constonly && rt_numused > 0)
946         {
947                 strcat(buf, " FROM");
948
949                 i = 0;
950                 sep = " ";
951                 foreach(l, query->rtable)
952                 {
953                         if (rt_used[i++])
954                         {
955                                 rte = (RangeTblEntry *) lfirst(l);
956
957                                 if (!strcmp(rte->refname, "*NEW*"))
958                                         continue;
959
960                                 if (!strcmp(rte->refname, "*CURRENT*"))
961                                         continue;
962
963                                 strcat(buf, sep);
964                                 sep = ", ";
965                                 strcat(buf, "\"");
966                                 strcat(buf, rte->relname);
967                                 strcat(buf, "\"");
968                                 if (strcmp(rte->relname, rte->refname) != 0)
969                                 {
970                                         strcat(buf, " \"");
971                                         strcat(buf, rte->refname);
972                                         strcat(buf, "\"");
973                                 }
974                         }
975                 }
976         }
977
978         /* Add the WHERE clause if given */
979         if (query->qual != NULL)
980         {
981                 strcat(buf, " WHERE ");
982                 strcat(buf, get_rule_expr(qh, 0, query->qual, (rt_numused > 1)));
983         }
984
985         /* Add the GROUP BY CLAUSE */
986         if (query->groupClause != NULL)
987         {
988                 strcat(buf, " GROUP BY ");
989                 sep = "";
990                 foreach(l, query->groupClause)
991                 {
992                         strcat(buf, sep);
993                         sep = ", ";
994                         strcat(buf, get_rule_expr(qh, 0, lfirst(l), (rt_numused > 1)));
995                 }
996         }
997
998         /* ----------
999          * Copy the query string into allocated space and return it
1000          * ----------
1001          */
1002         return pstrdup(buf);
1003 }
1004
1005
1006 /* ----------
1007  * get_insert_query_def                 - Parse back an INSERT parsetree
1008  * ----------
1009  */
1010 static char *
1011 get_insert_query_def(Query *query, QryHier *qh)
1012 {
1013         char            buf[BUFSIZE];
1014         char       *sep;
1015         TargetEntry *tle;
1016         RangeTblEntry *rte;
1017         bool       *rt_used;
1018         int                     rt_length;
1019         int                     rt_numused = 0;
1020         bool            rt_constonly = TRUE;
1021         int                     i;
1022         List       *l;
1023
1024         /* ----------
1025          * We need to know if other tables than *NEW* or *CURRENT*
1026          * are used in the query. If not, it's an INSERT ... VALUES,
1027          * otherwise an INSERT ... SELECT.
1028          * ----------
1029          */
1030         rt_length = length(query->rtable);
1031         rt_used = palloc(sizeof(bool) * rt_length);
1032         for (i = 0; i < rt_length; i++)
1033         {
1034                 if (check_if_rte_used(i + 1, (Node *) (query->targetList), 0))
1035                 {
1036                         rt_used[i] = TRUE;
1037                         rt_numused++;
1038                 }
1039                 else
1040                 {
1041                         if (check_if_rte_used(i + 1, (Node *) (query->qual), 0))
1042                         {
1043                                 rt_used[i] = TRUE;
1044                                 rt_numused++;
1045                         }
1046                         else
1047                                 rt_used[i] = FALSE;
1048                 }
1049         }
1050
1051         i = 0;
1052         foreach(l, query->rtable)
1053         {
1054                 if (!rt_used[i++])
1055                         continue;
1056
1057                 rte = (RangeTblEntry *) lfirst(l);
1058                 if (!strcmp(rte->refname, "*NEW*"))
1059                         continue;
1060                 if (!strcmp(rte->refname, "*CURRENT*"))
1061                         continue;
1062
1063                 rt_constonly = FALSE;
1064                 break;
1065         }
1066
1067         /* ----------
1068          * Start the query with INSERT INTO relname
1069          * ----------
1070          */
1071         rte = (RangeTblEntry *) nth(query->resultRelation - 1, query->rtable);
1072         strcpy(buf, "INSERT INTO \"");
1073         strcat(buf, rte->relname);
1074         strcat(buf, "\"");
1075
1076         /* Add the target list */
1077         sep = " (";
1078         foreach(l, query->targetList)
1079         {
1080                 tle = (TargetEntry *) lfirst(l);
1081
1082                 strcat(buf, sep);
1083                 sep = ", ";
1084                 strcat(buf, "\"");
1085                 strcat(buf, tle->resdom->resname);
1086                 strcat(buf, "\"");
1087         }
1088         strcat(buf, ") ");
1089
1090         /* Add the VALUES or the SELECT */
1091         if (rt_constonly && query->qual == NULL)
1092         {
1093                 strcat(buf, "VALUES (");
1094                 sep = "";
1095                 foreach(l, query->targetList)
1096                 {
1097                         tle = (TargetEntry *) lfirst(l);
1098
1099                         strcat(buf, sep);
1100                         sep = ", ";
1101                         strcat(buf, get_tle_expr(qh, 0, tle, (rt_numused > 1)));
1102                 }
1103                 strcat(buf, ")");
1104         }
1105         else
1106                 strcat(buf, get_select_query_def(query, qh));
1107
1108         /* ----------
1109          * Copy the query string into allocated space and return it
1110          * ----------
1111          */
1112         return pstrdup(buf);
1113 }
1114
1115
1116 /* ----------
1117  * get_update_query_def                 - Parse back an UPDATE parsetree
1118  * ----------
1119  */
1120 static char *
1121 get_update_query_def(Query *query, QryHier *qh)
1122 {
1123         char            buf[BUFSIZE];
1124         char       *sep;
1125         TargetEntry *tle;
1126         RangeTblEntry *rte;
1127         List       *l;
1128
1129         /* ----------
1130          * Start the query with UPDATE relname SET
1131          * ----------
1132          */
1133         rte = (RangeTblEntry *) nth(query->resultRelation - 1, query->rtable);
1134         strcpy(buf, "UPDATE ");
1135         strcat(buf, rte->relname);
1136         strcat(buf, " SET ");
1137
1138         /* Add the comma separated list of 'attname = value' */
1139         sep = "";
1140         foreach(l, query->targetList)
1141         {
1142                 tle = (TargetEntry *) lfirst(l);
1143
1144                 strcat(buf, sep);
1145                 sep = ", ";
1146                 strcat(buf, "\"");
1147                 strcat(buf, tle->resdom->resname);
1148                 strcat(buf, "\" = ");
1149                 strcat(buf, get_tle_expr(qh, query->resultRelation,
1150                                                                  tle, TRUE));
1151         }
1152
1153         /* Finally add a WHERE clause if given */
1154         if (query->qual != NULL)
1155         {
1156                 strcat(buf, " WHERE ");
1157                 strcat(buf, get_rule_expr(qh, query->resultRelation,
1158                                                                   query->qual, TRUE));
1159         }
1160
1161         /* ----------
1162          * Copy the query string into allocated space and return it
1163          * ----------
1164          */
1165         return pstrdup(buf);
1166 }
1167
1168
1169 /* ----------
1170  * get_delete_query_def                 - Parse back a DELETE parsetree
1171  * ----------
1172  */
1173 static char *
1174 get_delete_query_def(Query *query, QryHier *qh)
1175 {
1176         char            buf[BUFSIZE];
1177         RangeTblEntry *rte;
1178
1179         /* ----------
1180          * Start the query with DELETE FROM relname
1181          * ----------
1182          */
1183         rte = (RangeTblEntry *) nth(query->resultRelation - 1, query->rtable);
1184         strcpy(buf, "DELETE FROM \"");
1185         strcat(buf, rte->relname);
1186         strcat(buf, "\"");
1187
1188         /* Add a WHERE clause if given */
1189         if (query->qual != NULL)
1190         {
1191                 strcat(buf, " WHERE ");
1192                 strcat(buf, get_rule_expr(qh, 0, query->qual, FALSE));
1193         }
1194
1195         /* ----------
1196          * Copy the query string into allocated space and return it
1197          * ----------
1198          */
1199         return pstrdup(buf);
1200 }
1201
1202
1203 /* ----------
1204  * get_rule_expr                        - Parse back an expression
1205  * ----------
1206  */
1207 static char *
1208 get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
1209 {
1210         char            buf[BUFSIZE];
1211
1212         if (node == NULL)
1213                 return pstrdup("");
1214
1215         buf[0] = '\0';
1216
1217         /* ----------
1218          * Up to now I don't know if all the node types below
1219          * can really occur in rules actions and qualifications.
1220          * There might be some work left.
1221          * ----------
1222          */
1223         switch (nodeTag(node))
1224         {
1225                 case T_TargetEntry:
1226                         {
1227                                 TargetEntry *tle = (TargetEntry *) node;
1228
1229                                 return get_rule_expr(qh, rt_index,
1230                                                                          (Node *) (tle->expr), varprefix);
1231                         }
1232                         break;
1233
1234                 case T_Aggref:
1235                         {
1236                                 Aggref     *aggref = (Aggref *) node;
1237
1238                                 strcat(buf, "\"");
1239                                 strcat(buf, aggref->aggname);
1240                                 strcat(buf, "\"(");
1241                                 strcat(buf, get_rule_expr(qh, rt_index,
1242                                                                   (Node *) (aggref->target), varprefix));
1243                                 strcat(buf, ")");
1244                                 return pstrdup(buf);
1245                         }
1246                         break;
1247
1248                 case T_GroupClause:
1249                         {
1250                                 GroupClause *grp = (GroupClause *) node;
1251                                 List       *l;
1252                                 TargetEntry *tle = NULL;
1253
1254                                 foreach(l, qh->query->targetList)
1255                                 {
1256                                         if (((TargetEntry *) lfirst(l))->resdom->resgroupref ==
1257                                                 grp->tleGroupref)
1258                                         {
1259                                                 tle = (TargetEntry *) lfirst(l);
1260                                                 break;
1261                                         }
1262                                 }
1263
1264                                 if (tle == NULL)
1265                                         elog(ERROR, "GROUP BY expression not found in targetlist");
1266
1267                                 return get_rule_expr(qh, rt_index, (Node *) tle, varprefix);
1268                         }
1269                         break;
1270
1271                 case T_Expr:
1272                         {
1273                                 Expr       *expr = (Expr *) node;
1274
1275                                 /* ----------
1276                                  * Expr nodes have to be handled a bit detailed
1277                                  * ----------
1278                                  */
1279                                 switch (expr->opType)
1280                                 {
1281                                         case OP_EXPR:
1282                                                 strcat(buf, get_rule_expr(qh, rt_index,
1283                                                                                            (Node *) get_leftop(expr),
1284                                                                                                   varprefix));
1285                                                 strcat(buf, " ");
1286                                                 strcat(buf, get_opname(((Oper *) expr->oper)->opno));
1287                                                 strcat(buf, " ");
1288                                                 strcat(buf, get_rule_expr(qh, rt_index,
1289                                                                                           (Node *) get_rightop(expr),
1290                                                                                                   varprefix));
1291                                                 return pstrdup(buf);
1292                                                 break;
1293
1294                                         case OR_EXPR:
1295                                                 strcat(buf, "(");
1296                                                 strcat(buf, get_rule_expr(qh, rt_index,
1297                                                                                            (Node *) get_leftop(expr),
1298                                                                                                   varprefix));
1299                                                 strcat(buf, ") OR (");
1300                                                 strcat(buf, get_rule_expr(qh, rt_index,
1301                                                                                           (Node *) get_rightop(expr),
1302                                                                                                   varprefix));
1303                                                 strcat(buf, ")");
1304                                                 return pstrdup(buf);
1305                                                 break;
1306
1307                                         case AND_EXPR:
1308                                                 strcat(buf, "(");
1309                                                 strcat(buf, get_rule_expr(qh, rt_index,
1310                                                                                            (Node *) get_leftop(expr),
1311                                                                                                   varprefix));
1312                                                 strcat(buf, ") AND (");
1313                                                 strcat(buf, get_rule_expr(qh, rt_index,
1314                                                                                           (Node *) get_rightop(expr),
1315                                                                                                   varprefix));
1316                                                 strcat(buf, ")");
1317                                                 return pstrdup(buf);
1318                                                 break;
1319
1320                                         case NOT_EXPR:
1321                                                 strcat(buf, "NOT (");
1322                                                 strcat(buf, get_rule_expr(qh, rt_index,
1323                                                                                            (Node *) get_leftop(expr),
1324                                                                                                   varprefix));
1325                                                 strcat(buf, ")");
1326                                                 return pstrdup(buf);
1327                                                 break;
1328
1329                                         case FUNC_EXPR:
1330                                                 return get_func_expr(qh, rt_index,
1331                                                                                          (Expr *) node,
1332                                                                                          varprefix);
1333                                                 break;
1334
1335                                         default:
1336                                                 printf("\n%s\n", nodeToString(node));
1337                                                 elog(ERROR, "Expr not yet supported");
1338                                 }
1339                         }
1340                         break;
1341
1342                 case T_Var:
1343                         {
1344                                 Var                *var = (Var *) node;
1345                                 RangeTblEntry *rte;
1346                                 int                     sup = var->varlevelsup;
1347
1348                                 while (sup-- > 0)
1349                                         qh = qh->parent;
1350
1351                                 rte = (RangeTblEntry *) nth(var->varno - 1, qh->query->rtable);
1352
1353                                 if (!strcmp(rte->refname, "*NEW*"))
1354                                         strcat(buf, "new.");
1355                                 else
1356                                 {
1357                                         if (!strcmp(rte->refname, "*CURRENT*"))
1358                                                 strcat(buf, "old.");
1359                                         else
1360                                         {
1361                                                 if (strcmp(rte->relname, rte->refname) != 0)
1362                                                 {
1363                                                         strcat(buf, "\"");
1364                                                         strcat(buf, rte->refname);
1365                                                         strcat(buf, "\".");
1366                                                 }
1367                                         }
1368                                 }
1369                                 strcat(buf, "\"");
1370                                 strcat(buf, get_attribute_name(rte->relid, var->varattno));
1371                                 strcat(buf, "\"");
1372
1373                                 return pstrdup(buf);
1374                         }
1375                         break;
1376
1377                 case T_List:
1378                         {
1379                                 printf("\n%s\n", nodeToString(node));
1380                                 elog(ERROR, "List not yet supported");
1381                         }
1382                         break;
1383
1384                 case T_SubLink:
1385                         return get_sublink_expr(qh, rt_index, node, varprefix);
1386                         break;
1387
1388                 case T_Const:
1389                         return get_const_expr((Const *) node);
1390                         break;
1391
1392                 default:
1393                         printf("\n%s\n", nodeToString(node));
1394                         elog(ERROR, "get_ruledef of %s: unknown node type %d get_rule_expr()",
1395                                  rulename, nodeTag(node));
1396                         break;
1397         }
1398
1399         return FALSE;
1400 }
1401
1402
1403 /* ----------
1404  * get_func_expr                        - Parse back a Func node
1405  * ----------
1406  */
1407 static char *
1408 get_func_expr(QryHier *qh, int rt_index, Expr *expr, bool varprefix)
1409 {
1410         char            buf[BUFSIZE];
1411         HeapTuple       proctup;
1412         Form_pg_proc procStruct;
1413         List       *l;
1414         char       *sep;
1415         Func       *func = (Func *) (expr->oper);
1416         char       *proname;
1417
1418         /* ----------
1419          * Get the functions pg_proc tuple
1420          * ----------
1421          */
1422         proctup = SearchSysCacheTuple(PROOID,
1423                                                                 ObjectIdGetDatum(func->funcid), 0, 0, 0);
1424         if (!HeapTupleIsValid(proctup))
1425                 elog(ERROR, "cache lookup for proc %u failed", func->funcid);
1426
1427         procStruct = (Form_pg_proc) GETSTRUCT(proctup);
1428         proname = nameout(&(procStruct->proname));
1429
1430         if (procStruct->pronargs == 1 && procStruct->proargtypes[0] == InvalidOid)
1431         {
1432                 if (!strcmp(proname, "nullvalue"))
1433                 {
1434                         strcpy(buf, "(");
1435                         strcat(buf, get_rule_expr(qh, rt_index, lfirst(expr->args),
1436                                                                           varprefix));
1437                         strcat(buf, ") ISNULL");
1438                         return pstrdup(buf);
1439                 }
1440                 if (!strcmp(proname, "nonnullvalue"))
1441                 {
1442                         strcpy(buf, "(");
1443                         strcat(buf, get_rule_expr(qh, rt_index, lfirst(expr->args),
1444                                                                           varprefix));
1445                         strcat(buf, ") NOTNULL");
1446                         return pstrdup(buf);
1447                 }
1448         }
1449
1450         /* ----------
1451          * Build a string of proname(args)
1452          * ----------
1453          */
1454         strcpy(buf, "\"");
1455         strcat(buf, proname);
1456         strcat(buf, "\"(");
1457         sep = "";
1458         foreach(l, expr->args)
1459         {
1460                 strcat(buf, sep);
1461                 sep = ", ";
1462                 strcat(buf, get_rule_expr(qh, rt_index, lfirst(l), varprefix));
1463         }
1464         strcat(buf, ")");
1465
1466         /* ----------
1467          * Copy the function call string into allocated space and return it
1468          * ----------
1469          */
1470         return pstrdup(buf);
1471 }
1472
1473
1474 /* ----------
1475  * get_tle_expr                         - A target list expression is a bit
1476  *                                        different from a normal expression.
1477  *                                        If the target column has an
1478  *                                        an atttypmod, the parser usually
1479  *                                        puts a padding-/cut-function call
1480  *                                        around the expression itself. We
1481  *                                        we must get rid of it, otherwise
1482  *                                        dump/reload/dump... would blow up
1483  *                                        the expressions.
1484  * ----------
1485  */
1486 static char *
1487 get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix)
1488 {
1489         HeapTuple       proctup;
1490         Form_pg_proc procStruct;
1491         Expr       *expr;
1492         Func       *func;
1493         Const      *second_arg;
1494
1495         /* ----------
1496          * Check if the result has an atttypmod and if the
1497          * expression in the targetlist entry is a function call
1498          * ----------
1499          */
1500         if (tle->resdom->restypmod < 0)
1501                 return get_rule_expr(qh, rt_index, tle->expr, varprefix);
1502         if (nodeTag(tle->expr) != T_Expr)
1503                 return get_rule_expr(qh, rt_index, tle->expr, varprefix);
1504         expr = (Expr *) (tle->expr);
1505         if (expr->opType != FUNC_EXPR)
1506                 return get_rule_expr(qh, rt_index, tle->expr, varprefix);
1507
1508         func = (Func *) (expr->oper);
1509
1510         /* ----------
1511          * Get the functions pg_proc tuple
1512          * ----------
1513          */
1514         proctup = SearchSysCacheTuple(PROOID,
1515                                                                 ObjectIdGetDatum(func->funcid), 0, 0, 0);
1516         if (!HeapTupleIsValid(proctup))
1517                 elog(ERROR, "cache lookup for proc %u failed", func->funcid);
1518
1519         procStruct = (Form_pg_proc) GETSTRUCT(proctup);
1520
1521         /* ----------
1522          * It must be a function with two arguments where the first
1523          * is of the same type as the return value and the second is
1524          * an int4.
1525          * ----------
1526          */
1527         if (procStruct->pronargs != 2)
1528                 return get_rule_expr(qh, rt_index, tle->expr, varprefix);
1529         if (procStruct->prorettype != procStruct->proargtypes[0])
1530                 return get_rule_expr(qh, rt_index, tle->expr, varprefix);
1531         if (procStruct->proargtypes[1] != INT4OID)
1532                 return get_rule_expr(qh, rt_index, tle->expr, varprefix);
1533
1534         /* ----------
1535          * Finally (to be totally safe) the second argument must be a
1536          * const and match the value in the results atttypmod.
1537          * ----------
1538          */
1539         second_arg = (Const *) nth(1, expr->args);
1540         if (nodeTag((Node *) second_arg) != T_Const)
1541                 return get_rule_expr(qh, rt_index, tle->expr, varprefix);
1542         if ((int4) (second_arg->constvalue) != tle->resdom->restypmod)
1543                 return get_rule_expr(qh, rt_index, tle->expr, varprefix);
1544
1545         /* ----------
1546          * Whow - got it. Now get rid of the padding function
1547          * ----------
1548          */
1549         return get_rule_expr(qh, rt_index, lfirst(expr->args), varprefix);
1550 }
1551
1552
1553 /* ----------
1554  * get_const_expr                       - Make a string representation
1555  *                                        with the type cast out of a Const
1556  * ----------
1557  */
1558 static char *
1559 get_const_expr(Const *constval)
1560 {
1561         HeapTuple       typetup;
1562         Form_pg_type typeStruct;
1563         FmgrInfo        finfo_output;
1564         char       *extval;
1565         bool            isnull = FALSE;
1566         char            buf[BUFSIZE];
1567         char            namebuf[64];
1568
1569         if (constval->constisnull)
1570                 return "NULL";
1571
1572         typetup = SearchSysCacheTuple(TYPOID,
1573                                                  ObjectIdGetDatum(constval->consttype), 0, 0, 0);
1574         if (!HeapTupleIsValid(typetup))
1575                 elog(ERROR, "cache lookup of type %d failed", constval->consttype);
1576
1577         typeStruct = (Form_pg_type) GETSTRUCT(typetup);
1578
1579         fmgr_info(typeStruct->typoutput, &finfo_output);
1580         extval = (char *) (*fmgr_faddr(&finfo_output)) (constval->constvalue,
1581                                                                                                         &isnull, -1);
1582
1583         sprintf(namebuf, "::\"%s\"", nameout(&(typeStruct->typname)));
1584         if (strcmp(namebuf, "::unknown") == 0)
1585                 namebuf[0] = '\0';
1586         sprintf(buf, "'%s'%s", extval, namebuf);
1587         return pstrdup(buf);
1588 }
1589
1590
1591 /* ----------
1592  * get_sublink_expr                     - Parse back a sublink
1593  * ----------
1594  */
1595 static char *
1596 get_sublink_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
1597 {
1598         SubLink    *sublink = (SubLink *) node;
1599         Query      *query = (Query *) (sublink->subselect);
1600         Expr       *expr;
1601         List       *l;
1602         char       *sep;
1603         char            buf[BUFSIZE];
1604
1605         buf[0] = '\0';
1606
1607         if (sublink->lefthand != NULL)
1608         {
1609                 if (length(sublink->lefthand) > 1)
1610                         strcat(buf, "(");
1611
1612                 sep = "";
1613                 foreach(l, sublink->lefthand)
1614                 {
1615                         strcat(buf, sep);
1616                         sep = ", ";
1617                         strcat(buf, get_rule_expr(qh, rt_index,
1618                                                                           lfirst(l), varprefix));
1619                 }
1620
1621                 if (length(sublink->lefthand) > 1)
1622                         strcat(buf, ") ");
1623                 else
1624                         strcat(buf, " ");
1625         }
1626
1627         switch (sublink->subLinkType)
1628         {
1629                 case EXISTS_SUBLINK:
1630                         strcat(buf, "EXISTS ");
1631                         break;
1632
1633                 case ANY_SUBLINK:
1634                         expr = (Expr *) lfirst(sublink->oper);
1635                         strcat(buf, get_opname(((Oper *) (expr->oper))->opno));
1636                         strcat(buf, " ANY ");
1637                         break;
1638
1639                 case ALL_SUBLINK:
1640                         expr = (Expr *) lfirst(sublink->oper);
1641                         strcat(buf, get_opname(((Oper *) (expr->oper))->opno));
1642                         strcat(buf, " ALL ");
1643                         break;
1644
1645                 case EXPR_SUBLINK:
1646                         expr = (Expr *) lfirst(sublink->oper);
1647                         strcat(buf, get_opname(((Oper *) (expr->oper))->opno));
1648                         strcat(buf, " ");
1649                         break;
1650
1651                 default:
1652                         elog(ERROR, "unupported sublink type %d",
1653                                  sublink->subLinkType);
1654                         break;
1655         }
1656
1657         strcat(buf, "(");
1658         strcat(buf, get_query_def(query, qh));
1659         strcat(buf, ")");
1660
1661         return pstrdup(buf);
1662 }
1663
1664
1665 /* ----------
1666  * get_relation_name                    - Get a relation name by Oid
1667  * ----------
1668  */
1669 static char *
1670 get_relation_name(Oid relid)
1671 {
1672         HeapTuple       classtup;
1673         Form_pg_class classStruct;
1674
1675         classtup = SearchSysCacheTuple(RELOID,
1676                                                                    ObjectIdGetDatum(relid), 0, 0, 0);
1677         if (!HeapTupleIsValid(classtup))
1678                 elog(ERROR, "cache lookup of relation %u failed", relid);
1679
1680         classStruct = (Form_pg_class) GETSTRUCT(classtup);
1681         return nameout(&(classStruct->relname));
1682 }
1683
1684
1685 /* ----------
1686  * get_attribute_name                   - Get an attribute name by it's
1687  *                                        relations Oid and it's attnum
1688  * ----------
1689  */
1690 static char *
1691 get_attribute_name(Oid relid, int2 attnum)
1692 {
1693         HeapTuple       atttup;
1694         Form_pg_attribute attStruct;
1695
1696         atttup = SearchSysCacheTuple(ATTNUM,
1697                                                   ObjectIdGetDatum(relid), (Datum) attnum, 0, 0);
1698         if (!HeapTupleIsValid(atttup))
1699                 elog(ERROR, "cache lookup of attribute %d in relation %u failed",
1700                          attnum, relid);
1701
1702         attStruct = (Form_pg_attribute) GETSTRUCT(atttup);
1703         return nameout(&(attStruct->attname));
1704 }
1705
1706
1707 /* ----------
1708  * check_if_rte_used                    - Check a targetlist or qual
1709  *                                        if a given rangetable entry
1710  *                                        is used in it
1711  * ----------
1712  */
1713 static bool
1714 check_if_rte_used(int rt_index, Node *node, int sup)
1715 {
1716         if (node == NULL)
1717                 return FALSE;
1718
1719         switch (nodeTag(node))
1720         {
1721                 case T_TargetEntry:
1722                         {
1723                                 TargetEntry *tle = (TargetEntry *) node;
1724
1725                                 return check_if_rte_used(rt_index,
1726                                                                                  (Node *) (tle->expr), sup);
1727                         }
1728                         break;
1729
1730                 case T_Aggref:
1731                         {
1732                                 Aggref     *aggref = (Aggref *) node;
1733
1734                                 return check_if_rte_used(rt_index,
1735                                                                                  (Node *) (aggref->target), sup);
1736                         }
1737                         break;
1738
1739                 case T_GroupClause:
1740                         return FALSE;
1741                         break;
1742
1743                 case T_Expr:
1744                         {
1745                                 Expr       *expr = (Expr *) node;
1746
1747                                 return check_if_rte_used(rt_index,
1748                                                                                  (Node *) (expr->args), sup);
1749                         }
1750                         break;
1751
1752                 case T_Var:
1753                         {
1754                                 Var                *var = (Var *) node;
1755
1756                                 return var->varno == rt_index && var->varlevelsup == sup;
1757                         }
1758                         break;
1759
1760                 case T_List:
1761                         {
1762                                 List       *l;
1763
1764                                 foreach(l, (List *) node)
1765                                 {
1766                                         if (check_if_rte_used(rt_index, lfirst(l), sup))
1767                                                 return TRUE;
1768                                 }
1769                                 return FALSE;
1770                         }
1771                         break;
1772
1773                 case T_SubLink:
1774                         {
1775                                 SubLink    *sublink = (SubLink *) node;
1776                                 Query      *query = (Query *) sublink->subselect;
1777
1778                                 if (check_if_rte_used(rt_index, (Node *) (query->qual), sup + 1))
1779                                         return TRUE;
1780
1781                                 if (check_if_rte_used(rt_index, (Node *) (sublink->lefthand), sup))
1782                                         return TRUE;
1783
1784                                 return FALSE;
1785                         }
1786                         break;
1787
1788                 case T_Const:
1789                         return FALSE;
1790                         break;
1791
1792                 default:
1793                         elog(ERROR, "get_ruledef of %s: unknown node type %d in check_if_rte_used()",
1794                                  rulename, nodeTag(node));
1795                         break;
1796         }
1797
1798         return FALSE;
1799 }