1 /**********************************************************************
2 * get_ruledef.c - Function to get a rules definition text
6 * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.21 1999/07/17 20:17:59 momjian Exp $
8 * This software is copyrighted by Jan Wieck - Hamburg.
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.
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
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.
36 **********************************************************************/
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"
54 typedef struct QryHier
56 struct QryHier *parent;
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";
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);
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);
108 * get_ruledef - Do it all and return a text
109 * that could be used as a statement
110 * to recreate the rule
114 pg_get_ruledef(NameData *rname)
126 * We need the rules name somewhere deep down
129 rulename = nameout(rname);
132 * Connect to SPI manager
135 if (SPI_connect() != SPI_OK_CONNECT)
136 elog(ERROR, "get_ruledef: cannot connect to SPI manager");
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.
144 if (plan_getrule == NULL)
149 argtypes[0] = NAMEOID;
150 plan = SPI_prepare(query_getrule, 1, argtypes);
152 elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getrule);
153 plan_getrule = SPI_saveplan(plan);
157 * Get the pg_rewrite tuple for this rule
160 args[0] = PointerGetDatum(rulename);
161 nulls[0] = (rulename == NULL) ? 'n' : ' ';
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)
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] = '-';
176 ruletup = SPI_tuptable->vals[0];
177 rulettc = SPI_tuptable->tupdesc;
180 * Get the rules definition and put it into executors memory
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);
190 * Disconnect from SPI manager
193 if (SPI_finish() != SPI_OK_FINISH)
194 elog(ERROR, "get_ruledef: SPI_finish() failed");
205 * get_viewdef - Mainly the same thing, but we
206 * only return the SELECT part of a view
210 pg_get_viewdef(NameData *rname)
220 char name1[NAMEDATALEN + 5];
221 char name2[NAMEDATALEN + 5];
224 * We need the rules name somewhere deep down
227 rulename = nameout(rname);
230 * Connect to SPI manager
233 if (SPI_connect() != SPI_OK_CONNECT)
234 elog(ERROR, "get_viewdef: cannot connect to SPI manager");
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.
242 if (plan_getview == NULL)
247 argtypes[0] = NAMEOID;
248 argtypes[1] = NAMEOID;
249 plan = SPI_prepare(query_getview, 2, argtypes);
251 elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getview);
252 plan_getview = SPI_saveplan(plan);
256 * Get the pg_rewrite tuple for this rule
259 sprintf(name1, "_RET%s", rulename);
260 sprintf(name2, "_ret%s", rulename);
261 args[0] = PointerGetDatum(name1);
262 args[1] = PointerGetDatum(name2);
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)
274 * Get the rules definition and put it into executors memory
277 ruletup = SPI_tuptable->vals[0];
278 rulettc = SPI_tuptable->tupdesc;
279 tmp = make_viewdef(ruletup, rulettc);
281 len = strlen(tmp) + VARHDRSZ;
282 ruledef = SPI_palloc(len);
283 VARSIZE(ruledef) = len;
284 memcpy(VARDATA(ruledef), tmp, len - VARHDRSZ);
287 * Disconnect from SPI manager
290 if (SPI_finish() != SPI_OK_FINISH)
291 elog(ERROR, "get_viewdef: SPI_finish() failed");
302 * get_viewdef - Mainly the same thing, but we
303 * only return the SELECT part of a view
307 pg_get_indexdef(Oid indexrelid)
316 Form_pg_index idxrec;
317 Form_pg_class idxrelrec;
318 Form_pg_class indrelrec;
325 char keybuf[BUFSIZE];
329 * Connect to SPI manager
332 if (SPI_connect() != SPI_OK_CONNECT)
333 elog(ERROR, "get_indexdef: cannot connect to SPI manager");
336 * On the first call prepare the plans to lookup pg_am
340 if (plan_getam == NULL)
345 argtypes[0] = OIDOID;
346 plan = SPI_prepare(query_getam, 1, argtypes);
348 elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getam);
349 plan_getam = SPI_saveplan(plan);
351 argtypes[0] = OIDOID;
352 plan = SPI_prepare(query_getopclass, 1, argtypes);
354 elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getopclass);
355 plan_getopclass = SPI_saveplan(plan);
359 * Fetch the pg_index tuple by the Oid of the index
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);
369 * Fetch the pg_class tuple of the index relation
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);
379 * Fetch the pg_class tuple of the indexed relation
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);
389 * Get the am name for the index relation
392 spi_args[0] = ObjectIdGetDatum(idxrelrec->relam);
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");
405 * Start the index definition
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));
415 * Collect the indexed attributes
420 for (keyno = 0; keyno < INDEX_MAX_KEYS; keyno++)
422 if (idxrec->indkey[keyno] == InvalidAttrNumber)
429 * Add the indexed field name
432 strcat(keybuf, "\"");
433 if (idxrec->indkey[keyno] == ObjectIdAttributeNumber - 1)
434 strcat(keybuf, "oid");
436 strcat(keybuf, get_attribute_name(idxrec->indrelid,
437 idxrec->indkey[keyno]));
438 strcat(keybuf, "\"");
441 * If not a functional index, add the operator class name
444 if (idxrec->indproc == InvalidOid)
446 spi_args[0] = ObjectIdGetDatum(idxrec->indclass[keyno]);
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, "\"");
464 * For functional index say 'func (attrs) opclass'
467 if (idxrec->indproc != InvalidOid)
470 Form_pg_proc procStruct;
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);
477 procStruct = (Form_pg_proc) GETSTRUCT(proctup);
479 strcat(buf, nameout(&(procStruct->proname)));
484 spi_args[0] = ObjectIdGetDatum(idxrec->indclass[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");
496 strcat(buf, SPI_getvalue(spi_tup, spi_ttc, spi_fno));
501 * For the others say 'attr opclass [, ...]'
513 * Create the result in upper executor memory
516 len = strlen(buf) + VARHDRSZ;
517 indexdef = SPI_palloc(len);
518 VARSIZE(indexdef) = len;
519 memcpy(VARDATA(indexdef), buf, len - VARHDRSZ);
522 * Disconnect from SPI manager
525 if (SPI_finish() != SPI_OK_FINISH)
526 elog(ERROR, "get_viewdef: SPI_finish() failed");
533 * get_userbyid - Get a user name by usesysid and
534 * fallback to 'unknown (UID=n)'
538 pg_get_userbyid(int4 uid)
541 Form_pg_shadow user_rec;
545 * Allocate space for the result
548 result = (NameData *) palloc(NAMEDATALEN);
549 memset(result->data, 0, NAMEDATALEN);
552 * Get the pg_shadow entry and print the result
555 usertup = SearchSysCacheTuple(USESYSID,
556 ObjectIdGetDatum(uid), 0, 0, 0);
557 if (HeapTupleIsValid(usertup))
559 user_rec = (Form_pg_shadow) GETSTRUCT(usertup);
560 StrNCpy(result->data, (&(user_rec->usename))->data, NAMEDATALEN);
563 sprintf((char *) result, "unknown (UID=%d)", uid);
570 * make_ruledef - reconstruct the CREATE RULE command
571 * for a given pg_rewrite tuple
575 make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
589 * Allocate space for the returned rule definition text
592 buf = palloc(BUFSIZE);
595 * Get the attribute values from the rules tuple
598 fno = SPI_fnumber(rulettc, "ev_type");
599 ev_type = (char) SPI_getbinval(ruletup, rulettc, fno, &isnull);
601 fno = SPI_fnumber(rulettc, "ev_class");
602 ev_class = (Oid) SPI_getbinval(ruletup, rulettc, fno, &isnull);
604 fno = SPI_fnumber(rulettc, "ev_attr");
605 ev_attr = (int2) SPI_getbinval(ruletup, rulettc, fno, &isnull);
607 fno = SPI_fnumber(rulettc, "is_instead");
608 is_instead = (bool) SPI_getbinval(ruletup, rulettc, fno, &isnull);
610 fno = SPI_fnumber(rulettc, "ev_qual");
611 ev_qual = SPI_getvalue(ruletup, rulettc, fno);
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);
620 * Build the rules definition text
623 strcpy(buf, "CREATE RULE \"");
626 strcat(buf, rulename);
627 strcat(buf, "\" AS ON ");
629 /* The event the rule is fired for */
633 strcat(buf, "SELECT TO \"");
637 strcat(buf, "UPDATE TO \"");
641 strcat(buf, "INSERT TO \"");
645 strcat(buf, "DELETE TO \"");
649 elog(ERROR, "get_ruledef: rule %s has unsupported event type %d",
654 /* The relation the rule is fired on */
655 strcat(buf, get_relation_name(ev_class));
660 strcat(buf, get_attribute_name(ev_class, ev_attr));
664 /* If the rule has an event qualification, add it */
667 if (strlen(ev_qual) > 0 && strcmp(ev_qual, "<>") != 0)
673 qual = stringToNode(ev_qual);
674 query = (Query *) lfirst(actions);
678 strcat(buf, " WHERE ");
679 strcat(buf, get_rule_expr(&qh, 0, qual, TRUE));
684 /* The INSTEAD keyword (if so) */
686 strcat(buf, "INSTEAD ");
688 /* Finally the rules actions */
689 if (length(actions) > 1)
695 foreach(action, actions)
697 query = (Query *) lfirst(action);
698 strcat(buf, get_query_def(query, NULL));
705 if (length(actions) == 0)
707 strcat(buf, "NOTHING;");
713 query = (Query *) lfirst(actions);
714 strcat(buf, get_query_def(query, NULL));
728 * make_viewdef - reconstruct the SELECT part of a
733 make_viewdef(HeapTuple ruletup, TupleDesc rulettc)
748 * Get the attribute values from the rules tuple
751 fno = SPI_fnumber(rulettc, "ev_type");
752 ev_type = (char) SPI_getbinval(ruletup, rulettc, fno, &isnull);
754 fno = SPI_fnumber(rulettc, "ev_class");
755 ev_class = (Oid) SPI_getbinval(ruletup, rulettc, fno, &isnull);
757 fno = SPI_fnumber(rulettc, "ev_attr");
758 ev_attr = (int2) SPI_getbinval(ruletup, rulettc, fno, &isnull);
760 fno = SPI_fnumber(rulettc, "is_instead");
761 is_instead = (bool) SPI_getbinval(ruletup, rulettc, fno, &isnull);
763 fno = SPI_fnumber(rulettc, "ev_qual");
764 ev_qual = SPI_getvalue(ruletup, rulettc, fno);
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);
771 if (length(actions) != 1)
774 query = (Query *) lfirst(actions);
776 if (ev_type != '1' || ev_attr >= 0 || !is_instead || strcmp(ev_qual, "<>"))
779 strcpy(buf, get_query_def(query, NULL));
791 * get_query_def - Parse back one action from
792 * the parsetree in the actions
797 get_query_def(Query *query, QryHier *parentqh)
801 qh.parent = parentqh;
804 switch (query->commandType)
807 return get_select_query_def(query, &qh);
811 return get_update_query_def(query, &qh);
815 return get_insert_query_def(query, &qh);
819 return get_delete_query_def(query, &qh);
827 elog(ERROR, "get_ruledef of %s: query command type %d not implemented yet",
828 rulename, query->commandType);
837 * get_select_query_def - Parse back a SELECT parsetree
841 get_select_query_def(Query *query, QryHier *qh)
850 bool rt_constonly = TRUE;
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
860 rt_length = length(query->rtable);
861 rt_used = palloc(sizeof(bool) * rt_length);
862 for (i = 0; i < rt_length; i++)
864 if (check_if_rte_used(i + 1, (Node *) (query->targetList), 0))
871 if (check_if_rte_used(i + 1, (Node *) (query->qual), 0))
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
888 foreach(l, query->rtable)
893 rte = (RangeTblEntry *) lfirst(l);
894 if (!strcmp(rte->refname, "*NEW*"))
896 if (!strcmp(rte->refname, "*CURRENT*"))
899 rt_constonly = FALSE;
904 * Build up the query string - first we say SELECT
907 strcpy(buf, "SELECT");
909 /* Then we tell what to select (the targetlist) */
911 foreach(l, query->targetList)
913 bool tell_as = FALSE;
915 tle = (TargetEntry *) lfirst(l);
919 strcat(buf, get_tle_expr(qh, 0, tle, (rt_numused > 1)));
921 /* Check if we must say AS ... */
922 if (nodeTag(tle->expr) != T_Var)
923 tell_as = strcmp(tle->resdom->resname, "?column?");
926 Var *var = (Var *) (tle->expr);
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))
938 strcat(buf, " AS \"");
939 strcat(buf, tle->resdom->resname);
944 /* If we need other tables that *NEW* or *CURRENT* add the FROM clause */
945 if (!rt_constonly && rt_numused > 0)
947 strcat(buf, " FROM");
951 foreach(l, query->rtable)
955 rte = (RangeTblEntry *) lfirst(l);
957 if (!strcmp(rte->refname, "*NEW*"))
960 if (!strcmp(rte->refname, "*CURRENT*"))
966 strcat(buf, rte->relname);
968 if (strcmp(rte->relname, rte->refname) != 0)
971 strcat(buf, rte->refname);
978 /* Add the WHERE clause if given */
979 if (query->qual != NULL)
981 strcat(buf, " WHERE ");
982 strcat(buf, get_rule_expr(qh, 0, query->qual, (rt_numused > 1)));
985 /* Add the GROUP BY CLAUSE */
986 if (query->groupClause != NULL)
988 strcat(buf, " GROUP BY ");
990 foreach(l, query->groupClause)
994 strcat(buf, get_rule_expr(qh, 0, lfirst(l), (rt_numused > 1)));
999 * Copy the query string into allocated space and return it
1002 return pstrdup(buf);
1007 * get_insert_query_def - Parse back an INSERT parsetree
1011 get_insert_query_def(Query *query, QryHier *qh)
1020 bool rt_constonly = TRUE;
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.
1030 rt_length = length(query->rtable);
1031 rt_used = palloc(sizeof(bool) * rt_length);
1032 for (i = 0; i < rt_length; i++)
1034 if (check_if_rte_used(i + 1, (Node *) (query->targetList), 0))
1041 if (check_if_rte_used(i + 1, (Node *) (query->qual), 0))
1052 foreach(l, query->rtable)
1057 rte = (RangeTblEntry *) lfirst(l);
1058 if (!strcmp(rte->refname, "*NEW*"))
1060 if (!strcmp(rte->refname, "*CURRENT*"))
1063 rt_constonly = FALSE;
1068 * Start the query with INSERT INTO relname
1071 rte = (RangeTblEntry *) nth(query->resultRelation - 1, query->rtable);
1072 strcpy(buf, "INSERT INTO \"");
1073 strcat(buf, rte->relname);
1076 /* Add the target list */
1078 foreach(l, query->targetList)
1080 tle = (TargetEntry *) lfirst(l);
1085 strcat(buf, tle->resdom->resname);
1090 /* Add the VALUES or the SELECT */
1091 if (rt_constonly && query->qual == NULL)
1093 strcat(buf, "VALUES (");
1095 foreach(l, query->targetList)
1097 tle = (TargetEntry *) lfirst(l);
1101 strcat(buf, get_tle_expr(qh, 0, tle, (rt_numused > 1)));
1106 strcat(buf, get_select_query_def(query, qh));
1109 * Copy the query string into allocated space and return it
1112 return pstrdup(buf);
1117 * get_update_query_def - Parse back an UPDATE parsetree
1121 get_update_query_def(Query *query, QryHier *qh)
1130 * Start the query with UPDATE relname SET
1133 rte = (RangeTblEntry *) nth(query->resultRelation - 1, query->rtable);
1134 strcpy(buf, "UPDATE ");
1135 strcat(buf, rte->relname);
1136 strcat(buf, " SET ");
1138 /* Add the comma separated list of 'attname = value' */
1140 foreach(l, query->targetList)
1142 tle = (TargetEntry *) lfirst(l);
1147 strcat(buf, tle->resdom->resname);
1148 strcat(buf, "\" = ");
1149 strcat(buf, get_tle_expr(qh, query->resultRelation,
1153 /* Finally add a WHERE clause if given */
1154 if (query->qual != NULL)
1156 strcat(buf, " WHERE ");
1157 strcat(buf, get_rule_expr(qh, query->resultRelation,
1158 query->qual, TRUE));
1162 * Copy the query string into allocated space and return it
1165 return pstrdup(buf);
1170 * get_delete_query_def - Parse back a DELETE parsetree
1174 get_delete_query_def(Query *query, QryHier *qh)
1180 * Start the query with DELETE FROM relname
1183 rte = (RangeTblEntry *) nth(query->resultRelation - 1, query->rtable);
1184 strcpy(buf, "DELETE FROM \"");
1185 strcat(buf, rte->relname);
1188 /* Add a WHERE clause if given */
1189 if (query->qual != NULL)
1191 strcat(buf, " WHERE ");
1192 strcat(buf, get_rule_expr(qh, 0, query->qual, FALSE));
1196 * Copy the query string into allocated space and return it
1199 return pstrdup(buf);
1204 * get_rule_expr - Parse back an expression
1208 get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
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.
1223 switch (nodeTag(node))
1227 TargetEntry *tle = (TargetEntry *) node;
1229 return get_rule_expr(qh, rt_index,
1230 (Node *) (tle->expr), varprefix);
1236 Aggref *aggref = (Aggref *) node;
1239 strcat(buf, aggref->aggname);
1241 strcat(buf, get_rule_expr(qh, rt_index,
1242 (Node *) (aggref->target), varprefix));
1244 return pstrdup(buf);
1250 GroupClause *grp = (GroupClause *) node;
1252 TargetEntry *tle = NULL;
1254 foreach(l, qh->query->targetList)
1256 if (((TargetEntry *) lfirst(l))->resdom->resgroupref ==
1259 tle = (TargetEntry *) lfirst(l);
1265 elog(ERROR, "GROUP BY expression not found in targetlist");
1267 return get_rule_expr(qh, rt_index, (Node *) tle, varprefix);
1273 Expr *expr = (Expr *) node;
1276 * Expr nodes have to be handled a bit detailed
1279 switch (expr->opType)
1282 strcat(buf, get_rule_expr(qh, rt_index,
1283 (Node *) get_leftop(expr),
1286 strcat(buf, get_opname(((Oper *) expr->oper)->opno));
1288 strcat(buf, get_rule_expr(qh, rt_index,
1289 (Node *) get_rightop(expr),
1291 return pstrdup(buf);
1296 strcat(buf, get_rule_expr(qh, rt_index,
1297 (Node *) get_leftop(expr),
1299 strcat(buf, ") OR (");
1300 strcat(buf, get_rule_expr(qh, rt_index,
1301 (Node *) get_rightop(expr),
1304 return pstrdup(buf);
1309 strcat(buf, get_rule_expr(qh, rt_index,
1310 (Node *) get_leftop(expr),
1312 strcat(buf, ") AND (");
1313 strcat(buf, get_rule_expr(qh, rt_index,
1314 (Node *) get_rightop(expr),
1317 return pstrdup(buf);
1321 strcat(buf, "NOT (");
1322 strcat(buf, get_rule_expr(qh, rt_index,
1323 (Node *) get_leftop(expr),
1326 return pstrdup(buf);
1330 return get_func_expr(qh, rt_index,
1336 printf("\n%s\n", nodeToString(node));
1337 elog(ERROR, "Expr not yet supported");
1344 Var *var = (Var *) node;
1346 int sup = var->varlevelsup;
1351 rte = (RangeTblEntry *) nth(var->varno - 1, qh->query->rtable);
1353 if (!strcmp(rte->refname, "*NEW*"))
1354 strcat(buf, "new.");
1357 if (!strcmp(rte->refname, "*CURRENT*"))
1358 strcat(buf, "old.");
1361 if (strcmp(rte->relname, rte->refname) != 0)
1364 strcat(buf, rte->refname);
1370 strcat(buf, get_attribute_name(rte->relid, var->varattno));
1373 return pstrdup(buf);
1379 printf("\n%s\n", nodeToString(node));
1380 elog(ERROR, "List not yet supported");
1385 return get_sublink_expr(qh, rt_index, node, varprefix);
1389 return get_const_expr((Const *) node);
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));
1404 * get_func_expr - Parse back a Func node
1408 get_func_expr(QryHier *qh, int rt_index, Expr *expr, bool varprefix)
1412 Form_pg_proc procStruct;
1415 Func *func = (Func *) (expr->oper);
1419 * Get the functions pg_proc tuple
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);
1427 procStruct = (Form_pg_proc) GETSTRUCT(proctup);
1428 proname = nameout(&(procStruct->proname));
1430 if (procStruct->pronargs == 1 && procStruct->proargtypes[0] == InvalidOid)
1432 if (!strcmp(proname, "nullvalue"))
1435 strcat(buf, get_rule_expr(qh, rt_index, lfirst(expr->args),
1437 strcat(buf, ") ISNULL");
1438 return pstrdup(buf);
1440 if (!strcmp(proname, "nonnullvalue"))
1443 strcat(buf, get_rule_expr(qh, rt_index, lfirst(expr->args),
1445 strcat(buf, ") NOTNULL");
1446 return pstrdup(buf);
1451 * Build a string of proname(args)
1455 strcat(buf, proname);
1458 foreach(l, expr->args)
1462 strcat(buf, get_rule_expr(qh, rt_index, lfirst(l), varprefix));
1467 * Copy the function call string into allocated space and return it
1470 return pstrdup(buf);
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
1487 get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix)
1490 Form_pg_proc procStruct;
1496 * Check if the result has an atttypmod and if the
1497 * expression in the targetlist entry is a function call
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);
1508 func = (Func *) (expr->oper);
1511 * Get the functions pg_proc tuple
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);
1519 procStruct = (Form_pg_proc) GETSTRUCT(proctup);
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
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);
1535 * Finally (to be totally safe) the second argument must be a
1536 * const and match the value in the results atttypmod.
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);
1546 * Whow - got it. Now get rid of the padding function
1549 return get_rule_expr(qh, rt_index, lfirst(expr->args), varprefix);
1554 * get_const_expr - Make a string representation
1555 * with the type cast out of a Const
1559 get_const_expr(Const *constval)
1562 Form_pg_type typeStruct;
1563 FmgrInfo finfo_output;
1565 bool isnull = FALSE;
1569 if (constval->constisnull)
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);
1577 typeStruct = (Form_pg_type) GETSTRUCT(typetup);
1579 fmgr_info(typeStruct->typoutput, &finfo_output);
1580 extval = (char *) (*fmgr_faddr(&finfo_output)) (constval->constvalue,
1583 sprintf(namebuf, "::\"%s\"", nameout(&(typeStruct->typname)));
1584 if (strcmp(namebuf, "::unknown") == 0)
1586 sprintf(buf, "'%s'%s", extval, namebuf);
1587 return pstrdup(buf);
1592 * get_sublink_expr - Parse back a sublink
1596 get_sublink_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
1598 SubLink *sublink = (SubLink *) node;
1599 Query *query = (Query *) (sublink->subselect);
1607 if (sublink->lefthand != NULL)
1609 if (length(sublink->lefthand) > 1)
1613 foreach(l, sublink->lefthand)
1617 strcat(buf, get_rule_expr(qh, rt_index,
1618 lfirst(l), varprefix));
1621 if (length(sublink->lefthand) > 1)
1627 switch (sublink->subLinkType)
1629 case EXISTS_SUBLINK:
1630 strcat(buf, "EXISTS ");
1634 expr = (Expr *) lfirst(sublink->oper);
1635 strcat(buf, get_opname(((Oper *) (expr->oper))->opno));
1636 strcat(buf, " ANY ");
1640 expr = (Expr *) lfirst(sublink->oper);
1641 strcat(buf, get_opname(((Oper *) (expr->oper))->opno));
1642 strcat(buf, " ALL ");
1646 expr = (Expr *) lfirst(sublink->oper);
1647 strcat(buf, get_opname(((Oper *) (expr->oper))->opno));
1652 elog(ERROR, "unupported sublink type %d",
1653 sublink->subLinkType);
1658 strcat(buf, get_query_def(query, qh));
1661 return pstrdup(buf);
1666 * get_relation_name - Get a relation name by Oid
1670 get_relation_name(Oid relid)
1673 Form_pg_class classStruct;
1675 classtup = SearchSysCacheTuple(RELOID,
1676 ObjectIdGetDatum(relid), 0, 0, 0);
1677 if (!HeapTupleIsValid(classtup))
1678 elog(ERROR, "cache lookup of relation %u failed", relid);
1680 classStruct = (Form_pg_class) GETSTRUCT(classtup);
1681 return nameout(&(classStruct->relname));
1686 * get_attribute_name - Get an attribute name by it's
1687 * relations Oid and it's attnum
1691 get_attribute_name(Oid relid, int2 attnum)
1694 Form_pg_attribute attStruct;
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",
1702 attStruct = (Form_pg_attribute) GETSTRUCT(atttup);
1703 return nameout(&(attStruct->attname));
1708 * check_if_rte_used - Check a targetlist or qual
1709 * if a given rangetable entry
1714 check_if_rte_used(int rt_index, Node *node, int sup)
1719 switch (nodeTag(node))
1723 TargetEntry *tle = (TargetEntry *) node;
1725 return check_if_rte_used(rt_index,
1726 (Node *) (tle->expr), sup);
1732 Aggref *aggref = (Aggref *) node;
1734 return check_if_rte_used(rt_index,
1735 (Node *) (aggref->target), sup);
1745 Expr *expr = (Expr *) node;
1747 return check_if_rte_used(rt_index,
1748 (Node *) (expr->args), sup);
1754 Var *var = (Var *) node;
1756 return var->varno == rt_index && var->varlevelsup == sup;
1764 foreach(l, (List *) node)
1766 if (check_if_rte_used(rt_index, lfirst(l), sup))
1775 SubLink *sublink = (SubLink *) node;
1776 Query *query = (Query *) sublink->subselect;
1778 if (check_if_rte_used(rt_index, (Node *) (query->qual), sup + 1))
1781 if (check_if_rte_used(rt_index, (Node *) (sublink->lefthand), sup))
1793 elog(ERROR, "get_ruledef of %s: unknown node type %d in check_if_rte_used()",
1794 rulename, nodeTag(node));