Query *query = castNode(Query, stmt->query);
IntoClause *into = stmt->into;
bool is_matview = (into->viewQuery != NULL);
+ bool do_refresh = false;
DestReceiver *dest;
- Oid save_userid = InvalidOid;
- int save_sec_context = 0;
- int save_nestlevel = 0;
ObjectAddress address;
List *rewritten;
PlannedStmt *plan;
Assert(query->commandType == CMD_SELECT);
/*
- * For materialized views, lock down security-restricted operations and
- * arrange to make GUC variable changes local to this command. This is
- * not necessary for security, but this keeps the behavior similar to
- * REFRESH MATERIALIZED VIEW. Otherwise, one could create a materialized
- * view not possible to refresh.
+ * For materialized views, always skip data during table creation, and use
+ * REFRESH instead (see below).
*/
if (is_matview)
{
- GetUserIdAndSecContext(&save_userid, &save_sec_context);
- SetUserIdAndSecContext(save_userid,
- save_sec_context | SECURITY_RESTRICTED_OPERATION);
- save_nestlevel = NewGUCNestLevel();
+ do_refresh = !into->skipData;
+ into->skipData = true;
}
if (into->skipData)
PopActiveSnapshot();
}
- if (is_matview)
+ /*
+ * For materialized views, reuse the REFRESH logic, which locks down
+ * security-restricted operations and restricts the search_path. This
+ * reduces the chance that a subsequent refresh will fail.
+ */
+ if (do_refresh)
{
- /* Roll back any GUC changes */
- AtEOXact_GUC(false, save_nestlevel);
+ RefreshMatViewByOid(address.objectId, false, false,
+ pstate->p_sourcetext, NULL, qc);
- /* Restore userid and security context */
- SetUserIdAndSecContext(save_userid, save_sec_context);
+ if (qc)
+ qc->commandTag = CMDTAG_SELECT;
}
return address;
/*
* ExecRefreshMatView -- execute a REFRESH MATERIALIZED VIEW command
*
+ * If WITH NO DATA was specified, this is effectively like a TRUNCATE;
+ * otherwise it is like a TRUNCATE followed by an INSERT using the SELECT
+ * statement associated with the materialized view. The statement node's
+ * skipData field shows whether the clause was used.
+ */
+ObjectAddress
+ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
+ ParamListInfo params, QueryCompletion *qc)
+{
+ Oid matviewOid;
+ LOCKMODE lockmode;
+
+ /* Determine strength of lock needed. */
+ lockmode = stmt->concurrent ? ExclusiveLock : AccessExclusiveLock;
+
+ /*
+ * Get a lock until end of transaction.
+ */
+ matviewOid = RangeVarGetRelidExtended(stmt->relation,
+ lockmode, 0,
+ RangeVarCallbackMaintainsTable,
+ NULL);
+
+ return RefreshMatViewByOid(matviewOid, stmt->skipData, stmt->concurrent,
+ queryString, params, qc);
+}
+
+/*
+ * RefreshMatViewByOid -- refresh materialized view by OID
+ *
* This refreshes the materialized view by creating a new table and swapping
* the relfilenumbers of the new table and the old materialized view, so the OID
* of the original materialized view is preserved. Thus we do not lose GRANT
* nor references to this materialized view.
*
- * If WITH NO DATA was specified, this is effectively like a TRUNCATE;
- * otherwise it is like a TRUNCATE followed by an INSERT using the SELECT
- * statement associated with the materialized view. The statement node's
- * skipData field shows whether the clause was used.
+ * If skipData is true, this is effectively like a TRUNCATE; otherwise it is
+ * like a TRUNCATE followed by an INSERT using the SELECT statement associated
+ * with the materialized view.
*
* Indexes are rebuilt too, via REINDEX. Since we are effectively bulk-loading
* the new heap, it's better to create the indexes afterwards than to fill them
* reflect the result set of the materialized view's query.
*/
ObjectAddress
-ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
- ParamListInfo params, QueryCompletion *qc)
+RefreshMatViewByOid(Oid matviewOid, bool skipData, bool concurrent,
+ const char *queryString, ParamListInfo params,
+ QueryCompletion *qc)
{
- Oid matviewOid;
Relation matviewRel;
RewriteRule *rule;
List *actions;
Oid OIDNewHeap;
DestReceiver *dest;
uint64 processed = 0;
- bool concurrent;
- LOCKMODE lockmode;
char relpersistence;
Oid save_userid;
int save_sec_context;
int save_nestlevel;
ObjectAddress address;
- /* Determine strength of lock needed. */
- concurrent = stmt->concurrent;
- lockmode = concurrent ? ExclusiveLock : AccessExclusiveLock;
-
- /*
- * Get a lock until end of transaction.
- */
- matviewOid = RangeVarGetRelidExtended(stmt->relation,
- lockmode, 0,
- RangeVarCallbackMaintainsTable,
- NULL);
matviewRel = table_open(matviewOid, NoLock);
relowner = matviewRel->rd_rel->relowner;
errmsg("CONCURRENTLY cannot be used when the materialized view is not populated")));
/* Check that conflicting options have not been specified. */
- if (concurrent && stmt->skipData)
+ if (concurrent && skipData)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("%s and %s options cannot be used together",
* Tentatively mark the matview as populated or not (this will roll back
* if we fail later).
*/
- SetMatViewPopulatedState(matviewRel, !stmt->skipData);
+ SetMatViewPopulatedState(matviewRel, !skipData);
/* Concurrent refresh builds new data in temp tablespace, and does diff. */
if (concurrent)
dest = CreateTransientRelDestReceiver(OIDNewHeap);
/* Generate the data, if wanted. */
- if (!stmt->skipData)
+ if (!skipData)
processed = refresh_matview_datafill(dest, dataQuery, queryString);
/* Make the matview match the newly generated data. */
* inserts and deletes it issues get counted by lower-level code.)
*/
pgstat_count_truncate(matviewRel);
- if (!stmt->skipData)
+ if (!skipData)
pgstat_count_heap_insert(matviewRel, processed);
}