📄 ruleutils.c
字号:
/********************************************************************** * get_ruledef.c - Function to get a rules definition text * out of it's tuple * * IDENTIFICATION * $Header: /usr/local/cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.18.2.3 1999/09/05 22:55:28 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * * The author hereby grants permission to use, copy, modify, * distribute, and license this software and its documentation * for any purpose, provided that existing copyright notices are * retained in all copies and that this notice is included * verbatim in any distributions. No written agreement, license, * or royalty fee is required for any of the authorized uses. * Modifications to this software may be copyrighted by their * author and need not follow the licensing terms described * here, provided that the new terms are clearly indicated on * the first page of each file where they apply. * * IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS * SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN * IF THE AUTHOR HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * * THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON * AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAVE NO * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, * ENHANCEMENTS, OR MODIFICATIONS. * **********************************************************************/#include <unistd.h>#include <fcntl.h>#include "postgres.h"#include "executor/spi.h"#include "optimizer/clauses.h"#include "utils/lsyscache.h"#include "catalog/pg_index.h"#include "catalog/pg_operator.h"#include "catalog/pg_shadow.h"#define BUFSIZE 8192/* ---------- * Local data types * ---------- */typedef struct QryHier{ struct QryHier *parent; Query *query;} QryHier;typedef struct { Index rt_index; int levelsup;} check_if_rte_used_context;/* ---------- * Global data * ---------- */static char *rulename;static void *plan_getrule = NULL;static char *query_getrule = "SELECT * FROM pg_rewrite WHERE rulename = $1";static void *plan_getview = NULL;static char *query_getview = "SELECT * FROM pg_rewrite WHERE rulename = $1 or rulename = $2";static void *plan_getam = NULL;static char *query_getam = "SELECT * FROM pg_am WHERE oid = $1";static void *plan_getopclass = NULL;static char *query_getopclass = "SELECT * FROM pg_opclass WHERE oid = $1";/* ---------- * Global functions * ---------- */text *pg_get_ruledef(NameData *rname);text *pg_get_viewdef(NameData *rname);text *pg_get_indexdef(Oid indexrelid);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);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 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);static bool check_if_rte_used_walker(Node *node, check_if_rte_used_context *context);/* ---------- * get_ruledef - Do it all and return a text * that could be used as a statement * to recreate the rule * ---------- */text *pg_get_ruledef(NameData *rname){ text *ruledef; Datum args[1]; char nulls[2]; int spirc; HeapTuple ruletup; TupleDesc rulettc; char *tmp; int len; /* ---------- * We need the rules name somewhere deep down * ---------- */ rulename = nameout(rname); /* ---------- * Connect to SPI manager * ---------- */ if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "get_ruledef: cannot connect to SPI manager"); /* ---------- * On the first call prepare the plan to lookup pg_proc. * We read pg_proc over the SPI manager instead of using * the syscache to be checked for read access on pg_proc. * ---------- */ if (plan_getrule == NULL) { Oid argtypes[1]; void *plan; argtypes[0] = NAMEOID; plan = SPI_prepare(query_getrule, 1, argtypes); if (plan == NULL) elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getrule); plan_getrule = SPI_saveplan(plan); } /* ---------- * Get the pg_rewrite tuple for this rule * ---------- */ args[0] = PointerGetDatum(rulename); nulls[0] = (rulename == NULL) ? 'n' : ' '; nulls[1] = '\0'; spirc = SPI_execp(plan_getrule, args, nulls, 1); if (spirc != SPI_OK_SELECT) elog(ERROR, "failed to get pg_rewrite tuple for %s", rulename); if (SPI_processed != 1) { if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "get_ruledef: SPI_finish() failed"); ruledef = SPI_palloc(VARHDRSZ + 1); VARSIZE(ruledef) = VARHDRSZ + 1; VARDATA(ruledef)[0] = '-'; return ruledef; } ruletup = SPI_tuptable->vals[0]; rulettc = SPI_tuptable->tupdesc; /* ---------- * Get the rules definition and put it into executors memory * ---------- */ tmp = make_ruledef(ruletup, rulettc); len = strlen(tmp) + VARHDRSZ; ruledef = SPI_palloc(len); VARSIZE(ruledef) = len; memcpy(VARDATA(ruledef), tmp, len - VARHDRSZ); /* ---------- * Disconnect from SPI manager * ---------- */ if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "get_ruledef: SPI_finish() failed"); /* ---------- * Easy - isn't it? * ---------- */ return ruledef;}/* ---------- * get_viewdef - Mainly the same thing, but we * only return the SELECT part of a view * ---------- */text *pg_get_viewdef(NameData *rname){ text *ruledef; Datum args[2]; char nulls[3]; int spirc; HeapTuple ruletup; TupleDesc rulettc; char *tmp; int len; char name1[NAMEDATALEN + 5]; char name2[NAMEDATALEN + 5]; /* ---------- * We need the rules name somewhere deep down * ---------- */ rulename = nameout(rname); /* ---------- * Connect to SPI manager * ---------- */ if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "get_viewdef: cannot connect to SPI manager"); /* ---------- * On the first call prepare the plan to lookup pg_proc. * We read pg_proc over the SPI manager instead of using * the syscache to be checked for read access on pg_proc. * ---------- */ if (plan_getview == NULL) { Oid argtypes[2]; void *plan; argtypes[0] = NAMEOID; argtypes[1] = NAMEOID; plan = SPI_prepare(query_getview, 2, argtypes); if (plan == NULL) elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getview); plan_getview = SPI_saveplan(plan); } /* ---------- * Get the pg_rewrite tuple for this rule * ---------- */ sprintf(name1, "_RET%s", rulename); sprintf(name2, "_ret%s", rulename); args[0] = PointerGetDatum(name1); args[1] = PointerGetDatum(name2); nulls[0] = ' '; nulls[1] = ' '; nulls[2] = '\0'; 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); if (SPI_processed != 1) tmp = "Not a view"; else { /* ---------- * Get the rules definition and put it into executors memory * ---------- */ ruletup = SPI_tuptable->vals[0]; rulettc = SPI_tuptable->tupdesc; tmp = make_viewdef(ruletup, rulettc); } len = strlen(tmp) + VARHDRSZ; ruledef = SPI_palloc(len); VARSIZE(ruledef) = len; memcpy(VARDATA(ruledef), tmp, len - VARHDRSZ); /* ---------- * Disconnect from SPI manager * ---------- */ if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "get_viewdef: SPI_finish() failed"); /* ---------- * Easy - isn't it? * ---------- */ return ruledef;}/* ---------- * get_viewdef - Mainly the same thing, but we * only return the SELECT part of a view * ---------- */text *pg_get_indexdef(Oid indexrelid){ text *indexdef; HeapTuple ht_idx; HeapTuple ht_idxrel; HeapTuple ht_indrel; HeapTuple spi_tup; TupleDesc spi_ttc; int spi_fno; Form_pg_index idxrec; Form_pg_class idxrelrec; Form_pg_class indrelrec; Datum spi_args[1]; char spi_nulls[2]; int spirc; int len; int keyno; char buf[BUFSIZE]; char keybuf[BUFSIZE]; char *sep; /* ---------- * Connect to SPI manager * ---------- */ if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "get_indexdef: cannot connect to SPI manager"); /* ---------- * On the first call prepare the plans to lookup pg_am * and pg_opclass. * ---------- */ if (plan_getam == NULL) { Oid argtypes[1]; void *plan; argtypes[0] = OIDOID; plan = SPI_prepare(query_getam, 1, argtypes); if (plan == NULL) elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getam); plan_getam = SPI_saveplan(plan); argtypes[0] = OIDOID; plan = SPI_prepare(query_getopclass, 1, argtypes); if (plan == NULL) elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getopclass); plan_getopclass = SPI_saveplan(plan); } /* ---------- * Fetch the pg_index tuple by the Oid of the index * ---------- */ ht_idx = SearchSysCacheTuple(INDEXRELID, ObjectIdGetDatum(indexrelid), 0, 0, 0); if (!HeapTupleIsValid(ht_idx)) elog(ERROR, "syscache lookup for index %u failed", indexrelid); idxrec = (Form_pg_index) GETSTRUCT(ht_idx); /* ---------- * Fetch the pg_class tuple of the index relation * ---------- */ ht_idxrel = SearchSysCacheTuple(RELOID, ObjectIdGetDatum(idxrec->indexrelid), 0, 0, 0); if (!HeapTupleIsValid(ht_idxrel)) elog(ERROR, "syscache lookup for relid %u failed", idxrec->indexrelid); idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel); /* ---------- * Fetch the pg_class tuple of the indexed relation * ---------- */ ht_indrel = SearchSysCacheTuple(RELOID, ObjectIdGetDatum(idxrec->indrelid), 0, 0, 0); if (!HeapTupleIsValid(ht_indrel)) elog(ERROR, "syscache lookup for relid %u failed", idxrec->indrelid); indrelrec = (Form_pg_class) GETSTRUCT(ht_indrel); /* ---------- * Get the am name for the index relation * ---------- */ spi_args[0] = ObjectIdGetDatum(idxrelrec->relam); spi_nulls[0] = ' '; spi_nulls[1] = '\0'; spirc = SPI_execp(plan_getam, spi_args, spi_nulls, 1); if (spirc != SPI_OK_SELECT) elog(ERROR, "failed to get pg_am tuple for index %s", nameout(&(idxrelrec->relname))); if (SPI_processed != 1) elog(ERROR, "failed to get pg_am tuple for index %s", nameout(&(idxrelrec->relname))); spi_tup = SPI_tuptable->vals[0]; spi_ttc = SPI_tuptable->tupdesc; spi_fno = SPI_fnumber(spi_ttc, "amname"); /* ---------- * Start the index definition * ---------- */ sprintf(buf, "CREATE %sINDEX \"%s\" ON \"%s\" USING %s (", idxrec->indisunique ? "UNIQUE " : "", nameout(&(idxrelrec->relname)), nameout(&(indrelrec->relname)), SPI_getvalue(spi_tup, spi_ttc, spi_fno)); /* ---------- * Collect the indexed attributes * ---------- */ sep = ""; keybuf[0] = '\0'; for (keyno = 0; keyno < INDEX_MAX_KEYS; keyno++) { if (idxrec->indkey[keyno] == InvalidAttrNumber) break; strcat(keybuf, sep); sep = ", "; /* ---------- * Add the indexed field name * ---------- */ strcat(keybuf, "\""); if (idxrec->indkey[keyno] == ObjectIdAttributeNumber - 1) strcat(keybuf, "oid"); else strcat(keybuf, get_attribute_name(idxrec->indrelid, idxrec->indkey[keyno])); strcat(keybuf, "\""); /* ---------- * If not a functional index, add the operator class name * ---------- */ if (idxrec->indproc == InvalidOid) { spi_args[0] = ObjectIdGetDatum(idxrec->indclass[keyno]); spi_nulls[0] = ' '; spi_nulls[1] = '\0'; spirc = SPI_execp(plan_getopclass, spi_args, spi_nulls, 1); if (spirc != SPI_OK_SELECT) elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[keyno]); if (SPI_processed != 1) elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[keyno]); 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, "\""); } } /* ---------- * For functional index say 'func (attrs) opclass' * ---------- */ if (idxrec->indproc != InvalidOid) { HeapTuple proctup; Form_pg_proc procStruct; proctup = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(idxrec->indproc), 0, 0, 0); if (!HeapTupleIsValid(proctup)) 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, ") "); spi_args[0] = ObjectIdGetDatum(idxrec->indclass[0]); spi_nulls[0] = ' '; spi_nulls[1] = '\0'; spirc = SPI_execp(plan_getopclass, spi_args, spi_nulls, 1); if (spirc != SPI_OK_SELECT) elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[0]); if (SPI_processed != 1) elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[0]); 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, "\""); } else /* ---------- * For the others say 'attr opclass [, ...]' * ---------- */ strcat(buf, keybuf); /* ---------- * Finish * ---------- */ strcat(buf, ")"); /* ---------- * Create the result in upper executor memory * ---------- */ len = strlen(buf) + VARHDRSZ; indexdef = SPI_palloc(len); VARSIZE(indexdef) = len; memcpy(VARDATA(indexdef), buf, len - VARHDRSZ); /* ---------- * Disconnect from SPI manager * ---------- */ if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "get_viewdef: SPI_finish() failed"); return indexdef;}/* ---------- * get_userbyid - Get a user name by usesysid and * fallback to 'unknown (UID=n)' * ---------- */NameData *pg_get_userbyid(int4 uid){ HeapTuple usertup; Form_pg_shadow user_rec; NameData *result; /* ---------- * Allocate space for the result * ---------- */ result = (NameData *) palloc(NAMEDATALEN); memset(result->data, 0, NAMEDATALEN); /* ---------- * Get the pg_shadow entry and print the result * ---------- */ usertup = SearchSysCacheTuple(USESYSID, ObjectIdGetDatum(uid), 0, 0, 0); if (HeapTupleIsValid(usertup)) { user_rec = (Form_pg_shadow) GETSTRUCT(usertup); StrNCpy(result->data, (&(user_rec->usename))->data, NAMEDATALEN); } else sprintf((char *) result, "unknown (UID=%d)", uid); return result;}/* ---------- * make_ruledef - reconstruct the CREATE RULE command * for a given pg_rewrite tuple * ---------- */static char *make_ruledef(HeapTuple ruletup, TupleDesc rulettc){ char *buf; char ev_type; Oid ev_class; int2 ev_attr; bool is_instead; char *ev_qual; char *ev_action; List *actions = NIL; int fno; bool isnull; /* ---------- * Allocate space for the returned rule definition text * ---------- */ buf = palloc(BUFSIZE); /* ---------- * Get the attribute values from the rules tuple * ---------- */ fno = SPI_fnumber(rulettc, "ev_type"); ev_type = (char) SPI_getbinval(ruletup, rulettc, fno, &isnull); fno = SPI_fnumber(rulettc, "ev_class"); ev_class = (Oid) SPI_getbinval(ruletup, rulettc, fno, &isnull); fno = SPI_fnumber(rulettc, "ev_attr"); ev_attr = (int2) SPI_getbinval(ruletup, rulettc, fno, &isnull);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -