return false;
}
+/*
+ * query_uses_temp_object - convenience wrapper for find_temp_object
+ *
+ * If the Query includes any use of a temporary object, fill *temp_object
+ * with the address of one such object and return true.
+ */
+bool
+query_uses_temp_object(Query *query, ObjectAddress *temp_object)
+{
+ bool result;
+ ObjectAddresses *addrs;
+
+ addrs = new_object_addresses();
+
+ /* Collect all dependencies from the Query */
+ collectDependenciesOfExpr(addrs, (Node *) query, NIL);
+
+ /* Look for one that is temp */
+ result = find_temp_object(addrs, false, temp_object);
+
+ free_object_addresses(addrs);
+
+ return result;
+}
+
/*
* Given an array of dependency references, eliminate any duplicates.
*/
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/analyze.h"
-#include "parser/parse_relation.h"
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteHandler.h"
#include "rewrite/rewriteSupport.h"
ListCell *cell;
bool check_option;
ObjectAddress address;
+ ObjectAddress temp_object;
/*
* Run parse analysis to convert the raw parse tree to a Query. Note this
*/
view = copyObject(stmt->view); /* don't corrupt original command */
if (view->relpersistence == RELPERSISTENCE_PERMANENT
- && isQueryUsingTempRelation(viewParse))
+ && query_uses_temp_object(viewParse, &temp_object))
{
view->relpersistence = RELPERSISTENCE_TEMP;
ereport(NOTICE,
(errmsg("view \"%s\" will be a temporary view",
- view->relname)));
+ view->relname),
+ errdetail("It depends on temporary %s.",
+ getObjectDescription(&temp_object, false))));
}
/*
#include "postgres.h"
#include "access/sysattr.h"
+#include "catalog/dependency.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
/* additional work needed for CREATE MATERIALIZED VIEW */
if (stmt->objtype == OBJECT_MATVIEW)
{
+ ObjectAddress temp_object;
+
/*
* Prohibit a data-modifying CTE in the query used to create a
* materialized view. It's not sufficiently clear what the user would
* creation query. It would be hard to refresh data or incrementally
* maintain it if a source disappeared.
*/
- if (isQueryUsingTempRelation(query))
+ if (query_uses_temp_object(query, &temp_object))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("materialized views must not use temporary tables or views")));
+ errmsg("materialized views must not use temporary objects"),
+ errdetail("This view depends on temporary %s.",
+ getObjectDescription(&temp_object, false))));
/*
* A materialized view would either need to save parameters for use in
#include "access/htup_details.h"
#include "access/relation.h"
-#include "access/sysattr.h"
#include "access/table.h"
#include "catalog/heap.h"
#include "catalog/namespace.h"
-#include "catalog/pg_type.h"
#include "funcapi.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
-#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/varlena.h"
static int specialAttNum(const char *attname);
static bool rte_visible_if_lateral(ParseState *pstate, RangeTblEntry *rte);
static bool rte_visible_if_qualified(ParseState *pstate, RangeTblEntry *rte);
-static bool isQueryUsingTempRelation_walker(Node *node, void *context);
/*
}
-/*
- * Examine a fully-parsed query, and return true iff any relation underlying
- * the query is a temporary relation (table, view, or materialized view).
- */
-bool
-isQueryUsingTempRelation(Query *query)
-{
- return isQueryUsingTempRelation_walker((Node *) query, NULL);
-}
-
-static bool
-isQueryUsingTempRelation_walker(Node *node, void *context)
-{
- if (node == NULL)
- return false;
-
- if (IsA(node, Query))
- {
- Query *query = (Query *) node;
- ListCell *rtable;
-
- foreach(rtable, query->rtable)
- {
- RangeTblEntry *rte = lfirst(rtable);
-
- if (rte->rtekind == RTE_RELATION)
- {
- Relation rel = table_open(rte->relid, AccessShareLock);
- char relpersistence = rel->rd_rel->relpersistence;
-
- table_close(rel, AccessShareLock);
- if (relpersistence == RELPERSISTENCE_TEMP)
- return true;
- }
- }
-
- return query_tree_walker(query,
- isQueryUsingTempRelation_walker,
- context,
- QTW_IGNORE_JOINALIASES);
- }
-
- return expression_tree_walker(node,
- isQueryUsingTempRelation_walker,
- context);
-}
-
/*
* addRTEPermissionInfo
* Creates RTEPermissionInfo for a given RTE and adds it into the
bool local_temp_okay,
ObjectAddress *foundobj);
+extern bool query_uses_temp_object(Query *query, ObjectAddress *temp_object);
+
extern ObjectAddresses *new_object_addresses(void);
extern void add_exact_object_address(const ObjectAddress *object,
extern const NameData *attnumAttName(Relation rd, int attid);
extern Oid attnumTypeId(Relation rd, int attid);
extern Oid attnumCollationId(Relation rd, int attid);
-extern bool isQueryUsingTempRelation(Query *query);
#endif /* PARSE_RELATION_H */
-- should be created in temp object schema
CREATE VIEW v1_temp AS SELECT * FROM temp_table;
NOTICE: view "v1_temp" will be a temporary view
+DETAIL: It depends on temporary table temp_table.
-- should be created in temp object schema
CREATE TEMP VIEW v2_temp AS SELECT * FROM base_table;
-- should be created in temp_views schema
-- should fail
CREATE VIEW temp_view_test.v3_temp AS SELECT * FROM temp_table;
NOTICE: view "v3_temp" will be a temporary view
+DETAIL: It depends on temporary table temp_table.
ERROR: cannot create temporary relation in non-temporary schema
-- should fail
CREATE SCHEMA test_view_schema
FROM base_table t1, temp_table t2
WHERE t1.id = t2.id;
NOTICE: view "v4_temp" will be a temporary view
+DETAIL: It depends on temporary table temp_table.
-- should be temp
CREATE VIEW v5_temp AS
SELECT t1.a AS t1_a, t2.a AS t2_a, t3.a AS t3_a
FROM base_table t1, base_table2 t2, temp_table t3
WHERE t1.id = t2.id and t2.id = t3.id;
NOTICE: view "v5_temp" will be a temporary view
+DETAIL: It depends on temporary table temp_table.
-- subqueries
CREATE VIEW v4 AS SELECT * FROM base_table WHERE id IN (SELECT id FROM base_table2);
CREATE VIEW v5 AS SELECT t1.id, t2.a FROM base_table t1, (SELECT * FROM base_table2) t2;
CREATE VIEW v8 AS SELECT * FROM base_table WHERE EXISTS (SELECT 1);
CREATE VIEW v6_temp AS SELECT * FROM base_table WHERE id IN (SELECT id FROM temp_table);
NOTICE: view "v6_temp" will be a temporary view
+DETAIL: It depends on temporary table temp_table.
CREATE VIEW v7_temp AS SELECT t1.id, t2.a FROM base_table t1, (SELECT * FROM temp_table) t2;
NOTICE: view "v7_temp" will be a temporary view
+DETAIL: It depends on temporary table temp_table.
CREATE VIEW v8_temp AS SELECT * FROM base_table WHERE EXISTS (SELECT 1 FROM temp_table);
NOTICE: view "v8_temp" will be a temporary view
+DETAIL: It depends on temporary table temp_table.
CREATE VIEW v9_temp AS SELECT * FROM base_table WHERE NOT EXISTS (SELECT 1 FROM temp_table);
NOTICE: view "v9_temp" will be a temporary view
+DETAIL: It depends on temporary table temp_table.
-- a view should also be temporary if it references a temporary view
CREATE VIEW v10_temp AS SELECT * FROM v7_temp;
NOTICE: view "v10_temp" will be a temporary view
+DETAIL: It depends on temporary view v7_temp.
CREATE VIEW v11_temp AS SELECT t1.id, t2.a FROM base_table t1, v10_temp t2;
NOTICE: view "v11_temp" will be a temporary view
+DETAIL: It depends on temporary view v10_temp.
CREATE VIEW v12_temp AS SELECT true FROM v11_temp;
NOTICE: view "v12_temp" will be a temporary view
+DETAIL: It depends on temporary view v11_temp.
-- a view should also be temporary if it references a temporary sequence
CREATE SEQUENCE seq1;
CREATE TEMPORARY SEQUENCE seq1_temp;
-CREATE VIEW v9 AS SELECT seq1.is_called FROM seq1;
-CREATE VIEW v13_temp AS SELECT seq1_temp.is_called FROM seq1_temp;
+CREATE VIEW v9 AS SELECT nextval('seq1');
+CREATE VIEW v13_temp AS SELECT nextval('seq1_temp');
NOTICE: view "v13_temp" will be a temporary view
+DETAIL: It depends on temporary sequence seq1_temp.
SELECT relname FROM pg_class
WHERE relname LIKE 'v_'
AND relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'temp_view_test')
CREATE VIEW nontemp1 AS SELECT * FROM t1 CROSS JOIN t2;
CREATE VIEW temporal1 AS SELECT * FROM t1 CROSS JOIN tt;
NOTICE: view "temporal1" will be a temporary view
+DETAIL: It depends on temporary table tt.
CREATE VIEW nontemp2 AS SELECT * FROM t1 INNER JOIN t2 ON t1.num = t2.num2;
CREATE VIEW temporal2 AS SELECT * FROM t1 INNER JOIN tt ON t1.num = tt.num2;
NOTICE: view "temporal2" will be a temporary view
+DETAIL: It depends on temporary table tt.
CREATE VIEW nontemp3 AS SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num2;
CREATE VIEW temporal3 AS SELECT * FROM t1 LEFT JOIN tt ON t1.num = tt.num2;
NOTICE: view "temporal3" will be a temporary view
+DETAIL: It depends on temporary table tt.
CREATE VIEW nontemp4 AS SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num2 AND t2.value = 'xxx';
CREATE VIEW temporal4 AS SELECT * FROM t1 LEFT JOIN tt ON t1.num = tt.num2 AND tt.value = 'xxx';
NOTICE: view "temporal4" will be a temporary view
+DETAIL: It depends on temporary table tt.
SELECT relname FROM pg_class
WHERE relname LIKE 'nontemp%'
AND relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'testviewschm2')
AND EXISTS (SELECT g FROM tbl4 LEFT JOIN tbl3 ON tbl4.h = tbl3.f)
AND NOT EXISTS (SELECT g FROM tbl4 LEFT JOIN tmptbl ON tbl4.h = tmptbl.j);
NOTICE: view "mytempview" will be a temporary view
+DETAIL: It depends on temporary table tmptbl.
SELECT count(*) FROM pg_class where relname LIKE 'mytempview'
And relnamespace IN (SELECT OID FROM pg_namespace WHERE nspname LIKE 'pg_temp%');
count
CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c)
from gstest2 group by rollup ((a,b,c),(c,d));
NOTICE: view "gstest_view" will be a temporary view
+DETAIL: It depends on temporary table gstest2.
select pg_get_viewdef('gstest_view'::regclass, true);
pg_get_viewdef
---------------------------------------
-- no tuple locks on materialized views
SELECT * FROM mvtest_tvvm FOR SHARE;
ERROR: cannot lock rows in materialized view "mvtest_tvvm"
+-- we don't support temp materialized views, so disallow this case:
+CREATE TEMP TABLE mvtest_temp_t (id int NOT NULL, type text NOT NULL, amt numeric NOT NULL);
+CREATE MATERIALIZED VIEW mvtest_temp_tm AS SELECT * FROM mvtest_temp_t;
+ERROR: materialized views must not use temporary objects
+DETAIL: This view depends on temporary table mvtest_temp_t.
-- test join of mv and view
SELECT type, m.totamt AS mtot, v.totamt AS vtot FROM mvtest_tm m LEFT JOIN mvtest_tv v USING (type) ORDER BY type;
type | mtot | vtot
WINDOW w AS (ORDER BY name)
;
NOTICE: view "planets_view" will be a temporary view
+DETAIL: It depends on temporary table planets.
SELECT pg_get_viewdef('planets_view');
pg_get_viewdef
--------------------------------------------------
-- a view should also be temporary if it references a temporary sequence
CREATE SEQUENCE seq1;
CREATE TEMPORARY SEQUENCE seq1_temp;
-CREATE VIEW v9 AS SELECT seq1.is_called FROM seq1;
-CREATE VIEW v13_temp AS SELECT seq1_temp.is_called FROM seq1_temp;
+CREATE VIEW v9 AS SELECT nextval('seq1');
+CREATE VIEW v13_temp AS SELECT nextval('seq1_temp');
SELECT relname FROM pg_class
WHERE relname LIKE 'v_'
-- no tuple locks on materialized views
SELECT * FROM mvtest_tvvm FOR SHARE;
+-- we don't support temp materialized views, so disallow this case:
+CREATE TEMP TABLE mvtest_temp_t (id int NOT NULL, type text NOT NULL, amt numeric NOT NULL);
+CREATE MATERIALIZED VIEW mvtest_temp_tm AS SELECT * FROM mvtest_temp_t;
+
-- test join of mv and view
SELECT type, m.totamt AS mtot, v.totamt AS vtot FROM mvtest_tm m LEFT JOIN mvtest_tv v USING (type) ORDER BY type;