On http://github.com/pbaros/postgres can be seen changes and my attempt
to implement materialized views. The first commit to the repository
implements following:
Materialized view can be created, dropped and used in SELECT statement.
CREATE MATERIALIZED VIEW mvname AS SELECT ...;
DROP MATERIALIZED VIEW mvname [CASCADE];
SELECT * FROM mvname;
also works:
COMMENT ON MATERIALIZED VIEW mvname IS 'etc.';
SELECT pg_get_viewdef(mvname);
... also you can look at enclosed patch.
*** ./src/backend/access/common/reloptions.c.orig 2010-06-23 16:31:24.000000000 +0200
--- ./src/backend/access/common/reloptions.c 2010-06-25 13:51:58.000000000 +0200
***************
*** 775,780 ****
--- 775,781 ----
switch (classForm->relkind)
{
case RELKIND_RELATION:
+ case RELKIND_MATVIEW:
case RELKIND_TOASTVALUE:
case RELKIND_UNCATALOGED:
options = heap_reloptions(classForm->relkind, datum, false);
***************
*** 1172,1177 ****
--- 1173,1179 ----
}
return (bytea *) rdopts;
case RELKIND_RELATION:
+ case RELKIND_MATVIEW:
return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
default:
/* sequences, composite types and views are not supported */
*** ./src/backend/access/heap/heapam.c.orig 2010-06-23 16:31:24.000000000 +0200
--- ./src/backend/access/heap/heapam.c 2010-06-25 13:52:55.000000000 +0200
***************
*** 1877,1883 ****
* Note: below this point, heaptup is the data we actually intend to store
* into the relation; tup is the caller's original untoasted data.
*/
! if (relation->rd_rel->relkind != RELKIND_RELATION)
{
/* toast table entries should never be recursively toasted */
Assert(!HeapTupleHasExternal(tup));
--- 1877,1884 ----
* Note: below this point, heaptup is the data we actually intend to store
* into the relation; tup is the caller's original untoasted data.
*/
! if (relation->rd_rel->relkind != RELKIND_RELATION &&
! relation->rd_rel->relkind != RELKIND_MATVIEW)
{
/* toast table entries should never be recursively toasted */
Assert(!HeapTupleHasExternal(tup));
*** ./src/backend/catalog/dependency.c.orig 2010-06-23 16:31:25.000000000 +0200
--- ./src/backend/catalog/dependency.c 2010-06-25 13:53:46.000000000 +0200
***************
*** 2731,2736 ****
--- 2731,2740 ----
appendStringInfo(buffer, _("view %s"),
relname);
break;
+ case RELKIND_MATVIEW:
+ appendStringInfo(buffer, _("materialized view %s"),
+ relname);
+ break;
case RELKIND_COMPOSITE_TYPE:
appendStringInfo(buffer, _("composite type %s"),
relname);
*** ./src/backend/catalog/heap.c.orig 2010-06-23 16:31:25.000000000 +0200
--- ./src/backend/catalog/heap.c 2010-06-25 13:54:25.000000000 +0200
***************
*** 758,763 ****
--- 758,764 ----
case RELKIND_RELATION:
case RELKIND_INDEX:
case RELKIND_TOASTVALUE:
+ case RELKIND_MATVIEW:
/* The relation is real, but as yet empty */
new_rel_reltup->relpages = 0;
new_rel_reltup->reltuples = 0;
***************
*** 776,782 ****
/* Initialize relfrozenxid */
if (relkind == RELKIND_RELATION ||
! relkind == RELKIND_TOASTVALUE)
{
/*
* Initialize to the minimum XID that could put tuples in the table.
--- 777,784 ----
/* Initialize relfrozenxid */
if (relkind == RELKIND_RELATION ||
! relkind == RELKIND_TOASTVALUE ||
! relkind == RELKIND_MATVIEW)
{
/*
* Initialize to the minimum XID that could put tuples in the table.
***************
*** 1027,1032 ****
--- 1029,1035 ----
*/
if (IsUnderPostmaster && (relkind == RELKIND_RELATION ||
relkind == RELKIND_VIEW ||
+ relkind == RELKIND_MATVIEW ||
relkind == RELKIND_COMPOSITE_TYPE))
new_array_oid = AssignTypeArrayOid();
*** ./src/backend/catalog/system_views.sql.orig 2010-06-23 16:31:25.000000000 +0200
--- ./src/backend/catalog/system_views.sql 2010-06-25 13:55:24.000000000 +0200
***************
*** 76,82 ****
pg_get_userbyid(C.relowner) AS viewowner,
pg_get_viewdef(C.oid) AS definition
FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
! WHERE C.relkind = 'v';
CREATE VIEW pg_tables AS
SELECT
--- 76,82 ----
pg_get_userbyid(C.relowner) AS viewowner,
pg_get_viewdef(C.oid) AS definition
FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
! WHERE C.relkind = 'v' OR C.relkind = 'm';
CREATE VIEW pg_tables AS
SELECT
*** ./src/backend/commands/comment.c.orig 2010-06-23 16:31:25.000000000 +0200
--- ./src/backend/commands/comment.c 2010-06-25 13:58:10.000000000 +0200
***************
*** 107,112 ****
--- 107,113 ----
case OBJECT_SEQUENCE:
case OBJECT_TABLE:
case OBJECT_VIEW:
+ case OBJECT_MATVIEW:
CommentRelation(stmt->objtype, stmt->objname, stmt->comment);
break;
case OBJECT_COLUMN:
***************
*** 580,585 ****
--- 581,593 ----
errmsg("\"%s\" is not a view",
RelationGetRelationName(relation))));
break;
+ case OBJECT_MATVIEW:
+ if (relation->rd_rel->relkind != RELKIND_MATVIEW)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a materialized view",
+ RelationGetRelationName(relation))));
+ break;
}
/* Create the comment using the relation's oid */
*** ./src/backend/commands/copy.c.orig 2010-06-23 16:31:25.000000000 +0200
--- ./src/backend/commands/copy.c 2010-06-25 14:01:28.000000000 +0200
***************
*** 1227,1233 ****
if (cstate->rel)
{
! if (cstate->rel->rd_rel->relkind != RELKIND_RELATION)
{
if (cstate->rel->rd_rel->relkind == RELKIND_VIEW)
ereport(ERROR,
--- 1227,1233 ----
if (cstate->rel)
{
! if (cstate->rel->rd_rel->relkind != RELKIND_RELATION && cstate->rel->rd_rel->relkind != RELKIND_MATVIEW)
{
if (cstate->rel->rd_rel->relkind == RELKIND_VIEW)
ereport(ERROR,
***************
*** 1701,1707 ****
Assert(cstate->rel);
! if (cstate->rel->rd_rel->relkind != RELKIND_RELATION)
{
if (cstate->rel->rd_rel->relkind == RELKIND_VIEW)
ereport(ERROR,
--- 1701,1707 ----
Assert(cstate->rel);
! if (cstate->rel->rd_rel->relkind != RELKIND_RELATION && cstate->rel->rd_rel->relkind != RELKIND_MATVIEW)
{
if (cstate->rel->rd_rel->relkind == RELKIND_VIEW)
ereport(ERROR,
*** ./src/backend/commands/indexcmds.c.orig 2010-06-23 16:31:25.000000000 +0200
--- ./src/backend/commands/indexcmds.c 2010-06-25 14:03:39.000000000 +0200
***************
*** 181,186 ****
--- 181,187 ----
/* Note: during bootstrap may see uncataloged relation */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
+ rel->rd_rel->relkind != RELKIND_MATVIEW &&
rel->rd_rel->relkind != RELKIND_UNCATALOGED)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
*** ./src/backend/commands/tablecmds.c.orig 2010-06-23 16:31:25.000000000 +0200
--- ./src/backend/commands/tablecmds.c 2010-06-25 14:04:35.000000000 +0200
***************
*** 205,210 ****
--- 205,216 ----
gettext_noop("view \"%s\" does not exist, skipping"),
gettext_noop("\"%s\" is not a view"),
gettext_noop("Use DROP VIEW to remove a view.")},
+ {RELKIND_MATVIEW,
+ ERRCODE_UNDEFINED_TABLE,
+ gettext_noop("materialized view \"%s\" does not exist"),
+ gettext_noop("materialized view \"%s\" does not exist, skipping"),
+ gettext_noop("\"%s\" is not a materialized view"),
+ gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
{RELKIND_INDEX,
ERRCODE_UNDEFINED_OBJECT,
gettext_noop("index \"%s\" does not exist"),
***************
*** 678,683 ****
--- 684,693 ----
relkind = RELKIND_VIEW;
break;
+ case OBJECT_MATVIEW:
+ relkind = RELKIND_MATVIEW;
+ break;
+
default:
elog(ERROR, "unrecognized drop object type: %d",
(int) drop->removeType);
***************
*** 6439,6444 ****
--- 6449,6455 ----
{
case RELKIND_RELATION:
case RELKIND_VIEW:
+ case RELKIND_MATVIEW:
/* ok to change owner */
break;
case RELKIND_INDEX:
***************
*** 7715,7720 ****
--- 7726,7732 ----
switch (stmttype)
{
case OBJECT_TABLE:
+ case OBJECT_MATVIEW:
/*
* For mostly-historical reasons, we allow ALTER TABLE to apply to
***************
*** 7747,7752 ****
--- 7759,7765 ----
{
case RELKIND_RELATION:
case RELKIND_VIEW:
+ case RELKIND_MATVIEW:
/* ok to change schema */
break;
case RELKIND_SEQUENCE:
*** ./src/backend/commands/view.c.orig 2010-06-23 16:31:25.000000000 +0200
--- ./src/backend/commands/view.c 2010-06-25 14:05:52.000000000 +0200
***************
*** 97,103 ****
*---------------------------------------------------------------------
*/
static Oid
! DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
{
Oid viewOid,
namespaceId;
--- 97,103 ----
*---------------------------------------------------------------------
*/
static Oid
! DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace, char relkind)
{
Oid viewOid,
namespaceId;
***************
*** 155,160 ****
--- 155,168 ----
rel = relation_open(viewOid, AccessExclusiveLock);
/*
+ * Check if do not try to replace materialized view.
+ */
+ if (rel->rd_rel->relkind == RELKIND_MATVIEW)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("CREATE OR REPLACE on materialized view is not supported!"),
+ errhint("Use CREATE MATERIALIZED VIEW ...")));
+ /*
* Make sure it *is* a view, and do permissions checks.
*/
if (rel->rd_rel->relkind != RELKIND_VIEW)
***************
*** 239,245 ****
* existing view, so we don't need more code to complain if "replace"
* is false).
*/
! return DefineRelation(createStmt, RELKIND_VIEW);
}
}
--- 247,253 ----
* existing view, so we don't need more code to complain if "replace"
* is false).
*/
! return DefineRelation(createStmt, relkind);
}
}
***************
*** 299,305 ****
}
static void
! DefineViewRules(Oid viewOid, Query *viewParse, bool replace)
{
/*
* Set up the ON SELECT rule. Since the query has already been through
--- 307,313 ----
}
static void
! DefineViewRules(Oid viewOid, Query *viewParse, bool is_instead, bool replace, bool is_materialized)
{
/*
* Set up the ON SELECT rule. Since the query has already been through
***************
*** 308,315 ****
DefineQueryRewrite(pstrdup(ViewSelectRuleName),
viewOid,
NULL,
! CMD_SELECT,
! true,
replace,
list_make1(viewParse));
--- 316,323 ----
DefineQueryRewrite(pstrdup(ViewSelectRuleName),
viewOid,
NULL,
! is_materialized ? CMD_REFRESH : CMD_SELECT,
! is_materialized ? false : is_instead,
replace,
list_make1(viewParse));
***************
*** 465,471 ****
* aborted.
*/
viewOid = DefineVirtualRelation(view, viewParse->targetList,
! stmt->replace);
/*
* The relation we have just created is not visible to any other commands
--- 473,479 ----
* aborted.
*/
viewOid = DefineVirtualRelation(view, viewParse->targetList,
! stmt->replace, (stmt->ismaterialized ? RELKIND_MATVIEW : RELKIND_VIEW));
/*
* The relation we have just created is not visible to any other commands
***************
*** 483,487 ****
/*
* Now create the rules associated with the view.
*/
! DefineViewRules(viewOid, viewParse, stmt->replace);
}
--- 491,495 ----
/*
* Now create the rules associated with the view.
*/
! DefineViewRules(viewOid, viewParse, true, stmt->replace, stmt->ismaterialized);
}
*** ./src/backend/nodes/copyfuncs.c.orig 2010-06-23 16:31:25.000000000 +0200
--- ./src/backend/nodes/copyfuncs.c 2010-06-25 14:10:17.000000000 +0200
***************
*** 2845,2850 ****
--- 2845,2851 ----
COPY_NODE_FIELD(aliases);
COPY_NODE_FIELD(query);
COPY_SCALAR_FIELD(replace);
+ COPY_SCALAR_FIELD(ismaterialized);
return newnode;
}
*** ./src/backend/nodes/equalfuncs.c.orig 2010-06-23 16:31:25.000000000 +0200
--- ./src/backend/nodes/equalfuncs.c 2010-06-25 14:10:37.000000000 +0200
***************
*** 1381,1386 ****
--- 1381,1387 ----
COMPARE_NODE_FIELD(aliases);
COMPARE_NODE_FIELD(query);
COMPARE_SCALAR_FIELD(replace);
+ COMPARE_SCALAR_FIELD(ismaterialized);
return true;
}
*** ./src/backend/parser/gram.y.orig 2010-06-23 16:31:25.000000000 +0200
--- ./src/backend/parser/gram.y 2010-06-25 15:06:19.000000000 +0200
***************
*** 310,316 ****
%type <fun_param_mode> arg_class
%type <typnam> func_return func_type
! %type <boolean> TriggerForType OptTemp
%type <oncommit> OnCommitOption
%type <node> for_locking_item
--- 310,316 ----
%type <fun_param_mode> arg_class
%type <typnam> func_return func_type
! %type <boolean> TriggerForType OptTemp OptMater
%type <oncommit> OnCommitOption
%type <node> for_locking_item
***************
*** 502,508 ****
LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP
LOCATION LOCK_P LOGIN_P
! MAPPING MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NOCREATEDB
NOCREATEROLE NOCREATEUSER NOINHERIT NOLOGIN_P NONE NOSUPERUSER
--- 502,508 ----
LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP
LOCATION LOCK_P LOGIN_P
! MAPPING MATCH MATERIALIZED MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NOCREATEDB
NOCREATEROLE NOCREATEUSER NOINHERIT NOLOGIN_P NONE NOSUPERUSER
***************
*** 517,523 ****
QUOTE
! RANGE READ REAL REASSIGN RECHECK RECURSIVE REFERENCES REINDEX
RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA RESET RESTART
RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE
--- 517,523 ----
QUOTE
! RANGE READ REAL REASSIGN RECHECK RECURSIVE REFERENCES REFRESH REINDEX
RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA RESET RESTART
RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE
***************
*** 1598,1603 ****
--- 1598,1611 ----
n->relkind = OBJECT_VIEW;
$$ = (Node *)n;
}
+ | ALTER MATERIALIZED VIEW qualified_name alter_table_cmds
+ {
+ AlterTableStmt *n = makeNode(AlterTableStmt);
+ n->relation = $4;
+ n->cmds = $5;
+ n->relkind = OBJECT_MATVIEW;
+ $$ = (Node *)n;
+ }
;
alter_table_cmds:
***************
*** 1914,1919 ****
--- 1922,1934 ----
n->def = (Node *)$2;
$$ = (Node *)n;
}
+ /* ALTER TABLE <name> REFRESH */
+ | REFRESH
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_Refresh;
+ $$ = (Node *)n;
+ }
;
alter_column_default:
***************
*** 4046,4051 ****
--- 4061,4067 ----
drop_type: TABLE { $$ = OBJECT_TABLE; }
| SEQUENCE { $$ = OBJECT_SEQUENCE; }
| VIEW { $$ = OBJECT_VIEW; }
+ | MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; }
| INDEX { $$ = OBJECT_INDEX; }
| TYPE_P { $$ = OBJECT_TYPE; }
| DOMAIN_P { $$ = OBJECT_DOMAIN; }
***************
*** 4102,4108 ****
* The COMMENT ON statement can take different forms based upon the type of
* the object associated with the comment. The form of the statement is:
*
! * COMMENT ON [ [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | VIEW |
* CONVERSION | LANGUAGE | OPERATOR CLASS | LARGE OBJECT |
* CAST | COLUMN | SCHEMA | TABLESPACE | ROLE |
* TEXT SEARCH PARSER | TEXT SEARCH DICTIONARY |
--- 4118,4124 ----
* The COMMENT ON statement can take different forms based upon the type of
* the object associated with the comment. The form of the statement is:
*
! * COMMENT ON [ [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | [MATERIALIZED] VIEW |
* CONVERSION | LANGUAGE | OPERATOR CLASS | LARGE OBJECT |
* CAST | COLUMN | SCHEMA | TABLESPACE | ROLE |
* TEXT SEARCH PARSER | TEXT SEARCH DICTIONARY |
***************
*** 4281,4286 ****
--- 4297,4303 ----
| DOMAIN_P { $$ = OBJECT_TYPE; }
| TYPE_P { $$ = OBJECT_TYPE; }
| VIEW { $$ = OBJECT_VIEW; }
+ | MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; }
| CONVERSION_P { $$ = OBJECT_CONVERSION; }
| TABLESPACE { $$ = OBJECT_TABLESPACE; }
| ROLE { $$ = OBJECT_ROLE; }
***************
*** 6319,6337 ****
/*****************************************************************************
*
* QUERY:
! * CREATE [ OR REPLACE ] [ TEMP ] VIEW <viewname> '('target-list ')'
* AS <query> [ WITH [ CASCADED | LOCAL ] CHECK OPTION ]
*
*****************************************************************************/
! ViewStmt: CREATE OptTemp VIEW qualified_name opt_column_list
AS SelectStmt opt_check_option
{
ViewStmt *n = makeNode(ViewStmt);
! n->view = $4;
n->view->istemp = $2;
! n->aliases = $5;
! n->query = $7;
n->replace = false;
$$ = (Node *) n;
}
--- 6336,6355 ----
/*****************************************************************************
*
* QUERY:
! * CREATE [ OR REPLACE ] [ TEMP ] [ MATERIALIZED ] VIEW <viewname> '('target-list ')'
* AS <query> [ WITH [ CASCADED | LOCAL ] CHECK OPTION ]
*
*****************************************************************************/
! ViewStmt: CREATE OptTemp OptMater VIEW qualified_name opt_column_list
AS SelectStmt opt_check_option
{
ViewStmt *n = makeNode(ViewStmt);
! n->view = $5;
n->view->istemp = $2;
! n->ismaterialized = $3;
! n->aliases = $6;
! n->query = $8;
n->replace = false;
$$ = (Node *) n;
}
***************
*** 6341,6346 ****
--- 6359,6365 ----
ViewStmt *n = makeNode(ViewStmt);
n->view = $6;
n->view->istemp = $4;
+ n->ismaterialized = false;
n->aliases = $7;
n->query = $9;
n->replace = true;
***************
*** 6348,6353 ****
--- 6367,6377 ----
}
;
+
+ OptMater: MATERIALIZED { $$ = true; }
+ | /* EMPTY */ { $$ = false; }
+ ;
+
opt_check_option:
WITH CHECK OPTION
{
***************
*** 10982,10987 ****
--- 11006,11012 ----
| REASSIGN
| RECHECK
| RECURSIVE
+ | REFRESH
| REINDEX
| RELATIVE_P
| RELEASE
***************
*** 11211,11216 ****
--- 11236,11242 ----
| LIMIT
| LOCALTIME
| LOCALTIMESTAMP
+ | MATERIALIZED
| NOT
| NULL_P
| OFF
*** ./src/backend/rewrite/rewriteDefine.c.orig 2010-06-23 16:31:25.000000000 +0200
--- ./src/backend/rewrite/rewriteDefine.c 2010-06-25 14:27:04.000000000 +0200
***************
*** 159,165 ****
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced,
! (evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
/*
* Also install dependencies on objects referenced in action and qual.
--- 159,165 ----
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced,
! (evtype == CMD_SELECT || evtype == CMD_REFRESH) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
/*
* Also install dependencies on objects referenced in action and qual.
***************
*** 246,252 ****
* Verify relation is of a type that rules can sensibly be applied to.
*/
if (event_relation->rd_rel->relkind != RELKIND_RELATION &&
! event_relation->rd_rel->relkind != RELKIND_VIEW)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table or view",
--- 246,253 ----
* Verify relation is of a type that rules can sensibly be applied to.
*/
if (event_relation->rd_rel->relkind != RELKIND_RELATION &&
! event_relation->rd_rel->relkind != RELKIND_VIEW &&
! event_relation->rd_rel->relkind != RELKIND_MATVIEW)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table or view",
***************
*** 288,294 ****
errhint("Use triggers instead.")));
}
! if (event_type == CMD_SELECT)
{
/*
* Rules ON SELECT are restricted to view definitions
--- 289,295 ----
errhint("Use triggers instead.")));
}
! if (event_type == CMD_SELECT || event_type == CMD_REFRESH)
{
/*
* Rules ON SELECT are restricted to view definitions
***************
*** 313,319 ****
* ... the one action must be a SELECT, ...
*/
query = (Query *) linitial(action);
! if (!is_instead ||
query->commandType != CMD_SELECT ||
query->utilityStmt != NULL ||
query->intoClause != NULL)
--- 314,320 ----
* ... the one action must be a SELECT, ...
*/
query = (Query *) linitial(action);
! if ((!is_instead && (event_relation->rd_rel->relkind != RELKIND_MATVIEW)) ||
query->commandType != CMD_SELECT ||
query->utilityStmt != NULL ||
query->intoClause != NULL)
***************
*** 349,355 ****
RewriteRule *rule;
rule = event_relation->rd_rules->rules[i];
! if (rule->event == CMD_SELECT)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("\"%s\" is already a view",
--- 350,356 ----
RewriteRule *rule;
rule = event_relation->rd_rules->rules[i];
! if (rule->event == CMD_SELECT || rule->event == CMD_REFRESH)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("\"%s\" is already a view",
***************
*** 499,505 ****
*
* XXX what about getting rid of its TOAST table? For now, we don't.
*/
! if (RelisBecomingView)
RelationDropStorage(event_relation);
/* Close rel, but keep lock till commit... */
--- 500,506 ----
*
* XXX what about getting rid of its TOAST table? For now, we don't.
*/
! if (RelisBecomingView && (event_relation->rd_rel->relkind != RELKIND_MATVIEW))
RelationDropStorage(event_relation);
/* Close rel, but keep lock till commit... */
*** ./src/backend/rewrite/rewriteHandler.c.orig 2010-06-23 16:31:25.000000000 +0200
--- ./src/backend/rewrite/rewriteHandler.c 2010-06-25 14:36:41.000000000 +0200
***************
*** 1390,1396 ****
for (i = 0; i < rules->numLocks; i++)
{
rule = rules->rules[i];
! if (rule->event != CMD_SELECT)
continue;
if (rule->attrno > 0)
--- 1390,1396 ----
for (i = 0; i < rules->numLocks; i++)
{
rule = rules->rules[i];
! if (rule->event != CMD_SELECT && rule->event != CMD_REFRESH)
continue;
if (rule->attrno > 0)
***************
*** 1422,1427 ****
--- 1422,1433 ----
{
rule = lfirst(l);
+ /*
+ * Prevent firing rule, if it is REFRESH rule
+ */
+ if (rule->event == CMD_REFRESH)
+ continue;
+
parsetree = ApplyRetrieveRule(parsetree,
rule,
rt_index,
***************
*** 1659,1664 ****
--- 1665,1700 ----
rt_entry_relation = heap_open(rt_entry->relid, NoLock);
/*
+ * Inserting, updating or deleting row in materilized views are not allowed
+ */
+ if (rt_entry_relation->rd_rel->relkind == RELKIND_MATVIEW)
+ {
+ heap_close(rt_entry_relation, NoLock);
+ switch (parsetree->commandType)
+ {
+ case CMD_INSERT:
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot insert into a materialized view")));
+ break;
+ case CMD_UPDATE:
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot update a materialized view")));
+ break;
+ case CMD_DELETE:
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot delete from a materialized view")));
+ break;
+ default:
+ elog(ERROR, "unrecognized commandType: %d",
+ (int) parsetree->commandType);
+ break;
+ }
+ }
+
+ /*
* If it's an INSERT or UPDATE, rewrite the targetlist into standard
* form. This will be needed by the planner anyway, and doing it now
* ensures that any references to NEW.field will behave sanely.
*** ./src/backend/rewrite/rewriteSupport.c.orig 2010-06-23 16:31:25.000000000 +0200
--- ./src/backend/rewrite/rewriteSupport.c 2010-06-25 12:38:58.000000000 +0200
***************
*** 69,75 ****
{
/* Do the update */
classForm->relhasrules = relHasRules;
! if (relIsBecomingView)
classForm->relkind = RELKIND_VIEW;
simple_heap_update(relationRelation, &tuple->t_self, tuple);
--- 69,77 ----
{
/* Do the update */
classForm->relhasrules = relHasRules;
!
! /* do not change RELKIND if its Materialized View */
! if (relIsBecomingView && classForm->relkind != RELKIND_MATVIEW)
classForm->relkind = RELKIND_VIEW;
simple_heap_update(relationRelation, &tuple->t_self, tuple);
*** ./src/backend/tcop/utility.c.orig 2010-06-23 16:31:25.000000000 +0200
--- ./src/backend/tcop/utility.c 2010-06-25 14:38:20.000000000 +0200
***************
*** 606,611 ****
--- 606,612 ----
case OBJECT_TABLE:
case OBJECT_SEQUENCE:
case OBJECT_VIEW:
+ case OBJECT_MATVIEW:
case OBJECT_INDEX:
RemoveRelations(stmt);
break;
***************
*** 1544,1549 ****
--- 1545,1553 ----
case OBJECT_VIEW:
tag = "DROP VIEW";
break;
+ case OBJECT_MATVIEW:
+ tag = "DROP MATERIALIZED VIEW";
+ break;
case OBJECT_INDEX:
tag = "DROP INDEX";
break;
***************
*** 1775,1780 ****
--- 1779,1787 ----
case OBJECT_VIEW:
tag = "ALTER VIEW";
break;
+ case OBJECT_MATVIEW:
+ tag = "ALTER MATERIALIZED VIEW";
+ break;
default:
tag = "???";
break;
*** ./src/backend/utils/adt/ruleutils.c.orig 2010-06-23 16:31:25.000000000 +0200
--- ./src/backend/utils/adt/ruleutils.c 2010-06-25 14:40:10.000000000 +0200
***************
*** 2384,2391 ****
query = (Query *) linitial(actions);
! if (ev_type != '1' || ev_attr >= 0 || !is_instead ||
! strcmp(ev_qual, "<>") != 0 || query->commandType != CMD_SELECT)
{
appendStringInfo(buf, "Not a view");
return;
--- 2384,2393 ----
query = (Query *) linitial(actions);
! /* ev_type == 1 SELECT, ev_type == 6 REFRESH */
! if (ev_attr >= 0 || strcmp(ev_qual, "<>") != 0 || query->commandType != CMD_SELECT ||
! ((!is_instead || ev_type != '1') &&
! (is_instead || ev_type != '6')))
{
appendStringInfo(buf, "Not a view");
return;
*** ./src/include/catalog/pg_class.h.orig 2010-06-23 16:31:26.000000000 +0200
--- ./src/include/catalog/pg_class.h 2010-06-24 09:45:08.000000000 +0200
***************
*** 147,152 ****
--- 147,153 ----
#define RELKIND_UNCATALOGED 'u' /* temporary heap */
#define RELKIND_TOASTVALUE 't' /* moved off huge values */
#define RELKIND_VIEW 'v' /* view */
+ #define RELKIND_MATVIEW 'm' /* materialized view */
#define RELKIND_COMPOSITE_TYPE 'c' /* composite type */
#endif /* PG_CLASS_H */
*** ./src/include/nodes/nodes.h.orig 2010-06-23 16:31:26.000000000 +0200
--- ./src/include/nodes/nodes.h 2010-06-25 14:41:37.000000000 +0200
***************
*** 512,517 ****
--- 512,518 ----
CMD_DELETE,
CMD_UTILITY, /* cmds like create, destroy, copy, vacuum,
* etc. */
+ CMD_REFRESH, /* refreshing tables, like materialized views */
CMD_NOTHING /* dummy command for instead nothing rules
* with qual */
} CmdType;
*** ./src/include/nodes/parsenodes.h.orig 2010-06-23 16:31:26.000000000 +0200
--- ./src/include/nodes/parsenodes.h 2010-06-24 14:30:11.000000000 +0200
***************
*** 1055,1060 ****
--- 1055,1061 ----
OBJECT_INDEX,
OBJECT_LANGUAGE,
OBJECT_LARGEOBJECT,
+ OBJECT_MATVIEW,
OBJECT_OPCLASS,
OBJECT_OPERATOR,
OBJECT_OPFAMILY,
***************
*** 1150,1156 ****
AT_EnableReplicaRule, /* ENABLE REPLICA RULE name */
AT_DisableRule, /* DISABLE RULE name */
AT_AddInherit, /* INHERIT parent */
! AT_DropInherit /* NO INHERIT parent */
} AlterTableType;
typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
--- 1151,1158 ----
AT_EnableReplicaRule, /* ENABLE REPLICA RULE name */
AT_DisableRule, /* DISABLE RULE name */
AT_AddInherit, /* INHERIT parent */
! AT_DropInherit, /* NO INHERIT parent */
! AT_Refresh /* alter materialized view REFRESH */
} AlterTableType;
typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
***************
*** 2181,2186 ****
--- 2183,2189 ----
List *aliases; /* target column names */
Node *query; /* the SELECT query */
bool replace; /* replace an existing view? */
+ bool ismaterialized; /* materialize view? */
} ViewStmt;
/* ----------------------
*** ./src/include/parser/kwlist.h.orig 2010-06-23 16:31:26.000000000 +0200
--- ./src/include/parser/kwlist.h 2010-06-24 14:14:30.000000000 +0200
***************
*** 229,234 ****
--- 229,235 ----
PG_KEYWORD("login", LOGIN_P, UNRESERVED_KEYWORD)
PG_KEYWORD("mapping", MAPPING, UNRESERVED_KEYWORD)
PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD)
+ PG_KEYWORD("materialized", MATERIALIZED, RESERVED_KEYWORD)
PG_KEYWORD("maxvalue", MAXVALUE, UNRESERVED_KEYWORD)
PG_KEYWORD("minute", MINUTE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("minvalue", MINVALUE, UNRESERVED_KEYWORD)
***************
*** 302,307 ****
--- 303,309 ----
PG_KEYWORD("recheck", RECHECK, UNRESERVED_KEYWORD)
PG_KEYWORD("recursive", RECURSIVE, UNRESERVED_KEYWORD)
PG_KEYWORD("references", REFERENCES, RESERVED_KEYWORD)
+ PG_KEYWORD("refresh", REFRESH, UNRESERVED_KEYWORD)
PG_KEYWORD("reindex", REINDEX, UNRESERVED_KEYWORD)
PG_KEYWORD("relative", RELATIVE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("release", RELEASE, UNRESERVED_KEYWORD)
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers