Commit cd243d27 authored by Tom Lane's avatar Tom Lane

Revise rule-printing routines to use expandable StringInfo buffers, so that

they have no hardwired limit on the length of a rule's text.  Fix a couple
of minor bugs in passing --- deparsed UPDATE queries didn't have quotes
around relation name, and quotes and backslashes in constant values weren't
backslash-quoted.
parent 4e907385
......@@ -3,7 +3,7 @@
* out of it's tuple
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.25 1999/09/02 03:04:04 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.26 1999/10/02 01:07:51 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
......@@ -40,6 +40,7 @@
#include "postgres.h"
#include "executor/spi.h"
#include "lib/stringinfo.h"
#include "optimizer/clauses.h"
#include "optimizer/tlist.h"
#include "utils/lsyscache.h"
......@@ -47,7 +48,6 @@
#include "catalog/pg_operator.h"
#include "catalog/pg_shadow.h"
#define BUFSIZE 8192
/* ----------
* Local data types
......@@ -92,21 +92,29 @@ NameData *pg_get_userbyid(int4 uid);
/* ----------
* Local functions
* ----------
*/
static char *make_ruledef(HeapTuple ruletup, TupleDesc rulettc);
static char *make_viewdef(HeapTuple ruletup, TupleDesc rulettc);
static char *get_query_def(Query *query, QryHier *parentqh);
static char *get_select_query_def(Query *query, QryHier *qh);
static char *get_insert_query_def(Query *query, QryHier *qh);
static char *get_update_query_def(Query *query, QryHier *qh);
static char *get_delete_query_def(Query *query, QryHier *qh);
*
* Most of these functions used to use fixed-size buffers to build their
* results. Now, they take an (already initialized) StringInfo object
* as a parameter, and append their text output to its contents.
* ----------
*/
static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc);
static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc);
static void get_query_def(StringInfo buf, Query *query, QryHier *parentqh);
static void get_select_query_def(StringInfo buf, Query *query, QryHier *qh);
static void get_insert_query_def(StringInfo buf, Query *query, QryHier *qh);
static void get_update_query_def(StringInfo buf, Query *query, QryHier *qh);
static void get_delete_query_def(StringInfo buf, Query *query, QryHier *qh);
static RangeTblEntry *get_rte_for_var(Var *var, QryHier *qh);
static char *get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix);
static char *get_func_expr(QryHier *qh, int rt_index, Expr *expr, bool varprefix);
static char *get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix);
static char *get_const_expr(Const *constval);
static char *get_sublink_expr(QryHier *qh, int rt_index, Node *node, bool varprefix);
static void get_rule_expr(StringInfo buf, QryHier *qh, int rt_index,
Node *node, bool varprefix);
static void get_func_expr(StringInfo buf, QryHier *qh, int rt_index,
Expr *expr, bool varprefix);
static void get_tle_expr(StringInfo buf, QryHier *qh, int rt_index,
TargetEntry *tle, bool varprefix);
static void get_const_expr(StringInfo buf, Const *constval);
static void get_sublink_expr(StringInfo buf, QryHier *qh, int rt_index,
Node *node, bool varprefix);
static char *get_relation_name(Oid relid);
static char *get_attribute_name(Oid relid, int2 attnum);
static bool check_if_rte_used(Node *node, Index rt_index, int levelsup);
......@@ -129,7 +137,7 @@ pg_get_ruledef(NameData *rname)
int spirc;
HeapTuple ruletup;
TupleDesc rulettc;
char *tmp;
StringInfoData buf;
int len;
/* ----------
......@@ -190,11 +198,13 @@ pg_get_ruledef(NameData *rname)
* Get the rules definition and put it into executors memory
* ----------
*/
tmp = make_ruledef(ruletup, rulettc);
len = strlen(tmp) + VARHDRSZ;
initStringInfo(&buf);
make_ruledef(&buf, ruletup, rulettc);
len = buf.len + VARHDRSZ;
ruledef = SPI_palloc(len);
VARSIZE(ruledef) = len;
memcpy(VARDATA(ruledef), tmp, len - VARHDRSZ);
memcpy(VARDATA(ruledef), buf.data, buf.len);
pfree(buf.data);
/* ----------
* Disconnect from SPI manager
......@@ -225,7 +235,7 @@ pg_get_viewdef(NameData *rname)
int spirc;
HeapTuple ruletup;
TupleDesc rulettc;
char *tmp;
StringInfoData buf;
int len;
char name1[NAMEDATALEN + 5];
char name2[NAMEDATALEN + 5];
......@@ -276,8 +286,9 @@ pg_get_viewdef(NameData *rname)
spirc = SPI_execp(plan_getview, args, nulls, 1);
if (spirc != SPI_OK_SELECT)
elog(ERROR, "failed to get pg_rewrite tuple for view %s", rulename);
initStringInfo(&buf);
if (SPI_processed != 1)
tmp = "Not a view";
appendStringInfo(&buf, "Not a view");
else
{
/* ----------
......@@ -286,12 +297,13 @@ pg_get_viewdef(NameData *rname)
*/
ruletup = SPI_tuptable->vals[0];
rulettc = SPI_tuptable->tupdesc;
tmp = make_viewdef(ruletup, rulettc);
make_viewdef(&buf, ruletup, rulettc);
}
len = strlen(tmp) + VARHDRSZ;
len = buf.len + VARHDRSZ;
ruledef = SPI_palloc(len);
VARSIZE(ruledef) = len;
memcpy(VARDATA(ruledef), tmp, len - VARHDRSZ);
memcpy(VARDATA(ruledef), buf.data, buf.len);
pfree(buf.data);
/* ----------
* Disconnect from SPI manager
......@@ -309,8 +321,7 @@ pg_get_viewdef(NameData *rname)
/* ----------
* get_viewdef - Mainly the same thing, but we
* only return the SELECT part of a view
* get_indexdef - Get the definition of an index
* ----------
*/
text *
......@@ -331,8 +342,8 @@ pg_get_indexdef(Oid indexrelid)
int spirc;
int len;
int keyno;
char buf[BUFSIZE];
char keybuf[BUFSIZE];
StringInfoData buf;
StringInfoData keybuf;
char *sep;
/* ----------
......@@ -415,7 +426,8 @@ pg_get_indexdef(Oid indexrelid)
* Start the index definition
* ----------
*/
sprintf(buf, "CREATE %sINDEX \"%s\" ON \"%s\" USING %s (",
initStringInfo(&buf);
appendStringInfo(&buf, "CREATE %sINDEX \"%s\" ON \"%s\" USING %s (",
idxrec->indisunique ? "UNIQUE " : "",
nameout(&(idxrelrec->relname)),
nameout(&(indrelrec->relname)),
......@@ -425,27 +437,26 @@ pg_get_indexdef(Oid indexrelid)
* Collect the indexed attributes
* ----------
*/
initStringInfo(&keybuf);
sep = "";
keybuf[0] = '\0';
for (keyno = 0; keyno < INDEX_MAX_KEYS; keyno++)
{
if (idxrec->indkey[keyno] == InvalidAttrNumber)
break;
strcat(keybuf, sep);
appendStringInfo(&keybuf, sep);
sep = ", ";
/* ----------
* Add the indexed field name
* ----------
*/
strcat(keybuf, "\"");
if (idxrec->indkey[keyno] == ObjectIdAttributeNumber - 1)
strcat(keybuf, "oid");
appendStringInfo(&keybuf, "\"oid\"");
else
strcat(keybuf, get_attribute_name(idxrec->indrelid,
appendStringInfo(&keybuf, "\"%s\"",
get_attribute_name(idxrec->indrelid,
idxrec->indkey[keyno]));
strcat(keybuf, "\"");
/* ----------
* If not a functional index, add the operator class name
......@@ -464,9 +475,8 @@ pg_get_indexdef(Oid indexrelid)
spi_tup = SPI_tuptable->vals[0];
spi_ttc = SPI_tuptable->tupdesc;
spi_fno = SPI_fnumber(spi_ttc, "opcname");
strcat(keybuf, " \"");
strcat(keybuf, SPI_getvalue(spi_tup, spi_ttc, spi_fno));
strcat(keybuf, "\"");
appendStringInfo(&keybuf, " \"%s\"",
SPI_getvalue(spi_tup, spi_ttc, spi_fno));
}
}
......@@ -485,11 +495,9 @@ pg_get_indexdef(Oid indexrelid)
elog(ERROR, "cache lookup for proc %u failed", idxrec->indproc);
procStruct = (Form_pg_proc) GETSTRUCT(proctup);
strcat(buf, "\"");
strcat(buf, nameout(&(procStruct->proname)));
strcat(buf, "\" (");
strcat(buf, keybuf);
strcat(buf, ") ");
appendStringInfo(&buf, "\"%s\" (%s) ",
nameout(&(procStruct->proname)),
keybuf.data);
spi_args[0] = ObjectIdGetDatum(idxrec->indclass[0]);
spi_nulls[0] = ' ';
......@@ -502,31 +510,32 @@ pg_get_indexdef(Oid indexrelid)
spi_tup = SPI_tuptable->vals[0];
spi_ttc = SPI_tuptable->tupdesc;
spi_fno = SPI_fnumber(spi_ttc, "opcname");
strcat(buf, "\"");
strcat(buf, SPI_getvalue(spi_tup, spi_ttc, spi_fno));
strcat(buf, "\"");
appendStringInfo(&buf, "\"%s\"",
SPI_getvalue(spi_tup, spi_ttc, spi_fno));
}
else
/* ----------
* For the others say 'attr opclass [, ...]'
* ----------
*/
strcat(buf, keybuf);
appendStringInfo(&buf, "%s", keybuf.data);
/* ----------
* Finish
* ----------
*/
strcat(buf, ")");
appendStringInfo(&buf, ")");
/* ----------
* Create the result in upper executor memory
* ----------
*/
len = strlen(buf) + VARHDRSZ;
len = buf.len + VARHDRSZ;
indexdef = SPI_palloc(len);
VARSIZE(indexdef) = len;
memcpy(VARDATA(indexdef), buf, len - VARHDRSZ);
memcpy(VARDATA(indexdef), buf.data, buf.len);
pfree(buf.data);
pfree(keybuf.data);
/* ----------
* Disconnect from SPI manager
......@@ -581,10 +590,9 @@ pg_get_userbyid(int4 uid)
* for a given pg_rewrite tuple
* ----------
*/
static char *
make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
static void
make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
{
char *buf;
char ev_type;
Oid ev_class;
int2 ev_attr;
......@@ -595,12 +603,6 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
int fno;
bool isnull;
/* ----------
* Allocate space for the returned rule definition text
* ----------
*/
buf = palloc(BUFSIZE);
/* ----------
* Get the attribute values from the rules tuple
* ----------
......@@ -630,29 +632,25 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
* Build the rules definition text
* ----------
*/
strcpy(buf, "CREATE RULE \"");
/* The rule name */
strcat(buf, rulename);
strcat(buf, "\" AS ON ");
appendStringInfo(buf, "CREATE RULE \"%s\" AS ON ", rulename);
/* The event the rule is fired for */
switch (ev_type)
{
case '1':
strcat(buf, "SELECT TO \"");
appendStringInfo(buf, "SELECT");
break;
case '2':
strcat(buf, "UPDATE TO \"");
appendStringInfo(buf, "UPDATE");
break;
case '3':
strcat(buf, "INSERT TO \"");
appendStringInfo(buf, "INSERT");
break;
case '4':
strcat(buf, "DELETE TO \"");
appendStringInfo(buf, "DELETE");
break;
default:
......@@ -662,14 +660,10 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
}
/* The relation the rule is fired on */
strcat(buf, get_relation_name(ev_class));
strcat(buf, "\"");
appendStringInfo(buf, " TO \"%s\"", get_relation_name(ev_class));
if (ev_attr > 0)
{
strcat(buf, ".\"");
strcat(buf, get_attribute_name(ev_class, ev_attr));
strcat(buf, "\"");
}
appendStringInfo(buf, ".\"%s\"",
get_attribute_name(ev_class, ev_attr));
/* If the rule has an event qualification, add it */
if (ev_qual == NULL)
......@@ -685,15 +679,15 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
qh.parent = NULL;
qh.query = query;
strcat(buf, " WHERE ");
strcat(buf, get_rule_expr(&qh, 0, qual, TRUE));
appendStringInfo(buf, " WHERE ");
get_rule_expr(buf, &qh, 0, qual, TRUE);
}
strcat(buf, " DO ");
appendStringInfo(buf, " DO ");
/* The INSTEAD keyword (if so) */
if (is_instead)
strcat(buf, "INSTEAD ");
appendStringInfo(buf, "INSTEAD ");
/* Finally the rules actions */
if (length(actions) > 1)
......@@ -701,36 +695,30 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
List *action;
Query *query;
strcat(buf, "(");
appendStringInfo(buf, "(");
foreach(action, actions)
{
query = (Query *) lfirst(action);
strcat(buf, get_query_def(query, NULL));
strcat(buf, "; ");
get_query_def(buf, query, NULL);
appendStringInfo(buf, "; ");
}
strcat(buf, ");");
appendStringInfo(buf, ");");
}
else
{
if (length(actions) == 0)
{
strcat(buf, "NOTHING;");
appendStringInfo(buf, "NOTHING;");
}
else
{
Query *query;
query = (Query *) lfirst(actions);
strcat(buf, get_query_def(query, NULL));
strcat(buf, ";");
get_query_def(buf, query, NULL);
appendStringInfo(buf, ";");
}
}
/* ----------
* That's it
* ----------
*/
return buf;
}
......@@ -739,10 +727,9 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
* view rewrite rule
* ----------
*/
static char *
make_viewdef(HeapTuple ruletup, TupleDesc rulettc)
static void
make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
{
char buf[BUFSIZE];
Query *query;
char ev_type;
Oid ev_class;
......@@ -779,21 +766,21 @@ make_viewdef(HeapTuple ruletup, TupleDesc rulettc)
actions = (List *) stringToNode(ev_action);
if (length(actions) != 1)
return "Not a view";
{
appendStringInfo(buf, "Not a view");
return;
}
query = (Query *) lfirst(actions);
if (ev_type != '1' || ev_attr >= 0 || !is_instead || strcmp(ev_qual, "<>"))
return "Not a view";
strcpy(buf, get_query_def(query, NULL));
strcat(buf, ";");
{
appendStringInfo(buf, "Not a view");
return;
}
/* ----------
* That's it
* ----------
*/
return pstrdup(buf);
get_query_def(buf, query, NULL);
appendStringInfo(buf, ";");
}
......@@ -803,8 +790,8 @@ make_viewdef(HeapTuple ruletup, TupleDesc rulettc)
* list
* ----------
*/
static char *
get_query_def(Query *query, QryHier *parentqh)
static void
get_query_def(StringInfo buf, Query *query, QryHier *parentqh)
{
QryHier qh;
......@@ -814,23 +801,23 @@ get_query_def(Query *query, QryHier *parentqh)
switch (query->commandType)
{
case CMD_SELECT:
return get_select_query_def(query, &qh);
get_select_query_def(buf, query, &qh);
break;
case CMD_UPDATE:
return get_update_query_def(query, &qh);
get_update_query_def(buf, query, &qh);
break;
case CMD_INSERT:
return get_insert_query_def(query, &qh);
get_insert_query_def(buf, query, &qh);
break;
case CMD_DELETE:
return get_delete_query_def(query, &qh);
get_delete_query_def(buf, query, &qh);
break;
case CMD_NOTHING:
return "NOTHING";
appendStringInfo(buf, "NOTHING");
break;
default:
......@@ -838,8 +825,6 @@ get_query_def(Query *query, QryHier *parentqh)
rulename, query->commandType);
break;
}
return NULL;
}
......@@ -847,10 +832,9 @@ get_query_def(Query *query, QryHier *parentqh)
* get_select_query_def - Parse back a SELECT parsetree
* ----------
*/
static char *
get_select_query_def(Query *query, QryHier *qh)
static void
get_select_query_def(StringInfo buf, Query *query, QryHier *qh)
{
char buf[BUFSIZE];
char *sep;
TargetEntry *tle;
RangeTblEntry *rte;
......@@ -908,7 +892,7 @@ get_select_query_def(Query *query, QryHier *qh)
* Build up the query string - first we say SELECT
* ----------
*/
strcpy(buf, "SELECT");
appendStringInfo(buf, "SELECT");
/* Then we tell what to select (the targetlist) */
sep = " ";
......@@ -917,10 +901,10 @@ get_select_query_def(Query *query, QryHier *qh)
bool tell_as = FALSE;
tle = (TargetEntry *) lfirst(l);
strcat(buf, sep);
appendStringInfo(buf, sep);
sep = ", ";
strcat(buf, get_tle_expr(qh, 0, tle, (rt_numused > 1)));
get_tle_expr(buf, qh, 0, tle, (rt_numused > 1));
/* Check if we must say AS ... */
if (! IsA(tle->expr, Var))
......@@ -938,17 +922,13 @@ get_select_query_def(Query *query, QryHier *qh)
/* and do if so */
if (tell_as)
{
strcat(buf, " AS \"");
strcat(buf, tle->resdom->resname);
strcat(buf, "\"");
}
appendStringInfo(buf, " AS \"%s\"", tle->resdom->resname);
}
/* If we need other tables that *NEW* or *CURRENT* add the FROM clause */
if (!rt_constonly && rt_numused > 0)
{
strcat(buf, " FROM");
appendStringInfo(buf, " FROM");
i = 0;
sep = " ";
......@@ -964,17 +944,11 @@ get_select_query_def(Query *query, QryHier *qh)
if (!strcmp(rte->refname, "*CURRENT*"))
continue;
strcat(buf, sep);
appendStringInfo(buf, sep);
sep = ", ";
strcat(buf, "\"");
strcat(buf, rte->relname);
strcat(buf, "\"");
appendStringInfo(buf, "\"%s\"", rte->relname);
if (strcmp(rte->relname, rte->refname) != 0)
{
strcat(buf, " \"");
strcat(buf, rte->refname);
strcat(buf, "\"");
}
appendStringInfo(buf, " \"%s\"", rte->refname);
}
}
}
......@@ -982,14 +956,14 @@ get_select_query_def(Query *query, QryHier *qh)
/* Add the WHERE clause if given */
if (query->qual != NULL)
{
strcat(buf, " WHERE ");
strcat(buf, get_rule_expr(qh, 0, query->qual, (rt_numused > 1)));
appendStringInfo(buf, " WHERE ");
get_rule_expr(buf, qh, 0, query->qual, (rt_numused > 1));
}
/* Add the GROUP BY CLAUSE */
if (query->groupClause != NULL)
{
strcat(buf, " GROUP BY ");
appendStringInfo(buf, " GROUP BY ");
sep = "";
foreach(l, query->groupClause)
{
......@@ -998,17 +972,11 @@ get_select_query_def(Query *query, QryHier *qh)
groupexpr = get_sortgroupclause_expr(grp,
query->targetList);
strcat(buf, sep);
strcat(buf, get_rule_expr(qh, 0, groupexpr, (rt_numused > 1)));
appendStringInfo(buf, sep);
get_rule_expr(buf, qh, 0, groupexpr, (rt_numused > 1));
sep = ", ";
}
}
/* ----------
* Copy the query string into allocated space and return it
* ----------
*/
return pstrdup(buf);
}
......@@ -1016,10 +984,9 @@ get_select_query_def(Query *query, QryHier *qh)
* get_insert_query_def - Parse back an INSERT parsetree
* ----------
*/
static char *
get_insert_query_def(Query *query, QryHier *qh)
static void
get_insert_query_def(StringInfo buf, Query *query, QryHier *qh)
{
char buf[BUFSIZE];
char *sep;
TargetEntry *tle;
RangeTblEntry *rte;
......@@ -1072,9 +1039,7 @@ get_insert_query_def(Query *query, QryHier *qh)
* ----------
*/
rte = (RangeTblEntry *) nth(query->resultRelation - 1, query->rtable);
strcpy(buf, "INSERT INTO \"");
strcat(buf, rte->relname);
strcat(buf, "\"");
appendStringInfo(buf, "INSERT INTO \"%s\"", rte->relname);
/* Add the target list */
sep = " (";
......@@ -1082,37 +1047,29 @@ get_insert_query_def(Query *query, QryHier *qh)
{
tle = (TargetEntry *) lfirst(l);
strcat(buf, sep);
appendStringInfo(buf, sep);
sep = ", ";
strcat(buf, "\"");
strcat(buf, tle->resdom->resname);
strcat(buf, "\"");
appendStringInfo(buf, "\"%s\"", tle->resdom->resname);
}
strcat(buf, ") ");
appendStringInfo(buf, ") ");
/* Add the VALUES or the SELECT */
if (rt_constonly && query->qual == NULL)
{
strcat(buf, "VALUES (");
appendStringInfo(buf, "VALUES (");
sep = "";
foreach(l, query->targetList)
{
tle = (TargetEntry *) lfirst(l);
strcat(buf, sep);
appendStringInfo(buf, sep);
sep = ", ";
strcat(buf, get_tle_expr(qh, 0, tle, (rt_numused > 1)));
get_tle_expr(buf, qh, 0, tle, (rt_numused > 1));
}
strcat(buf, ")");
appendStringInfo(buf, ")");
}
else
strcat(buf, get_select_query_def(query, qh));
/* ----------
* Copy the query string into allocated space and return it
* ----------
*/
return pstrdup(buf);
get_select_query_def(buf, query, qh);
}
......@@ -1120,10 +1077,9 @@ get_insert_query_def(Query *query, QryHier *qh)
* get_update_query_def - Parse back an UPDATE parsetree
* ----------
*/
static char *
get_update_query_def(Query *query, QryHier *qh)
static void
get_update_query_def(StringInfo buf, Query *query, QryHier *qh)
{
char buf[BUFSIZE];
char *sep;
TargetEntry *tle;
RangeTblEntry *rte;
......@@ -1134,9 +1090,7 @@ get_update_query_def(Query *query, QryHier *qh)
* ----------
*/
rte = (RangeTblEntry *) nth(query->resultRelation - 1, query->rtable);
strcpy(buf, "UPDATE ");
strcat(buf, rte->relname);
strcat(buf, " SET ");
appendStringInfo(buf, "UPDATE \"%s\" SET ", rte->relname);
/* Add the comma separated list of 'attname = value' */
sep = "";
......@@ -1144,28 +1098,18 @@ get_update_query_def(Query *query, QryHier *qh)
{
tle = (TargetEntry *) lfirst(l);
strcat(buf, sep);
appendStringInfo(buf, sep);
sep = ", ";
strcat(buf, "\"");
strcat(buf, tle->resdom->resname);
strcat(buf, "\" = ");
strcat(buf, get_tle_expr(qh, query->resultRelation,
tle, TRUE));
appendStringInfo(buf, "\"%s\" = ", tle->resdom->resname);
get_tle_expr(buf, qh, query->resultRelation, tle, TRUE);
}
/* Finally add a WHERE clause if given */
if (query->qual != NULL)
{
strcat(buf, " WHERE ");
strcat(buf, get_rule_expr(qh, query->resultRelation,
query->qual, TRUE));
appendStringInfo(buf, " WHERE ");
get_rule_expr(buf, qh, query->resultRelation, query->qual, TRUE);
}
/* ----------
* Copy the query string into allocated space and return it
* ----------
*/
return pstrdup(buf);
}
......@@ -1173,10 +1117,9 @@ get_update_query_def(Query *query, QryHier *qh)
* get_delete_query_def - Parse back a DELETE parsetree
* ----------
*/
static char *
get_delete_query_def(Query *query, QryHier *qh)
static void
get_delete_query_def(StringInfo buf, Query *query, QryHier *qh)
{
char buf[BUFSIZE];
RangeTblEntry *rte;
/* ----------
......@@ -1184,22 +1127,14 @@ get_delete_query_def(Query *query, QryHier *qh)
* ----------
*/
rte = (RangeTblEntry *) nth(query->resultRelation - 1, query->rtable);
strcpy(buf, "DELETE FROM \"");
strcat(buf, rte->relname);
strcat(buf, "\"");
appendStringInfo(buf, "DELETE FROM \"%s\"", rte->relname);
/* Add a WHERE clause if given */
if (query->qual != NULL)
{
strcat(buf, " WHERE ");
strcat(buf, get_rule_expr(qh, 0, query->qual, FALSE));
appendStringInfo(buf, " WHERE ");
get_rule_expr(buf, qh, 0, query->qual, FALSE);
}
/* ----------
* Copy the query string into allocated space and return it
* ----------
*/
return pstrdup(buf);
}
/*
......@@ -1221,28 +1156,26 @@ get_rte_for_var(Var *var, QryHier *qh)
* get_rule_expr - Parse back an expression
* ----------
*/
static char *
get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
static void
get_rule_expr(StringInfo buf, QryHier *qh, int rt_index,
Node *node, bool varprefix)
{
char buf[BUFSIZE];
if (node == NULL)
return pstrdup("");
buf[0] = '\0';
return;
/* ----------
* Each level of get_rule_expr must return an indivisible term
* Each level of get_rule_expr must emit an indivisible term
* (parenthesized if necessary) to ensure result is reparsed into
* the same expression tree.
*
* There might be some work left here to support additional node types...
* There might be some work left here to support additional node types.
* Can we ever see Param nodes here?
* ----------
*/
switch (nodeTag(node))
{
case T_Const:
return get_const_expr((Const *) node);
get_const_expr(buf, (Const *) node);
break;
case T_Var:
......@@ -1251,20 +1184,14 @@ get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
RangeTblEntry *rte = get_rte_for_var(var, qh);
if (!strcmp(rte->refname, "*NEW*"))
strcat(buf, "new.");
appendStringInfo(buf, "new.");
else if (!strcmp(rte->refname, "*CURRENT*"))
strcat(buf, "old.");
appendStringInfo(buf, "old.");
else
{
strcat(buf, "\"");
strcat(buf, rte->refname);
strcat(buf, "\".");
}
strcat(buf, "\"");
strcat(buf, get_attribute_name(rte->relid, var->varattno));
strcat(buf, "\"");
return pstrdup(buf);
appendStringInfo(buf, "\"%s\".", rte->refname);
appendStringInfo(buf, "\"%s\"",
get_attribute_name(rte->relid,
var->varattno));
}
break;
......@@ -1280,22 +1207,18 @@ get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
switch (expr->opType)
{
case OP_EXPR:
strcat(buf, "(");
appendStringInfo(buf, "(");
if (length(args) == 2)
{
/* binary operator */
strcat(buf,
get_rule_expr(qh, rt_index,
get_rule_expr(buf, qh, rt_index,
(Node *) lfirst(args),
varprefix));
strcat(buf, " ");
strcat(buf,
varprefix);
appendStringInfo(buf, " %s ",
get_opname(((Oper *) expr->oper)->opno));
strcat(buf, " ");
strcat(buf,
get_rule_expr(qh, rt_index,
get_rule_expr(buf, qh, rt_index,
(Node *) lsecond(args),
varprefix));
varprefix);
}
else
{
......@@ -1312,82 +1235,76 @@ get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
switch (optup->oprkind)
{
case 'l':
strcat(buf, get_opname(opno));
strcat(buf, " ");
strcat(buf,
get_rule_expr(qh, rt_index,
appendStringInfo(buf, "%s ",
get_opname(opno));
get_rule_expr(buf, qh, rt_index,
(Node *) lfirst(args),
varprefix));
varprefix);
break;
case 'r':
strcat(buf,
get_rule_expr(qh, rt_index,
get_rule_expr(buf, qh, rt_index,
(Node *) lfirst(args),
varprefix));
strcat(buf, " ");
strcat(buf, get_opname(opno));
varprefix);
appendStringInfo(buf, " %s",
get_opname(opno));
break;
default:
elog(ERROR, "get_rule_expr: bogus oprkind");
}
}
strcat(buf, ")");
return pstrdup(buf);
appendStringInfo(buf, ")");
break;
case OR_EXPR:
strcat(buf, "(");
strcat(buf, get_rule_expr(qh, rt_index,
appendStringInfo(buf, "(");
get_rule_expr(buf, qh, rt_index,
(Node *) lfirst(args),
varprefix));
varprefix);
/* It's not clear that we can ever see N-argument
* OR/AND clauses here, but might as well cope...
*/
while ((args = lnext(args)) != NIL)
{
strcat(buf, " OR ");
strcat(buf, get_rule_expr(qh, rt_index,
appendStringInfo(buf, " OR ");
get_rule_expr(buf, qh, rt_index,
(Node *) lfirst(args),
varprefix));
varprefix);
}
strcat(buf, ")");
return pstrdup(buf);
appendStringInfo(buf, ")");
break;
case AND_EXPR:
strcat(buf, "(");
strcat(buf, get_rule_expr(qh, rt_index,
appendStringInfo(buf, "(");
get_rule_expr(buf, qh, rt_index,
(Node *) lfirst(args),
varprefix));
varprefix);
while ((args = lnext(args)) != NIL)
{
strcat(buf, " AND ");
strcat(buf, get_rule_expr(qh, rt_index,
appendStringInfo(buf, " AND ");
get_rule_expr(buf, qh, rt_index,
(Node *) lfirst(args),
varprefix));
varprefix);
}
strcat(buf, ")");
return pstrdup(buf);
appendStringInfo(buf, ")");
break;
case NOT_EXPR:
strcat(buf, "(NOT ");
strcat(buf, get_rule_expr(qh, rt_index,
(Node *) get_leftop(expr),
varprefix));
strcat(buf, ")");
return pstrdup(buf);
appendStringInfo(buf, "(NOT ");
get_rule_expr(buf, qh, rt_index,
(Node *) lfirst(args),
varprefix);
appendStringInfo(buf, ")");
break;
case FUNC_EXPR:
return get_func_expr(qh, rt_index,
get_func_expr(buf, qh, rt_index,
(Expr *) node,
varprefix);
break;
default:
printf("\n%s\n", nodeToString(node));
elog(ERROR, "get_rule_expr: expr type not supported");
elog(ERROR, "get_rule_expr: expr opType %d not supported",
expr->opType);
}
}
break;
......@@ -1396,43 +1313,44 @@ get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
{
Aggref *aggref = (Aggref *) node;
strcat(buf, "\"");
strcat(buf, aggref->aggname);
strcat(buf, "\"(");
strcat(buf, get_rule_expr(qh, rt_index,
(Node *) (aggref->target), varprefix));
strcat(buf, ")");
return pstrdup(buf);
appendStringInfo(buf, "\"%s\"(", aggref->aggname);
get_rule_expr(buf, qh, rt_index,
(Node *) (aggref->target), varprefix);
appendStringInfo(buf, ")");
}
break;
case T_Iter:
get_rule_expr(buf, qh, rt_index,
((Iter *) node)->iterexpr, varprefix);
break;
case T_ArrayRef:
{
ArrayRef *aref = (ArrayRef *) node;
List *lowlist;
List *uplist;
strcat(buf, get_rule_expr(qh, rt_index,
aref->refexpr, varprefix));
get_rule_expr(buf, qh, rt_index,
aref->refexpr, varprefix);
lowlist = aref->reflowerindexpr;
foreach(uplist, aref->refupperindexpr)
{
strcat(buf, "[");
appendStringInfo(buf, "[");
if (lowlist)
{
strcat(buf, get_rule_expr(qh, rt_index,
get_rule_expr(buf, qh, rt_index,
(Node *) lfirst(lowlist),
varprefix));
strcat(buf, ":");
varprefix);
appendStringInfo(buf, ":");
lowlist = lnext(lowlist);
}
strcat(buf, get_rule_expr(qh, rt_index,
get_rule_expr(buf, qh, rt_index,
(Node *) lfirst(uplist),
varprefix));
strcat(buf, "]");
varprefix);
appendStringInfo(buf, "]");
}
/* XXX need to do anything with refassgnexpr? */
return pstrdup(buf);
}
break;
......@@ -1441,28 +1359,27 @@ get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
CaseExpr *caseexpr = (CaseExpr *) node;
List *temp;
strcat(buf, "CASE");
appendStringInfo(buf, "CASE");
foreach(temp, caseexpr->args)
{
CaseWhen *when = (CaseWhen *) lfirst(temp);
strcat(buf, " WHEN ");
strcat(buf, get_rule_expr(qh, rt_index,
when->expr, varprefix));
strcat(buf, " THEN ");
strcat(buf, get_rule_expr(qh, rt_index,
when->result, varprefix));
appendStringInfo(buf, " WHEN ");
get_rule_expr(buf, qh, rt_index,
when->expr, varprefix);
appendStringInfo(buf, " THEN ");
get_rule_expr(buf, qh, rt_index,
when->result, varprefix);
}
strcat(buf, " ELSE ");
strcat(buf, get_rule_expr(qh, rt_index,
caseexpr->defresult, varprefix));
strcat(buf, " END");
return pstrdup(buf);
appendStringInfo(buf, " ELSE ");
get_rule_expr(buf, qh, rt_index,
caseexpr->defresult, varprefix);
appendStringInfo(buf, " END");
}
break;
case T_SubLink:
return get_sublink_expr(qh, rt_index, node, varprefix);
get_sublink_expr(buf, qh, rt_index, node, varprefix);
break;
default:
......@@ -1471,8 +1388,6 @@ get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
rulename, nodeTag(node));
break;
}
return FALSE;
}
......@@ -1480,10 +1395,10 @@ get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
* get_func_expr - Parse back a Func node
* ----------
*/
static char *
get_func_expr(QryHier *qh, int rt_index, Expr *expr, bool varprefix)
static void
get_func_expr(StringInfo buf, QryHier *qh, int rt_index,
Expr *expr, bool varprefix)
{
char buf[BUFSIZE];
HeapTuple proctup;
Form_pg_proc procStruct;
List *l;
......@@ -1503,23 +1418,24 @@ get_func_expr(QryHier *qh, int rt_index, Expr *expr, bool varprefix)
procStruct = (Form_pg_proc) GETSTRUCT(proctup);
proname = nameout(&(procStruct->proname));
/*
* nullvalue() and nonnullvalue() should get turned into special syntax
*/
if (procStruct->pronargs == 1 && procStruct->proargtypes[0] == InvalidOid)
{
if (!strcmp(proname, "nullvalue"))
{
strcpy(buf, "(");
strcat(buf, get_rule_expr(qh, rt_index, lfirst(expr->args),
varprefix));
strcat(buf, " ISNULL)");
return pstrdup(buf);
appendStringInfo(buf, "(");
get_rule_expr(buf, qh, rt_index, lfirst(expr->args), varprefix);
appendStringInfo(buf, " ISNULL)");
return;
}
if (!strcmp(proname, "nonnullvalue"))
{
strcpy(buf, "(");
strcat(buf, get_rule_expr(qh, rt_index, lfirst(expr->args),
varprefix));
strcat(buf, " NOTNULL)");
return pstrdup(buf);
appendStringInfo(buf, "(");
get_rule_expr(buf, qh, rt_index, lfirst(expr->args), varprefix);
appendStringInfo(buf, " NOTNULL)");
return;
}
}
......@@ -1527,40 +1443,31 @@ get_func_expr(QryHier *qh, int rt_index, Expr *expr, bool varprefix)
* Build a string of proname(args)
* ----------
*/
strcpy(buf, "\"");
strcat(buf, proname);
strcat(buf, "\"(");
appendStringInfo(buf, "\"%s\"(", proname);
sep = "";
foreach(l, expr->args)
{
strcat(buf, sep);
appendStringInfo(buf, sep);
sep = ", ";
strcat(buf, get_rule_expr(qh, rt_index, lfirst(l), varprefix));
get_rule_expr(buf, qh, rt_index, lfirst(l), varprefix);
}
strcat(buf, ")");
/* ----------
* Copy the function call string into allocated space and return it
* ----------
*/
return pstrdup(buf);
appendStringInfo(buf, ")");
}
/* ----------
* get_tle_expr - A target list expression is a bit
* different from a normal expression.
* If the target column has an
* an atttypmod, the parser usually
* puts a padding-/cut-function call
* around the expression itself. We
* we must get rid of it, otherwise
* dump/reload/dump... would blow up
* get_tle_expr
*
* A target list expression is a bit different from a normal expression.
* If the target column has an atttypmod, the parser usually puts a
* padding-/cut-function call around the expression itself.
* We must get rid of it, otherwise dump/reload/dump... would blow up
* the expressions.
* ----------
*/
static char *
get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix)
static void
get_tle_expr(StringInfo buf, QryHier *qh, int rt_index,
TargetEntry *tle, bool varprefix)
{
Expr *expr = (Expr *) (tle->expr);
Func *func;
......@@ -1577,7 +1484,10 @@ get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix)
if (tle->resdom->restypmod < 0 ||
! IsA(expr, Expr) ||
expr->opType != FUNC_EXPR)
return get_rule_expr(qh, rt_index, tle->expr, varprefix);
{
get_rule_expr(buf, qh, rt_index, tle->expr, varprefix);
return;
}
func = (Func *) (expr->oper);
......@@ -1600,7 +1510,10 @@ get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix)
if (procStruct->pronargs != 2 ||
procStruct->prorettype != procStruct->proargtypes[0] ||
procStruct->proargtypes[1] != INT4OID)
return get_rule_expr(qh, rt_index, tle->expr, varprefix);
{
get_rule_expr(buf, qh, rt_index, tle->expr, varprefix);
return;
}
/*
* Furthermore, the name of the function must be the same
......@@ -1615,47 +1528,57 @@ get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix)
typeStruct = (Form_pg_type) GETSTRUCT(tup);
if (strncmp(procStruct->proname.data, typeStruct->typname.data,
NAMEDATALEN) != 0)
return get_rule_expr(qh, rt_index, tle->expr, varprefix);
{
get_rule_expr(buf, qh, rt_index, tle->expr, varprefix);
return;
}
/* ----------
* Finally (to be totally safe) the second argument must be a
* const and match the value in the results atttypmod.
* ----------
*/
second_arg = (Const *) nth(1, expr->args);
second_arg = (Const *) lsecond(expr->args);
if (! IsA(second_arg, Const) ||
((int4) second_arg->constvalue) != tle->resdom->restypmod)
return get_rule_expr(qh, rt_index, tle->expr, varprefix);
DatumGetInt32(second_arg->constvalue) != tle->resdom->restypmod)
{
get_rule_expr(buf, qh, rt_index, tle->expr, varprefix);
return;
}
/* ----------
* Whow - got it. Now get rid of the padding function
* ----------
*/
return get_rule_expr(qh, rt_index, lfirst(expr->args), varprefix);
get_rule_expr(buf, qh, rt_index, lfirst(expr->args), varprefix);
}
/* ----------
* get_const_expr - Make a string representation
* with the type cast out of a Const
* get_const_expr
*
* Make a string representation with the type cast out of a Const
* ----------
*/
static char *
get_const_expr(Const *constval)
static void
get_const_expr(StringInfo buf, Const *constval)
{
HeapTuple typetup;
Form_pg_type typeStruct;
FmgrInfo finfo_output;
char *extval;
char *valptr;
bool isnull = FALSE;
char buf[BUFSIZE];
char namebuf[64];
if (constval->constisnull)
return pstrdup("NULL");
{
appendStringInfo(buf, "NULL");
return;
}
typetup = SearchSysCacheTuple(TYPOID,
ObjectIdGetDatum(constval->consttype), 0, 0, 0);
ObjectIdGetDatum(constval->consttype),
0, 0, 0);
if (!HeapTupleIsValid(typetup))
elog(ERROR, "cache lookup of type %u failed", constval->consttype);
......@@ -1665,11 +1588,34 @@ get_const_expr(Const *constval)
extval = (char *) (*fmgr_faddr(&finfo_output)) (constval->constvalue,
&isnull, -1);
sprintf(namebuf, "::\"%s\"", nameout(&(typeStruct->typname)));
if (strcmp(namebuf, "::unknown") == 0)
namebuf[0] = '\0';
sprintf(buf, "'%s'%s", extval, namebuf);
return pstrdup(buf);
/*
* We must quote any funny characters in the constant's representation.
* XXX Any MULTIBYTE considerations here?
*/
appendStringInfoChar(buf, '\'');
for (valptr = extval; *valptr; valptr++)
{
char ch = *valptr;
if (ch == '\'' || ch == '\\')
{
appendStringInfoChar(buf, '\\');
appendStringInfoChar(buf, ch);
}
else if (ch >= 0 && ch < ' ')
{
appendStringInfo(buf, "\\%03o", ch);
}
else
appendStringInfoChar(buf, ch);
}
appendStringInfoChar(buf, '\'');
pfree(extval);
extval = (char *) nameout(&(typeStruct->typname));
/* probably would be better to recognize UNKNOWN by OID... */
if (strcmp(extval, "unknown") != 0)
appendStringInfo(buf, "::\"%s\"", extval);
pfree(extval);
}
......@@ -1677,75 +1623,67 @@ get_const_expr(Const *constval)
* get_sublink_expr - Parse back a sublink
* ----------
*/
static char *
get_sublink_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
static void
get_sublink_expr(StringInfo buf, QryHier *qh, int rt_index,
Node *node, bool varprefix)
{
SubLink *sublink = (SubLink *) node;
Query *query = (Query *) (sublink->subselect);
Oper *oper;
List *l;
char *sep;
char buf[BUFSIZE];
buf[0] = '\0';
strcat(buf, "(");
appendStringInfo(buf, "(");
if (sublink->lefthand != NULL)
{
if (length(sublink->lefthand) > 1)
strcat(buf, "(");
appendStringInfo(buf, "(");
sep = "";
foreach(l, sublink->lefthand)
{
strcat(buf, sep);
appendStringInfo(buf, sep);
sep = ", ";
strcat(buf, get_rule_expr(qh, rt_index,
lfirst(l), varprefix));
get_rule_expr(buf, qh, rt_index, lfirst(l), varprefix);
}
if (length(sublink->lefthand) > 1)
strcat(buf, ") ");
appendStringInfo(buf, ") ");
else
strcat(buf, " ");
appendStringInfo(buf, " ");
}
switch (sublink->subLinkType)
{
case EXISTS_SUBLINK:
strcat(buf, "EXISTS ");
appendStringInfo(buf, "EXISTS ");
break;
case ANY_SUBLINK:
oper = (Oper *) lfirst(sublink->oper);
strcat(buf, get_opname(oper->opno));
strcat(buf, " ANY ");
appendStringInfo(buf, "%s ANY ", get_opname(oper->opno));
break;
case ALL_SUBLINK:
oper = (Oper *) lfirst(sublink->oper);
strcat(buf, get_opname(oper->opno));
strcat(buf, " ALL ");
appendStringInfo(buf, "%s ALL ", get_opname(oper->opno));
break;
case EXPR_SUBLINK:
oper = (Oper *) lfirst(sublink->oper);
strcat(buf, get_opname(oper->opno));
strcat(buf, " ");
appendStringInfo(buf, "%s ", get_opname(oper->opno));
break;
default:
elog(ERROR, "unupported sublink type %d",
elog(ERROR, "get_sublink_expr: unsupported sublink type %d",
sublink->subLinkType);
break;
}
strcat(buf, "(");
strcat(buf, get_query_def(query, qh));
strcat(buf, "))");
return pstrdup(buf);
appendStringInfo(buf, "(");
get_query_def(buf, query, qh);
appendStringInfo(buf, "))");
}
......
......@@ -1092,7 +1092,7 @@ toyemp |SELECT "emp"."name", "emp"."age", "emp"."location", ('12'::"i
QUERY: SELECT tablename, rulename, definition FROM pg_rules
ORDER BY tablename, rulename;
tablename |rulename |definition
-------------+---------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------+---------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
rtest_emp |rtest_emp_del |CREATE RULE "rtest_emp_del" AS ON DELETE TO "rtest_emp" DO INSERT INTO "rtest_emplog" ("ename", "who", "action", "newsal", "oldsal") VALUES (old."ename", "getpgusername"(), 'fired'::"bpchar", '$0.00'::"money", old."salary");
rtest_emp |rtest_emp_ins |CREATE RULE "rtest_emp_ins" AS ON INSERT TO "rtest_emp" DO INSERT INTO "rtest_emplog" ("ename", "who", "action", "newsal", "oldsal") VALUES (new."ename", "getpgusername"(), 'hired'::"bpchar", new."salary", '$0.00'::"money");
rtest_emp |rtest_emp_upd |CREATE RULE "rtest_emp_upd" AS ON UPDATE TO "rtest_emp" WHERE (new."salary" <> old."salary") DO INSERT INTO "rtest_emplog" ("ename", "who", "action", "newsal", "oldsal") VALUES (new."ename", "getpgusername"(), 'honored'::"bpchar", new."salary", old."salary");
......@@ -1105,20 +1105,20 @@ rtest_order1 |rtest_order_r2 |CREATE RULE "rtest_order_r2" AS ON INSERT TO "rtes
rtest_order1 |rtest_order_r3 |CREATE RULE "rtest_order_r3" AS ON INSERT TO "rtest_order1" DO INSTEAD INSERT INTO "rtest_order2" ("a", "b", "c") VALUES (new."a", "nextval"('rtest_seq'::"text"), 'rule 3 - this should run 3rd or 4th'::"text");
rtest_order1 |rtest_order_r4 |CREATE RULE "rtest_order_r4" AS ON INSERT TO "rtest_order1" WHERE ("rtest_order2"."a" < '100'::"int4") DO INSTEAD INSERT INTO "rtest_order2" ("a", "b", "c") VALUES (new."a", "nextval"('rtest_seq'::"text"), 'rule 4 - this should run 2nd'::"text");
rtest_person |rtest_pers_del |CREATE RULE "rtest_pers_del" AS ON DELETE TO "rtest_person" DO DELETE FROM "rtest_admin" WHERE ("rtest_admin"."pname" = old."pname");
rtest_person |rtest_pers_upd |CREATE RULE "rtest_pers_upd" AS ON UPDATE TO "rtest_person" DO UPDATE rtest_admin SET "pname" = new."pname" WHERE ("rtest_admin"."pname" = old."pname");
rtest_person |rtest_pers_upd |CREATE RULE "rtest_pers_upd" AS ON UPDATE TO "rtest_person" DO UPDATE "rtest_admin" SET "pname" = new."pname" WHERE ("rtest_admin"."pname" = old."pname");
rtest_system |rtest_sys_del |CREATE RULE "rtest_sys_del" AS ON DELETE TO "rtest_system" DO (DELETE FROM "rtest_interface" WHERE ("rtest_interface"."sysname" = old."sysname"); DELETE FROM "rtest_admin" WHERE ("rtest_admin"."sysname" = old."sysname"); );
rtest_system |rtest_sys_upd |CREATE RULE "rtest_sys_upd" AS ON UPDATE TO "rtest_system" DO (UPDATE rtest_interface SET "sysname" = new."sysname" WHERE ("rtest_interface"."sysname" = old."sysname"); UPDATE rtest_admin SET "sysname" = new."sysname" WHERE ("rtest_admin"."sysname" = old."sysname"); );
rtest_system |rtest_sys_upd |CREATE RULE "rtest_sys_upd" AS ON UPDATE TO "rtest_system" DO (UPDATE "rtest_interface" SET "sysname" = new."sysname" WHERE ("rtest_interface"."sysname" = old."sysname"); UPDATE "rtest_admin" SET "sysname" = new."sysname" WHERE ("rtest_admin"."sysname" = old."sysname"); );
rtest_t4 |rtest_t4_ins1 |CREATE RULE "rtest_t4_ins1" AS ON INSERT TO "rtest_t4" WHERE ((new."a" >= '10'::"int4") AND (new."a" < '20'::"int4")) DO INSTEAD INSERT INTO "rtest_t5" ("a", "b") VALUES (new."a", new."b");
rtest_t4 |rtest_t4_ins2 |CREATE RULE "rtest_t4_ins2" AS ON INSERT TO "rtest_t4" WHERE ((new."a" >= '20'::"int4") AND (new."a" < '30'::"int4")) DO INSERT INTO "rtest_t6" ("a", "b") VALUES (new."a", new."b");
rtest_t5 |rtest_t5_ins |CREATE RULE "rtest_t5_ins" AS ON INSERT TO "rtest_t5" WHERE (new."a" > '15'::"int4") DO INSERT INTO "rtest_t7" ("a", "b") VALUES (new."a", new."b");
rtest_t6 |rtest_t6_ins |CREATE RULE "rtest_t6_ins" AS ON INSERT TO "rtest_t6" WHERE (new."a" > '25'::"int4") DO INSTEAD INSERT INTO "rtest_t8" ("a", "b") VALUES (new."a", new."b");
rtest_v1 |rtest_v1_del |CREATE RULE "rtest_v1_del" AS ON DELETE TO "rtest_v1" DO INSTEAD DELETE FROM "rtest_t1" WHERE ("rtest_t1"."a" = old."a");
rtest_v1 |rtest_v1_ins |CREATE RULE "rtest_v1_ins" AS ON INSERT TO "rtest_v1" DO INSTEAD INSERT INTO "rtest_t1" ("a", "b") VALUES (new."a", new."b");
rtest_v1 |rtest_v1_upd |CREATE RULE "rtest_v1_upd" AS ON UPDATE TO "rtest_v1" DO INSTEAD UPDATE rtest_t1 SET "a" = new."a", "b" = new."b" WHERE ("rtest_t1"."a" = old."a");
rtest_v1 |rtest_v1_upd |CREATE RULE "rtest_v1_upd" AS ON UPDATE TO "rtest_v1" DO INSTEAD UPDATE "rtest_t1" SET "a" = new."a", "b" = new."b" WHERE ("rtest_t1"."a" = old."a");
shoelace |shoelace_del |CREATE RULE "shoelace_del" AS ON DELETE TO "shoelace" DO INSTEAD DELETE FROM "shoelace_data" WHERE ("shoelace_data"."sl_name" = old."sl_name");
shoelace |shoelace_ins |CREATE RULE "shoelace_ins" AS ON INSERT TO "shoelace" DO INSTEAD INSERT INTO "shoelace_data" ("sl_name", "sl_avail", "sl_color", "sl_len", "sl_unit") VALUES (new."sl_name", new."sl_avail", new."sl_color", new."sl_len", new."sl_unit");
shoelace |shoelace_upd |CREATE RULE "shoelace_upd" AS ON UPDATE TO "shoelace" DO INSTEAD UPDATE shoelace_data SET "sl_name" = new."sl_name", "sl_avail" = new."sl_avail", "sl_color" = new."sl_color", "sl_len" = new."sl_len", "sl_unit" = new."sl_unit" WHERE ("shoelace_data"."sl_name" = old."sl_name");
shoelace |shoelace_upd |CREATE RULE "shoelace_upd" AS ON UPDATE TO "shoelace" DO INSTEAD UPDATE "shoelace_data" SET "sl_name" = new."sl_name", "sl_avail" = new."sl_avail", "sl_color" = new."sl_color", "sl_len" = new."sl_len", "sl_unit" = new."sl_unit" WHERE ("shoelace_data"."sl_name" = old."sl_name");
shoelace_data|log_shoelace |CREATE RULE "log_shoelace" AS ON UPDATE TO "shoelace_data" WHERE (new."sl_avail" <> old."sl_avail") DO INSERT INTO "shoelace_log" ("sl_name", "sl_avail", "log_who", "log_when") VALUES (new."sl_name", new."sl_avail", 'Al Bundy'::"name", 'epoch'::"datetime");
shoelace_ok |shoelace_ok_ins|CREATE RULE "shoelace_ok_ins" AS ON INSERT TO "shoelace_ok" DO INSTEAD UPDATE shoelace SET "sl_avail" = ("shoelace"."sl_avail" + new."ok_quant") WHERE ("shoelace"."sl_name" = new."ok_name");
shoelace_ok |shoelace_ok_ins|CREATE RULE "shoelace_ok_ins" AS ON INSERT TO "shoelace_ok" DO INSTEAD UPDATE "shoelace" SET "sl_avail" = ("shoelace"."sl_avail" + new."ok_quant") WHERE ("shoelace"."sl_name" = new."ok_name");
(27 rows)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment