add materialized view feature to opengauss

This commit is contained in:
sqyyeah 2020-08-17 11:48:18 +08:00
parent 3eade03f9c
commit d5337ceca7
120 changed files with 3796 additions and 823 deletions

View File

@ -2276,3 +2276,44 @@ SELECT like_escape( name::text, ''::citext ) = like_escape( name::text, '' ) AS
t
(5 rows)
-- Ensure correct behavior for citext with materialized views.
CREATE TABLE citext_table (
id serial primary key,
name citext
);
INSERT INTO citext_table (name)
VALUES ('one'), ('two'), ('three'), (NULL), (NULL);
CREATE MATERIALIZED VIEW citext_matview AS
SELECT * FROM citext_table;
CREATE UNIQUE INDEX citext_matview_id
ON citext_matview (id);
SELECT *
FROM citext_matview m
FULL JOIN citext_table t ON (t.id = m.id AND t *= m)
WHERE t.id IS NULL OR m.id IS NULL;
id | name | id | name
----+------+----+------
(0 rows)
UPDATE citext_table SET name = 'Two' WHERE name = 'TWO';
SELECT *
FROM citext_matview m
FULL JOIN citext_table t ON (t.id = m.id AND t *= m)
WHERE t.id IS NULL OR m.id IS NULL;
id | name | id | name
----+------+----+------
| | 2 | Two
2 | two | |
(2 rows)
REFRESH MATERIALIZED VIEW CONCURRENTLY citext_matview;
SELECT * FROM citext_matview ORDER BY id;
id | name
----+-------
1 | one
2 | Two
3 | three
4 |
5 |
(5 rows)

View File

@ -2276,3 +2276,43 @@ SELECT like_escape( name::text, ''::citext ) = like_escape( name::text, '' ) AS
t
(5 rows)
-- Ensure correct behavior for citext with materialized views.
CREATE TABLE citext_table (
id serial primary key,
name citext
);
INSERT INTO citext_table (name)
VALUES ('one'), ('two'), ('three'), (NULL), (NULL);
CREATE MATERIALIZED VIEW citext_matview AS
SELECT * FROM citext_table;
CREATE UNIQUE INDEX citext_matview_id
ON citext_matview (id);
SELECT *
FROM citext_matview m
FULL JOIN citext_table t ON (t.id = m.id AND t *= m)
WHERE t.id IS NULL OR m.id IS NULL;
id | name | id | name
----+------+----+------
(0 rows)
UPDATE citext_table SET name = 'Two' WHERE name = 'TWO';
SELECT *
FROM citext_matview m
FULL JOIN citext_table t ON (t.id = m.id AND t *= m)
WHERE t.id IS NULL OR m.id IS NULL;
id | name | id | name
----+------+----+------
| | 2 | Two
2 | two | |
(2 rows)
REFRESH MATERIALIZED VIEW CONCURRENTLY citext_matview;
SELECT * FROM citext_matview ORDER BY id;
id | name
----+-------
1 | one
2 | Two
3 | three
4 |
5 |
(5 rows)

View File

@ -681,3 +681,26 @@ SELECT COUNT(*) = 19::bigint AS t FROM try;
SELECT like_escape( name, '' ) = like_escape( name::text, '' ) AS t FROM srt;
SELECT like_escape( name::text, ''::citext ) = like_escape( name::text, '' ) AS t FROM srt;
-- Ensure correct behavior for citext with materialized views.
CREATE TABLE citext_table (
id serial primary key,
name citext
);
INSERT INTO citext_table (name)
VALUES ('one'), ('two'), ('three'), (NULL), (NULL);
CREATE MATERIALIZED VIEW citext_matview AS
SELECT * FROM citext_table;
CREATE UNIQUE INDEX citext_matview_id
ON citext_matview (id);
SELECT *
FROM citext_matview m
FULL JOIN citext_table t ON (t.id = m.id AND t *= m)
WHERE t.id IS NULL OR m.id IS NULL;
UPDATE citext_table SET name = 'Two' WHERE name = 'TWO';
SELECT *
FROM citext_matview m
FULL JOIN citext_table t ON (t.id = m.id AND t *= m)
WHERE t.id IS NULL OR m.id IS NULL;
REFRESH MATERIALIZED VIEW CONCURRENTLY citext_matview;
SELECT * FROM citext_matview ORDER BY id;

View File

@ -446,7 +446,7 @@ void sql_exec_dumpalltables(PGconn* conn, struct options* opts)
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace "
" LEFT JOIN pg_catalog.pg_database d ON d.datname = pg_catalog.current_database(),"
" pg_catalog.pg_tablespace t "
"WHERE relkind IN ('r'%s%s) AND "
"WHERE relkind IN ('r', 'm'%s%s) AND "
" %s"
" t.oid = CASE"
" WHEN reltablespace <> 0 THEN reltablespace"
@ -545,7 +545,7 @@ void sql_exec_searchtables(PGconn* conn, struct options* opts)
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace \n"
" LEFT JOIN pg_catalog.pg_database d ON d.datname = pg_catalog.current_database(),\n"
" pg_catalog.pg_tablespace t \n"
"WHERE relkind IN ('r', 'i', 'S', 't') AND \n"
"WHERE relkind IN ('r', 'm', 'i', 'S', 't') AND \n"
" t.oid = CASE\n"
" WHEN reltablespace <> 0 THEN reltablespace\n"
" ELSE dattablespace\n"

View File

@ -339,7 +339,7 @@ static void get_rel_infos(ClusterInfo* cluster, DbInfo* dbinfo)
" FROM pg_catalog.pg_class p INNER JOIN pg_catalog.pg_namespace n ON (p.relnamespace = n.oid)"
" LEFT OUTER JOIN pg_catalog.pg_tablespace t ON (p.reltablespace = t.oid)"
" WHERE p.oid < 16384 AND"
" p.relkind IN ('r', 'i', 't') AND"
" p.relkind IN ('r', 'm', 'i', 't') AND"
" p.relisshared= false "
" ORDER BY 1",
is_exists ? ", t.relative " : "");

View File

@ -788,7 +788,7 @@ static void set_frozenxids(void)
"UPDATE pg_catalog.pg_class "
"SET relfrozenxid64 = '%lu' "
/* only heap and TOAST are vacuumed */
"WHERE relkind IN ('r', 't')",
"WHERE relkind IN ('r', 'm','t')",
old_cluster.controldata.chkpnt_nxtxid));
PQfinish(conn);

View File

@ -138,6 +138,7 @@ void old_8_3_check_for_tsquery_usage(ClusterInfo* cluster)
"FROM pg_catalog.pg_class c, "
" pg_catalog.pg_namespace n, "
" pg_catalog.pg_attribute a "
/* materialized views didn't exist in 8.3, so no need to check 'm' */
"WHERE c.relkind = 'r' AND "
" c.oid = a.attrelid AND "
" NOT a.attisdropped AND "
@ -302,6 +303,7 @@ void old_8_3_rebuild_tsvector_tables(ClusterInfo* cluster, bool check_mode)
"FROM pg_catalog.pg_class c, "
" pg_catalog.pg_namespace n, "
" pg_catalog.pg_attribute a "
/* materialized views didn't exist in 8.3, so no need to check 'm' */
"WHERE c.relkind = 'r' AND "
" c.oid = a.attrelid AND "
" NOT a.attisdropped AND "

View File

@ -194,6 +194,7 @@ static Datum pgstat_relation(Relation rel, FunctionCallInfo fcinfo)
switch (rel->rd_rel->relkind) {
case RELKIND_RELATION:
case RELKIND_MATVIEW:
case RELKIND_TOASTVALUE:
case RELKIND_UNCATALOGED:
case RELKIND_SEQUENCE:

View File

@ -166,7 +166,7 @@ static int vacuumlo(const char* database, const struct _param* param)
strcat(buf, " AND a.atttypid = t.oid ");
strcat(buf, " AND c.relnamespace = s.oid ");
strcat(buf, " AND t.typname in ('oid', 'lo') ");
strcat(buf, " AND c.relkind = 'r'");
strcat(buf, " AND c.relkind = in ('r', 'm')");
strcat(buf, " AND s.nspname !~ '^pg_'");
res = PQexec(conn, buf);
if (PQresultStatus(res) != PGRES_TUPLES_OK) {

View File

@ -2139,7 +2139,7 @@ static void setup_privileges(void)
privileges_setup[0] = xstrdup("UPDATE pg_class "
" SET relacl = E'{\"=r/\\\\\"$POSTGRES_SUPERUSERNAME\\\\\"\"}' "
" WHERE relkind IN ('r', 'v', 'S') AND relacl IS NULL;\n");
" WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n");
privileges_setup[1] = xstrdup("GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n");
privileges_setup[2] = xstrdup("GRANT CREATE, USAGE ON SCHEMA public TO PUBLIC;\n");
privileges_setup[3] = xstrdup("REVOKE ALL ON pg_largeobject FROM PUBLIC;\n");

View File

@ -268,8 +268,10 @@ static void flagInhTables(TableInfo* ptblinfo, int inumTables, InhInfo* inhinfo,
TableInfo** parents;
for (i = 0; i < inumTables; i++) {
/* Sequences and views never have parents */
if (ptblinfo[i].relkind == RELKIND_SEQUENCE || ptblinfo[i].relkind == RELKIND_VIEW)
/* Some kinds never have parents */
if (ptblinfo[i].relkind == RELKIND_SEQUENCE ||
ptblinfo[i].relkind == RELKIND_VIEW ||
tblinfo[i].relkind == RELKIND_MATVIEW)
continue;
/* Don't bother computing anything for non-target tables, either */
@ -308,8 +310,10 @@ static void flagInhAttrs(TableInfo* ptblinfo, int inumTables)
int numParents;
TableInfo** parents;
/* Sequences and views never have parents */
if (tbinfo->relkind == RELKIND_SEQUENCE || tbinfo->relkind == RELKIND_VIEW)
/* Some kinds never have parents */
if (tbinfo->relkind == RELKIND_SEQUENCE ||
tbinfo->relkind == RELKIND_VIEW ||
tbinfo->relkind == RELKIND_MATVIEW)
continue;
/* Don't bother computing anything for non-target tables, either */

View File

@ -293,6 +293,7 @@ static void exclude_error_tables(Archive* fout, SimpleOidList* oids);
static NamespaceInfo* findNamespace(Archive* fout, Oid nsoid, Oid objoid);
static void dumpTableData(Archive* fout, TableDataInfo* tdinfo);
static void refreshMatViewData(Archive* fout, TableDataInfo* tdinfo);
static void guessConstraintInheritance(TableInfo* tblinfo, int numTables);
static void dumpComment(Archive* fout, const char* target, const char* nmspace, const char* owner, CatalogId catalogId,
int subid, DumpId dumpId);
@ -359,6 +360,7 @@ static void addBoundaryDependencies(DumpableObject** dobjs, int numObjs, Dumpabl
static void getDomainConstraints(Archive* fout, TypeInfo* tyinfo);
static void getTableData(TableInfo* tblinfo, int numTables);
static void makeTableDataInfo(TableInfo* tbinfo, bool oids);
static void buildMatViewRefreshDependencies(Archive* fout);
static void getTableDataFKConstraints(void);
static char* format_function_arguments(FuncInfo* finfo, char* funcargs);
static char* format_function_arguments_old(Archive* fout, FuncInfo* finfo, int nallargs, const char** allargtypes,
@ -842,6 +844,7 @@ int main(int argc, char** argv)
if (!schemaOnly) {
getTableData(tblinfo, numTables);
buildMatViewRefreshDependencies(fout);
if (dataOnly)
getTableDataFKConstraints();
}
@ -1846,10 +1849,11 @@ static void expand_table_name_patterns(
"SELECT c.oid"
"\nFROM pg_catalog.pg_class c"
"\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace"
"\nWHERE c.relkind in ('%c', '%c', '%c', '%c')\n",
"\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n",
RELKIND_RELATION,
RELKIND_SEQUENCE,
RELKIND_VIEW,
RELKIND_MATVIEW,
RELKIND_FOREIGN_TABLE);
processSQLNamePattern(GetConnection(fout),
@ -2483,6 +2487,42 @@ static void dumpTableData(Archive* fout, TableDataInfo* tdinfo)
destroyPQExpBuffer(copyBuf);
}
/*
* refreshMatViewData -
* load or refresh the contents of a single materialized view
*
* Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
* statement.
*/
static void refreshMatViewData(Archive *fout, TableDataInfo *tdinfo)
{
TableInfo *tbinfo = tdinfo->tdtable;
PQExpBuffer q;
q = createPQExpBuffer();
appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n", fmtId(tbinfo->dobj.name));
ArchiveEntry(fout, tdinfo->dobj.catId, /* catalog ID */
tdinfo->dobj.dumpId, /* dump ID */
tbinfo->dobj.name, /* Name */
tbinfo->dobj.nmspace->dobj.name, /* Namespace */
NULL, /* Tablespace */
tbinfo->rolname, /* Owner */
false, /* with oids */
"MATERIALIZED VIEW DATA", /* Desc */
SECTION_POST_DATA, /* Section */
q->data, /* Create */
"", /* Del */
NULL, /* Copy */
tdinfo->dobj.dependencies, /* Deps */
tdinfo->dobj.nDeps, /* # Deps */
NULL, /* Dumper */
NULL); /* Dumper Arg */
destroyPQExpBuffer(q);
}
/*
* getTableData -
* set up dumpable objects representing the contents of tables
@ -2537,7 +2577,10 @@ static void makeTableDataInfo(TableInfo* tbinfo, bool boids)
/* OK, let's dump it */
tdinfo = (TableDataInfo*)pg_malloc(sizeof(TableDataInfo));
tdinfo->dobj.objType = DO_TABLE_DATA;
if (tbinfo->relkind == RELKIND_MATVIEW)
tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
else
tdinfo->dobj.objType = DO_TABLE_DATA;
/*
* Note: use tableoid 0 so that this object won't be mistaken for
@ -2556,6 +2599,108 @@ static void makeTableDataInfo(TableInfo* tbinfo, bool boids)
tbinfo->dataObj = tdinfo;
}
/*
* The refresh for a materialized view must be dependent on the refresh for
* any materialized view that this one is dependent on.
*
* This must be called after all the objects are created, but before they are
* sorted.
*/
static void buildMatViewRefreshDependencies(Archive *fout)
{
PQExpBuffer query = createPQExpBuffer();
PGresult *res = NULL;
int ntups, i;
int i_classid, i_objid, i_refobjid;
/* Make sure we are in proper schema */
selectSourceSchema(fout, "pg_catalog");
if (fout->remoteVersion >= 90204) {
appendPQExpBuffer(query, "with recursive w as "
"( "
"select d1.objid, d2.refobjid, c2.relkind as refrelkind "
"from pg_depend d1 "
"join pg_class c1 on c1.oid = d1.objid "
"and c1.relkind = 'm' "
"join pg_rewrite r1 on r1.ev_class = d1.objid "
"join pg_depend d2 on d2.classid = 'pg_rewrite'::regclass "
"and d2.objid = r1.oid "
"and d2.refobjid <> d1.objid "
"join pg_class c2 on c2.oid = d2.refobjid "
"and c2.relkind in ('m','v') "
"where d1.classid = 'pg_class'::regclass "
"union "
"select w.objid, d3.refobjid, c3.relkind "
"from w "
"join pg_rewrite r3 on r3.ev_class = w.refobjid "
"join pg_depend d3 on d3.classid = 'pg_rewrite'::regclass "
"and d3.objid = r3.oid "
"and d3.refobjid <> w.refobjid "
"join pg_class c3 on c3.oid = d3.refobjid "
"and c3.relkind in ('m','v') "
") "
"select 'pg_class'::regclass::oid as classid, objid, refobjid "
"from w "
"where refrelkind = 'm'");
}
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
i_classid = PQfnumber(res, "classid");
i_objid = PQfnumber(res, "objid");
i_refobjid = PQfnumber(res, "refobjid");
for (i = 0; i < ntups; i++) {
CatalogId objId;
CatalogId refobjId;
DumpableObject *dobj;
DumpableObject *refdobj;
TableInfo *tbinfo;
TableInfo *reftbinfo;
objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
objId.oid = atooid(PQgetvalue(res, i, i_objid));
refobjId.tableoid = objId.tableoid;
refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
dobj = findObjectByCatalogId(objId);
if (dobj == NULL)
continue;
Assert(dobj->objType == DO_TABLE);
tbinfo = (TableInfo*)dobj;
Assert(tbinfo->relkind == RELKIND_MATVIEW);
dobj = (DumpableObject*)tbinfo->dataObj;
if (dobj == NULL) {
continue;
}
Assert(dobj->objType == DO_REFRESH_MATVIEW);
refdobj = findObjectByCatalogId(refobjId);
if (refdobj == NULL) {
continue;
}
Assert(refdobj->objType == DO_TABLE);
reftbinfo = (TableInfo*)refdobj;
Assert(reftbinfo->relkind == RELKIND_MATVIEW);
refdobj = (DumpableObject*)reftbinfo->dataObj;
if (refdobj == NULL) {
continue;
}
Assert(refdobj->objType == DO_REFRESH_MATVIEW);
addObjectDependency(dobj, refdobj->dumpId);
}
PQclear(res);
destroyPQExpBuffer(query);
}
/*
* getTableDataFKConstraints -
* add dump-order dependencies reflecting foreign key constraints
@ -5306,6 +5451,7 @@ TableInfo* getTables(Archive* fout, int* numTables)
int i_toastoid = 0;
int i_toastfrozenxid = 0, i_toastfrozenxid64 = 0;
int i_relpersistence = 0;
int i_relispopulated = 0;
int i_relbucket = 0;
int i_owning_tab = 0;
int i_owning_col = 0;
@ -5363,7 +5509,7 @@ TableInfo* getTables(Archive* fout, int* numTables)
* defined to inherit from a system catalog (pretty weird, but...)
*
* We ignore relations that are not ordinary tables, sequences, views,
* composite types, or foreign tables.
* materialized views, composite types, or foreign tables.
*
* Composite-type table entries won't be dumped as such, but we have to
* make a DumpableObject for them so that we can track dependencies of the
@ -5397,7 +5543,7 @@ TableInfo* getTables(Archive* fout, int* numTables)
"c.relfrozenxid, %s, tc.oid AS toid, "
"tc.relfrozenxid AS tfrozenxid, "
"%s, "
"c.relpersistence, "
"c.relpersistence, 't' as relispopulated,"
"%s, ",
username_subquery,
isHasRelfrozenxid64 ? "c.relfrozenxid64" : "0 AS relfrozenxid64",
@ -5445,7 +5591,7 @@ TableInfo* getTables(Archive* fout, int* numTables)
"c.relfrozenxid, %s, tc.oid AS toid, "
"tc.relfrozenxid AS tfrozenxid, "
"%s, "
"c.relpersistence, "
"c.relpersistence, 't' as relispopulated, "
"%s, ",
username_subquery,
isHasRelfrozenxid64 ? "c.relfrozenxid64" : "0 AS c.relfrozenxid64",
@ -5478,13 +5624,14 @@ TableInfo* getTables(Archive* fout, int* numTables)
"d.objsubid = 0 AND "
"d.refclassid = c.tableoid AND d.deptype = 'a') "
"LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
"WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c') AND c.relnamespace != %d "
"WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') AND c.relnamespace != %d "
"ORDER BY c.oid",
RELKIND_SEQUENCE,
RELKIND_RELATION,
RELKIND_SEQUENCE,
RELKIND_VIEW,
RELKIND_COMPOSITE_TYPE,
RELKIND_MATVIEW,
RELKIND_FOREIGN_TABLE,
CSTORE_NAMESPACE);
}
@ -5501,7 +5648,7 @@ TableInfo* getTables(Archive* fout, int* numTables)
"c.relhasindex, c.relhasrules, c.relhasoids, "
"c.relfrozenxid, tc.oid AS toid, "
"tc.relfrozenxid AS tfrozenxid, "
"'p' AS relpersistence, "
"'p' AS relpersistence, 't' as relispopulated, "
"CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL::Oid END AS reloftype, "
"d.refobjid AS owning_tab, "
"d.refobjsubid AS owning_col, "
@ -5538,7 +5685,7 @@ TableInfo* getTables(Archive* fout, int* numTables)
"c.relhasindex, c.relhasrules, c.relhasoids, "
"c.relfrozenxid, tc.oid AS toid, "
"tc.relfrozenxid AS tfrozenxid, "
"'p' AS relpersistence, "
"'p' AS relpersistence, 't' as relispopulated, "
"NULL AS reloftype, "
"d.refobjid AS owning_tab, "
"d.refobjsubid AS owning_col, "
@ -5573,7 +5720,7 @@ TableInfo* getTables(Archive* fout, int* numTables)
"c.relhasindex, c.relhasrules, c.relhasoids, "
"c.relfrozenxid, tc.oid AS toid, "
"tc.relfrozenxid AS tfrozenxid, "
"'p' AS relpersistence, "
"'p' AS relpersistence, 't' as relispopulated, "
"NULL AS reloftype, "
"d.refobjid AS owning_tab, "
"d.refobjsubid AS owning_col, "
@ -5609,7 +5756,7 @@ TableInfo* getTables(Archive* fout, int* numTables)
"0 AS relfrozenxid, "
"0 AS toid, "
"0 AS tfrozenxid, "
"'p' AS relpersistence, "
"'p' AS relpersistence, 't' as relispopulated, "
"NULL AS reloftype, "
"d.refobjid AS owning_tab, "
"d.refobjsubid AS owning_col, "
@ -5644,7 +5791,7 @@ TableInfo* getTables(Archive* fout, int* numTables)
"0 AS relfrozenxid, "
"0 AS toid, "
"0 AS tfrozenxid, "
"'p' AS relpersistence, "
"'p' AS relpersistence, 't' as relispopulated, "
"NULL AS reloftype, "
"d.refobjid AS owning_tab, "
"d.refobjsubid AS owning_col, "
@ -5675,7 +5822,7 @@ TableInfo* getTables(Archive* fout, int* numTables)
"0 AS relfrozenxid, "
"0 AS toid, "
"0 AS tfrozenxid, "
"'p' AS relpersistence, "
"'p' AS relpersistence, 't' as relispopulated, "
"NULL AS reloftype, "
"NULL::oid AS owning_tab, "
"NULL::int4 AS owning_col, "
@ -5701,7 +5848,7 @@ TableInfo* getTables(Archive* fout, int* numTables)
"0 AS relfrozenxid, "
"0 AS toid, "
"0 AS tfrozenxid, "
"'p' AS relpersistence, "
"'p' AS relpersistence, 't' as relispopulated, "
"NULL AS reloftype, "
"NULL::oid AS owning_tab, "
"NULL::int4 AS owning_col, "
@ -5737,7 +5884,7 @@ TableInfo* getTables(Archive* fout, int* numTables)
"0 as relfrozenxid, "
"0 AS toid, "
"0 AS tfrozenxid, "
"'p' AS relpersistence, "
"'p' AS relpersistence, 't' as relispopulated, "
"NULL AS reloftype, "
"NULL::oid AS owning_tab, "
"NULL::int4 AS owning_col, "
@ -5780,6 +5927,7 @@ TableInfo* getTables(Archive* fout, int* numTables)
i_relhasindex = PQfnumber(res, "relhasindex");
i_relhasrules = PQfnumber(res, "relhasrules");
i_relhasoids = PQfnumber(res, "relhasoids");
i_relispopulated = PQfnumber(res, "relispopulated");
i_relfrozenxid = PQfnumber(res, "relfrozenxid");
i_relfrozenxid64 = PQfnumber(res, "relfrozenxid64");
i_toastoid = PQfnumber(res, "toid");
@ -5838,6 +5986,7 @@ TableInfo* getTables(Archive* fout, int* numTables)
tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
tblinfo[i].isMOT = false;
tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
tblinfo[i].frozenxid64 = atoxid(PQgetvalue(res, i, i_relfrozenxid64));
@ -5955,6 +6104,7 @@ TableInfo* getTables(Archive* fout, int* numTables)
else
selectDumpableTable(fout, &tblinfo[i]);
tblinfo[i].interesting = tblinfo[i].dobj.dump;
tblinfo[i].postponed_def = false; /* might get set during sort */
/*
* in Upgrade scenario no need to take a lock on table as
@ -6135,8 +6285,9 @@ void getIndexes(Archive* fout, TableInfo tblinfo[], int numTables)
TableInfo* tbinfo = &tblinfo[i];
int32 contrants_processed = 0;
/* Only plain tables and mot have indexes */
if ((tbinfo->relkind != RELKIND_RELATION && !tbinfo->isMOT) || !tbinfo->hasindex)
/* Only plain tables, materialized views and mot have indexes */
if ((tbinfo->relkind != RELKIND_RELATION && !tbinfo->isMOT && tbinfo->relkind != RELKIND_MATVIEW) ||
!tbinfo->hasindex)
continue;
/* Ignore indexes of tables not to be dumped */
@ -6931,13 +7082,15 @@ RuleInfo* getRules(Archive* fout, int* numRules)
ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
if (ruleinfo[i].ruletable != NULL) {
/*
* If the table is a view, force its ON SELECT rule to be sorted
* before the view itself --- this ensures that any dependencies
* for the rule affect the table's positioning. Other rules are
* forced to appear after their table.
* If the table is a view or materialized view, force its ON
* SELECT rule to be sorted before the view itself --- this
* ensures that any dependencies for the rule affect the table's
* positioning. Other rules are forced to appear after their
* table.
*/
if (ruleinfo[i].ruletable->relkind == RELKIND_VIEW && ruleinfo[i].ev_type == '1' &&
ruleinfo[i].is_instead) {
if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW || ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead) {
addObjectDependency(&ruleinfo[i].ruletable->dobj, ruleinfo[i].dobj.dumpId);
/* We'll merge the rule into CREATE VIEW, if possible */
ruleinfo[i].separate = false;
@ -9069,6 +9222,9 @@ static void dumpDumpableObject(Archive* fout, DumpableObject* dobj)
case DO_INDEX:
dumpIndex(fout, (IndxInfo*)dobj);
break;
case DO_REFRESH_MATVIEW:
refreshMatViewData(fout, (TableDataInfo*) dobj);
break;
case DO_RULE:
dumpRule(fout, (RuleInfo*)dobj);
break;
@ -15704,37 +15860,67 @@ static char* changeTableName(const char* tableName)
oldTableName = NULL;
return newTableName;
}
/*
* Create the AS clause for a view or materialized view. The semicolon is
* stripped because a materialized view must add a WITH NO DATA clause.
*
* This returns a new buffer which must be freed by the caller.
*/
static PQExpBuffer createViewAsClause(Archive* fout, TableInfo* tbinfo)
{
PQExpBuffer query = createPQExpBuffer();
PQExpBuffer result = createPQExpBuffer();
PGresult *res;
int len;
/* Fetch the view definition */
if (fout->remoteVersion >= 70300) {
/* Beginning in 7.3, viewname is not unique; rely on OID */
appendPQExpBuffer(query, "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
tbinfo->dobj.catId.oid);
} else {
appendPQExpBuffer(query, "SELECT definition AS viewdef "
"FROM pg_views WHERE viewname = ");
appendStringLiteralAH(query, tbinfo->dobj.name, fout);
appendPQExpBuffer(query, ";");
}
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
if (PQntuples(res) != 1) {
if (PQntuples(res) < 1)
exit_horribly(NULL, "query to obtain definition of view \"%s\" returned no data\n", tbinfo->dobj.name);
else
exit_horribly(NULL, "query to obtain definition of view \"%s\" returned more than one definition\n",
tbinfo->dobj.name);
}
len = PQgetlength(res, 0, 0);
if (len == 0)
exit_horribly(NULL, "definition of view \"%s\" appears to be empty (length zero)\n", tbinfo->dobj.name);
/* Strip off the trailing semicolon so that other things may follow. */
Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
PQclear(res);
destroyPQExpBuffer(query);
return result;
}
/*
* dumpViewSchema
* write the declaration (not data) of one user-defined view
* write the declaration (not data) of one user-defined view
*/
static void dumpViewSchema(
Archive* fout, TableInfo* tbinfo, PQExpBuffer query, PQExpBuffer q, PQExpBuffer delq, PQExpBuffer labelq)
static void dumpViewSchema(Archive* fout, TableInfo* tbinfo, PQExpBuffer query, PQExpBuffer q, PQExpBuffer delq,
PQExpBuffer labelq)
{
char* viewdef = NULL;
char* schemainfo = NULL;
PGresult* defres = NULL;
PGresult* schemares = NULL;
/* Beginning in 7.3, viewname is not unique; rely on OID */
appendPQExpBuffer(
query, "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef", tbinfo->dobj.catId.oid);
defres = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
if (PQntuples(defres) != 1) {
if (PQntuples(defres) < 1)
exit_horribly(NULL, "query to obtain definition of view %s returned no data\n", fmtId(tbinfo->dobj.name));
else
exit_horribly(NULL,
"query to obtain definition of view %s returned more than one definition\n",
fmtId(tbinfo->dobj.name));
}
viewdef = PQgetvalue(defres, 0, 0);
if (strlen(viewdef) == 0) {
exit_horribly(NULL, "definition of view %s appears to be empty (length zero)\n", fmtId(tbinfo->dobj.name));
}
char *schemainfo = NULL;
PGresult *schemares = NULL;
PQExpBuffer result;
/* Fetch views'schema info */
resetPQExpBuffer(query);
@ -15774,13 +15960,15 @@ static void dumpViewSchema(
appendPQExpBuffer(q, "CREATE VIEW %s(%s)", fmtId(tbinfo->dobj.name), schemainfo);
if ((tbinfo->reloptions != NULL) && strlen(tbinfo->reloptions) > 0)
appendPQExpBuffer(q, " WITH (%s)", tbinfo->reloptions);
appendPQExpBuffer(q, " AS\n %s\n", viewdef);
result = createViewAsClause(fout, tbinfo);
appendPQExpBuffer(q, " AS\n %s;\n", result->data);
destroyPQExpBuffer(result);
appendPQExpBuffer(labelq, "VIEW %s", fmtId(tbinfo->dobj.name));
PQclear(defres);
PQclear(schemares);
}
/*
* dumpTableSchema
* write the declaration (not data) of one user-defined table or view
@ -15799,6 +15987,7 @@ static void dumpTableSchema(Archive* fout, TableInfo* tbinfo)
char* storage = NULL;
char* srvname = NULL;
char* ftoptions = NULL;
char* ft_write_only_str = NULL;
char** ft_frmt_clmn = NULL; /* formatter columns */
char* formatter = NULL;
int cnt_ft_frmt_clmns = 0; /* no of formatter columns */
@ -15830,90 +16019,96 @@ static void dumpTableSchema(Archive* fout, TableInfo* tbinfo)
reltypename = "VIEW";
dumpViewSchema(fout, tbinfo, query, q, delq, labelq);
} else {
switch (tbinfo->relkind) {
case RELKIND_FOREIGN_TABLE:
int i_srvname;
int i_ftoptions;
int i_ftwriteonly;
if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) {
int i_srvname;
int i_ftoptions;
int i_ftwriteonly;
char* ft_write_only_str = NULL;
reltypename = "FOREIGN TABLE";
reltypename = "FOREIGN TABLE";
/* retrieve name of foreign server and generic options */
appendPQExpBuffer(query,
"SELECT fs.srvname, "
"pg_catalog.array_to_string(ARRAY("
"SELECT pg_catalog.quote_ident(option_name) || "
"' ' || pg_catalog.quote_literal(option_value) "
"FROM pg_catalog.pg_options_to_table(ftoptions) "
"ORDER BY option_name"
"), E',\n ') AS ftoptions, ft.ftwriteonly ftwriteonly "
"FROM pg_catalog.pg_foreign_table ft "
"JOIN pg_catalog.pg_foreign_server fs "
"ON (fs.oid = ft.ftserver) "
"WHERE ft.ftrelid = '%u'",
tbinfo->dobj.catId.oid);
res = ExecuteSqlQueryForSingleRow(fout, query->data);
i_srvname = PQfnumber(res, "srvname");
i_ftoptions = PQfnumber(res, "ftoptions");
i_ftwriteonly = PQfnumber(res, "ftwriteonly");
srvname = gs_strdup(PQgetvalue(res, 0, i_srvname));
ftoptions = gs_strdup(PQgetvalue(res, 0, i_ftoptions));
ft_write_only_str = PQgetvalue(res, 0, i_ftwriteonly);
if (('t' == ft_write_only_str[0]) || ('1' == ft_write_only_str[0]) ||
(('o' == ft_write_only_str[0]) && ('n' == ft_write_only_str[1]))) {
ft_write_only = true;
}
PQclear(res);
if (binary_upgrade && ((ftoptions != NULL) && ftoptions[0]) && (NULL != strstr(ftoptions, "error_table"))) {
binary_upgrade_set_error_table_oids(fout, q, tbinfo);
}
if (((ftoptions != NULL) && ftoptions[0]) && (NULL != strstr(ftoptions, "formatter"))) {
int i_formatter = 0;
char* temp_iter = NULL;
int iterf = 0;
resetPQExpBuffer(query);
/* retrieve name of foreign server and generic options */
appendPQExpBuffer(query,
"WITH ft_options AS "
"(SELECT (pg_catalog.pg_options_to_table(ft.ftoptions)).option_name, "
"(pg_catalog.pg_options_to_table(ft.ftoptions)).option_value "
"SELECT fs.srvname, "
"pg_catalog.array_to_string(ARRAY("
"SELECT pg_catalog.quote_ident(option_name) || "
"' ' || pg_catalog.quote_literal(option_value) "
"FROM pg_catalog.pg_options_to_table(ftoptions) "
"ORDER BY option_name"
"), E',\n ') AS ftoptions, ft.ftwriteonly ftwriteonly "
"FROM pg_catalog.pg_foreign_table ft "
"WHERE ft.ftrelid = %u) "
"SELECT option_value FROM ft_options WHERE option_name = 'formatter'",
"JOIN pg_catalog.pg_foreign_server fs "
"ON (fs.oid = ft.ftserver) "
"WHERE ft.ftrelid = '%u'",
tbinfo->dobj.catId.oid);
res = ExecuteSqlQueryForSingleRow(fout, query->data);
i_formatter = PQfnumber(res, "option_value");
formatter = gs_strdup(PQgetvalue(res, 0, i_formatter));
PQclear(res);
temp_iter = formatter;
cnt_ft_frmt_clmns = 1;
while (*temp_iter != '\0') {
if ('.' == *temp_iter)
cnt_ft_frmt_clmns++;
temp_iter++;
i_srvname = PQfnumber(res, "srvname");
i_ftoptions = PQfnumber(res, "ftoptions");
i_ftwriteonly = PQfnumber(res, "ftwriteonly");
srvname = gs_strdup(PQgetvalue(res, 0, i_srvname));
ftoptions = gs_strdup(PQgetvalue(res, 0, i_ftoptions));
ft_write_only_str = PQgetvalue(res, 0, i_ftwriteonly);
if (('t' == ft_write_only_str[0]) || ('1' == ft_write_only_str[0]) ||
(('o' == ft_write_only_str[0]) && ('n' == ft_write_only_str[1]))) {
ft_write_only = true;
}
ft_frmt_clmn = (char**)pg_malloc(cnt_ft_frmt_clmns * sizeof(char*));
iterf = 0;
temp_iter = formatter;
while (*temp_iter != '\0') {
ft_frmt_clmn[iterf] = temp_iter;
while (*temp_iter && '.' != *temp_iter)
temp_iter++;
if ('.' == *temp_iter) {
iterf++;
*temp_iter = '\0';
PQclear(res);
if (binary_upgrade && ((ftoptions != NULL) && ftoptions[0]) &&
(NULL != strstr(ftoptions, "error_table"))) {
binary_upgrade_set_error_table_oids(fout, q, tbinfo);
}
if (((ftoptions != NULL) && ftoptions[0]) && (NULL != strstr(ftoptions, "formatter"))) {
int i_formatter = 0;
char *temp_iter = NULL;
int iterf = 0;
resetPQExpBuffer(query);
appendPQExpBuffer(query,
"WITH ft_options AS "
"(SELECT (pg_catalog.pg_options_to_table(ft.ftoptions)).option_name, "
"(pg_catalog.pg_options_to_table(ft.ftoptions)).option_value "
"FROM pg_catalog.pg_foreign_table ft "
"WHERE ft.ftrelid = %u) "
"SELECT option_value FROM ft_options WHERE option_name = 'formatter'",
tbinfo->dobj.catId.oid);
res = ExecuteSqlQueryForSingleRow(fout, query->data);
i_formatter = PQfnumber(res, "option_value");
formatter = gs_strdup(PQgetvalue(res, 0, i_formatter));
PQclear(res);
temp_iter = formatter;
cnt_ft_frmt_clmns = 1;
while (*temp_iter != '\0') {
if ('.' == *temp_iter)
cnt_ft_frmt_clmns++;
temp_iter++;
}
ft_frmt_clmn = (char**)pg_malloc(cnt_ft_frmt_clmns * sizeof(char*));
iterf = 0;
temp_iter = formatter;
while (*temp_iter != '\0') {
ft_frmt_clmn[iterf] = temp_iter;
while (*temp_iter && '.' != *temp_iter)
temp_iter++;
if ('.' == *temp_iter) {
iterf++;
*temp_iter = '\0';
temp_iter++;
}
}
}
}
} else {
reltypename = "TABLE";
srvname = NULL;
ftoptions = NULL;
break;
case RELKIND_MATVIEW:
reltypename = "MATERIALIZED VIEW";
srvname = NULL;
ftoptions = NULL;
break;
default:
reltypename = "TABLE";
srvname = NULL;
ftoptions = NULL;
}
if ((NULL != tbinfo->reloptions) && ((NULL != strstr(tbinfo->reloptions, "orientation=column")) ||
@ -16006,8 +16201,7 @@ static void dumpTableSchema(Archive* fout, TableInfo* tbinfo)
* No applications will dump data from DN which is doing expansion.
* So, if tbinfo->relbucket is 1, just skip dumpping pg_hashbucket.
*/
if (enableHashbucket == true && fout->getHashbucketInfo == false && tbinfo->relbucket != (Oid)1)
{
if (enableHashbucket == true && fout->getHashbucketInfo == false && tbinfo->relbucket != (Oid)1) {
PQExpBuffer getHashbucketQuery = createPQExpBuffer();
if (tbinfo->relbucket == (Oid)-1)
@ -16074,146 +16268,148 @@ static void dumpTableSchema(Archive* fout, TableInfo* tbinfo)
}
/* Dump the attributes */
actual_atts = 0;
for (j = 0; j < attrNums; j++) {
/*
* Normally, dump if it's locally defined in this table, and not
* dropped. But for binary upgrade, we'll dump all the columns,
* and then fix up the dropped and nonlocal cases below.
*/
if (shouldPrintColumn(tbinfo, j)) {
if (tbinfo->relkind != RELKIND_MATVIEW) {
actual_atts = 0;
for (j = 0; j < attrNums; j++) {
/*
* Default value --- suppress if to be printed separately.
* Normally, dump if it's locally defined in this table, and not
* dropped. But for binary upgrade, we'll dump all the columns,
* and then fix up the dropped and nonlocal cases below.
*/
bool has_default = (tbinfo->attrdefs[j] != NULL && !tbinfo->attrdefs[j]->separate);
/*
* Not Null constraint --- suppress if inherited, except in
* binary-upgrade case where that won't work.
*/
bool has_notnull = (tbinfo->notnull[j] && (!tbinfo->inhNotNull[j] || binary_upgrade));
/* Skip column if fully defined by reloftype */
if ((tbinfo->reloftype != NULL) && !has_default && !has_notnull && !binary_upgrade)
continue;
/* Format properly if not first attr */
if (actual_atts == 0)
appendPQExpBuffer(q, " (");
else
appendPQExpBuffer(q, ",");
appendPQExpBuffer(q, "\n ");
actual_atts++;
/* Attribute name */
appendPQExpBuffer(q, "%s ", fmtId(tbinfo->attnames[j]));
if (tbinfo->attisdropped[j]) {
if (shouldPrintColumn(tbinfo, j)) {
/*
* ALTER TABLE DROP COLUMN clears pg_attribute.atttypid,
* so we will not have gotten a valid type name; insert
* INTEGER as a stopgap. We'll clean things up later.
* Default value --- suppress if to be printed separately.
*/
appendPQExpBuffer(q, "INTEGER /* dummy */");
/* Skip all the rest, too */
continue;
}
bool has_default = (tbinfo->attrdefs[j] != NULL && !tbinfo->attrdefs[j]->separate);
/* Attribute type */
if ((tbinfo->reloftype != NULL) && !binary_upgrade) {
appendPQExpBuffer(q, "WITH OPTIONS");
} else if (fout->remoteVersion >= 70100) {
appendPQExpBuffer(q, "%s", tbinfo->atttypnames[j]);
} else {
/* If no format_type, fake it */
name = myFormatType(tbinfo->atttypnames[j], tbinfo->atttypmod[j]);
appendPQExpBuffer(q, "%s", name);
GS_FREE(name);
}
/*
* Not Null constraint --- suppress if inherited, except in
* binary-upgrade case where that won't work.
*/
bool has_notnull = (tbinfo->notnull[j] && (!tbinfo->inhNotNull[j] || binary_upgrade));
/* Add collation if not default for the type */
if (OidIsValid(tbinfo->attcollation[j])) {
CollInfo* coll = NULL;
/* Skip column if fully defined by reloftype */
if ((tbinfo->reloftype != NULL) && !has_default && !has_notnull && !binary_upgrade)
continue;
coll = findCollationByOid(tbinfo->attcollation[j]);
if (NULL != coll) {
/* always schema-qualify, don't try to be smart */
appendPQExpBuffer(q, " COLLATE %s.", fmtId(coll->dobj.nmspace->dobj.name));
appendPQExpBuffer(q, "%s", fmtId(coll->dobj.name));
/* Format properly if not first attr */
if (actual_atts == 0)
appendPQExpBuffer(q, " (");
else
appendPQExpBuffer(q, ",");
appendPQExpBuffer(q, "\n ");
actual_atts++;
/* Attribute name */
appendPQExpBuffer(q, "%s ", fmtId(tbinfo->attnames[j]));
if (tbinfo->attisdropped[j]) {
/*
* ALTER TABLE DROP COLUMN clears pg_attribute.atttypid,
* so we will not have gotten a valid type name; insert
* INTEGER as a stopgap. We'll clean things up later.
*/
appendPQExpBuffer(q, "INTEGER /* dummy */");
/* Skip all the rest, too */
continue;
}
}
if (NULL != formatter) {
int iter = 0;
for (iter = 0; iter < cnt_ft_frmt_clmns; iter++) {
if ((0 == strncmp(tbinfo->attnames[j], ft_frmt_clmn[iter], strlen(tbinfo->attnames[j]))) &&
('(' == ft_frmt_clmn[iter][strlen(tbinfo->attnames[j])])) {
appendPQExpBuffer(q, " position%s", &ft_frmt_clmn[iter][strlen(tbinfo->attnames[j])]);
/* Attribute type */
if ((tbinfo->reloftype != NULL) && !binary_upgrade) {
appendPQExpBuffer(q, "WITH OPTIONS");
} else if (fout->remoteVersion >= 70100) {
appendPQExpBuffer(q, "%s", tbinfo->atttypnames[j]);
} else {
/* If no format_type, fake it */
name = myFormatType(tbinfo->atttypnames[j], tbinfo->atttypmod[j]);
appendPQExpBuffer(q, "%s", name);
GS_FREE(name);
}
/* Add collation if not default for the type */
if (OidIsValid(tbinfo->attcollation[j])) {
CollInfo *coll = NULL;
coll = findCollationByOid(tbinfo->attcollation[j]);
if (NULL != coll) {
/* always schema-qualify, don't try to be smart */
appendPQExpBuffer(q, " COLLATE %s.", fmtId(coll->dobj.nmspace->dobj.name));
appendPQExpBuffer(q, "%s", fmtId(coll->dobj.name));
}
}
if (NULL != formatter) {
int iter = 0;
for (iter = 0; iter < cnt_ft_frmt_clmns; iter++) {
if ((0 == strncmp(tbinfo->attnames[j], ft_frmt_clmn[iter], strlen(tbinfo->attnames[j]))) &&
('(' == ft_frmt_clmn[iter][strlen(tbinfo->attnames[j])])) {
appendPQExpBuffer(q, " position%s", &ft_frmt_clmn[iter][strlen(tbinfo->attnames[j])]);
}
}
}
if (has_default)
appendPQExpBuffer(q, " DEFAULT %s", tbinfo->attrdefs[j]->adef_expr);
if (has_notnull)
appendPQExpBuffer(q, " NOT NULL");
}
if (has_default)
appendPQExpBuffer(q, " DEFAULT %s", tbinfo->attrdefs[j]->adef_expr);
if (has_notnull)
appendPQExpBuffer(q, " NOT NULL");
}
}
/*
* 1) do not use --include-alter-table
* 2) the SQL command not need to be modified
*/
if (!include_alter_table || !isChangeCreateSQL) {
/*
* Add non-inherited CHECK constraints, if any.
* 1) do not use --include-alter-table
* 2) the SQL command not need to be modified
*/
for (j = 0; j < tbinfo->ncheck; j++) {
ConstraintInfo* constr = &(tbinfo->checkexprs[j]);
if (!include_alter_table || !isChangeCreateSQL) {
/*
* Add non-inherited CHECK constraints, if any.
*/
for (j = 0; j < tbinfo->ncheck; j++) {
ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
if (constr->separate || !constr->conislocal)
continue;
if (constr->separate || !constr->conislocal) {
continue;
}
if (actual_atts == 0)
appendPQExpBuffer(q, " (\n ");
else
appendPQExpBuffer(q, ",\n ");
if (actual_atts == 0)
appendPQExpBuffer(q, " (\n ");
else
appendPQExpBuffer(q, ",\n ");
appendPQExpBuffer(q, "CONSTRAINT %s ", fmtId(constr->dobj.name));
appendPQExpBuffer(q, "%s", constr->condef);
appendPQExpBuffer(q, "CONSTRAINT %s ", fmtId(constr->dobj.name));
appendPQExpBuffer(q, "%s", constr->condef);
actual_atts++;
actual_atts++;
}
}
}
if (actual_atts) {
appendPQExpBuffer(q, "\n)");
} else if (!((tbinfo->reloftype != NULL) && !binary_upgrade)) {
/*
* We must have a parenthesized attribute list, even though empty,
* when not using the OF TYPE syntax.
*/
appendPQExpBuffer(q, " (\n)");
}
if (numParents > 0 && !binary_upgrade) {
appendPQExpBuffer(q, "\nINHERITS (");
for (k = 0; k < numParents; k++) {
TableInfo* parentRel = parents[k];
if (k > 0)
appendPQExpBuffer(q, ", ");
if (parentRel->dobj.nmspace != tbinfo->dobj.nmspace)
appendPQExpBuffer(q, "%s.", fmtId(parentRel->dobj.nmspace->dobj.name));
appendPQExpBuffer(q, "%s", fmtId(parentRel->dobj.name));
if (actual_atts) {
appendPQExpBuffer(q, "\n)");
} else if (!((tbinfo->reloftype != NULL) && !binary_upgrade)) {
/*
* We must have a parenthesized attribute list, even though empty,
* when not using the OF TYPE syntax.
*/
appendPQExpBuffer(q, " (\n)");
}
appendPQExpBuffer(q, ")");
if (numParents > 0 && !binary_upgrade) {
appendPQExpBuffer(q, "\nINHERITS (");
for (k = 0; k < numParents; k++) {
TableInfo *parentRel = parents[k];
if (k > 0)
appendPQExpBuffer(q, ", ");
if (parentRel->dobj.nmspace != tbinfo->dobj.nmspace)
appendPQExpBuffer(q, "%s.", fmtId(parentRel->dobj.nmspace->dobj.name));
appendPQExpBuffer(q, "%s", fmtId(parentRel->dobj.name));
}
appendPQExpBuffer(q, ")");
}
if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
}
if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
if (((tbinfo->reloptions != NULL) && strlen(tbinfo->reloptions) > 0) ||
((tbinfo->toast_reloptions != NULL) && strlen(tbinfo->toast_reloptions) > 0)) {
bool addcomma = false;
@ -16545,6 +16741,18 @@ static void dumpTableSchema(Archive* fout, TableInfo* tbinfo)
}
#endif
/*
* For materialized views, create the AS clause just like a view.
* At this point, we always mark the view as not populated.
*/
if (tbinfo->relkind == RELKIND_MATVIEW)
{
PQExpBuffer result;
result = createViewAsClause(fout, tbinfo);
appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n", result->data);
destroyPQExpBuffer(result);
}
appendPQExpBuffer(q, ";\n");
if (include_alter_table && isChangeCreateSQL) {
@ -16981,7 +17189,7 @@ static void dumpTableSchema(Archive* fout, TableInfo* tbinfo)
tbinfo->rolname,
(strcmp(reltypename, "TABLE") == 0) ? tbinfo->hasoids : false,
reltypename,
SECTION_PRE_DATA,
tbinfo->postponed_def ? SECTION_POST_DATA : SECTION_PRE_DATA,
q->data,
delq->data,
NULL,
@ -18970,6 +19178,7 @@ static void addBoundaryDependencies(DumpableObject** dobjs, int numObjs, Dumpabl
addObjectDependency(postDataBound, dobj->dumpId);
break;
case DO_INDEX:
case DO_REFRESH_MATVIEW:
case DO_TRIGGER:
case DO_DEFAULT_ACL:
case DO_RLSPOLICY:

View File

@ -88,7 +88,8 @@ typedef enum {
DO_PRE_DATA_BOUNDARY,
DO_POST_DATA_BOUNDARY,
DO_FTBL_CONSTRAINT, /* dump informational constraint info of the HDFS foreign table */
DO_RLSPOLICY /* dump row level security policy of table */
DO_RLSPOLICY, /* dump row level security policy of table */
DO_REFRESH_MATVIEW
} DumpableObjectType;
typedef struct _dumpableObject {
@ -201,11 +202,12 @@ typedef struct _tableInfo {
char relkind;
int1 relcmprs; /* row compression attribution */
char relpersistence; /* relation persistence */
bool relispopulated; /* relation is populated */
char relreplident; /* replica identifier */
char* reltablespace; /* relation tablespace */
char* reloptions; /* options specified by WITH (...) */
Oid relbucket; /* relation bucket OID */
char* toast_reloptions; /* ditto, for the TOAST table */
char* toast_reloptions; /* WITH options for the TOAST table */
bool hasindex; /* does it have any indexes? */
bool hasrules; /* does it have any rules? */
bool hastriggers; /* does it have any triggers? */
@ -225,6 +227,7 @@ typedef struct _tableInfo {
bool relrowmovement;
bool interesting; /* true if need to collect more data */
bool postponed_def; /* matview must be postponed into post-data */
#ifdef PGXC
/* PGXC table locator Data */

View File

@ -15,6 +15,7 @@
*/
#include "pg_backup_archiver.h"
#include "dumpmem.h"
#include "catalog/pg_class.h"
#ifdef GAUSS_SFT_TEST
#include "gauss_sft.h"
@ -29,8 +30,9 @@ static const char* modulename = gettext_noop("sorter");
* Objects are sorted by priority levels, and within an equal priority level
* by OID. (This is a relatively crude hack to provide semi-reasonable
* behavior for old databases without full dependency info.) Note: collations,
* extensions, text search, foreign-data, and default ACL objects can't really
* happen here, so the rather bogus priorities for them don't matter.
* extensions, text search, foreign-data, materialized view, event trigger,
* and default ACL objects can't really happen here, so the rather bogus
* priorities for them don't matter.
*
* NOTE: object-type priorities must match the section assignments made in
* pg_dump.c; that is, PRE_DATA objects must sort before DO_PRE_DATA_BOUNDARY,
@ -70,7 +72,8 @@ static const int oldObjectTypePriority[] = {
9, /* DO_BLOB */
12, /* DO_BLOB_DATA */
10, /* DO_PRE_DATA_BOUNDARY */
13 /* DO_POST_DATA_BOUNDARY */
13, /* DO_POST_DATA_BOUNDARY */
15 /* DO_REFRESH_MATVIEW */
};
/*
@ -116,7 +119,8 @@ static const int newObjectTypePriority[] = {
24, /* DO_BLOB_DATA */
22, /* DO_PRE_DATA_BOUNDARY */
25, /* DO_POST_DATA_BOUNDARY */
31 /* DO_FTBL_CONSTRAINT */
31, /* DO_FTBL_CONSTRAINT */
33 /* DO_REFRESH_MATVIEW */
};
static DumpId postDataBoundId;
@ -654,6 +658,7 @@ static void repairTypeFuncLoop(DumpableObject* typeobj, DumpableObject* funcobj)
* will be an implicit dependency in the other direction, we need to break
* the loop. If there are no other objects in the loop then we can remove
* the implicit dependency and leave the ON SELECT rule non-separate.
* This applies to matviews, as well.
*/
static void repairViewRuleLoop(DumpableObject* viewobj, DumpableObject* ruleobj)
{
@ -668,7 +673,9 @@ static void repairViewRuleLoop(DumpableObject* viewobj, DumpableObject* ruleobj)
* Because findLoop() finds shorter cycles before longer ones, it's likely
* that we will have previously fired repairViewRuleLoop() and removed the
* rule's dependency on the view. Put it back to ensure the rule won't be
* emitted before the view...
* emitted before the view.
*
* Note: this approach does *not* work for matviews, at the moment.
*/
static void repairViewRuleMultiLoop(DumpableObject* viewobj, DumpableObject* ruleobj)
{
@ -692,6 +699,34 @@ static void repairViewRuleMultiLoop(DumpableObject* viewobj, DumpableObject* rul
addObjectDependency(ruleobj, postDataBoundId);
}
/*
* If a matview is involved in a multi-object loop, we can't currently fix
* that by splitting off the rule. As a stopgap, we try to fix it by
* dropping the constraint that the matview be dumped in the pre-data section.
* This is sufficient to handle cases where a matview depends on some unique
* index, as can happen if it has a GROUP BY for example.
*
* Note that the "next object" is not necessarily the matview itself;
* it could be the matview's rowtype, for example. We may come through here
* several times while removing all the pre-data linkages. In particular,
* if there are other matviews that depend on the one with the circularity
* problem, we'll come through here for each such matview and mark them all
* as postponed. (This works because all MVs have pre-data dependencies
* to begin with, so each of them will get visited.)
*/
static void repairMatViewBoundaryMultiLoop(DumpableObject* boundaryobj, DumpableObject* nextobj)
{
/* remove boundary's dependency on object after it in loop */
removeObjectDependency(boundaryobj, nextobj->dumpId);
/* if that object is a matview, mark it as postponed into post-data */
if (nextobj->objType == DO_TABLE) {
TableInfo* nextinfo = (TableInfo*)nextobj;
if (nextinfo->relkind == RELKIND_MATVIEW)
nextinfo->postponed_def = true;
}
}
/*
* Because we make tables depend on their CHECK constraints, while there
* will be an automatic dependency in the other direction, we need to break
@ -791,24 +826,29 @@ static bool repairDependencyViewLoops(DumpableObject** loop, int nLoop)
int i = 0;
int j = 0;
/* View and its ON SELECT rule */
/* View (including matview) and its ON SELECT rule */
if (nLoop == 2 && loop[0]->objType == DO_TABLE && loop[1]->objType == DO_RULE &&
(((TableInfo*)loop[0])->relkind == 'v' || /* RELKIND_VIEW */
((TableInfo*)loop[0])->relkind == 'm') && /* RELKIND_MATVIEW */
((RuleInfo*)loop[1])->ev_type == '1' && ((RuleInfo*)loop[1])->is_instead &&
((RuleInfo*)loop[1])->ruletable == (TableInfo*)loop[0]) {
repairViewRuleLoop(loop[0], loop[1]);
return true;
}
if (nLoop == 2 && loop[1]->objType == DO_TABLE && loop[0]->objType == DO_RULE &&
(((TableInfo*)loop[1])->relkind == 'v' || /* RELKIND_VIEW */
((TableInfo*)loop[1])->relkind == 'm') && /* RELKIND_MATVIEW */
((RuleInfo*)loop[0])->ev_type == '1' && ((RuleInfo*)loop[0])->is_instead &&
((RuleInfo*)loop[0])->ruletable == (TableInfo*)loop[1]) {
repairViewRuleLoop(loop[1], loop[0]);
return true;
}
/* Indirect loop involving view and ON SELECT rule */
/* Indirect loop involving view (but not matview) and ON SELECT rule */
if (nLoop > 2) {
for (i = 0; i < nLoop; i++) {
if (loop[i]->objType == DO_TABLE) {
/* RELKIND_VIEW */
if (loop[i]->objType == DO_TABLE && ((TableInfo*)loop[i])->relkind == 'v') {
for (j = 0; j < nLoop; j++) {
if (loop[j]->objType == DO_RULE && ((RuleInfo*)loop[j])->ev_type == '1' &&
((RuleInfo*)loop[j])->is_instead && ((RuleInfo*)loop[j])->ruletable == (TableInfo*)loop[i]) {
@ -825,7 +865,21 @@ static bool repairDependencyTblChkConstraintLoops(DumpableObject** loop, int nLo
{
int i = 0;
int j = 0;
/* Indirect loop involving matview and data boundary */
if (nLoop > 2) {
for (i = 0; i < nLoop; i++) {
if (loop[i]->objType == DO_TABLE && ((TableInfo*)loop[i])->relkind == 'm') { /* RELKIND_MATVIEW */
for (j = 0; j < nLoop; j++) {
if (loop[j]->objType == DO_PRE_DATA_BOUNDARY) {
DumpableObject* nextobj;
nextobj = (j < nLoop - 1) ? loop[j + 1] : loop[0];
repairMatViewBoundaryMultiLoop(loop[j], nextobj);
return true;
}
}
}
}
}
/* Table and CHECK constraint */
if (nLoop == 2 && loop[0]->objType == DO_TABLE && loop[1]->objType == DO_CONSTRAINT &&
((ConstraintInfo*)loop[1])->contype == 'c' && ((ConstraintInfo*)loop[1])->contable == (TableInfo*)loop[0]) {
@ -1072,6 +1126,11 @@ static void describeDumpableObject(DumpableObject* obj, char* buf, int bufsize)
buf, bufsize, bufsize - 1, "INDEX %s (ID %d OID %u)", obj->name, obj->dumpId, obj->catId.oid);
securec_check_ss_c(nRet, "\0", "\0");
return;
case DO_REFRESH_MATVIEW:
nRet = snprintf(
buf, bufsize, "REFRESH MATERIALIZED VIEW %s (ID %d OID %u)", obj->name, obj->dumpId, obj->catId.oid);
securec_check_ss_c(nRet, "\0", "\0");
return;
case DO_RULE:
nRet = snprintf_s(
buf, bufsize, bufsize - 1, "RULE %s (ID %d OID %u)", obj->name, obj->dumpId, obj->catId.oid);

View File

@ -383,7 +383,7 @@ static backslashResult exec_command(const char* cmd, PsqlScanState scan_state, P
success = describeTableDetails(pattern, show_verbose, show_system);
else
/* standard listing of interesting things */
success = listTables("tvsE", NULL, show_verbose, show_system);
success = listTables("tvmsE", NULL, show_verbose, show_system);
break;
case 'a':
success = describeAggregates(pattern, show_verbose, show_system);
@ -449,6 +449,7 @@ static backslashResult exec_command(const char* cmd, PsqlScanState scan_state, P
break;
case 't':
case 'v':
case 'm':
case 'i':
case 's':
case 'E':

View File

@ -739,12 +739,15 @@ bool permissionsList(const char* pattern)
printfPQExpBuffer(&buf,
"SELECT n.nspname as \"%s\",\n"
" c.relname as \"%s\",\n"
" CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'S' THEN '%s' WHEN 'f' THEN '%s' END as \"%s\",\n"
" CASE c.relkind"
" WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'm' THEN '%s' WHEN 'S' THEN '%s' WHEN 'f' THEN '%s'"
" END as \"%s\",\n"
" ",
gettext_noop("Schema"),
gettext_noop("Name"),
gettext_noop("table"),
gettext_noop("view"),
gettext_noop("materialized view"),
gettext_noop("sequence"),
gettext_noop("foreign table"),
gettext_noop("Type"));
@ -763,7 +766,7 @@ bool permissionsList(const char* pattern)
appendPQExpBuffer(&buf,
"\nFROM pg_catalog.pg_class c\n"
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
"WHERE c.relkind IN ('r', 'v', 'S', 'f')\n");
"WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n");
/*
* Unless a schema pattern is specified, we suppress system and temp
@ -1429,7 +1432,7 @@ static bool describeOneTableDetails(const char* schemaname, const char* relation
* types, and foreign tables (c.f. CommentObject() in comment.c).
*/
if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || tableinfo.relkind == 'f' ||
tableinfo.relkind == 'c')
tableinfo.relkind == 'c' || tableinfo.relkind == 'm')
appendPQExpBuffer(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)");
}
@ -1454,6 +1457,12 @@ static bool describeOneTableDetails(const char* schemaname, const char* relation
case 'v':
printfPQExpBuffer(&title, _("View \"%s.%s\""), schemaname, relationname);
break;
case 'm':
if (tableinfo.relpersistence == 'u')
printfPQExpBuffer(&title, _("Unlogged materialized view \"%s.%s\""), schemaname, relationname);
else
printfPQExpBuffer(&title, _("Materialized view \"%s.%s\""), schemaname, relationname);
break;
case 'S':
printfPQExpBuffer(&title, _("Sequence \"%s.%s\""), schemaname, relationname);
break;
@ -1487,7 +1496,8 @@ static bool describeOneTableDetails(const char* schemaname, const char* relation
headers[1] = gettext_noop("Type");
cols = 2;
if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || tableinfo.relkind == 'f' || tableinfo.relkind == 'c') {
if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || tableinfo.relkind == 'f' ||
tableinfo.relkind == 'c' || tableinfo.relkind == 'm') {
show_modifiers = true;
headers[cols++] = gettext_noop("Modifiers");
modifiers = (char**)pg_malloc_zero((unsigned long)(numrows + 1) * sizeof(*modifiers));
@ -1504,11 +1514,11 @@ static bool describeOneTableDetails(const char* schemaname, const char* relation
if (verbose) {
headers[cols++] = gettext_noop("Storage");
if (tableinfo.relkind == 'r' || tableinfo.relkind == 'f')
if (tableinfo.relkind == 'r' || tableinfo.relkind == 'f' || tableinfo.relkind == 'm')
headers[cols++] = gettext_noop("Stats target");
/* Column comments, if the relkind supports this feature. */
if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || tableinfo.relkind == 'c' ||
tableinfo.relkind == 'f')
tableinfo.relkind == 'f' || tableinfo.relkind == 'm')
headers[cols++] = gettext_noop("Description");
}
@ -1518,8 +1528,8 @@ static bool describeOneTableDetails(const char* schemaname, const char* relation
for (i = 0; i < cols; i++)
printTableAddHeader(&cont, headers[i], true, 'l');
/* Check if table is a view */
if (tableinfo.relkind == 'v' && verbose) {
/* Check if table is a view or materialized view */
if ((tableinfo.relkind == 'v' || tableinfo.relkind == 'm') && verbose) {
PGresult* result = NULL;
printfPQExpBuffer(&buf, "SELECT pg_catalog.pg_get_viewdef('%s'::pg_catalog.oid, true);", oid);
@ -1598,13 +1608,13 @@ static bool describeOneTableDetails(const char* schemaname, const char* relation
false);
/* Statistics target, if the relkind supports this feature */
if (tableinfo.relkind == 'r' || tableinfo.relkind == 'f') {
if (tableinfo.relkind == 'r' || tableinfo.relkind == 'f' || tableinfo.relkind == 'm') {
printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false);
}
/* Column comments, if the relkind supports this feature. */
if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || tableinfo.relkind == 'c' ||
tableinfo.relkind == 'f')
tableinfo.relkind == 'f' || tableinfo.relkind == 'm')
printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false);
}
}
@ -1698,39 +1708,7 @@ static bool describeOneTableDetails(const char* schemaname, const char* relation
}
PQclear(result);
} else if (view_def != NULL) {
PGresult* result = NULL;
/* Footer information about a view */
printTableAddFooter(&cont, _("View definition:"));
printTableAddFooter(&cont, view_def);
/* print rules */
if (tableinfo.hasrules) {
printfPQExpBuffer(&buf,
"SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true))\n"
"FROM pg_catalog.pg_rewrite r\n"
"WHERE r.ev_class = '%s' AND r.rulename != '_RETURN' ORDER BY 1;",
oid);
result = PSQLexec(buf.data, false);
if (result == NULL)
goto error_return;
if (PQntuples(result) > 0) {
printTableAddFooter(&cont, _("Rules:"));
for (i = 0; i < PQntuples(result); i++) {
const char* ruledef = NULL;
/* Everything after "CREATE RULE" is echoed verbatim */
ruledef = PQgetvalue(result, i, 1);
ruledef += 12;
printfPQExpBuffer(&buf, " %s", ruledef);
printTableAddFooter(&cont, buf.data);
}
}
PQclear(result);
}
} else if (tableinfo.relkind == 'S') {
/* Footer information about a sequence */
PGresult* result = NULL;
@ -1766,7 +1744,7 @@ static bool describeOneTableDetails(const char* schemaname, const char* relation
* don't print anything.
*/
PQclear(result);
} else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'f') {
} else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'f' || tableinfo.relkind == 'm') {
/* Footer information about a table */
PGresult* result = NULL;
int tuples = 0;
@ -2038,7 +2016,7 @@ static bool describeOneTableDetails(const char* schemaname, const char* relation
}
/* print rules */
if (tableinfo.hasrules) {
if (tableinfo.hasrules && tableinfo.relkind != 'm') {
if (pset.sversion >= 80300) {
printfPQExpBuffer(&buf,
"SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), "
@ -2128,6 +2106,39 @@ static bool describeOneTableDetails(const char* schemaname, const char* relation
}
}
if (view_def != NULL) {
PGresult *result = NULL;
/* Footer information about a view */
printTableAddFooter(&cont, _("View definition:"));
printTableAddFooter(&cont, view_def);
/* print rules */
if (tableinfo.hasrules) {
printfPQExpBuffer(&buf,
"SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true))\n"
"FROM pg_catalog.pg_rewrite r\n"
"WHERE r.ev_class = '%s' AND r.rulename != '_RETURN' ORDER BY 1;",
oid);
result = PSQLexec(buf.data, false);
if (result == NULL)
goto error_return;
if (PQntuples(result) > 0) {
printTableAddFooter(&cont, _("Rules:"));
for (i = 0; i < PQntuples(result); i++) {
const char *ruledef = NULL;
/* Everything after "CREATE RULE" is echoed verbatim */
ruledef = PQgetvalue(result, i, 1);
ruledef += 12;
printfPQExpBuffer(&buf, " %s", ruledef);
printTableAddFooter(&cont, buf.data);
}
}
PQclear(result);
}
}
/*
* Print triggers next, if any (but only user-defined triggers). This
* could apply to either a table or a view.
@ -2252,7 +2263,7 @@ static bool describeOneTableDetails(const char* schemaname, const char* relation
/*
* Finish printing the footer information about a table.
*/
if (tableinfo.relkind == 'r' || tableinfo.relkind == 'f') {
if (tableinfo.relkind == 'r' || tableinfo.relkind == 'f' || tableinfo.relkind == 'm') {
PGresult* result = NULL;
int tuples;
@ -2485,8 +2496,8 @@ static bool describeOneTableDetails(const char* schemaname, const char* relation
printTableAddFooter(&cont, buf.data);
}
/* OIDs, if verbose */
if (verbose) {
/* OIDs, if verbose and not a materialized view */
if (verbose && tableinfo.relkind != 'm') {
const char* s = _("Has OIDs");
printfPQExpBuffer(&buf, "%s: %s", s, (tableinfo.hasoids ? _("yes") : _("no")));
@ -2680,7 +2691,7 @@ error_return:
static void add_tablespace_footer(printTableContent* const cont, char relkind, Oid tablespace, const bool newline)
{
/* relkinds for which we support tablespaces */
if (relkind == 'r' || relkind == 'i') {
if (relkind == 'r' || relkind == 'm' || relkind == 'i') {
/*
* We ignore the database default tablespace so that users not using
* tablespaces don't need to know about them. This case also covers
@ -2978,6 +2989,7 @@ bool listDbRoleSettings(const char* pattern, const char* pattern2)
* t - tables
* i - indexes
* v - views
* m - materialized views
* s - sequences
* E - foreign table (Note: different from 'f', the relkind value)
* (any order of the above is fine)
@ -2988,6 +3000,7 @@ bool listTables(const char* tabtypes, const char* pattern, bool verbose, bool sh
bool showTables = strchr(tabtypes, 't') != NULL;
bool showIndexes = strchr(tabtypes, 'i') != NULL;
bool showViews = strchr(tabtypes, 'v') != NULL;
bool showMatViews = strchr(tabtypes, 'm') != NULL;
bool showSeq = strchr(tabtypes, 's') != NULL;
bool showForeign = strchr(tabtypes, 'E') != NULL;
@ -2997,8 +3010,8 @@ bool listTables(const char* tabtypes, const char* pattern, bool verbose, bool sh
bool* translate_columns = NULL;
int trues[] = {2};
if (!(showTables || showIndexes || showViews || showSeq || showForeign)) {
showTables = showViews = showSeq = showForeign = true;
if (!(showTables || showIndexes || showViews || showMatViews || showSeq || showForeign)) {
showTables = showViews = showMatViews = showSeq = showForeign = true;
}
initPQExpBuffer(&buf);
@ -3010,13 +3023,21 @@ bool listTables(const char* tabtypes, const char* pattern, bool verbose, bool sh
printfPQExpBuffer(&buf,
"SELECT n.nspname as \"%s\",\n"
" c.relname as \"%s\",\n"
" CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' WHEN 's' THEN "
"'%s' WHEN 'f' THEN '%s' END as \"%s\",\n"
" CASE c.relkind"
" WHEN 'r' THEN '%s'"
" WHEN 'v' THEN '%s'"
" WHEN 'm' THEN '%s'"
" WHEN 'i' THEN '%s'"
" WHEN 'S' THEN '%s'"
" WHEN 's' THEN '%s'"
" WHEN 'f' THEN '%s'"
" END as \"%s\",\n"
" pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
gettext_noop("Schema"),
gettext_noop("Name"),
gettext_noop("table"),
gettext_noop("view"),
gettext_noop("materialized view"),
gettext_noop("index"),
gettext_noop("sequence"),
gettext_noop("special"),
@ -3062,6 +3083,8 @@ bool listTables(const char* tabtypes, const char* pattern, bool verbose, bool sh
appendPQExpBuffer(&buf, "'r',");
if (showViews)
appendPQExpBuffer(&buf, "'v',");
if (showMatViews)
appendPQExpBuffer(&buf, "'m',");
if (showIndexes)
appendPQExpBuffer(&buf, "'i',");
if (showSeq)

View File

@ -257,6 +257,7 @@ void slashUsage(unsigned short int pager)
fprintf(output, _(" \\di[S+] [PATTERN] list indexes\n"));
fprintf(output, _(" \\dl list large objects, same as \\lo_list\n"));
fprintf(output, _(" \\dL[S+] [PATTERN] list procedural languages\n"));
fprintf(output, _(" \\dm[S+] [PATTERN] list materialized views\n"));
fprintf(output, _(" \\dn[S+] [PATTERN] list schemas\n"));
fprintf(output, _(" \\do[S] [PATTERN] list operators\n"));
fprintf(output, _(" \\dO[S+] [PATTERN] list collations\n"));

View File

@ -370,11 +370,11 @@ static const SchemaQuery Query_for_list_of_relations = {
/* qualresult */
NULL};
static const SchemaQuery Query_for_list_of_tsvf = {
static const SchemaQuery Query_for_list_of_tsvmf = {
/* catname */
"pg_catalog.pg_class c",
/* selcondition */
"c.relkind IN ('r', 'S', 'v', 'f')",
"c.relkind IN ('r', 'S', 'v', 'm', 'f')",
/* viscondition */
"pg_catalog.pg_table_is_visible(c.oid)",
/* namespace */
@ -384,11 +384,25 @@ static const SchemaQuery Query_for_list_of_tsvf = {
/* qualresult */
NULL};
static const SchemaQuery Query_for_list_of_tf = {
static const SchemaQuery Query_for_list_of_tmf = {
/* catname */
"pg_catalog.pg_class c",
/* selcondition */
"c.relkind IN ('r', 'f')",
"c.relkind IN ('r', 'm', 'f')",
/* viscondition */
"pg_catalog.pg_table_is_visible(c.oid)",
/* namespace */
"c.relnamespace",
/* result */
"pg_catalog.quote_ident(c.relname)",
/* qualresult */
NULL};
static const SchemaQuery Query_for_list_of_tm = {
/* catname */
"pg_catalog.pg_class c",
/* selcondition */
"c.relkind IN ('r', 'm')",
/* viscondition */
"pg_catalog.pg_table_is_visible(c.oid)",
/* namespace */
@ -412,6 +426,21 @@ static const SchemaQuery Query_for_list_of_views = {
/* qualresult */
NULL};
static const SchemaQuery Query_for_list_of_matviews = {
/* catname */
"pg_catalog.pg_class c",
/* selcondition */
"c.relkind IN ('m')",
/* viscondition */
"pg_catalog.pg_table_is_visible(c.oid)",
/* namespace */
"c.relnamespace",
/* result */
"pg_catalog.quote_ident(c.relname)",
/* qualresult */
NULL
};
/*
* Queries to get lists of names of various kinds of things, possibly
* restricted to names matching a partially entered name. In these queries,
@ -657,6 +686,7 @@ static const pgsql_thing_t words_after_create[] = {
{"GROUP", Query_for_list_of_roles, NULL, 0},
{"LANGUAGE", Query_for_list_of_languages, NULL, 0},
{"INDEX", NULL, &Query_for_list_of_indexes, 0},
{"MATERIALIZED VIEW", NULL, NULL},
#ifdef PGXC
{"NODE", Query_for_list_of_available_nodenames, NULL, 0},
{"NODE GROUP", Query_for_list_of_available_nodegroup_names, NULL, 0},

View File

@ -815,11 +815,13 @@ static List* objectsInSchemaToOids(GrantObjectType objtype, List* nspnames)
switch (objtype) {
case ACL_OBJECT_RELATION:
/* Process regular tables, views and foreign tables */
/* Process regular tables, views, materialized views and foreign tables */
objs = getRelationsInNamespace(namespaceId, RELKIND_RELATION);
objects = list_concat(objects, objs);
objs = getRelationsInNamespace(namespaceId, RELKIND_VIEW);
objects = list_concat(objects, objs);
objs = getRelationsInNamespace(namespaceId, RELKIND_MATVIEW);
objects = list_concat(objects, objs);
objs = getRelationsInNamespace(namespaceId, RELKIND_FOREIGN_TABLE);
objects = list_concat(objects, objs);
break;

View File

@ -973,6 +973,10 @@
"btrecordcmp", 1,
AddBuiltinFunc(_0(2987), _1("btrecordcmp"), _2(2), _3(true), _4(false), _5(btrecordcmp), _6(23), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('i'), _18(0), _19(2, 2249, 2249), _20(NULL), _21(NULL), _22(NULL), _23(NULL), _24("btrecordcmp"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false))
),
AddFuncGroup(
"btrecordimagecmp", 1,
AddBuiltinFunc(_0(3997), _1("btrecordimagecmp"), _2(2), _3(true), _4(false), _5(btrecordimagecmp), _6(23), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('i'), _18(0), _19(2, 2249, 2249), _20(NULL), _21(NULL), _22(NULL), _23(NULL), _24("btrecordimagecmp"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false))
),
AddFuncGroup(
"btreltimecmp", 1,
AddBuiltinFunc(_0(380), _1("btreltimecmp"), _2(2), _3(true), _4(false), _5(btreltimecmp), _6(23), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('i'), _18(0), _19(2, 703, 703), _20(NULL), _21(NULL), _22(NULL), _23(NULL), _24("btreltimecmp"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false))
@ -8004,6 +8008,30 @@
"record_gt", 1,
AddBuiltinFunc(_0(2984), _1("record_gt"), _2(2), _3(true), _4(false), _5(record_gt), _6(16), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('i'), _18(0), _19(2, 2249, 2249), _20(NULL), _21(NULL), _22(NULL), _23(NULL), _24("record_gt"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false))
),
AddFuncGroup(
"record_image_eq", 1,
AddBuiltinFunc(_0(3991), _1("record_image_eq"), _2(2), _3(true), _4(false), _5(record_image_eq), _6(16), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('i'), _18(0), _19(2, 2249, 2249), _20(NULL), _21(NULL), _22(NULL), _23(NULL), _24("record_image_eq"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false))
),
AddFuncGroup(
"record_image_ge", 1,
AddBuiltinFunc(_0(3996), _1("record_image_ge"), _2(2), _3(true), _4(false), _5(record_image_ge), _6(16), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('i'), _18(0), _19(2, 2249, 2249), _20(NULL), _21(NULL), _22(NULL), _23(NULL), _24("record_image_ge"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false))
),
AddFuncGroup(
"record_image_gt", 1,
AddBuiltinFunc(_0(3994), _1("record_image_gt"), _2(2), _3(true), _4(false), _5(record_image_gt), _6(16), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('i'), _18(0), _19(2, 2249, 2249), _20(NULL), _21(NULL), _22(NULL), _23(NULL), _24("record_image_gt"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false))
),
AddFuncGroup(
"record_image_le", 1,
AddBuiltinFunc(_0(3995), _1("record_image_le"), _2(2), _3(true), _4(false), _5(record_image_le), _6(16), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('i'), _18(0), _19(2, 2249, 2249), _20(NULL), _21(NULL), _22(NULL), _23(NULL), _24("record_image_le"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false))
),
AddFuncGroup(
"record_image_lt", 1,
AddBuiltinFunc(_0(3993), _1("record_image_lt"), _2(2), _3(true), _4(false), _5(record_image_lt), _6(16), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('i'), _18(0), _19(2, 2249, 2249), _20(NULL), _21(NULL), _22(NULL), _23(NULL), _24("record_image_lt"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false))
),
AddFuncGroup(
"record_image_ne", 1,
AddBuiltinFunc(_0(3992), _1("record_image_ne"), _2(2), _3(true), _4(false), _5(record_image_ne), _6(16), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('i'), _18(0), _19(2, 2249, 2249), _20(NULL), _21(NULL), _22(NULL), _23(NULL), _24("record_image_ne"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false))
),
AddFuncGroup(
"record_in", 1,
AddBuiltinFunc(_0(2290), _1("record_in"), _2(3), _3(true), _4(false), _5(record_in), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('s'), _18(0), _19(3, 2275, 26, 23), _20(NULL), _21(NULL), _22(NULL), _23(NULL), _24("record_in"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false))

View File

@ -3034,6 +3034,9 @@ static void getRelationDescription(StringInfo buffer, Oid relid)
case RELKIND_VIEW:
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);
break;

View File

@ -970,7 +970,7 @@ void InsertPgClassTuple(
else
nulls[Anum_pg_class_reloptions - 1] = true;
if (relkind == RELKIND_RELATION || relkind == RELKIND_TOASTVALUE)
if (relkind == RELKIND_RELATION || relkind == RELKIND_MATVIEW || relkind == RELKIND_TOASTVALUE)
values[Anum_pg_class_relfrozenxid64 - 1] = u_sess->utils_cxt.RecentXmin;
else
values[Anum_pg_class_relfrozenxid64 - 1] = InvalidTransactionId;
@ -1027,6 +1027,7 @@ static void AddNewRelationTuple(Relation pg_class_desc, Relation new_rel_desc, O
switch (relkind) {
case RELKIND_RELATION:
case RELKIND_MATVIEW:
case RELKIND_INDEX:
case RELKIND_TOASTVALUE:
/* The relation is real, but as yet empty */
@ -1049,7 +1050,7 @@ static void AddNewRelationTuple(Relation pg_class_desc, Relation new_rel_desc, O
}
/* Initialize relfrozenxid */
if (relkind == RELKIND_RELATION || relkind == RELKIND_TOASTVALUE) {
if (relkind == RELKIND_RELATION || relkind == RELKIND_MATVIEW || relkind == RELKIND_TOASTVALUE) {
/*
* Initialize to the minimum XID that could put tuples in the table.
* We know that no xacts older than RecentXmin are still running, so
@ -1828,8 +1829,8 @@ Oid heap_create_with_catalog(const char* relname, Oid relnamespace, Oid reltable
* supplied.
*/
if (u_sess->proc_cxt.IsBinaryUpgrade && OidIsValid(u_sess->upg_cxt.binary_upgrade_next_heap_pg_class_oid) &&
(relkind == RELKIND_RELATION || relkind == RELKIND_SEQUENCE || relkind == RELKIND_VIEW ||
relkind == RELKIND_COMPOSITE_TYPE || relkind == RELKIND_FOREIGN_TABLE)) {
(relkind == RELKIND_RELATION || relkind == RELKIND_SEQUENCE || relkind == RELKIND_VIEW ||
relkind == RELKIND_MATVIEW || relkind == RELKIND_COMPOSITE_TYPE || relkind == RELKIND_FOREIGN_TABLE)) {
relid = u_sess->upg_cxt.binary_upgrade_next_heap_pg_class_oid;
u_sess->upg_cxt.binary_upgrade_next_heap_pg_class_oid = InvalidOid;
@ -1879,6 +1880,7 @@ Oid heap_create_with_catalog(const char* relname, Oid relnamespace, Oid reltable
switch (relkind) {
case RELKIND_RELATION:
case RELKIND_VIEW:
case RELKIND_MATVIEW:
case RELKIND_FOREIGN_TABLE:
relacl = get_user_default_acl(ACL_OBJECT_RELATION, ownerid, relnamespace);
break;
@ -1957,13 +1959,12 @@ Oid heap_create_with_catalog(const char* relname, Oid relnamespace, Oid reltable
/*
* Decide whether to create an array type over the relation's rowtype. We
* do not create any array types for system catalogs (ie, those made
* during initdb). We create array types for regular relations, views,
* composite types and foreign tables ... but not, eg, for toast tables or
* sequences.
* during initdb). We do not create them where the use of a relation as
* such is an implementation detail: toast tables, sequences and indexes.
*/
if (IsUnderPostmaster && !u_sess->attr.attr_common.IsInplaceUpgrade &&
(relkind == RELKIND_RELATION || relkind == RELKIND_VIEW || relkind == RELKIND_FOREIGN_TABLE ||
relkind == RELKIND_COMPOSITE_TYPE))
(relkind == RELKIND_RELATION || relkind == RELKIND_VIEW || relkind == RELKIND_MATVIEW ||
relkind == RELKIND_FOREIGN_TABLE || relkind == RELKIND_COMPOSITE_TYPE))
new_array_oid = AssignTypeArrayOid();
/*
@ -2203,7 +2204,7 @@ Oid heap_create_with_catalog(const char* relname, Oid relnamespace, Oid reltable
register_on_commit_action(relid, oncommit);
if (relpersistence == RELPERSISTENCE_UNLOGGED) {
Assert(relkind == RELKIND_RELATION || relkind == RELKIND_TOASTVALUE);
Assert(relkind == RELKIND_RELATION || relkind == RELKIND_MATVIEW || relkind == RELKIND_TOASTVALUE);
heap_create_init_fork(new_rel_desc);
}

View File

@ -186,6 +186,7 @@ ObjectAddress get_object_address(
case OBJECT_SEQUENCE:
case OBJECT_TABLE:
case OBJECT_VIEW:
case OBJECT_MATVIEW:
case OBJECT_FOREIGN_TABLE:
address = get_relation_by_qualified_name(objtype, objname, &relation, lockmode, missing_ok);
break;
@ -530,6 +531,12 @@ static ObjectAddress get_relation_by_qualified_name(
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
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;
case OBJECT_FOREIGN_TABLE:
if (relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
ereport(ERROR,
@ -763,6 +770,7 @@ void check_object_ownership(
case OBJECT_SEQUENCE:
case OBJECT_TABLE:
case OBJECT_VIEW:
case OBJECT_MATVIEW:
case OBJECT_FOREIGN_TABLE:
case OBJECT_COLUMN:
case OBJECT_RULE:

View File

@ -169,6 +169,18 @@ CREATE VIEW pg_tables AS
LEFT JOIN pg_object po ON (po.object_oid = C.oid and po.object_type = 'r')
WHERE C.relkind = 'r';
CREATE VIEW pg_matviews AS
SELECT
N.nspname AS schemaname,
C.relname AS matviewname,
pg_get_userbyid(C.relowner) AS matviewowner,
T.spcname AS tablespace,
C.relhasindex AS hasindexes,
pg_get_viewdef(C.oid) AS definition
FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
LEFT JOIN pg_tablespace T ON (T.oid = C.reltablespace)
WHERE C.relkind = 'm';
CREATE VIEW pg_indexes AS
SELECT
N.nspname AS schemaname,
@ -180,7 +192,7 @@ CREATE VIEW pg_indexes AS
JOIN pg_class I ON (I.oid = X.indexrelid)
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
LEFT JOIN pg_tablespace T ON (T.oid = I.reltablespace)
WHERE C.relkind = 'r' AND I.relkind = 'i';
WHERE C.relkind IN ('r', 'm') AND I.relkind = 'i';
-- For global temporary table
CREATE VIEW pg_gtt_relstats WITH (security_barrier) AS
@ -442,6 +454,7 @@ SELECT
l.objoid, l.classoid, l.objsubid,
CASE WHEN rel.relkind = 'r' THEN 'table'::text
WHEN rel.relkind = 'v' THEN 'view'::text
WHEN rel.relkind = 'm' THEN 'materialized view'::text
WHEN rel.relkind = 'S' THEN 'sequence'::text
WHEN rel.relkind = 'f' THEN 'foreign table'::text END AS objtype,
rel.relnamespace AS objnamespace,
@ -629,7 +642,7 @@ CREATE VIEW pg_stat_all_tables AS
FROM pg_class C LEFT JOIN
pg_index I ON C.oid = I.indrelid
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE C.relkind IN ('r', 't')
WHERE C.relkind IN ('r', 't', 'm')
GROUP BY C.oid, N.nspname, C.relname;
CREATE VIEW pg_stat_xact_all_tables AS
@ -649,7 +662,7 @@ CREATE VIEW pg_stat_xact_all_tables AS
FROM pg_class C LEFT JOIN
pg_index I ON C.oid = I.indrelid
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE C.relkind IN ('r', 't')
WHERE C.relkind IN ('r', 't', 'm')
GROUP BY C.oid, N.nspname, C.relname;
CREATE VIEW pg_stat_sys_tables AS
@ -694,7 +707,7 @@ CREATE VIEW pg_statio_all_tables AS
pg_class T ON C.reltoastrelid = T.oid LEFT JOIN
pg_class X ON T.reltoastidxid = X.oid
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE C.relkind IN ('r', 't')
WHERE C.relkind IN ('r', 't', 'm')
GROUP BY C.oid, N.nspname, C.relname, T.oid, X.oid;
CREATE VIEW pg_statio_sys_tables AS
@ -721,7 +734,7 @@ CREATE VIEW pg_stat_all_indexes AS
pg_index X ON C.oid = X.indrelid JOIN
pg_class I ON I.oid = X.indexrelid
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE C.relkind IN ('r', 't');
WHERE C.relkind IN ('r', 't', 'm');
CREATE VIEW pg_stat_sys_indexes AS
SELECT * FROM pg_stat_all_indexes
@ -747,7 +760,7 @@ CREATE VIEW pg_statio_all_indexes AS
pg_index X ON C.oid = X.indrelid JOIN
pg_class I ON I.oid = X.indexrelid
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE C.relkind IN ('r', 't');
WHERE C.relkind IN ('r', 't', 'm');
CREATE VIEW pg_statio_sys_indexes AS
SELECT * FROM pg_statio_all_indexes

View File

@ -96,8 +96,9 @@ void BootstrapToastTable(char* relName, Oid toastOid, Oid toastIndexOid)
Relation rel;
rel = heap_openrv(makeRangeVar(NULL, relName, -1), AccessExclusiveLock);
if (rel->rd_rel->relkind != RELKIND_RELATION)
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table", relName)));
if (rel->rd_rel->relkind != RELKIND_RELATION && rel->rd_rel->relkind != RELKIND_MATVIEW)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table or materialized view", relName)));
/* create_toast_table does all the work */
if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum)0, false, NULL))
@ -106,6 +107,13 @@ void BootstrapToastTable(char* relName, Oid toastOid, Oid toastIndexOid)
heap_close(rel, NoLock);
}
void NewRelationCreateToastTable(Oid relOid, Datum reloptions)
{
Relation rel;
rel = heap_open(relOid, AccessExclusiveLock);
(void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions, false, NULL);
heap_close(rel, NoLock);
}
/*
* create_toast_table --- internal workhorse

View File

@ -2181,6 +2181,7 @@ static IntoClause* _copyIntoClause(const IntoClause* from)
COPY_SCALAR_FIELD(onCommit);
COPY_SCALAR_FIELD(row_compress);
COPY_STRING_FIELD(tableSpaceName);
COPY_NODE_FIELD(viewQuery);
COPY_SCALAR_FIELD(skipData);
#ifdef PGXC
COPY_NODE_FIELD(distributeby);
@ -4800,11 +4801,22 @@ static CreateTableAsStmt* _copyCreateTableAsStmt(const CreateTableAsStmt* from)
COPY_NODE_FIELD(query);
COPY_NODE_FIELD(into);
COPY_SCALAR_FIELD(relkind);
COPY_SCALAR_FIELD(is_select_into);
return newnode;
}
static RefreshMatViewStmt* _copyRefreshMatViewStmt(const RefreshMatViewStmt* from)
{
RefreshMatViewStmt* newnode = makeNode(RefreshMatViewStmt);
COPY_SCALAR_FIELD(skipData);
COPY_NODE_FIELD(relation);
return newnode;
}
static CreateSeqStmt* _copyCreateSeqStmt(const CreateSeqStmt* from)
{
CreateSeqStmt* newnode = makeNode(CreateSeqStmt);
@ -6399,6 +6411,9 @@ void* copyObject(const void* from)
case T_CreateTableAsStmt:
retval = _copyCreateTableAsStmt((CreateTableAsStmt*)from);
break;
case T_RefreshMatViewStmt:
retval = _copyRefreshMatViewStmt((RefreshMatViewStmt*)from);
break;
case T_ReplicaIdentityStmt:
retval = _copyReplicaIdentityStmt((ReplicaIdentityStmt*)from);
break;

View File

@ -120,6 +120,7 @@ static bool _equalIntoClause(const IntoClause* a, const IntoClause* b)
COMPARE_SCALAR_FIELD(onCommit);
COMPARE_SCALAR_FIELD(row_compress);
COMPARE_STRING_FIELD(tableSpaceName);
COMPARE_NODE_FIELD(viewQuery);
COMPARE_SCALAR_FIELD(skipData);
return true;
@ -1554,11 +1555,21 @@ static bool _equalCreateTableAsStmt(const CreateTableAsStmt* a, const CreateTabl
{
COMPARE_NODE_FIELD(query);
COMPARE_NODE_FIELD(into);
COMPARE_SCALAR_FIELD(relkind);
COMPARE_SCALAR_FIELD(is_select_into);
return true;
}
static bool _equalRefreshMatViewStmt(const RefreshMatViewStmt* a, const RefreshMatViewStmt* b)
{
COMPARE_SCALAR_FIELD(skipData);
COMPARE_NODE_FIELD(relation);
return true;
}
static bool _equalReplicaIdentityStmt(const ReplicaIdentityStmt* a, const ReplicaIdentityStmt* b)
{
COMPARE_SCALAR_FIELD(identity_type);
@ -3130,6 +3141,9 @@ bool equal(const void* a, const void* b)
case T_CreateTableAsStmt:
retval = _equalCreateTableAsStmt((CreateTableAsStmt*)a, (CreateTableAsStmt*)b);
break;
case T_RefreshMatViewStmt:
retval = _equalRefreshMatViewStmt((RefreshMatViewStmt*)a, (RefreshMatViewStmt*)b);
break;
case T_CreateSeqStmt:
retval = _equalCreateSeqStmt((CreateSeqStmt*)a, (CreateSeqStmt*)b);
break;

View File

@ -617,7 +617,34 @@ Param* makeParam(ParamKind paramkind, int paramid, Oid paramtype, int32 paramtyp
}
/*
* makeIndexInfo
* makeColumnDef -
* build a ColumnDef node to represent a simple column definition.
*
* Type and collation are specified by OID.
* Other properties are all basic to start with.
*/
ColumnDef* makeColumnDef(const char* colname, Oid typeOid, int32 typmod, Oid collOid)
{
ColumnDef* n = makeNode(ColumnDef);
n->colname = pstrdup(colname);
n->typname = makeTypeNameFromOid(typeOid, typmod);
n->inhcount = 0;
n->is_local = true;
n->is_not_null = false;
n->is_from_type = false;
n->storage = 0;
n->raw_default = NULL;
n->cooked_default = NULL;
n->collClause = NULL;
n->collOid = collOid;
n->constraints = NIL;
n->fdwoptions = NIL;
return n;
}
/* makeIndexInfo
* create an IndexInfo node
*/
IndexInfo* makeIndexInfo(int numattrs, List* expressions, List* predicates, bool unique, bool isready, bool concurrent)

View File

@ -2824,6 +2824,9 @@ bool raw_expression_tree_walker(Node* node, bool (*walker)(), void* context)
return true;
}
/* colNames, options are deemed uninteresting */
/* viewQuery should be null in raw parsetree, but check it */
if (p2walker(into->viewQuery, context))
return true;
} break;
case T_List:
foreach (temp, (List*)node) {

View File

@ -1854,6 +1854,7 @@ static void _outIntoClause(StringInfo str, IntoClause* node)
WRITE_ENUM_FIELD(onCommit, OnCommitAction);
WRITE_ENUM_FIELD(row_compress, RelCompressType);
WRITE_STRING_FIELD(tableSpaceName);
WRITE_NODE_FIELD(viewQuery);
WRITE_BOOL_FIELD(skipData);
}

View File

@ -1511,6 +1511,7 @@ static IntoClause* _readIntoClause(void)
READ_ENUM_FIELD(onCommit, OnCommitAction);
READ_ENUM_FIELD(row_compress, RelCompressType);
READ_STRING_FIELD(tableSpaceName);
READ_NODE_FIELD(viewQuery);
READ_BOOL_FIELD(skipData);
READ_DONE();

View File

@ -227,6 +227,7 @@ Query* transformTopLevelStmt(ParseState* pstate, Node* parseTree, bool isFirstNo
ctas->query = parseTree;
ctas->into = stmt->intoClause;
ctas->relkind = OBJECT_TABLE;
ctas->is_select_into = true;
/*
@ -2993,7 +2994,7 @@ static Query* transformExplainStmt(ParseState* pstate, ExplainStmt* stmt)
{
Query* result = NULL;
/* transform contained query, allowing SELECT INTO */
/* transform a CREATE TABLE AS, SELECT ... INTO, or CREATE MATERIALIZED VIEW Statement */
stmt->query = (Node*)transformTopLevelStmt(pstate, stmt->query);
/* represent the command as a utility Query */
@ -3014,9 +3015,56 @@ static Query* transformCreateTableAsStmt(ParseState* pstate, CreateTableAsStmt*
{
Query* result = NULL;
ListCell* lc = NULL;
Query* query;
/* transform contained query */
stmt->query = (Node*)transformStmt(pstate, stmt->query);
query = transformStmt(pstate, stmt->query);
stmt->query = (Node*)query;
/* additional work needed for CREATE MATERIALIZED VIEW */
if (stmt->relkind == OBJECT_MATVIEW) {
/*
* Prohibit a data-modifying CTE in the query used to create a
* materialized view. It's not sufficiently clear what the user would
* want to happen if the MV is refreshed or incrementally maintained.
*/
if (query->hasModifyingCTE)
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg(
"materialized views must not use data-modifying statements in WITH")));
/*
* Check whether any temporary database objects are used in the creation query.
* It would be hard to refresh data or incrementally maintain it if a source
* disappeared.
*/
if (isQueryUsingTempRelation(query))
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("materialized views must not use temporary tables or views")));
/*
* A materialized view would either need to save parameters for use in
* maintaining/loading the data or prohibit them entirely. The latter
* seems safer and more sane.
*/
if (query_contains_extern_params(query))
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("materialized views may not be defined using bound parameters")));
/*
* For now, we disallow unlogged materialized views, because it
* seems like a bad idea for them to just go to empty after a crash.
* (If we could mark them as unpopulated, that would be better, but
* that requires catalog changes which crash recovery can't presently
* handle.)
*/
if (stmt->into->rel->relpersistence == RELPERSISTENCE_UNLOGGED)
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialized views cannot be UNLOGGED")));
/*
* At runtime, we'll need a copy of the parsed-but-not-rewritten Query
* for purposes of creating the view's ON SELECT rule. We stash that
* in the IntoClause because that's where intorel_startup() can
* conveniently get it from.
*/
stmt->into->viewQuery = (Node*)copyObject(query);
}
/* if result type of new relation is unknown-type, then resolve as type TEXT */
foreach (lc, ((Query*)stmt->query)->targetList) {

View File

@ -277,7 +277,7 @@ static void ParseUpdateMultiSet(List *set_target_list, SelectStmt *stmt, core_yy
DropOwnedStmt ReassignOwnedStmt
AlterTSConfigurationStmt AlterTSDictionaryStmt AnonyBlockStmt
BarrierStmt AlterNodeStmt CreateNodeStmt DropNodeStmt AlterCoordinatorStmt
CreateNodeGroupStmt AlterNodeGroupStmt DropNodeGroupStmt
CreateMatViewStmt RefreshMatViewStmt CreateNodeGroupStmt AlterNodeGroupStmt DropNodeGroupStmt
CreateResourcePoolStmt AlterResourcePoolStmt DropResourcePoolStmt
CreateWorkloadGroupStmt AlterWorkloadGroupStmt DropWorkloadGroupStmt
CreateAppWorkloadGroupMappingStmt AlterAppWorkloadGroupMappingStmt DropAppWorkloadGroupMappingStmt
@ -398,7 +398,7 @@ static void ParseUpdateMultiSet(List *set_target_list, SelectStmt *stmt, core_yy
%type <defelt> fdw_option
%type <range> OptTempTableName
%type <into> into_clause create_as_target
%type <into> into_clause create_as_target create_mv_target
%type <defelt> createfunc_opt_item createproc_opt_item common_func_opt_item dostmt_opt_item
%type <fun_param> func_arg func_arg_with_default table_func_column
@ -407,6 +407,7 @@ static void ParseUpdateMultiSet(List *set_target_list, SelectStmt *stmt, core_yy
%type <boolean> opt_trusted opt_restart_seqs
%type <ival> OptTemp
%type <ival> OptNoLog
%type <oncommit> OnCommitOption
%type <node> for_locking_item
@ -661,7 +662,7 @@ static void ParseUpdateMultiSet(List *set_target_list, SelectStmt *stmt, core_yy
LABEL LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING LEAKPROOF
LEAST LESS LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP
LOCATION LOCK_P LOG_P LOGGING LOOP
MAPPING MATCH MATCHED MAXEXTENTS MAXSIZE MAXTRANS MAXVALUE MERGE MINUS_P MINUTE_P MINVALUE MINEXTENTS MODE MODIFY_P MONTH_P MOVE MOVEMENT
MAPPING MATCH MATERIALIZED MATCHED MAXEXTENTS MAXSIZE MAXTRANS MAXVALUE MERGE MINUS_P MINUTE_P MINVALUE MINEXTENTS MODE MODIFY_P MONTH_P MOVE MOVEMENT
NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NLSSORT NO NOCOMPRESS NOCYCLE NODE NOLOGGING NOMAXVALUE NOMINVALUE NONE
NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF NULLS_P NUMBER_P NUMERIC NUMSTR NVARCHAR2 NVL
@ -676,7 +677,7 @@ static void ParseUpdateMultiSet(List *set_target_list, SelectStmt *stmt, core_yy
QUERY QUOTE
RANGE RAW READ REAL REASSIGN REBUILD RECHECK RECURSIVE REF REFERENCES REINDEX REJECT_P
RANGE RAW READ REAL REASSIGN REBUILD RECHECK RECURSIVE REF REFERENCES REFRESH REINDEX REJECT_P
RELATIVE_P RELEASE RELOPTIONS REMOTE_P RENAME REPEATABLE REPLACE REPLICA
RESET RESIZE RESOURCE RESTART RESTRICT RETURN RETURNING RETURNS REUSE REVOKE RIGHT ROLE ROLLBACK ROLLUP
ROW ROWNUM ROWS RULE
@ -897,6 +898,7 @@ stmt :
| CreateDataSourceStmt
| CreateFunctionStmt
| CreateGroupStmt
| CreateMatViewStmt
| CreateNodeGroupStmt
| CreateNodeStmt
| CreateOpClassStmt
@ -958,6 +960,7 @@ stmt :
| IndexStmt
| InsertStmt
| ListenStmt
| RefreshMatViewStmt
| LoadStmt
| LockStmt
| MergeStmt
@ -2165,9 +2168,9 @@ DiscardStmt:
/*****************************************************************************
*
* ALTER [ TABLE | INDEX | SEQUENCE | VIEW ] variations
* ALTER [ TABLE | INDEX | SEQUENCE | VIEW | MATERIALIZED VIEW ] variations
*
* Note: we accept all subcommands for each of the four variants, and sort
* Note: we accept all subcommands for each of the five variants, and sort
* out what's really legal at execution time.
*****************************************************************************/
@ -2351,6 +2354,24 @@ AlterTableStmt:
n->missing_ok = true;
$$ = (Node *)n;
}
| ALTER MATERIALIZED VIEW qualified_name alter_table_cmds
{
AlterTableStmt *n = makeNode(AlterTableStmt);
n->relation = $4;
n->cmds = $5;
n->relkind = OBJECT_MATVIEW;
n->missing_ok = false;
$$ = (Node *)n;
}
| ALTER MATERIALIZED VIEW IF_P EXISTS qualified_name alter_table_cmds
{
AlterTableStmt *n = makeNode(AlterTableStmt);
n->relation = $6;
n->cmds = $7;
n->relkind = OBJECT_MATVIEW;
n->missing_ok = true;
$$ = (Node *)n;
}
;
modify_column_cmds:
modify_column_cmd { $$ = list_make1($1); }
@ -5146,6 +5167,7 @@ CreateAsStmt:
CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
ctas->query = $6;
ctas->into = $4;
ctas->relkind = OBJECT_TABLE;
ctas->is_select_into = false;
/* cram additional flags into the IntoClause */
$4->rel->relpersistence = $2;
@ -5166,6 +5188,7 @@ create_as_target:
$$->options = $3;
$$->onCommit = $4;
$$->row_compress = $5;
$$->viewQuery = NULL;
$$->tableSpaceName = $6;
$$->skipData = false; /* might get changed later */
/* PGXC_BEGIN */
@ -5181,6 +5204,66 @@ opt_with_data:
| /*EMPTY*/ { $$ = TRUE; }
;
/*****************************************************************************
*
* QUERY :
* CREATE MATERIALIZED VIEW relname AS SelectStmt
*
*****************************************************************************/
CreateMatViewStmt:
CREATE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt opt_with_data
{
CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
ctas->query = $7;
ctas->into = $5;
ctas->relkind = OBJECT_MATVIEW;
ctas->is_select_into = false;
/* cram additional flags into the IntoClause */
$5->rel->relpersistence = $2;
$5->skipData = !($8);
$$ = (Node *) ctas;
}
;
create_mv_target:
qualified_name opt_column_list opt_reloptions OptTableSpace
{
$$ = makeNode(IntoClause);
$$->rel = $1;
$$->colNames = $2;
$$->options = $3;
$$->onCommit = ONCOMMIT_NOOP;
$$->tableSpaceName = $4;
$$->viewQuery = NULL;
$$->skipData = false; /* might get changed later */
}
;
OptNoLog:
UNLOGGED { $$ = RELPERSISTENCE_UNLOGGED; }
| /*EMPTY*/ { $$ = RELPERSISTENCE_PERMANENT; }
;
/*****************************************************************************
*
* QUERY :
* REFRESH MATERIALIZED VIEW qualified_name
*
*****************************************************************************/
RefreshMatViewStmt:
REFRESH MATERIALIZED VIEW qualified_name opt_with_data
{
RefreshMatViewStmt *n = makeNode(RefreshMatViewStmt);
n->relation = $4;
n->skipData = !($5);
$$ = (Node *) n;
}
;
/*****************************************************************************
*
@ -5853,6 +5936,15 @@ AlterExtensionContentsStmt:
n->objname = $6;
$$ = (Node *)n;
}
| ALTER EXTENSION name add_drop MATERIALIZED VIEW any_name
{
AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt);
n->extname = $3;
n->action = $4;
n->objtype = OBJECT_MATVIEW;
n->objname = $7;
$$ = (Node *)n;
}
| ALTER EXTENSION name add_drop FOREIGN TABLE any_name
{
AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt);
@ -7731,6 +7823,7 @@ DropStmt: DROP drop_type IF_P EXISTS any_name_list opt_drop_behavior
drop_type: TABLE { $$ = OBJECT_TABLE; }
| SEQUENCE { $$ = OBJECT_SEQUENCE; }
| VIEW { $$ = OBJECT_VIEW; }
| MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; }
| INDEX { $$ = OBJECT_INDEX; }
| FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; }
| TYPE_P { $$ = OBJECT_TYPE; }
@ -7796,7 +7889,8 @@ opt_restart_seqs:
* EXTENSION | ROLE | TEXT SEARCH PARSER |
* TEXT SEARCH DICTIONARY | TEXT SEARCH TEMPLATE |
* TEXT SEARCH CONFIGURATION | FOREIGN TABLE |
* FOREIGN DATA WRAPPER | SERVER ] <objname> |
* FOREIGN DATA WRAPPER | SERVER | EVENT TRIGGER |
* MATERIALIZED VIEW] <objname> |
* AGGREGATE <aggname> (arg1, ...) |
* FUNCTION <funcname> (arg1, arg2, ...) |
* OPERATOR <op> (leftoperand_typ, rightoperand_typ) |
@ -7970,6 +8064,7 @@ comment_type:
| DOMAIN_P { $$ = OBJECT_DOMAIN; }
| TYPE_P { $$ = OBJECT_TYPE; }
| VIEW { $$ = OBJECT_VIEW; }
| MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; }
| COLLATION { $$ = OBJECT_COLLATION; }
| CONVERSION_P { $$ = OBJECT_CONVERSION; }
| TABLESPACE { $$ = OBJECT_TABLESPACE; }
@ -8071,6 +8166,7 @@ security_label_type:
| TABLESPACE { $$ = OBJECT_TABLESPACE; }
| TYPE_P { $$ = OBJECT_TYPE; }
| VIEW { $$ = OBJECT_VIEW; }
| MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; }
;
security_label: Sconst { $$ = $1; }
@ -10143,6 +10239,26 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
n->missing_ok = true;
$$ = (Node *)n;
}
| ALTER MATERIALIZED VIEW qualified_name RENAME TO name
{
RenameStmt *n = makeNode(RenameStmt);
n->renameType = OBJECT_MATVIEW;
n->relation = $4;
n->subname = NULL;
n->newname = $7;
n->missing_ok = false;
$$ = (Node *)n;
}
| ALTER MATERIALIZED VIEW IF_P EXISTS qualified_name RENAME TO name
{
RenameStmt *n = makeNode(RenameStmt);
n->renameType = OBJECT_MATVIEW;
n->relation = $6;
n->subname = NULL;
n->newname = $9;
n->missing_ok = true;
$$ = (Node *)n;
}
| ALTER INDEX qualified_name RENAME TO name
{
RenameStmt *n = makeNode(RenameStmt);
@ -10227,6 +10343,28 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
n->missing_ok = true;
$$ = (Node *)n;
}
| ALTER MATERIALIZED VIEW qualified_name RENAME opt_column name TO name
{
RenameStmt *n = makeNode(RenameStmt);
n->renameType = OBJECT_COLUMN;
n->relationType = OBJECT_MATVIEW;
n->relation = $4;
n->subname = $7;
n->newname = $9;
n->missing_ok = false;
$$ = (Node *)n;
}
| ALTER MATERIALIZED VIEW IF_P EXISTS qualified_name RENAME opt_column name TO name
{
RenameStmt *n = makeNode(RenameStmt);
n->renameType = OBJECT_COLUMN;
n->relationType = OBJECT_MATVIEW;
n->relation = $6;
n->subname = $9;
n->newname = $11;
n->missing_ok = true;
$$ = (Node *)n;
}
| ALTER TABLE relation_expr RENAME CONSTRAINT name TO name
{
RenameStmt *n = makeNode(RenameStmt);
@ -10640,6 +10778,24 @@ AlterObjectSchemaStmt:
n->missing_ok = true;
$$ = (Node *)n;
}
| ALTER MATERIALIZED VIEW qualified_name SET SCHEMA name
{
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
n->objectType = OBJECT_MATVIEW;
n->relation = $4;
n->newschema = $7;
n->missing_ok = false;
$$ = (Node *)n;
}
| ALTER MATERIALIZED VIEW IF_P EXISTS qualified_name SET SCHEMA name
{
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
n->objectType = OBJECT_MATVIEW;
n->relation = $6;
n->newschema = $9;
n->missing_ok = true;
$$ = (Node *)n;
}
| ALTER FOREIGN TABLE relation_expr SET SCHEMA name
{
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
@ -12660,6 +12816,8 @@ ExplainableStmt:
| MergeStmt
| DeclareCursorStmt
| CreateAsStmt
| CreateMatViewStmt
| RefreshMatViewStmt
| ExecuteStmt /* by default all are $$=$1 */
;
@ -12845,6 +13003,7 @@ ExecuteStmt: EXECUTE name execute_param_clause
n->params = $8;
ctas->query = (Node *) n;
ctas->into = $4;
ctas->relkind = OBJECT_TABLE;
ctas->is_select_into = false;
/* cram additional flags into the IntoClause */
$4->rel->relpersistence = $2;
@ -13646,6 +13805,7 @@ into_clause:
/* Here $$ is a temp table, so row_compress can be any value. To be safe, REL_CMPRS_PAGE_PLAIN is used. */
$$->row_compress = REL_CMPRS_PAGE_PLAIN;
$$->tableSpaceName = NULL;
$$->viewQuery = NULL;
$$->skipData = false;
}
| /*EMPTY*/
@ -17785,6 +17945,7 @@ unreserved_keyword:
| MAPPING
| MATCH
| MATCHED
| MATERIALIZED
| MAXEXTENTS
| MAXSIZE
| MAXTRANS
@ -17862,6 +18023,7 @@ unreserved_keyword:
| RECHECK
| RECURSIVE
| REF
| REFRESH
| REINDEX
| RELATIVE_P
| RELEASE

View File

@ -275,8 +275,12 @@ bool interpretInhOption(InhOption inhOpt)
* table/result set should be created with OIDs. This needs to be done after
* parsing the query string because the return value can depend upon the
* default_with_oids GUC var.
*
* In some situations, we want to reject an OIDS option even if it's present.
* That's (rather messily) handled here rather than reloptions.c, because that
* code explicitly punts checking for oids to here.
*/
bool interpretOidsOption(List* defList)
bool interpretOidsOption(List* defList, bool allowOids)
{
ListCell* cell = NULL;
@ -285,10 +289,16 @@ bool interpretOidsOption(List* defList)
DefElem* def = (DefElem*)lfirst(cell);
if (def->defnamespace == NULL && pg_strcasecmp(def->defname, "oids") == 0) {
if (!allowOids)
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unrecognized parameter \"%s\"", def->defname)));
return defGetBoolean(def);
}
}
/* Force no-OIDS result if caller disallows OIDS. */
if (!allowOids)
return false;
/* OIDS option was not specified, so use default. */
return false;
}

View File

@ -54,6 +54,7 @@ static Node* variable_paramref_hook(ParseState* pstate, ParamRef* pref);
static Node* variable_coerce_param_hook(
ParseState* pstate, Param* param, Oid targetTypeId, int32 targetTypeMod, int location);
static bool check_parameter_resolution_walker(Node* node, ParseState* pstate);
static bool query_contains_extern_params_walker(Node* node, void* context);
/*
* Set up to process a query containing references to fixed parameters.
@ -288,3 +289,30 @@ static bool check_parameter_resolution_walker(Node* node, ParseState* pstate)
}
return expression_tree_walker(node, (bool (*)())check_parameter_resolution_walker, (void*)pstate);
}
/*
* Check to see if a fully-parsed query tree contains any PARAM_EXTERN Params.
*/
bool query_contains_extern_params(Query* query)
{
return query_tree_walker(query, (bool (*)())query_contains_extern_params_walker, NULL, 0);
}
static bool query_contains_extern_params_walker(Node* node, void* context)
{
if (node == NULL)
return false;
if (IsA(node, Param)) {
Param* param = (Param*)node;
if (param->paramkind == PARAM_EXTERN)
return true;
return false;
}
if (IsA(node, Query)) {
/* Recurse into RTE subquery or not-yet-planned sublink subquery */
return query_tree_walker((Query*)node, (bool(*)())query_contains_extern_params_walker, context, 0);
}
return expression_tree_walker(node, (bool(*)())query_contains_extern_params_walker, context);
}

View File

@ -61,6 +61,7 @@ static int32* getValuesTypmods(RangeTblEntry* rte);
#ifndef PGXC
static int specialAttNum(const char* attname);
#endif
static bool isQueryUsingTempRelation_walker(Node* node, void* context);
static Oid getPartitionOidForRTE(RangeTblEntry* rte, RangeVar* relation, ParseState* pstate, Relation rel);
@ -549,12 +550,49 @@ Node* scanRTEForColumn(ParseState* pstate, RangeTblEntry* rte, char* colname, in
return result;
}
/*
* 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 = (RangeTblEntry*)lfirst(rtable);
if (rte->rtekind == RTE_RELATION) {
Relation rel = heap_open(rte->relid, AccessShareLock);
char relpersistence = rel->rd_rel->relpersistence;
heap_close(rel, AccessShareLock);
if (relpersistence == RELPERSISTENCE_TEMP || relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
return true;
}
}
return query_tree_walker(query, (bool (*)())isQueryUsingTempRelation_walker, context, QTW_IGNORE_JOINALIASES);
}
return expression_tree_walker(node, (bool (*)())isQueryUsingTempRelation_walker, context);
}
/*
* colNameToVar
* Search for an unqualified column name.
* If found, return the appropriate Var node (or expression).
* If not found, return NULL. If the name proves ambiguous, raise error.
* If localonly is true, only names in the innermost query are considered.
* Search for an unqualified column name.
* If found, return the appropriate Var node (or expression).
* If not found, return NULL. If the name proves ambiguous, raise error.
* If localonly is true, only names in the innermost query are considered.
*/
Node* colNameToVar(ParseState* pstate, char* colname, bool localonly, int location, RangeTblEntry** final_rte)
{

View File

@ -152,7 +152,7 @@ typedef struct {
*/
#define TRANSFORM_RELATION_LIKE_CLAUSE(rel_relkind) \
(((rel_relkind) != RELKIND_RELATION && (rel_relkind) != RELKIND_VIEW && (rel_relkind) != RELKIND_COMPOSITE_TYPE && \
(rel_relkind) != RELKIND_FOREIGN_TABLE) \
relation->rd_rel->relkind != RELKIND_MATVIEW && (rel_relkind) != RELKIND_FOREIGN_TABLE) \
? false \
: true)
@ -525,7 +525,7 @@ List* transformCreateStmt(CreateStmt* stmt, const char* queryString, const List*
if (cxt.hasoids) {
stmt->options = lappend(stmt->options, makeDefElem("oids", (Node*)makeInteger(cxt.hasoids)));
}
cxt.hasoids = interpretOidsOption(stmt->options);
cxt.hasoids = interpretOidsOption(stmt->options, true);
#ifdef PGXC
if (cxt.distributeby != NULL) {
@ -1159,7 +1159,7 @@ static void transformTableLikeClause(
if (!TRANSFORM_RELATION_LIKE_CLAUSE(relation->rd_rel->relkind))
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table, view, composite type, or foreign table",
errmsg("\"%s\" is not a table, view, materialized view, composite type, or foreign table",
RelationGetRelationName(relation))));
cancel_parser_errposition_callback(&pcbstate);
@ -3203,7 +3203,10 @@ void transformRuleStmt(RuleStmt* stmt, const char* queryString, List** actions,
* beforehand.
*/
rel = heap_openrv(stmt->relation, AccessExclusiveLock);
if (rel->rd_rel->relkind == RELKIND_MATVIEW)
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("rules on materialized views are not supported")));
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;

View File

@ -1665,6 +1665,7 @@ Datum pg_relation_filenode(PG_FUNCTION_ARGS)
switch (relform->relkind) {
case RELKIND_RELATION:
case RELKIND_MATVIEW:
case RELKIND_INDEX:
case RELKIND_SEQUENCE:
case RELKIND_TOASTVALUE:
@ -1783,6 +1784,7 @@ Datum pg_relation_filepath(PG_FUNCTION_ARGS)
switch (relform->relkind) {
case RELKIND_RELATION:
case RELKIND_MATVIEW:
case RELKIND_INDEX:
case RELKIND_SEQUENCE:
case RELKIND_TOASTVALUE:

View File

@ -174,7 +174,6 @@ static void quoteOneName(char* buffer, const char* name);
static void quoteRelationName(char* buffer, Relation rel);
static void ri_GenerateQual(StringInfo buf, const char* sep, const char* leftop, Oid leftoptype, Oid opoid,
const char* rightop, Oid rightoptype);
static void ri_add_cast_to(StringInfo buf, Oid typid);
static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
static int ri_NullCheck(Relation rel, HeapTuple tup, RI_QueryKey* key, int pairidx);
static void ri_BuildQueryKeyFull(RI_QueryKey* key, const RI_ConstraintInfo* riinfo, int32 constr_queryno);
@ -2319,61 +2318,8 @@ static void quoteRelationName(char* buffer, Relation rel)
static void ri_GenerateQual(StringInfo buf, const char* sep, const char* leftop, Oid leftoptype, Oid opoid,
const char* rightop, Oid rightoptype)
{
HeapTuple opertup;
Form_pg_operator operform;
char* oprname = NULL;
char* nspname = NULL;
opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(opoid));
if (!HeapTupleIsValid(opertup)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for operator %u", opoid)));
}
operform = (Form_pg_operator)GETSTRUCT(opertup);
Assert(operform->oprkind == 'b');
oprname = NameStr(operform->oprname);
nspname = get_namespace_name(operform->oprnamespace, true);
appendStringInfo(buf, " %s %s", sep, leftop);
if (leftoptype != operform->oprleft) {
ri_add_cast_to(buf, operform->oprleft);
}
appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
appendStringInfoString(buf, oprname);
appendStringInfo(buf, ") %s", rightop);
if (rightoptype != operform->oprright) {
ri_add_cast_to(buf, operform->oprright);
}
ReleaseSysCache(opertup);
}
/*
* Add a cast specification to buf. We spell out the type name the hard way,
* intentionally not using format_type_be(). This is to avoid corner cases
* for CHARACTER, BIT, and perhaps other types, where specifying the type
* using SQL-standard syntax results in undesirable data truncation. By
* doing it this way we can be certain that the cast will have default (-1)
* target typmod.
*/
static void ri_add_cast_to(StringInfo buf, Oid typid)
{
HeapTuple typetup;
Form_pg_type typform;
char* typname = NULL;
char* nspname = NULL;
typetup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (!HeapTupleIsValid(typetup)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for type %u", typid)));
}
typform = (Form_pg_type)GETSTRUCT(typetup);
typname = NameStr(typform->typname);
nspname = get_namespace_name(typform->typnamespace, true);
appendStringInfo(buf, "::%s.%s", quote_identifier(nspname), quote_identifier(typname));
ReleaseSysCache(typetup);
appendStringInfo(buf, " %s ", sep);
generate_operator_clause(buf, leftop, leftoptype, opoid, rightop, rightoptype);
}
/*

View File

@ -17,6 +17,7 @@
#include <ctype.h>
#include "access/tuptoaster.h"
#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "utils/builtins.h"
@ -1151,21 +1152,446 @@ Datum btrecordcmp(PG_FUNCTION_ARGS)
PG_RETURN_INT32(record_cmp(fcinfo));
}
void record_set_extra(RecordIOData** my_extra, int ncolumns, Oid tupType, int32 tupTypmod, PG_FUNCTION_ARGS)
/*
* record_image_cmp :
* Internal byte-oriented comparison function for records.
*
* Returns -1, 0 or 1
*
* Note: The normal concepts of "equality" do not apply here; different
* representation of values considered to be equal are not considered to be
* identical. As an example, for the citext type 'A' and 'a' are equal, but
* they are not identical.
*/
static int record_image_cmp(PG_FUNCTION_ARGS)
{
HeapTupleHeader record1 = PG_GETARG_HEAPTUPLEHEADER(0);
HeapTupleHeader record2 = PG_GETARG_HEAPTUPLEHEADER(1);
int32 result = 0;
Oid tupType1;
Oid tupType2;
int32 tupTypmod1;
int32 tupTypmod2;
TupleDesc tupdesc1;
TupleDesc tupdesc2;
HeapTupleData tuple1;
HeapTupleData tuple2;
int ncolumns1;
int ncolumns2;
RecordCompareData* my_extra;
int ncols;
Datum* values1;
Datum* values2;
bool* nulls1;
bool* nulls2;
int i1;
int i2;
int j;
/* Extract type info from the tuples */
tupType1 = HeapTupleHeaderGetTypeId(record1);
tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
ncolumns1 = tupdesc1->natts;
tupType2 = HeapTupleHeaderGetTypeId(record2);
tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2);
ncolumns2 = tupdesc2->natts;
/* Build temporary HeapTuple control structures */
tuple1.t_len = HeapTupleHeaderGetDatumLength(record1);
ItemPointerSetInvalid(&(tuple1.t_self));
tuple1.t_tableOid = InvalidOid;
tuple1.t_data = record1;
tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
ItemPointerSetInvalid(&(tuple2.t_self));
tuple2.t_tableOid = InvalidOid;
tuple2.t_data = record2;
/*
* We arrange to look up the needed comparison info just once per series
* of calls, assuming the record types don't change underneath us.
*/
ncols = Max(ncolumns1, ncolumns2);
my_extra = (RecordCompareData*)fcinfo->flinfo->fn_extra;
if (my_extra == NULL || my_extra->ncolumns < ncols) {
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(RecordCompareData) - sizeof(ColumnCompareData) + ncols * sizeof(ColumnCompareData));
my_extra = (RecordCompareData*)fcinfo->flinfo->fn_extra;
my_extra->ncolumns = ncols;
my_extra->record1_type = InvalidOid;
my_extra->record2_typmod = 0;
}
if (my_extra->record1_type != tupType1 || my_extra->record1_typmod != tupTypmod1 ||
my_extra->record2_type != tupType2 || my_extra->record2_typmod != tupTypmod2) {
MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData));
my_extra->record1_type = tupType1;
my_extra->record1_typmod = tupTypmod1;
my_extra->record2_type = tupType2;
my_extra->record2_typmod = tupTypmod2;
}
/* Break down the tuples into fields */
values1 = (Datum*)palloc(ncolumns1 * sizeof(Datum));
nulls1 = (bool*)palloc(ncolumns1 * sizeof(bool));
heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
values2 = (Datum*)palloc(ncolumns2 * sizeof(Datum));
nulls2 = (bool*)palloc(ncolumns2 * sizeof(bool));
heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
/*
* Scan corresponding columns, allowing for dropped columns in different
* places in the two rows. i1 and i2 are physical column indexes, j is
* the logical column index.
*/
i1 = i2 = j = 0;
while (i1 < ncolumns1 || i2 < ncolumns2) {
/*
* Skip dropped columns
*/
if (i1 < ncolumns1 && tupdesc1->attrs[i1]->attisdropped) {
i1++;
continue;
}
if (i2 < ncolumns2 && tupdesc2->attrs[i2]->attisdropped) {
i2++;
continue;
}
if (i1 >= ncolumns1 || i2 >= ncolumns2) {
break; /* we'll deal with mismatch below loop */
}
/*
* Have two matching columns, they must be same type
*/
if (tupdesc1->attrs[i1]->atttypid != tupdesc2->attrs[i2]->atttypid)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot compare dissimilar column types %s and %s at record column %d",
format_type_be(tupdesc1->attrs[i1]->atttypid),
format_type_be(tupdesc2->attrs[i2]->atttypid),
j + 1)));
/*
* We consider two NULLs equal; NULL > not-NULL.
*/
if (!nulls1[i1] || !nulls2[i2]) {
int cmpresult;
if (nulls1[i1]) {
/* arg1 is greater than arg2 */
result = 1;
break;
}
if (nulls2[i2]) {
/* arg1 is less than arg2 */
result = -1;
break;
}
/* Compare the pair of elements */
if (tupdesc1->attrs[i1]->attlen == -1) {
Size len1, len2;
struct varlena *arg1val;
struct varlena *arg2val;
len1 = toast_raw_datum_size(values1[i1]);
len2 = toast_raw_datum_size(values2[i2]);
arg1val = PG_DETOAST_DATUM_PACKED(values1[i1]);
arg2val = PG_DETOAST_DATUM_PACKED(values2[i2]);
cmpresult = memcmp(VARDATA_ANY(arg1val), VARDATA_ANY(arg2val), len1 - VARHDRSZ);
if ((cmpresult == 0) && (len1 != len2))
cmpresult = (len1 < len2) ? -1 : 1;
if ((Pointer)arg1val != (Pointer)values1[i1])
pfree(arg1val);
if ((Pointer)arg2val != (Pointer)values2[i2])
pfree(arg2val);
} else if (tupdesc1->attrs[i1]->attbyval) {
cmpresult = memcmp(&(values1[i1]), &(values2[i2]), tupdesc1->attrs[i1]->attlen);
} else {
cmpresult =
memcmp(DatumGetPointer(values1[i1]), DatumGetPointer(values2[i2]), tupdesc1->attrs[i1]->attlen);
}
if (cmpresult < 0) {
/* arg1 is less than arg2 */
result = -1;
break;
} else if (cmpresult > 0) {
/* arg1 is greater than arg2 */
result = 1;
break;
}
}
/* equal, so continue to next column */
i1++, i2++, j++;
}
/*
* If we didn't break out of the loop early, check for column count
* mismatch. (We do not report such mismatch if we found unequal column
* values; is that a feature or a bug?)
*/
if (result == 0) {
if (i1 != ncolumns1 || i2 != ncolumns2)
ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot compare record types with different numbers of columns")));
}
pfree(values1);
pfree(nulls1);
pfree(values2);
pfree(nulls2);
ReleaseTupleDesc(tupdesc1);
ReleaseTupleDesc(tupdesc2);
/* Avoid leaking memory when handed toasted input. */
PG_FREE_IF_COPY(record1, 0);
PG_FREE_IF_COPY(record2, 1);
return result;
}
/*
* record_image_eq :
* compares two records for identical contents, based on byte images
* result :
* returns true if the records are identical, false otherwise.
*
* Note: we do not use record_image_cmp here, since we can avoid
* de-toasting for unequal lengths this way.
*/
Datum record_image_eq(PG_FUNCTION_ARGS)
{
HeapTupleHeader record1 = PG_GETARG_HEAPTUPLEHEADER(0);
HeapTupleHeader record2 = PG_GETARG_HEAPTUPLEHEADER(1);
bool result = true;
Oid tupType1;
Oid tupType2;
int32 tupTypmod1;
int32 tupTypmod2;
TupleDesc tupdesc1;
TupleDesc tupdesc2;
HeapTupleData tuple1;
HeapTupleData tuple2;
int ncolumns1;
int ncolumns2;
RecordCompareData* my_extra;
int ncols;
Datum* values1;
Datum* values2;
bool* nulls1;
bool* nulls2;
int i1;
int i2;
int j;
/* Extract type info from the tuples */
tupType1 = HeapTupleHeaderGetTypeId(record1);
tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
ncolumns1 = tupdesc1->natts;
tupType2 = HeapTupleHeaderGetTypeId(record2);
tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2);
ncolumns2 = tupdesc2->natts;
/* Build temporary HeapTuple control structures */
tuple1.t_len = HeapTupleHeaderGetDatumLength(record1);
ItemPointerSetInvalid(&(tuple1.t_self));
tuple1.t_tableOid = InvalidOid;
tuple1.t_data = record1;
tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
ItemPointerSetInvalid(&(tuple2.t_self));
tuple2.t_tableOid = InvalidOid;
tuple2.t_data = record2;
/*
* We arrange to look up the needed comparison info just once per series
* of calls, assuming the record types don't change underneath us.
*/
ncols = Max(ncolumns1, ncolumns2);
my_extra = (RecordCompareData*)fcinfo->flinfo->fn_extra;
if (my_extra == NULL || my_extra->ncolumns < ncols) {
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(RecordCompareData) - sizeof(ColumnCompareData) + ncols * sizeof(ColumnCompareData));
my_extra = (RecordCompareData*)fcinfo->flinfo->fn_extra;
my_extra->ncolumns = ncols;
my_extra->record1_type = InvalidOid;
my_extra->record1_typmod = 0;
my_extra->record2_type = InvalidOid;
my_extra->record2_typmod = 0;
}
if (my_extra->record1_type != tupType1 || my_extra->record1_typmod != tupTypmod1 ||
my_extra->record2_type != tupType2 || my_extra->record2_typmod != tupTypmod2) {
MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData));
my_extra->record1_type = tupType1;
my_extra->record1_typmod = tupTypmod1;
my_extra->record2_type = tupType2;
my_extra->record2_typmod = tupTypmod2;
}
/* Break down the tuples into fields */
values1 = (Datum*)palloc(ncolumns1 * sizeof(Datum));
nulls1 = (bool*)palloc(ncolumns1 * sizeof(bool));
heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
values2 = (Datum*)palloc(ncolumns2 * sizeof(Datum));
nulls2 = (bool*)palloc(ncolumns2 * sizeof(bool));
heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
/*
* Scan corresponding columns, allowing for dropped columns in different
* places in the two rows. i1 and i2 are physical column indexes, j is
* the logical column index.
*/
i1 = i2 = j = 0;
while (i1 < ncolumns1 || i2 < ncolumns2) {
/*
* Skip dropped columns
*/
if (i1 < ncolumns1 && tupdesc1->attrs[i1]->attisdropped) {
i1++;
continue;
}
if (i2 < ncolumns2 && tupdesc2->attrs[i2]->attisdropped) {
i2++;
continue;
}
if (i1 >= ncolumns1 || i2 >= ncolumns2) {
break; /* we'll deal with mismatch below loop */
}
/*
* Have two matching columns, they must be same type
*/
if (tupdesc1->attrs[i1]->atttypid != tupdesc2->attrs[i2]->atttypid)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot compare dissimilar column types %s and %s at record column %d",
format_type_be(tupdesc1->attrs[i1]->atttypid),
format_type_be(tupdesc2->attrs[i2]->atttypid),
j + 1)));
/*
* We consider two NULLs equal; NULL > not-NULL.
*/
if (!nulls1[i1] || !nulls2[i2]) {
if (nulls1[i1] || nulls2[i2]) {
result = false;
break;
}
/* Compare the pair of elements */
if (tupdesc1->attrs[i1]->attlen == -1) {
Size len1, len2;
len1 = toast_raw_datum_size(values1[i1]);
len2 = toast_raw_datum_size(values2[i2]);
/* No need to de-toast if lengths don't match. */
if (len1 != len2)
result = false;
else {
struct varlena *arg1val;
struct varlena *arg2val;
arg1val = PG_DETOAST_DATUM_PACKED(values1[i1]);
arg2val = PG_DETOAST_DATUM_PACKED(values2[i2]);
result = (memcmp(VARDATA_ANY(arg1val), VARDATA_ANY(arg2val), len1 - VARHDRSZ) == 0);
/* Only free memory if it's a copy made here. */
if ((Pointer)arg1val != (Pointer)values1[i1])
pfree(arg1val);
if ((Pointer)arg2val != (Pointer)values2[i2])
pfree(arg2val);
}
} else if (tupdesc1->attrs[i1]->attbyval) {
result = (memcmp(&(values1[i1]), &(values2[i2]), tupdesc1->attrs[i1]->attlen) == 0);
} else {
result = (memcmp(DatumGetPointer(values1[i1]),
DatumGetPointer(values2[i2]), tupdesc1->attrs[i1]->attlen) == 0);
}
if (!result) {
break;
}
}
/* equal, so continue to next column */
i1++, i2++, j++;
}
/*
* If we didn't break out of the loop early, check for column count
* mismatch. (We do not report such mismatch if we found unequal column
* values; is that a feature or a bug?)
*/
if (result) {
if (i1 != ncolumns1 || i2 != ncolumns2)
ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot compare record types with different numbers of columns")));
}
pfree(values1);
pfree(nulls1);
pfree(values2);
pfree(nulls2);
ReleaseTupleDesc(tupdesc1);
ReleaseTupleDesc(tupdesc2);
/* Avoid leaking memory when handed toasted input. */
PG_FREE_IF_COPY(record1, 0);
PG_FREE_IF_COPY(record2, 1);
return result;
}
Datum record_image_ne(PG_FUNCTION_ARGS)
{
PG_RETURN_BOOL(!DatumGetBool(record_image_eq(fcinfo)));
}
Datum record_image_lt(PG_FUNCTION_ARGS)
{
PG_RETURN_BOOL(record_image_cmp(fcinfo) < 0);
}
Datum record_image_gt(PG_FUNCTION_ARGS)
{
PG_RETURN_BOOL(record_image_cmp(fcinfo) > 0);
}
Datum record_image_le(PG_FUNCTION_ARGS)
{
PG_RETURN_BOOL(record_image_cmp(fcinfo) <= 0);
}
Datum record_image_ge(PG_FUNCTION_ARGS)
{
PG_RETURN_BOOL(record_image_cmp(fcinfo) >= 0);
}
Datum btrecordimagecmp(PG_FUNCTION_ARGS)
{
PG_RETURN_INT32(record_image_cmp(fcinfo));
}
void record_set_extra(RecordIOData **my_extra, int ncolumns, Oid tupType, int32 tupTypmod, PG_FUNCTION_ARGS)
{
*my_extra = (RecordIOData*)fcinfo->flinfo->fn_extra;
if ((*my_extra) == NULL || (*my_extra)->ncolumns != ncolumns) {
fcinfo->flinfo->fn_extra = MemoryContextAlloc(
fcinfo->flinfo->fn_mcxt, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData));
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData));
(*my_extra) = (RecordIOData*)fcinfo->flinfo->fn_extra;
(*my_extra)->record_type = InvalidOid;
(*my_extra)->record_typmod = 0;
}
if ((*my_extra)->record_type != tupType || (*my_extra)->record_typmod != tupTypmod) {
errno_t rc = memset_s((*my_extra),
sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData),
0,
errno_t rc = memset_s((*my_extra),
sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData), 0,
sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData));
securec_check(rc, "\0", "\0");
(*my_extra)->record_type = tupType;

View File

@ -273,6 +273,7 @@ static char* generate_relation_name(Oid relid, List* namespaces);
static char* generate_function_name(
Oid funcid, int nargs, List* argnames, Oid* argtypes, bool was_variadic, bool* use_variadic_p);
static char* generate_operator_name(Oid operid, Oid arg1, Oid arg2);
static void add_cast_to(StringInfo buf, Oid typid);
static text* string_to_text(char* str);
static char* flatten_reloptions(Oid relid);
@ -10139,6 +10140,83 @@ static char* generate_operator_name(Oid operid, Oid arg1, Oid arg2)
return buf.data;
}
/*
* generate_operator_clause --- generate a binary-operator WHERE clause
*
* This is used for internally-generated-and-executed SQL queries, where
* precision is essential and readability is secondary. The basic
* requirement is to append "leftop op rightop" to buf, where leftop and
* rightop are given as strings and are assumed to yield types leftoptype
* and rightoptype; the operator is identified by OID. The complexity
* comes from needing to be sure that the parser will select the desired
* operator when the query is parsed. We always name the operator using
* OPERATOR(schema.op) syntax, so as to avoid search-path uncertainties.
* We have to emit casts too, if either input isn't already the input type
* of the operator; else we are at the mercy of the parser's heuristics for
* ambiguous-operator resolution. The caller must ensure that leftop and
* rightop are suitable arguments for a cast operation; it's best to insert
* parentheses if they aren't just variables or parameters.
*/
void generate_operator_clause(StringInfo buf, const char* leftop, Oid leftoptype, Oid opoid, const char* rightop,
Oid rightoptype)
{
HeapTuple opertup;
Form_pg_operator operform;
char *oprname = NULL;
char *nspname = NULL;
opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(opoid));
if (!HeapTupleIsValid(opertup)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for operator %u", opoid)));
}
operform = (Form_pg_operator)GETSTRUCT(opertup);
Assert(operform->oprkind == 'b');
oprname = NameStr(operform->oprname);
nspname = get_namespace_name(operform->oprnamespace, true);
appendStringInfoString(buf, leftop);
if (leftoptype != operform->oprleft) {
add_cast_to(buf, operform->oprleft);
}
appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
appendStringInfoString(buf, oprname);
appendStringInfo(buf, ") %s", rightop);
if (rightoptype != operform->oprright) {
add_cast_to(buf, operform->oprright);
}
ReleaseSysCache(opertup);
}
/*
* Add a cast specification to buf. We spell out the type name the hard way,
* intentionally not using format_type_be(). This is to avoid corner cases
* for CHARACTER, BIT, and perhaps other types, where specifying the type
* using SQL-standard syntax results in undesirable data truncation. By
* doing it this way we can be certain that the cast will have default (-1)
* target typmod.
*/
static void add_cast_to(StringInfo buf, Oid typid)
{
HeapTuple typetup;
Form_pg_type typform;
char *typname = NULL;
char *nspname = NULL;
typetup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (!HeapTupleIsValid(typetup)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for type %u", typid)));
}
typform = (Form_pg_type)GETSTRUCT(typetup);
typname = NameStr(typform->typname);
nspname = get_namespace_name(typform->typnamespace, true);
appendStringInfo(buf, "::%s.%s", quote_identifier(nspname), quote_identifier(typname));
ReleaseSysCache(typetup);
}
/*
* generate_collation_name
* Compute the name to display for a collation specified by OID

View File

@ -2085,7 +2085,7 @@ static List* schema_get_xml_visible_tables(Oid nspid)
initStringInfo(&query);
appendStringInfo(&query,
"SELECT oid FROM pg_catalog.pg_class WHERE relnamespace = %u AND relkind IN ('r', 'v') AND "
"SELECT oid FROM pg_catalog.pg_class WHERE relnamespace = %u AND relkind IN ('r', 'm', 'v') AND "
"pg_catalog.has_table_privilege (oid, 'SELECT') ORDER BY relname;",
nspid);
@ -2111,7 +2111,7 @@ static List* database_get_xml_visible_tables(void)
{
/* At the moment there is no order required here. */
return query_to_oid_list(
"SELECT oid FROM pg_catalog.pg_class WHERE relkind IN ('r', 'v') AND pg_catalog.has_table_privilege "
"SELECT oid FROM pg_catalog.pg_class WHERE relkind IN ('r', 'm', 'v') AND pg_catalog.has_table_privilege "
"(pg_class.oid, 'SELECT') AND relnamespace IN (" XML_VISIBLE_SCHEMAS ");");
}

View File

@ -1055,6 +1055,7 @@ static void relation_parse_rel_options(Relation relation, HeapTuple tuple)
case RELKIND_TOASTVALUE:
case RELKIND_INDEX:
case RELKIND_VIEW:
case RELKIND_MATVIEW:
break;
default:
return;
@ -1641,8 +1642,9 @@ static Relation relation_build_desc(Oid targetRelId, bool insertIt, bool buildke
/*
* if no such tuple exists, return NULL
*/
if (!HeapTupleIsValid(pg_class_tuple))
if (!HeapTupleIsValid(pg_class_tuple)) {
return NULL;
}
/*
* get information from the pg_class_tuple
@ -2424,6 +2426,9 @@ static void formr_desc(const char* relationName, Oid relationReltype, bool issha
/* formr_desc is used only for permanent relations */
relation->rd_rel->relpersistence = RELPERSISTENCE_PERMANENT;
/* ... and they're always populated, too */
relation->relispopulated = true;
relation->rd_rel->relpages = 0;
relation->rd_rel->reltuples = 0;
relation->rd_rel->relallvisible = 0;
@ -3724,6 +3729,9 @@ Relation RelationBuildLocalRelation(const char* relname, Oid relnamespace, Tuple
break;
}
/* we keep this flag and set it true for all relations here, which may be useful for future */
rel->relispopulated = true;
/*
* Insert relation physical and logical identifiers (OIDs) into the right
* places. For a mapped relation, we set relfilenode to zero and rely on

View File

@ -1902,11 +1902,11 @@ PLpgSQL_type* plpgsql_parse_cwordtype(List* idents)
class_struct = (Form_pg_class)GETSTRUCT(classtup);
/*
* It must be a relation, sequence, view, composite type, or foreign table
* It must be a relation, sequence, view, materialized view, composite type, or foreign table
*/
if (class_struct->relkind != RELKIND_RELATION && class_struct->relkind != RELKIND_SEQUENCE &&
class_struct->relkind != RELKIND_VIEW && class_struct->relkind != RELKIND_COMPOSITE_TYPE &&
class_struct->relkind != RELKIND_FOREIGN_TABLE) {
class_struct->relkind != RELKIND_FOREIGN_TABLE && class_struct->relkind != RELKIND_MATVIEW) {
goto done;
}
/*
@ -2259,10 +2259,10 @@ static PLpgSQL_row* build_row_from_class(Oid class_oid)
class_struct = RelationGetForm(rel);
relname = RelationGetRelationName(rel);
/* accept relation, sequence, view, composite type, or foreign table */
/* accept relation, sequence, view, composite type, materialized view or foreign table */
if (class_struct->relkind != RELKIND_RELATION && class_struct->relkind != RELKIND_SEQUENCE &&
class_struct->relkind != RELKIND_VIEW && class_struct->relkind != RELKIND_COMPOSITE_TYPE &&
class_struct->relkind != RELKIND_FOREIGN_TABLE) {
class_struct->relkind != RELKIND_FOREIGN_TABLE && class_struct->relkind != RELKIND_MATVIEW) {
ereport(ERROR,
(errmodule(MOD_PLSQL),
errcode(ERRCODE_WRONG_OBJECT_TYPE),

View File

@ -20,7 +20,7 @@ OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
dbcommands.o define.o discard.o dropcmds.o explain.o extension.o \
foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \
portalcmds.o prepare.o proclang.o \
schemacmds.o seclabel.o sec_rls_cmds.o sequence.o tablecmds.o tablespace.o trigger.o \
tsearchcmds.o typecmds.o user.o vacuum.o vacuumlazy.o \

View File

@ -133,6 +133,7 @@ void ExecRenameStmt(RenameStmt* stmt)
case OBJECT_TABLE:
case OBJECT_SEQUENCE:
case OBJECT_VIEW:
case OBJECT_MATVIEW:
case OBJECT_INDEX:
case OBJECT_FOREIGN_TABLE:
RenameRelation(stmt);
@ -221,6 +222,7 @@ void ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt* stmt)
case OBJECT_SEQUENCE:
case OBJECT_TABLE:
case OBJECT_VIEW:
case OBJECT_MATVIEW:
case OBJECT_FOREIGN_TABLE:
if (stmt->objectType == OBJECT_FOREIGN_TABLE)
ereport(ERROR,

View File

@ -516,11 +516,11 @@ static void analyze_rel_internal(Relation onerel, VacuumStmt* vacstmt, BufferAcc
}
/*
* Check that it's a plain table or foreign table; we used to do this in
* get_rel_oids() but seems safer to check after we've locked the
* relation.
*/
if (onerel->rd_rel->relkind == RELKIND_RELATION) {
* Check that it's a plain table, materialized view, or foreign table; we
* used to do this in get_rel_oids() but seems safer to check after we've
* locked the relation.
*/
if (onerel->rd_rel->relkind == RELKIND_RELATION || onerel->rd_rel->relkind == RELKIND_MATVIEW) {
/* Regular table, so we'll use the regular row acquisition function */
/* Also get regular table's size */
if (RelationIsPartitioned(onerel)) {

View File

@ -475,6 +475,18 @@ void cluster_rel(Oid tableOid, Oid partitionOid, Oid indexOid, bool recheck, boo
if (OidIsValid(indexOid))
check_index_is_clusterable(OldHeap, indexOid, recheck, lockMode, &amid);
/*
* Quietly ignore the request if this is a materialized view which has not
* been populated from its query. No harm is done because there is no data
* to deal with, and we don't want to throw an error if this is part of a
* multi-relation request -- for example, CLUSTER was run on the entire
* database.
*/
if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW && !RelationIsPopulated(OldHeap)) {
relation_close(OldHeap, AccessExclusiveLock);
return;
}
/*
* There is no data on Coordinator except system tables, it is no sense to rewrite a relation
* on Coordinator.so we can skip to vacuum full user-define tables
@ -1278,6 +1290,8 @@ Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, int lockMode)
bool isNull = false;
int ss_c = 0;
HashBucketInfo bucketinfo;
Oid namespaceid;
char relpersistence;
OldHeap = heap_open(OIDOldHeap, lockMode);
OldHeapDesc = RelationGetDescr(OldHeap);
@ -1298,6 +1312,9 @@ Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, int lockMode)
if (isNull)
reloptions = (Datum)0;
namespaceid = RelationGetNamespace(OldHeap);
relpersistence = OldHeap->rd_rel->relpersistence;
/*
* Create the new heap, using a temporary name in the same namespace as
* the existing table. NOTE: there is some risk of collision with user
@ -1315,7 +1332,7 @@ Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, int lockMode)
bucketinfo.bucketOid = RelationGetBucketOid(OldHeap);
OIDNewHeap = heap_create_with_catalog(NewHeapName,
RelationGetNamespace(OldHeap),
namespaceid,
NewTableSpace,
InvalidOid,
InvalidOid,
@ -1323,8 +1340,8 @@ Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, int lockMode)
OldHeap->rd_rel->relowner,
OldHeapDesc,
NIL,
OldHeap->rd_rel->relkind,
OldHeap->rd_rel->relpersistence,
RELKIND_RELATION,
relpersistence,
false,
RelationIsMapped(OldHeap),
true,

View File

@ -91,20 +91,22 @@ void CommentObject(CommentStmt* stmt)
case OBJECT_COLUMN:
/*
* Allow comments only on columns of tables, views, composite
* types, and foreign tables (which are the only relkinds for
* which pg_dump will dump per-column comments). In particular we
* wish to disallow comments on index columns, because the naming
* of an index's columns may change across PG versions, so dumping
* per-column comments could create reload failures.
* Allow comments only on columns of tables, views, materialized
* views, composite types, and foreign tables (which are the only
* relkinds for which pg_dump will dump per-column comments). In
* particular we wish to disallow comments on index columns,
* because the naming of an index's columns may change across PG
* versions, so dumping per-column comments could create reload
* failures.
*/
if (relation != NULL) {
if (relation->rd_rel->relkind != RELKIND_RELATION && relation->rd_rel->relkind != RELKIND_VIEW &&
relation->rd_rel->relkind != RELKIND_MATVIEW &&
relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table, view, composite type, or foreign table",
errmsg("\"%s\" is not a table, view, materialized view, composite type, or foreign table",
RelationGetRelationName(relation))));
}
break;

View File

@ -2235,6 +2235,11 @@ static CopyState BeginCopyTo(
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot copy from view \"%s\"", RelationGetRelationName(rel)),
errhint("Try the COPY (SELECT ...) TO variant.")));
else if (rel->rd_rel->relkind == RELKIND_MATVIEW)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot copy from materialized view \"%s\"", RelationGetRelationName(rel)),
errhint("Try the COPY (SELECT ...) TO variant.")));
else if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) {
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@ -3404,7 +3409,11 @@ static uint64 CopyFrom(CopyState cstate)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot copy to view \"%s\"", RelationGetRelationName(cstate->rel))));
else if (cstate->rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) {
else if (cstate->rel->rd_rel->relkind == RELKIND_MATVIEW)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot copy to materialized view \"%s\"", RelationGetRelationName(cstate->rel))));
else if (cstate->rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) {
if (!CheckSupportedFDWType(RelationGetRelid(cstate->rel)))
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),

View File

@ -1,14 +1,17 @@
/* -------------------------------------------------------------------------
*
* createas.cpp
* Execution of CREATE TABLE ... AS, a/k/a SELECT INTO
* Execution of CREATE TABLE ... AS, a/k/a SELECT INTO.
* Since CREATE MATERIALIZED VIEW shares syntax and most behaviors,
* we implement that here, too.
*
* We implement this by diverting the query's normal output to a
* specialized DestReceiver type.
*
* Formerly, this command was implemented as a variant of SELECT, which led
* Formerly, CTAS was implemented as a variant of SELECT, which led
* to assorted legacy behaviors that we still try to preserve, notably that
* we must return a tuples-processed count in the completionTag.
* we must return a tuples-processed count in the completionTag. (We no
* longer do that for CTAS ... WITH NO DATA, however.)
*
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
@ -29,8 +32,13 @@
#include "access/xlog.h"
#include "catalog/toasting.h"
#include "commands/createas.h"
#include "commands/matview.h"
#include "commands/prepare.h"
#include "commands/tablecmds.h"
#include "commands/view.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_clause.h"
#include "rewrite/rewriteHandler.h"
#include "storage/smgr.h"
@ -39,6 +47,7 @@
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/rel_gs.h"
#include "utils/sec_rls_utils.h"
#include "utils/snapmgr.h"
typedef struct {
@ -46,16 +55,145 @@ typedef struct {
IntoClause* into; /* target relation specification */
/* These fields are filled by intorel_startup: */
Relation rel; /* relation to write to */
ObjectAddress reladdr; /* address of rel, for ExecCreateTableAs */
CommandId output_cid; /* cmin to insert in output tuples */
int hi_options; /* heap_insert performance options */
BulkInsertState bistate; /* bulk insert state */
} DR_intorel;
/* utility functions for CTAS definition creation */
static ObjectAddress create_ctas_internal(List* attrList, IntoClause* into);
static ObjectAddress create_ctas_nodata(const List* tlist, IntoClause* into);
/* the OID of the created table, for ExecCreateTableAs consumption */
static void intorel_startup(DestReceiver* self, int operation, TupleDesc typeinfo);
static void intorel_receive(TupleTableSlot* slot, DestReceiver* self);
static void intorel_shutdown(DestReceiver* self);
static void intorel_destroy(DestReceiver* self);
/*
* create_ctas_internal
*
* Internal utility used for the creation of the definition of a relation
* created via CREATE TABLE AS or a materialized view. Caller needs to
* provide a list of attributes (ColumnDef nodes).
*/
static ObjectAddress create_ctas_internal(List* attrList, IntoClause* into)
{
CreateStmt *create = makeNode(CreateStmt);
bool is_matview;
char relkind;
Datum toast_options;
static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
ObjectAddress intoRelationAddr;
Oid relOid;
/* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
is_matview = (into->viewQuery != NULL);
relkind = is_matview ? RELKIND_MATVIEW : RELKIND_RELATION;
/*
* Create the target relation by faking up a CREATE TABLE parsetree and
* passing it to DefineRelation.
*/
create->relation = into->rel;
create->tableElts = attrList;
create->inhRelations = NIL;
create->ofTypename = NULL;
create->constraints = NIL;
create->options = into->options;
create->oncommit = into->onCommit;
create->tablespacename = into->tableSpaceName;
create->if_not_exists = false;
/*
* Create the relation. (This will error out if there's an existing view,
* so we don't need more code to complain if "replace" is false.)
*/
relOid = DefineRelation(create, relkind, InvalidOid);
intoRelationAddr.classId = RelationRelationId;
intoRelationAddr.objectId = relOid;
intoRelationAddr.objectSubId = 0;
/*
* If necessary, create a TOAST table for the target table. Note that
* NewRelationCreateToastTable ends with CommandCounterIncrement(), so
* that the TOAST table will be visible for insertion.
*/
CommandCounterIncrement();
/* parse and validate reloptions for the toast table */
toast_options = transformRelOptions((Datum)0, create->options, "toast", validnsps, true, false);
(void)heap_reloptions(RELKIND_TOASTVALUE, toast_options, true);
NewRelationCreateToastTable(intoRelationAddr.objectId, toast_options);
/* Create the "view" part of a materialized view. */
if (is_matview) {
/* StoreViewQuery scribbles on tree, so make a copy */
Query *query = (Query*)copyObject(into->viewQuery);
StoreViewQuery(intoRelationAddr.objectId, query, false);
CommandCounterIncrement();
}
return intoRelationAddr;
}
static ObjectAddress create_ctas_nodata(const List* tlist, IntoClause* into)
{
List *attrList;
ListCell *t, *lc;
/*
* Build list of ColumnDefs from non-junk elements of the tlist. If a
* column name list was specified in CREATE TABLE AS, override the column
* names in the query. (Too few column names are OK, too many are not.)
*/
attrList = NIL;
lc = list_head(into->colNames);
foreach (t, tlist) {
TargetEntry* tle = (TargetEntry*)lfirst(t);
if (!tle->resjunk) {
ColumnDef *col;
char *colname;
if (lc) {
colname = strVal(lfirst(lc));
lc = lnext(lc);
} else
colname = tle->resname;
col = makeColumnDef(colname, exprType((Node*)tle->expr), exprTypmod((Node*)tle->expr),
exprCollation((Node*)tle->expr));
/*
* It's possible that the column is of a collatable type but the
* collation could not be resolved, so double-check. (We must
* check this here because DefineRelation would adopt the type's
* default collation rather than complaining.)
*/
if (!OidIsValid(col->collOid) && type_is_collatable(col->typname->typeOid))
ereport(ERROR,
(errcode(ERRCODE_INDETERMINATE_COLLATION),
errmsg("no collation was derived for column \"%s\" with collatable type %s",
col->colname,
format_type_be(col->typname->typeOid)),
errhint("Use the COLLATE clause to set the collation explicitly.")));
attrList = lappend(attrList, col);
}
}
if (lc != NULL)
ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("too many column names were specified")));
/* Create the relation definition using the ColumnDef list */
return create_ctas_internal(attrList, into);
}
/*
* ExecCreateTableAs -- execute a CREATE TABLE AS command
*/
@ -63,11 +201,14 @@ void ExecCreateTableAs(CreateTableAsStmt* stmt, const char* queryString, ParamLi
{
Query* query = (Query*)stmt->query;
IntoClause* into = stmt->into;
bool is_matview = (into->viewQuery != NULL);
DestReceiver* dest = NULL;
List* rewritten = NIL;
Oid save_userid = InvalidOid;
int save_sec_context = 0;
int save_nestlevel = 0;
List *rewritten = NULL;
PlannedStmt* plan = NULL;
QueryDesc* queryDesc = NULL;
ScanDirection dir;
/*
* Create the tuple receiver object and insert info it will need
@ -82,92 +223,110 @@ void ExecCreateTableAs(CreateTableAsStmt* stmt, const char* queryString, ParamLi
if (query->commandType == CMD_UTILITY && IsA(query->utilityStmt, ExecuteStmt)) {
ExecuteStmt* estmt = (ExecuteStmt*)query->utilityStmt;
Assert(!is_matview); /* excluded by syntax */
ExecuteQuery(estmt, into, queryString, params, dest, completionTag);
return;
}
Assert(query->commandType == CMD_SELECT);
/*
* Parse analysis was done already, but we still have to run the rule
* rewriter. We do not do AcquireRewriteLocks: we assume the query either
* came straight from the parser, or suitable locks were acquired by
* plancache.c.
*
* Because the rewriter and planner tend to scribble on the input, we make
* a preliminary copy of the source querytree. This prevents problems in
* the case that CTAS is in a portal or plpgsql function and is executed
* repeatedly. (See also the same hack in EXPLAIN and PREPARE.)
* 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.
*/
rewritten = QueryRewrite((Query*)copyObject(stmt->query));
if (is_matview) {
GetUserIdAndSecContext(&save_userid, &save_sec_context);
SetUserIdAndSecContext(save_userid, save_sec_context | SECURITY_RESTRICTED_OPERATION);
save_nestlevel = NewGUCNestLevel();
}
if (into->skipData) {
/*
* If WITH NO DATA was specified, do not go through the rewriter,
* planner and executor. Just define the relation using a code path
* similar to CREATE VIEW. This avoids dump/restore problems stemming
* from running the planner before all dependencies are set up.
*/
(void)create_ctas_nodata(query->targetList, into);
} else {
/*
* Parse analysis was done already, but we still have to run the rule
* rewriter. We do not do AcquireRewriteLocks: we assume the query either
* came straight from the parser, or suitable locks were acquired by
* plancache.c.
*
* Because the rewriter and planner tend to scribble on the input, we make
* a preliminary copy of the source querytree. This prevents problems in
* the case that CTAS is in a portal or plpgsql function and is executed
* repeatedly. (See also the same hack in EXPLAIN and PREPARE.)
*/
rewritten = QueryRewrite((Query*)copyObject(query));
/* SELECT should never rewrite to more or less than one SELECT query */
if (list_length(rewritten) != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unexpected rewrite result for CREATE TABLE AS SELECT")));
query = (Query*)linitial(rewritten);
Assert(query->commandType == CMD_SELECT);
/* SELECT should never rewrite to more or less than one SELECT query */
if (list_length(rewritten) != 1)
elog(ERROR, "unexpected rewrite result for %s",
is_matview ? "CREATE MATERIALIZED VIEW" : "CREATE TABLE AS SELECT");
query = (Query*)linitial(rewritten);
Assert(query->commandType == CMD_SELECT);
/* plan the query */
plan = pg_plan_query(query, 0, params);
/* plan the query */
plan = pg_plan_query(query, 0, params);
/*
* Use a snapshot with an updated command ID to ensure this query sees
* results of any previously executed queries. (This could only matter if
* the planner executed an allegedly-stable function that changed the
* database contents, but let's do it anyway to be parallel to the EXPLAIN
* code path.)
*/
PushCopiedSnapshot(GetActiveSnapshot());
UpdateActiveSnapshotCommandId();
/*
* Use a snapshot with an updated command ID to ensure this query sees
* results of any previously executed queries. (This could only matter if
* the planner executed an allegedly-stable function that changed the
* database contents, but let's do it anyway to be parallel to the EXPLAIN
* code path.)
*/
PushCopiedSnapshot(GetActiveSnapshot());
UpdateActiveSnapshotCommandId();
/* Create a QueryDesc, redirecting output to our tuple receiver */
queryDesc = CreateQueryDesc(plan, queryString, GetActiveSnapshot(), InvalidSnapshot, dest, params, 0);
/* Create a QueryDesc, redirecting output to our tuple receiver */
queryDesc = CreateQueryDesc(plan, queryString, GetActiveSnapshot(), InvalidSnapshot, dest, params, 0);
if (ENABLE_WORKLOAD_CONTROL && (IS_PGXC_COORDINATOR || IS_SINGLE_NODE)) {
/* Check if need track resource */
u_sess->exec_cxt.need_track_resource = WLMNeedTrackResource(queryDesc);
if (ENABLE_WORKLOAD_CONTROL && (IS_PGXC_COORDINATOR || IS_SINGLE_NODE)) {
/* Check if need track resource */
u_sess->exec_cxt.need_track_resource = WLMNeedTrackResource(queryDesc);
}
/* call ExecutorStart to prepare the plan for execution */
ExecutorStart(queryDesc, GetIntoRelEFlags(into));
/* workload client manager */
if (ENABLE_WORKLOAD_CONTROL) {
WLMInitQueryPlan(queryDesc);
dywlm_client_manager(queryDesc);
}
/* run the plan to completion */
ExecutorRun(queryDesc, ForwardScanDirection, 0L);
/* save the rowcount if we're given a completionTag to fill */
if (completionTag != NULL) {
errno_t rc;
rc = snprintf_s(completionTag, COMPLETION_TAG_BUFSIZE, COMPLETION_TAG_BUFSIZE - 1, "SELECT %lu",
queryDesc->estate->es_processed);
securec_check_ss(rc, "\0", "\0");
}
/* and clean up */
ExecutorFinish(queryDesc);
ExecutorEnd(queryDesc);
FreeQueryDesc(queryDesc);
PopActiveSnapshot();
}
/* call ExecutorStart to prepare the plan for execution */
ExecutorStart(queryDesc, GetIntoRelEFlags(into));
if (is_matview) {
/* Roll back any GUC changes */
AtEOXact_GUC(false, save_nestlevel);
/* workload client manager */
if (ENABLE_WORKLOAD_CONTROL) {
WLMInitQueryPlan(queryDesc);
dywlm_client_manager(queryDesc);
/* Restore userid and security context */
SetUserIdAndSecContext(save_userid, save_sec_context);
}
/*
* Normally, we run the plan to completion; but if skipData is specified,
* just do tuple receiver startup and shutdown.
*/
if (into->skipData)
dir = NoMovementScanDirection;
else
dir = ForwardScanDirection;
/* run the plan */
ExecutorRun(queryDesc, dir, 0L);
/* save the rowcount if we're given a completionTag to fill */
if (completionTag != NULL) {
errno_t rc;
rc = snprintf_s(completionTag,
COMPLETION_TAG_BUFSIZE,
COMPLETION_TAG_BUFSIZE - 1,
"SELECT %lu",
queryDesc->estate->es_processed);
securec_check_ss(rc, "\0", "\0");
}
/* and clean up */
ExecutorFinish(queryDesc);
ExecutorEnd(queryDesc);
FreeQueryDesc(queryDesc);
PopActiveSnapshot();
}
/*
@ -180,15 +339,23 @@ void ExecCreateTableAs(CreateTableAsStmt* stmt, const char* queryString, ParamLi
*/
int GetIntoRelEFlags(IntoClause* intoClause)
{
int flags;
/*
* We need to tell the executor whether it has to produce OIDs or not,
* because it doesn't have enough information to do so itself (since we
* can't build the target relation until after ExecutorStart).
*
* Disallow the OIDS option for materialized views.
*/
if (interpretOidsOption(intoClause->options))
return EXEC_FLAG_WITH_OIDS;
if (interpretOidsOption(intoClause->options, (intoClause->viewQuery == NULL)))
flags = EXEC_FLAG_WITH_OIDS;
else
return EXEC_FLAG_WITHOUT_OIDS;
flags = EXEC_FLAG_WITHOUT_OIDS;
if (intoClause->skipData)
flags |= EXEC_FLAG_WITH_NO_DATA;
return flags;
}
/*
@ -219,32 +386,20 @@ static void intorel_startup(DestReceiver* self, int operation, TupleDesc typeinf
{
DR_intorel* myState = (DR_intorel*)self;
IntoClause* into = myState->into;
CreateStmt* create = NULL;
Oid intoRelationId;
bool is_matview;
char relkind;
List* attrList;
ObjectAddress intoRelationAddr;
Relation intoRelationDesc;
RangeTblEntry* rte = NULL;
Datum toast_options;
ListCell* lc = NULL;
RangeTblEntry* rte;
ListCell* lc;
int attnum;
static const char* const validnsps[] = HEAP_RELOPT_NAMESPACES;
Assert(into != NULL); /* else somebody forgot to set it */
/*
* Create the target relation by faking up a CREATE TABLE parsetree and
* passing it to DefineRelation.
*/
create = makeNode(CreateStmt);
create->relation = into->rel;
create->tableElts = NIL; /* will fill below */
create->inhRelations = NIL;
create->ofTypename = NULL;
create->constraints = NIL;
create->options = into->options;
create->oncommit = into->onCommit;
create->row_compress = into->row_compress;
create->tablespacename = into->tableSpaceName;
create->if_not_exists = false;
/* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
is_matview = (into->viewQuery != NULL);
relkind = is_matview ? RELKIND_MATVIEW : RELKIND_RELATION;
/*
* Build column definitions using "pre-cooked" type and collation info. If
@ -252,40 +407,20 @@ static void intorel_startup(DestReceiver* self, int operation, TupleDesc typeinf
* column names derived from the query. (Too few column names are OK, too
* many are not.)
*/
attrList = NIL;
lc = list_head(into->colNames);
for (attnum = 0; attnum < typeinfo->natts; attnum++) {
Form_pg_attribute attribute = typeinfo->attrs[attnum];
ColumnDef* col = makeNode(ColumnDef);
TypeName* coltype = makeNode(TypeName);
ColumnDef *col;
char *colname;
if (lc != NULL) {
col->colname = strVal(lfirst(lc));
if (lc) {
colname = strVal(lfirst(lc));
lc = lnext(lc);
} else
col->colname = NameStr(attribute->attname);
col->typname = coltype;
col->inhcount = 0;
col->is_local = true;
col->is_not_null = false;
col->is_from_type = false;
col->storage = 0;
col->kvtype = attribute->attkvtype;
col->cmprs_mode = attribute->attcmprmode;
col->raw_default = NULL;
col->cooked_default = NULL;
col->collClause = NULL;
col->collOid = attribute->attcollation;
col->constraints = NIL;
col->fdwoptions = NIL;
colname = NameStr(attribute->attname);
coltype->names = NIL;
coltype->typeOid = attribute->atttypid;
coltype->setof = false;
coltype->pct_type = false;
coltype->typmods = NIL;
coltype->typemod = attribute->atttypmod;
coltype->arrayBounds = NIL;
coltype->location = -1;
col = makeColumnDef(colname, attribute->atttypid, attribute->atttypmod, attribute->attcollation);
/*
* It's possible that the column is of a collatable type but the
@ -293,43 +428,29 @@ static void intorel_startup(DestReceiver* self, int operation, TupleDesc typeinf
* this here because DefineRelation would adopt the type's default
* collation rather than complaining.)
*/
if (!OidIsValid(col->collOid) && type_is_collatable(coltype->typeOid))
ereport(ERROR,
if (!OidIsValid(col->collOid) && type_is_collatable(col->typname->typeOid))
ereport(ERROR,
(errcode(ERRCODE_INDETERMINATE_COLLATION),
errmsg("no collation was derived for column \"%s\" with collatable type %s",
errmsg("no collation was derived for column \"%s\" with collatable type %s",
col->colname,
format_type_be(coltype->typeOid)),
errhint("Use the COLLATE clause to set the collation explicitly.")));
format_type_be(col->typname->typeOid)),
errhint("Use the COLLATE clause to set the collation explicitly.")));
create->tableElts = lappend(create->tableElts, col);
attrList = lappend(attrList, col);
}
if (lc != NULL)
ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("CREATE TABLE AS specifies too many column names")));
ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("too many column names were specified")));
/*
* Actually create the target table
*/
intoRelationId = DefineRelation(create, RELKIND_RELATION, InvalidOid);
/*
* If necessary, create a TOAST table for the target table. Note that
* AlterTableCreateToastTable ends with CommandCounterIncrement(), so that
* the TOAST table will be visible for insertion.
*/
CommandCounterIncrement();
/* parse and validate reloptions for the toast table */
toast_options = transformRelOptions((Datum)0, create->options, "toast", validnsps, true, false);
(void)heap_reloptions(RELKIND_TOASTVALUE, toast_options, true);
AlterTableCreateToastTable(intoRelationId, toast_options);
intoRelationAddr = create_ctas_internal(attrList, into);
/*
* Finally we can open the target table
*/
intoRelationDesc = heap_open(intoRelationId, AccessExclusiveLock);
intoRelationDesc = heap_open(intoRelationAddr.objectId, AccessExclusiveLock);
/*
* Check INSERT permission on the constructed table.
@ -339,19 +460,33 @@ static void intorel_startup(DestReceiver* self, int operation, TupleDesc typeinf
*/
rte = makeNode(RangeTblEntry);
rte->rtekind = RTE_RELATION;
rte->relid = intoRelationId;
rte->relkind = RELKIND_RELATION;
rte->relid = intoRelationAddr.objectId;
rte->relkind = relkind;
rte->requiredPerms = ACL_INSERT;
for (attnum = 1; attnum <= intoRelationDesc->rd_att->natts; attnum++)
rte->insertedCols = bms_add_member(rte->insertedCols, attnum - FirstLowInvalidHeapAttributeNumber);
rte->insertedCols = bms_add_member(rte->insertedCols,
(void)ExecCheckRTPerms(list_make1(rte), true);
attnum - FirstLowInvalidHeapAttributeNumber);
ExecCheckRTPerms(list_make1(rte), true);
/*
* Make sure the constructed table does not have RLS enabled.
*
* check_enable_rls() will ereport(ERROR) itself if the user has requested
* something invalid, and otherwise will return RLS_ENABLED if RLS should
* be enabled here. We don't actually support that currently, so throw
* our own ereport(ERROR) if that happens.
*/
if (CheckEnableRlsPolicies(intoRelationDesc, GetUserId()) == RLS_ENABLED)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errmsg("policies not yet implemented for this command"))));
/*
* Fill private fields of myState for use by later routines
*/
myState->rel = intoRelationDesc;
myState->reladdr = intoRelationAddr;
myState->output_cid = GetCurrentCommandId(true);
/*

View File

@ -611,23 +611,32 @@ static void ExplainOneQuery(
CreateTableAsStmt* ctas = (CreateTableAsStmt*)query->utilityStmt;
List* rewritten = NIL;
// INSERT INTO statement needs target table to be created first,
// so we just support EXPLAIN ANALYZE.
//
if (!es->analyze) {
const char* stmt = ctas->is_select_into ? "SELECT INTO" : "CREATE TABLE AS SELECT";
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("EXPLAIN %s requires ANALYZE", stmt)));
}
// CREATE TABLE AS SELECT and SELECT INTO are rewritten so that the
// target table is created first. The SELECT query is then transformed
// into an INSERT INTO statement.
//
rewritten = QueryRewriteCTAS(query);
AssertEreport(list_length(rewritten) == 1, MOD_EXECUTOR, "unexpect list length");
ExplainOneQuery((Query*)linitial(rewritten), ctas->into, es, queryString, params);
if (ctas->relkind == OBJECT_MATVIEW) {
query = (Query*)ctas->query;
rewritten = QueryRewrite((Query*) copyObject(query));
} else {
rewritten = QueryRewriteCTAS(query);
}
Assert(list_length(rewritten) == 1);
// INSERT INTO statement needs target table to be created first,
// so we just support EXPLAIN ANALYZE.
//
if (!es->analyze) {
if (ctas->relkind == OBJECT_MATVIEW) {
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("EXPLAIN CREATE MATERIALIZED VIEW requires ANALYZE")));
}
const char* stmt = ctas->is_select_into ? "SELECT INTO" : "CREATE TABLE AS SELECT";
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("EXPLAIN %s requires ANALYZE", stmt)));
}
ExplainOneQuery((Query*) linitial(rewritten), ctas->into, es, queryString, params);
return;
}
@ -885,9 +894,13 @@ void ExplainOnePlan(
UpdateActiveSnapshotCommandId();
/*
* Normally we discard the query's output.
* Normally we discard the query's output, but if explaining CREATE TABLE
* AS, we'd better use the appropriate tuple receiver.
*/
dest = None_Receiver;
if (into != NULL && into->viewQuery != NULL)
dest = CreateIntoRelDestReceiver(into);
else
dest = None_Receiver;
/* Create a QueryDesc for the query */
queryDesc = CreateQueryDesc(

View File

@ -2476,14 +2476,14 @@ void ReindexDatabase(const char* databaseName, bool do_system, bool do_user, Ada
/*
* Scan pg_class to build a list of the relations we need to reindex.
*
* We only consider plain relations here (toast rels will be processed
* indirectly by reindex_relation).
* We only consider plain relations and materialized views here (toast
* rels will be processed indirectly by reindex_relation).
*/
relationRelation = heap_open(RelationRelationId, AccessShareLock);
scan = heap_beginscan(relationRelation, SnapshotNow, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) {
Form_pg_class classtuple = (Form_pg_class)GETSTRUCT(tuple);
if (classtuple->relkind != RELKIND_RELATION)
if (classtuple->relkind != RELKIND_RELATION && classtuple->relkind != RELKIND_MATVIEW)
continue;
/* Skip temp tables of other backends; we can't reindex them at all */
@ -2777,7 +2777,7 @@ static bool relationHasInformationalPrimaryKey(const Relation rel)
*/
static void handleErrMsgForInfoCnstrnt(const IndexStmt* stmt, const Relation rel)
{
if (rel->rd_rel->relkind != RELKIND_RELATION) {
if (rel->rd_rel->relkind != RELKIND_RELATION && rel->rd_rel->relkind != RELKIND_MATVIEW) {
Oid relationId = RelationGetRelid(rel);
/*
* @hdfs
@ -2810,7 +2810,8 @@ static void handleErrMsgForInfoCnstrnt(const IndexStmt* stmt, const Relation rel
}
} else {
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table", RelationGetRelationName(rel))));
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table or materialized view", RelationGetRelationName(rel))));
}
}
}

View File

@ -0,0 +1,367 @@
/* -------------------------------------------------------------------------
*
* matview.c
* materialized view support
*
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/backend/commands/matview.c
*
* -------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/multixact.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_am.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "commands/cluster.h"
#include "commands/matview.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
#include "executor/executor.h"
#include "executor/spi.h"
#include "miscadmin.h"
#include "parser/parse_relation.h"
#include "pgstat.h"
#include "rewrite/rewriteHandler.h"
#include "storage/smgr.h"
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "storage/lmgr.h"
typedef struct {
DestReceiver pub; /* publicly-known function pointers */
Oid transientoid; /* OID of new heap into which to store */
/* These fields are filled by transientrel_startup: */
Relation transientrel; /* relation to write to */
CommandId output_cid; /* cmin to insert in output tuples */
int hi_options; /* heap_insert performance options */
BulkInsertState bistate; /* bulk insert state */
} DR_transientrel;
static const int g_matview_maintenance_depth = 0;
static void transientrel_startup(DestReceiver* self, int operation, TupleDesc typeinfo);
static void transientrel_receive(TupleTableSlot* slot, DestReceiver* self);
static void transientrel_shutdown(DestReceiver* self);
static void transientrel_destroy(DestReceiver* self);
static uint64 refresh_matview_datafill(DestReceiver* dest, Query* query, const char* queryString);
static void refresh_by_heap_swap(Oid matviewOid, Oid OIDNewHeap);
/*
* ExecRefreshMatView -- execute a REFRESH MATERIALIZED VIEW command
*
* This refreshes the materialized view by creating a new table and swapping
* the relfilenodes 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.
*
* 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
* incrementally while we load.
*
* The matview's "populated" state is changed based on whether the contents
* reflect the result set of the materialized view's query.
*/
void ExecRefreshMatView(const RefreshMatViewStmt* stmt, const char* queryString, ParamListInfo params,
char* completionTag)
{
Oid matviewOid;
Relation matviewRel;
RewriteRule *rule;
List *actions;
Query *dataQuery;
Oid tableSpace;
Oid relowner;
Oid OIDNewHeap;
DestReceiver *dest;
uint64 processed = 0;
Oid save_userid;
int save_sec_context;
int save_nestlevel;
/* the flag will be init or reset at start of PostgresMain loop. */
u_sess->cmd_cxt.isUnderRefreshMatview = true;
/*
* Get a lock until end of transaction.
*/
matviewOid = RangeVarGetRelidExtended(stmt->relation, ExclusiveLock, false, false, false, false,
RangeVarCallbackOwnsTable, NULL);
matviewRel = heap_open(matviewOid, NoLock);
/* Make sure it is a materialized view. */
if (matviewRel->rd_rel->relkind != RELKIND_MATVIEW)
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("\"%s\" is not a materialized view", RelationGetRelationName(matviewRel))));
/* We're not using materialized views in the system catalogs. */
Assert(!IsSystemRelation(matviewRel));
/* We don't allow an oid column for a materialized view. */
Assert(!matviewRel->rd_rel->relhasoids);
/*
* Check that everything is correct for a refresh. Problems at this point
* are internal errors, so elog is sufficient.
*/
if (matviewRel->rd_rel->relhasrules == false || matviewRel->rd_rules->numLocks < 1)
elog(ERROR, "materialized view \"%s\" is missing rewrite information", RelationGetRelationName(matviewRel));
if (matviewRel->rd_rules->numLocks > 1)
elog(ERROR, "materialized view \"%s\" has too many rules", RelationGetRelationName(matviewRel));
rule = matviewRel->rd_rules->rules[0];
if (rule->event != CMD_SELECT || !(rule->isInstead))
elog(ERROR, "the rule for materialized view \"%s\" is not a SELECT INSTEAD OF rule",
RelationGetRelationName(matviewRel));
actions = rule->actions;
if (list_length(actions) != 1)
elog(ERROR, "the rule for materialized view \"%s\" is not a single action",
RelationGetRelationName(matviewRel));
/*
* The stored query was rewritten at the time of the MV definition, but
* has not been scribbled on by the planner.
*/
dataQuery = (Query*)linitial(actions);
Assert(IsA(dataQuery, Query));
/*
* Check for active uses of the relation in the current transaction, such
* as open scans.
*
* NB: We count on this to protect us against problems with refreshing the
* data using HEAP_INSERT_FROZEN.
*/
CheckTableNotInUse(matviewRel, "REFRESH MATERIALIZED VIEW");
relowner = matviewRel->rd_rel->relowner;
/*
* Switch to the owner's userid, so that any functions are run as that
* user. Also arrange to make GUC variable changes local to this command.
* Don't lock it down too tight to create a temporary table just yet. We
* will switch modes when we are about to execute user code.
*/
GetUserIdAndSecContext(&save_userid, &save_sec_context);
SetUserIdAndSecContext(relowner, save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
save_nestlevel = NewGUCNestLevel();
tableSpace = matviewRel->rd_rel->reltablespace;
heap_close(matviewRel, NoLock);
/* Create the transient table that will receive the regenerated data. */
OIDNewHeap = make_new_heap(matviewOid, tableSpace, ExclusiveLock);
dest = CreateTransientRelDestReceiver(OIDNewHeap);
/*
* Now lock down security-restricted operations.
*/
SetUserIdAndSecContext(relowner, save_sec_context | SECURITY_RESTRICTED_OPERATION);
/* Generate the data, if wanted. */
if (!stmt->skipData)
processed = refresh_matview_datafill(dest, dataQuery, queryString);
/* Make the matview match the newly generated data. */
t_thrd.storage_cxt.EnlargeDeadlockTimeout = true;
LockRelationOid(matviewOid, AccessExclusiveLock);
refresh_by_heap_swap(matviewOid, OIDNewHeap);
if (!stmt->skipData)
pgstat_count_heap_insert(matviewRel, processed);
/* Roll back any GUC changes */
AtEOXact_GUC(false, save_nestlevel);
/* Restore userid and security context */
SetUserIdAndSecContext(save_userid, save_sec_context);
u_sess->cmd_cxt.isUnderRefreshMatview = false;
}
/*
* refresh_matview_datafill
*/
static uint64 refresh_matview_datafill(DestReceiver* dest, Query* query, const char* queryString)
{
List *rewritten;
PlannedStmt *plan;
QueryDesc *queryDesc;
Query *copied_query;
uint64 processed;
/* Lock and rewrite, using a copy to preserve the original query. */
copied_query = (Query*)copyObject(query);
AcquireRewriteLocks(copied_query, false);
rewritten = QueryRewrite(copied_query);
/* SELECT should never rewrite to more or less than one SELECT query */
if (list_length(rewritten) != 1)
elog(ERROR, "unexpected rewrite result for REFRESH MATERIALIZED VIEW");
query = (Query*)linitial(rewritten);
/* Check for user-requested abort. */
CHECK_FOR_INTERRUPTS();
/* Plan the query which will generate data for the refresh. */
plan = pg_plan_query(query, 0, NULL);
/*
* Use a snapshot with an updated command ID to ensure this query sees
* results of any previously executed queries. (This could only matter if
* the planner executed an allegedly-stable function that changed the
* database contents, but let's do it anyway to be safe.)
*/
PushCopiedSnapshot(GetActiveSnapshot());
UpdateActiveSnapshotCommandId();
/* Create a QueryDesc, redirecting output to our tuple receiver */
queryDesc = CreateQueryDesc(plan, queryString, GetActiveSnapshot(), InvalidSnapshot, dest, NULL, 0);
/* call ExecutorStart to prepare the plan for execution */
ExecutorStart(queryDesc, EXEC_FLAG_WITHOUT_OIDS);
/* run the plan */
ExecutorRun(queryDesc, ForwardScanDirection, 0L);
processed = queryDesc->estate->es_processed;
/* and clean up */
ExecutorFinish(queryDesc);
ExecutorEnd(queryDesc);
PopActiveSnapshot();
return processed;
}
DestReceiver* CreateTransientRelDestReceiver(Oid transientoid)
{
DR_transientrel *self = (DR_transientrel*)palloc0(sizeof(DR_transientrel));
self->pub.receiveSlot = transientrel_receive;
self->pub.rStartup = transientrel_startup;
self->pub.rShutdown = transientrel_shutdown;
self->pub.rDestroy = transientrel_destroy;
self->pub.mydest = DestTransientRel;
self->transientoid = transientoid;
return (DestReceiver*)self;
}
/*
* transientrel_startup --- executor startup
*/
static void transientrel_startup(DestReceiver* self, int operation, TupleDesc typeinfo)
{
DR_transientrel* myState = (DR_transientrel*)self;
Relation transientrel;
transientrel = heap_open(myState->transientoid, NoLock);
/*
* Fill private fields of myState for use by later routines
*/
myState->transientrel = transientrel;
myState->output_cid = GetCurrentCommandId(true);
/*
* We can skip WAL-logging the insertions, unless PITR or streaming
* replication is in use. We can skip the FSM in any case.
*/
myState->hi_options = HEAP_INSERT_SKIP_FSM | HEAP_INSERT_FROZEN;
if (!XLogIsNeeded())
myState->hi_options |= HEAP_INSERT_SKIP_WAL;
myState->bistate = GetBulkInsertState();
/* Not using WAL requires smgr_targblock be initially invalid */
Assert(RelationGetTargetBlock(transientrel) == InvalidBlockNumber);
}
/*
* transientrel_receive --- receive one tuple
*/
static void transientrel_receive(TupleTableSlot* slot, DestReceiver* self)
{
DR_transientrel* myState = (DR_transientrel*)self;
HeapTuple tuple;
/*
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
tuple = ExecMaterializeSlot(slot);
heap_insert(myState->transientrel, tuple, myState->output_cid, myState->hi_options, myState->bistate);
/* We know this is a newly created relation, so there are no indexes */
}
/*
* transientrel_shutdown --- executor end
*/
static void transientrel_shutdown(DestReceiver* self)
{
DR_transientrel* myState = (DR_transientrel*)self;
FreeBulkInsertState(myState->bistate);
/* If we skipped using WAL, must heap_sync before commit */
if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
heap_sync(myState->transientrel);
/* close transientrel, but keep lock until commit */
heap_close(myState->transientrel, NoLock);
myState->transientrel = NULL;
}
/*
* transientrel_destroy --- release DestReceiver object
*/
static void transientrel_destroy(DestReceiver* self)
{
pfree(self);
}
/*
* Swap the physical files of the target and transient tables, then rebuild
* the target's indexes and throw away the transient table. Security context
* swapping is handled by the called function, so it is not needed here.
*/
static void refresh_by_heap_swap(Oid matviewOid, Oid OIDNewHeap)
{
finish_heap_swap(matviewOid, OIDNewHeap, false, false, true, u_sess->utils_cxt.RecentXmin);
}
/*
* This should be used to test whether the backend is in a context where it is
* OK to allow DML statements to modify materialized views. We only want to
* allow that for internal code driven by the materialized view definition,
* not for arbitrary user-supplied code.
*
* While the function names reflect the fact that their main intended use is
* incremental maintenance of materialized views (in response to changes to
* the data in referenced relations), they are initially used to allow REFRESH
* without blocking concurrent reads.
*/
bool MatViewIncrementalMaintenanceIsEnabled(void)
{
return g_matview_maintenance_depth > 0;
}

View File

@ -93,15 +93,16 @@ void ExecSecLabelStmt(SecLabelStmt* stmt)
/*
* Allow security labels only on columns of tables, views,
* composite types, and foreign tables (which are the only
* relkinds for which pg_dump will dump labels).
* materialized views, composite types, and foreign tables (which
* are the only relkinds for which pg_dump will dump labels).
*/
if (relation->rd_rel->relkind != RELKIND_RELATION && relation->rd_rel->relkind != RELKIND_VIEW &&
relation->rd_rel->relkind != RELKIND_MATVIEW &&
relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table, view, composite type, or foreign table",
errmsg("\"%s\" is not a table, view, materialized view, composite type, or foreign table",
RelationGetRelationName(relation))));
break;
default:

View File

@ -548,7 +548,7 @@ void DefineSequence(CreateSeqStmt* seq)
stmt->relation = seq->sequence;
stmt->inhRelations = NIL;
stmt->constraints = NIL;
stmt->options = list_make1(defWithOids(false));
stmt->options = NIL;
stmt->oncommit = ONCOMMIT_NOOP;
stmt->tablespacename = NULL;
stmt->if_not_exists = false;

View File

@ -288,6 +288,12 @@ static const struct dropmsgstrings dropmsgstringarray[] = {
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"),
@ -334,10 +340,11 @@ typedef OldToNewChunkIdMappingData* OldToNewChunkIdMapping;
#define ATT_NULL 0x0000
#define ATT_TABLE 0x0001
#define ATT_VIEW 0x0002
#define ATT_INDEX 0x0004
#define ATT_COMPOSITE_TYPE 0x0008
#define ATT_FOREIGN_TABLE 0x0010
#define ATT_SEQUENCE 0x0020
#define ATT_MATVIEW 0x0004
#define ATT_INDEX 0x0008
#define ATT_COMPOSITE_TYPE 0x00010
#define ATT_FOREIGN_TABLE 0x0020
#define ATT_SEQUENCE 0x0040
#define CSTORE_SUPPORT_AT_CMD(cmd) \
((cmd) == AT_AddPartition || (cmd) == AT_ExchangePartition || (cmd) == AT_TruncatePartition || \
@ -519,6 +526,7 @@ static const char* storage_name(char c);
static void RangeVarCallbackForDropRelation(
const RangeVar* rel, Oid relOid, Oid oldRelOid, bool target_is_partition, void* arg);
static void RangeVarCallbackForAlterRelation(
const RangeVar* rv, Oid relid, Oid oldrelid, bool target_is_partition, void* arg);
@ -1800,7 +1808,8 @@ Oid DefineRelation(CreateStmt* stmt, char relkind, Oid ownerId)
list_free_ext(pos);
}
localHasOids = interpretOidsOption(stmt->options);
localHasOids = interpretOidsOption(stmt->options,
(relkind == RELKIND_RELATION || relkind == RELKIND_FOREIGN_TABLE));
descriptor->tdhasoid = (localHasOids || parentOidCount > 0);
if ((pg_strcasecmp(storeChar, ORIENTATION_COLUMN) == 0 || pg_strcasecmp(storeChar, ORIENTATION_TIMESERIES) == 0) &&
@ -2210,6 +2219,9 @@ ObjectAddresses* PreCheckforRemoveRelation(DropStmt* drop, StringInfo tmp_queryS
relkind = RELKIND_VIEW;
relkind_s = "VIEW";
break;
case OBJECT_MATVIEW:
relkind = RELKIND_MATVIEW;
break;
case OBJECT_FOREIGN_TABLE:
relkind = RELKIND_FOREIGN_TABLE;
relkind_s = "FOREIGN TABLE";
@ -2386,7 +2398,7 @@ void RemoveRelationsonMainExecCN(DropStmt* drop, ObjectAddresses* objects)
/*
* RemoveRelations
* Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
* DROP FOREIGN TABLE on datanodes and none main execute coordinator.
* DROP MATERIALIZED VIEW, DROP FOREIGN TABLE on datanodes and none main execute coordinator.
*/
void RemoveRelations(DropStmt* drop, StringInfo tmp_queryString, RemoteQueryExecType* exec_type)
{
@ -2436,6 +2448,9 @@ void RemoveRelations(DropStmt* drop, StringInfo tmp_queryString, RemoteQueryExec
relkind = RELKIND_VIEW;
relkind_s = "VIEW";
break;
case OBJECT_MATVIEW:
relkind = RELKIND_MATVIEW;
break;
case OBJECT_FOREIGN_TABLE:
relkind = RELKIND_FOREIGN_TABLE;
relkind_s = "FOREIGN TABLE";
@ -4179,7 +4194,8 @@ static void renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing
* change names that are hardcoded into the system, hence the following
* restriction.
*/
if (relkind != RELKIND_RELATION && relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE &&
if (relkind != RELKIND_RELATION && relkind != RELKIND_VIEW && relkind != RELKIND_MATVIEW &&
relkind != RELKIND_COMPOSITE_TYPE &&
relkind != RELKIND_INDEX && relkind != RELKIND_FOREIGN_TABLE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@ -4513,15 +4529,17 @@ void RenameConstraint(RenameStmt* stmt)
}
/*
* Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE RENAME
* Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
* RENAME
*/
void RenameRelation(RenameStmt* stmt)
{
Oid relid;
/*
* Grab an exclusive lock on the target table, index, sequence or view,
* which we will NOT release until end of transaction.
* Grab an exclusive lock on the target table, index, sequence, view,
* materialized view, or foreign table, which we will NOT release until
* end of transaction.
*
* Lock level used here should match RenameRelationInternal, to avoid lock
* escalation.
@ -4561,8 +4579,9 @@ void RenameRelationInternal(Oid myrelid, const char* newrelname)
Oid namespaceId;
/*
* Grab an exclusive lock on the target table, index, sequence or view,
* which we will NOT release until end of transaction.
* Grab an exclusive lock on the target table, index, sequence, view,
* materialized view, or foreign table, which we will NOT release until
* end of transaction.
*/
targetrelation = relation_open(myrelid, AccessExclusiveLock);
@ -5965,12 +5984,12 @@ static void ATPrepCmd(List** wqueue, Relation rel, AlterTableCmd* cmd, bool recu
break;
case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
ATSimplePermissions(rel, ATT_TABLE | ATT_INDEX | ATT_FOREIGN_TABLE);
ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_FOREIGN_TABLE);
/* This command never recurses */
pass = AT_PASS_MISC;
break;
case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
ATSimplePermissions(rel, ATT_TABLE);
ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW);
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* No command-specific prep needed */
pass = AT_PASS_MISC;
@ -6053,7 +6072,7 @@ static void ATPrepCmd(List** wqueue, Relation rel, AlterTableCmd* cmd, bool recu
break;
case AT_ClusterOn: /* CLUSTER ON */
case AT_DropCluster: /* SET WITHOUT CLUSTER */
ATSimplePermissions(rel, ATT_TABLE);
ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW);
/* These commands never recurse */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
@ -6098,7 +6117,7 @@ static void ATPrepCmd(List** wqueue, Relation rel, AlterTableCmd* cmd, bool recu
break;
case AT_SetTableSpace: /* SET TABLESPACE */
case AT_SetPartitionTableSpace:
ATSimplePermissions(rel, ATT_TABLE | ATT_INDEX);
ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX);
/* This command never recurses */
ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
pass = AT_PASS_MISC; /* doesn't actually matter */
@ -6107,7 +6126,7 @@ static void ATPrepCmd(List** wqueue, Relation rel, AlterTableCmd* cmd, bool recu
case AT_SetRelOptions: /* SET ... */
case AT_ResetRelOptions: /* RESET ... */
case AT_ReplaceRelOptions: /* reset them all, then set just these */
ATSimplePermissions(rel, ATT_TABLE | ATT_INDEX | ATT_VIEW);
ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_VIEW);
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
@ -6306,7 +6325,7 @@ static void ATRewriteCatalogs(List** wqueue, LOCKMODE lockmode)
if (get_rel_persistence(tab->relid) == RELPERSISTENCE_GLOBAL_TEMP) {
gtt_create_storage_files(tab->relid);
}
if (tab->relkind == RELKIND_RELATION)
if (tab->relkind == RELKIND_RELATION || tab->relkind == RELKIND_MATVIEW)
AlterTableCreateToastTable(tab->relid, (Datum)0);
}
}
@ -7157,6 +7176,9 @@ static void ATSimplePermissions(Relation rel, int allowed_targets)
case RELKIND_VIEW:
actual_target = ATT_VIEW;
break;
case RELKIND_MATVIEW:
actual_target = ATT_MATVIEW;
break;
case RELKIND_INDEX:
actual_target = ATT_INDEX;
break;
@ -7207,18 +7229,27 @@ static void ATWrongRelkindError(Relation rel, int allowed_targets)
case ATT_TABLE:
msg = _("\"%s\" is not a table");
break;
case ATT_TABLE | ATT_INDEX:
msg = _("\"%s\" is not a table or index");
break;
case ATT_TABLE | ATT_VIEW:
msg = _("\"%s\" is not a table or view");
break;
case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX:
msg = _("\"%s\" is not a table, view, materialized view, or index");
break;
case ATT_TABLE | ATT_MATVIEW:
msg = _("\"%s\" is not a table or materialized view");
break;
case ATT_TABLE | ATT_MATVIEW | ATT_INDEX:
msg = _("\"%s\" is not a table, materialized view, or index");
break;
case ATT_TABLE | ATT_FOREIGN_TABLE:
msg = _("\"%s\" is not a table or foreign table");
break;
case ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE:
msg = _("\"%s\" is not a table, composite type, or foreign table");
break;
case ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_FOREIGN_TABLE:
msg = _("\"%s\" is not a table, materialized view, composite type, or foreign table");
break;
case ATT_VIEW:
msg = _("\"%s\" is not a view");
break;
@ -7351,7 +7382,7 @@ void find_composite_type_dependencies(Oid typeOid, Relation origRelation, const
rel = relation_open(pg_depend->objid, AccessShareLock);
att = rel->rd_att->attrs[pg_depend->objsubid - 1];
if (rel->rd_rel->relkind == RELKIND_RELATION) {
if (rel->rd_rel->relkind == RELKIND_RELATION || rel->rd_rel->relkind == RELKIND_MATVIEW) {
if (origTypeName != NULL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@ -8258,11 +8289,12 @@ static void ATPrepSetStatistics(Relation rel, const char* colName, Node* newValu
* to allow SET STATISTICS on system catalogs without requiring
* allowSystemTableMods to be turned on.
*/
if (rel->rd_rel->relkind != RELKIND_RELATION && rel->rd_rel->relkind != RELKIND_INDEX &&
if (rel->rd_rel->relkind != RELKIND_RELATION && rel->rd_rel->relkind != RELKIND_MATVIEW &&
rel->rd_rel->relkind != RELKIND_INDEX &&
rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table, index, or foreign table", RelationGetRelationName(rel))));
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table, materialized view, index, or foreign table",
RelationGetRelationName(rel))));
/* Permissions checks */
if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
@ -11653,6 +11685,7 @@ void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE
switch (tuple_class->relkind) {
case RELKIND_RELATION:
case RELKIND_VIEW:
case RELKIND_MATVIEW:
case RELKIND_FOREIGN_TABLE:
/* ok to change owner */
if (IS_PGXC_COORDINATOR && in_logic_cluster()) {
@ -11880,11 +11913,12 @@ void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE
AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId, tuple_class->relkind == RELKIND_COMPOSITE_TYPE);
/*
* If we are operating on a table, also change the ownership of any
* indexes and sequences that belong to the table, as well as the
* table's toast table (if it has one)
* If we are operating on a table or materialized view, also change
* the ownership of any indexes and sequences that belong to the
* relation, as well as its toast table (if it has one).
*/
if (tuple_class->relkind == RELKIND_RELATION || tuple_class->relkind == RELKIND_TOASTVALUE) {
if (tuple_class->relkind == RELKIND_RELATION || tuple_class->relkind == RELKIND_MATVIEW ||
tuple_class->relkind == RELKIND_TOASTVALUE) {
List* index_oid_list = NIL;
ListCell* i = NULL;
@ -11898,7 +11932,7 @@ void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE
list_free_ext(index_oid_list);
}
if (tuple_class->relkind == RELKIND_RELATION) {
if (tuple_class->relkind == RELKIND_RELATION || tuple_class->relkind == RELKIND_MATVIEW) {
/* If it has a toast table, recurse to change its ownership */
if (tuple_class->reltoastrelid != InvalidOid)
ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId, true, lockmode);
@ -12451,7 +12485,8 @@ static void ATExecSetRelOptions(Relation rel, List* defList, AlterTableType oper
break;
}
case RELKIND_TOASTVALUE:
case RELKIND_VIEW: {
case RELKIND_VIEW:
case RELKIND_MATVIEW:{
(void)heap_reloptions(rel->rd_rel->relkind, newOptions, true);
break;
}
@ -12461,7 +12496,8 @@ static void ATExecSetRelOptions(Relation rel, List* defList, AlterTableType oper
default:
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table, index, or TOAST table", RelationGetRelationName(rel))));
errmsg("\"%s\" is not a table, view, materialized view, index, or TOAST table",
RelationGetRelationName(rel))));
break;
}
@ -15001,8 +15037,9 @@ void AlterTableNamespace(AlterObjectSchemaStmt* stmt)
}
/*
* The guts of relocating a table to another namespace: besides moving
* the table itself, its dependent objects are relocated to the new schema.
* The guts of relocating a table or materialized view to another namespace:
* besides moving the relation itself, its dependent objects are relocated to
* the new schema.
*/
void AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid, ObjectAddresses* objsMoved)
{
@ -15019,7 +15056,7 @@ void AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid, Object
AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid, false, false, objsMoved);
/* Fix other dependent stuff */
if (rel->rd_rel->relkind == RELKIND_RELATION) {
if (rel->rd_rel->relkind == RELKIND_RELATION || rel->rd_rel->relkind == RELKIND_MATVIEW) {
AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved, AccessExclusiveLock);
AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid, false, objsMoved);
@ -15383,9 +15420,10 @@ void AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid, SubT
/*
* This is intended as a callback for RangeVarGetRelidExtended(). It allows
* the table to be locked only if (1) it's a plain table or TOAST table and
* (2) the current user is the owner (or the superuser). This meets the
* permission-checking needs of both CLUSTER and REINDEX TABLE; we expose it
* here so that it can be used by both.
* view, or TOAST table and (2) the current user is the owner (or the
* superuser). This meets the permission-checking needs of CLUSTER, REINDEX
* TABLE, and REFRESH MATERIALIZED VIEW; we expose it here so that it can be
* used by all.
*/
void RangeVarCallbackOwnsTable(const RangeVar* relation, Oid relId, Oid oldRelId, bool target_is_partition, void* arg)
{
@ -15405,8 +15443,9 @@ void RangeVarCallbackOwnsTable(const RangeVar* relation, Oid relId, Oid oldRelId
if (!relkind) {
return;
}
if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE) {
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table", relation->relname)));
if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE && relkind != RELKIND_MATVIEW) {
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table or materialized view",
relation->relname)));
}
/* Check permissions */
if (!pg_class_ownercheck(relId, GetUserId())) {
@ -15447,7 +15486,7 @@ void RangeVarCallbackOwnsRelation(
ReleaseSysCache(tuple);
}
/*
/* RangeVarCallbackForAlterRelation
* Common RangeVarGetRelid callback for rename, set schema, and alter table
* processing.
*/
@ -15538,7 +15577,9 @@ static void RangeVarCallbackForAlterRelation(
if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW) {
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a view", rv->relname)));
}
if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a materialized view", rv->relname)));
if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE) {
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a foreign table", rv->relname)));
}
@ -15576,11 +15617,11 @@ static void RangeVarCallbackForAlterRelation(
* Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
* to a different schema, such as indexes and TOAST tables.
*/
if (IsA(stmt, AlterObjectSchemaStmt) && relkind != RELKIND_RELATION && relkind != RELKIND_VIEW &&
relkind != RELKIND_SEQUENCE && relkind != RELKIND_FOREIGN_TABLE) {
if (IsA(stmt, AlterObjectSchemaStmt) && relkind != RELKIND_RELATION && relkind != RELKIND_VIEW &&
relkind != RELKIND_MATVIEW && relkind != RELKIND_SEQUENCE && relkind != RELKIND_FOREIGN_TABLE) {
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table, view, sequence, or foreign table", rv->relname)));
errmsg("\"%s\" is not a table, view, materialized view, sequence, or foreign table", rv->relname)));
}
ReleaseSysCache(tuple);
@ -20293,7 +20334,7 @@ static void ExecRewriteRowTable(AlteredTableInfo* tab, Oid NewTableSpace, LOCKMO
{
ForbidToRewriteOrTestCstoreIndex(tab);
Oid OIDNewHeap = make_new_heap(tab->relid, NewTableSpace);
Oid OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, AccessExclusiveLock);
/*
* Copy the heap data into the new table with the desired

View File

@ -63,6 +63,7 @@
#include "commands/defrem.h"
#include "commands/seclabel.h"
#include "commands/tablespace.h"
#include "commands/user.h"
#include "miscadmin.h"
#include "nodes/bitmapset.h"
#include "nodes/makefuncs.h"

View File

@ -1928,7 +1928,7 @@ Oid DefineCompositeType(RangeVar* typevar, List* coldeflist)
createStmt->tableElts = coldeflist;
createStmt->inhRelations = NIL;
createStmt->constraints = NIL;
createStmt->options = list_make1(defWithOids(false));
createStmt->options = NIL;
createStmt->oncommit = ONCOMMIT_NOOP;
createStmt->tablespacename = NULL;
createStmt->if_not_exists = false;
@ -2603,9 +2603,16 @@ static List* get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
*/
if (OidIsValid(rel->rd_rel->reltype))
find_composite_type_dependencies(rel->rd_rel->reltype, NULL, format_type_be(domainOid));
/* Otherwise we can ignore views, composite types, etc */
if (rel->rd_rel->relkind != RELKIND_RELATION) {
/*
* Otherwise, we can ignore relations except those with both
* storage and user-chosen column types.
*
* XXX If an index-only scan could satisfy "col::some_domain" from
* a suitable expression index, this should also check expression
* index columns.
*/
if (rel->rd_rel->relkind != RELKIND_RELATION && rel->rd_rel->relkind != RELKIND_MATVIEW) {
relation_close(rel, lockmode);
continue;
}

View File

@ -85,7 +85,6 @@ MemoryContext WaitCountGlobalContext = NULL;
/* Hook to check passwords in CreateRole() and AlterRole() */
THR_LOCAL check_password_hook_type check_password_hook = NULL;
static List* roleNamesToIds(const List* memberNames);
static void AddRoleMems(
const char* rolename, Oid roleid, const List* memberNames, List* memberIds, Oid grantorId, bool admin_opt);
static void DelRoleMems(const char* rolename, Oid roleid, const List* memberNames, List* memberIds, bool admin_opt);
@ -3237,7 +3236,7 @@ void ReassignOwnedObjects(ReassignOwnedStmt* stmt)
* Given a list of role names (as String nodes), generate a list of role OIDs
* in the same order.
*/
static List* roleNamesToIds(const List* memberNames)
List* roleNamesToIds(const List* memberNames)
{
List* result = NIL;
ListCell* l = NULL;

View File

@ -677,14 +677,11 @@ List* get_rel_oids(Oid relid, VacuumStmt* vacstmt)
Relation pgclass;
HeapScanDesc scan;
HeapTuple tuple;
ScanKeyData key;
/* Process all plain relations listed in pg_class */
ScanKeyInit(&key, Anum_pg_class_relkind, BTEqualStrategyNumber, F_CHAREQ, CharGetDatum(RELKIND_RELATION));
pgclass = heap_open(RelationRelationId, AccessShareLock);
scan = heap_beginscan(pgclass, SnapshotNow, 1, &key);
scan = heap_beginscan(pgclass, SnapshotNow, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) {
/* for dfs special vacuum, just skip for non-dfs table. */
@ -696,6 +693,11 @@ List* get_rel_oids(Oid relid, VacuumStmt* vacstmt)
Form_pg_class classForm = (Form_pg_class)GETSTRUCT(tuple);
/* Only vacuum table and materialized view */
if (classForm->relkind != RELKIND_RELATION && classForm->relkind != RELKIND_MATVIEW) {
continue;
}
/* Plain relation and valued partition relation */
if (classForm->parttype == PARTTYPE_NON_PARTITIONED_RELATION ||
classForm->parttype == PARTTYPE_VALUE_PARTITIONED_RELATION ||
@ -1155,10 +1157,11 @@ void vac_update_datfrozenxid(void)
Form_pg_class classForm = (Form_pg_class)GETSTRUCT(classTup);
/*
* Only consider heap and TOAST tables (anything else should have
* InvalidTransactionId in relfrozenxid anyway.)
* Only consider relations able to hold unfrozen XIDs (anything else
* should have InvalidTransactionId in relfrozenxid anyway.)
*/
if (classForm->relkind != RELKIND_RELATION && classForm->relkind != RELKIND_TOASTVALUE)
if (classForm->relkind != RELKIND_RELATION && classForm->relkind != RELKIND_MATVIEW &&
classForm->relkind != RELKIND_TOASTVALUE)
continue;
/* global temp table relstats not in pg_class */
@ -1811,13 +1814,14 @@ static bool vacuum_rel(Oid relid, VacuumStmt* vacstmt, bool do_toast)
}
/*
* Check that it's a vacuumable table; we used to do this in
* Check that it's a vacuumable relation; we used to do this in
* get_rel_oids() but seems safer to check after we've locked the
* relation.
*/
if (onerel->rd_rel->relkind == RELKIND_FOREIGN_TABLE && isMOTFromTblOid(onerel->rd_id)) {
;
} else if (onerel->rd_rel->relkind != RELKIND_RELATION && onerel->rd_rel->relkind != RELKIND_TOASTVALUE) {
} else if (onerel->rd_rel->relkind != RELKIND_RELATION && onerel->rd_rel->relkind != RELKIND_MATVIEW &&
onerel->rd_rel->relkind != RELKIND_TOASTVALUE) {
if (vacstmt->options & VACOPT_VERBOSE)
messageLevel = VERBOSEMESSAGE;

View File

@ -42,47 +42,6 @@
#endif
static void checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc);
static bool isViewOnTempTable_walker(Node* node, void* context);
/* ---------------------------------------------------------------------
* isViewOnTempTable
*
* Returns true iff any of the relations underlying this view are
* temporary tables.
* ---------------------------------------------------------------------
*/
static bool isViewOnTempTable(Query* viewParse)
{
return isViewOnTempTable_walker((Node*)viewParse, NULL);
}
static bool isViewOnTempTable_walker(Node* node, void* context)
{
if (node == NULL)
return false;
if (IsA(node, Query)) {
Query* query = (Query*)node;
ListCell* rtable = NULL;
foreach (rtable, query->rtable) {
RangeTblEntry* rte = (RangeTblEntry*)lfirst(rtable);
if (rte->rtekind == RTE_RELATION) {
Relation rel = heap_open(rte->relid, AccessShareLock);
char relpersistence = rel->rd_rel->relpersistence;
heap_close(rel, AccessShareLock);
if (relpersistence == RELPERSISTENCE_TEMP)
return true;
}
}
return query_tree_walker(query, (bool (*)())isViewOnTempTable_walker, context, QTW_IGNORE_JOINALIASES);
}
return expression_tree_walker(node, (bool (*)())isViewOnTempTable_walker, context);
}
/* ---------------------------------------------------------------------
* DefineVirtualRelation
@ -109,20 +68,8 @@ static Oid DefineVirtualRelation(RangeVar* relation, List* tlist, bool replace,
TargetEntry* tle = (TargetEntry*)lfirst(t);
if (!tle->resjunk) {
ColumnDef* def = makeNode(ColumnDef);
def->colname = pstrdup(tle->resname);
def->typname = makeTypeNameFromOid(exprType((Node*)tle->expr), exprTypmod((Node*)tle->expr));
def->inhcount = 0;
def->is_local = true;
def->is_not_null = false;
def->is_from_type = false;
def->storage = 0;
def->cmprs_mode = ATT_CMPR_NOCOMPRESS; /* dont compress */
def->raw_default = NULL;
def->cooked_default = NULL;
def->collClause = NULL;
def->collOid = exprCollation((Node*)tle->expr);
ColumnDef *def = makeColumnDef(tle->resname, exprType((Node *)tle->expr), exprTypmod((Node *)tle->expr),
exprCollation((Node *)tle->expr));
/*
* It's possible that the column is of a collatable type but the
@ -136,8 +83,7 @@ static Oid DefineVirtualRelation(RangeVar* relation, List* tlist, bool replace,
errhint("Use the COLLATE clause to set the collation explicitly.")));
} else
Assert(!OidIsValid(def->collOid));
def->constraints = NIL;
attrList = lappend(attrList, def);
}
}
@ -262,7 +208,6 @@ static Oid DefineVirtualRelation(RangeVar* relation, List* tlist, bool replace,
createStmt->inhRelations = NIL;
createStmt->constraints = NIL;
createStmt->options = options;
createStmt->options = lappend(options, defWithOids(false));
createStmt->oncommit = ONCOMMIT_NOOP;
createStmt->tablespacename = NULL;
createStmt->if_not_exists = false;
@ -488,7 +433,7 @@ void DefineView(ViewStmt* stmt, const char* queryString, bool isFirstNode)
* schema name.
*/
view = (RangeVar*)copyObject(stmt->view); /* don't corrupt original command */
if (view->relpersistence == RELPERSISTENCE_PERMANENT && isViewOnTempTable(viewParse)) {
if (view->relpersistence == RELPERSISTENCE_PERMANENT && isQueryUsingTempRelation(viewParse)) {
view->relpersistence = RELPERSISTENCE_TEMP;
ereport(NOTICE, (errmsg("view \"%s\" will be a temporary view", view->relname)));
}
@ -513,6 +458,15 @@ void DefineView(ViewStmt* stmt, const char* queryString, bool isFirstNode)
* command id counter (but do NOT pfree any memory!!!!)
*/
CommandCounterIncrement();
StoreViewQuery(viewOid, viewParse, stmt->replace);
}
/*
* Use the rules system to store the query for the view.
*/
void StoreViewQuery(Oid viewOid, Query* viewParse, bool replace)
{
/*
* The range table of 'viewParse' does not contain entries for the "OLD"
@ -523,7 +477,7 @@ void DefineView(ViewStmt* stmt, const char* queryString, bool isFirstNode)
/*
* Now create the rules associated with the view.
*/
DefineViewRules(viewOid, viewParse, stmt->replace);
DefineViewRules(viewOid, viewParse, replace);
}
bool IsViewTemp(ViewStmt* stmt, const char* queryString)
@ -539,7 +493,7 @@ bool IsViewTemp(ViewStmt* stmt, const char* queryString)
* long as the CREATE command is consistent with that --- no explicit
* schema name.
*/
if (view->relpersistence == RELPERSISTENCE_PERMANENT && isViewOnTempTable(viewParse)) {
if (view->relpersistence == RELPERSISTENCE_PERMANENT && isQueryUsingTempRelation(viewParse)) {
view->relpersistence = RELPERSISTENCE_TEMP;
}

View File

@ -1768,7 +1768,9 @@ static void set_subquery_pathlist(PlannerInfo* root, RelOptInfo* rel, Index rti,
/*
* It's possible that constraint exclusion proved the subquery empty. If
* so, it's convenient to turn it back into a dummy path so that we will
* recognize appropriate optimizations at this level.
* recognize appropriate optimizations at this query level. (But see
* create_append_plan in createplan.c, which has to reverse this
* substitution.)
*/
if (is_dummy_plan(rel->subplan)) {
set_dummy_rel_pathlist(rel);

View File

@ -1045,30 +1045,49 @@ static Plan* create_join_plan(PlannerInfo* root, JoinPath* best_path)
static Plan* create_append_plan(PlannerInfo* root, AppendPath* best_path)
{
Append* plan = NULL;
List* tlist = build_relation_tlist(best_path->path.parent);
RelOptInfo* rel = best_path->path.parent;
List* tlist = build_relation_tlist(rel);
List* subplans = NIL;
ListCell* subpaths = NULL;
/*
* It is possible for the subplans list to contain only one entry, or even
* no entries. Handle these cases specially.
* The subpaths list could be empty, if every child was proven empty by
* constraint exclusion. In that case generate a dummy plan that returns
* no rows.
*
* XXX ideally, if there's just one entry, we'd not bother to generate an
* Append node but just return the single child. At the moment this does
* not work because the varno of the child scan plan won't match the
* parent-rel Vars it'll be asked to emit.
* Note that an AppendPath with no members is also generated in certain
* cases where there was no appending construct at all, but we know the
* relation is empty (see set_dummy_rel_pathlist).
*/
if (best_path->subpaths == NIL) {
/* Generate a Result plan with constant-FALSE gating qual */
return (Plan*)make_result(root, tlist, (Node*)list_make1(makeBoolConst(false, false)), NULL);
/*
* If this is a dummy path for a subquery, we have to wrap the
* subquery's original plan in a SubqueryScan so that setrefs.c will
* do the right things. (In particular, it must pull up the
* subquery's rangetable so that the executor will apply permissions
* checks to those rels at runtime.)
*/
if (rel->rtekind == RTE_SUBQUERY) {
Assert(is_dummy_plan(rel->subplan));
return (Plan*)make_subqueryscan(tlist, NIL, rel->relid, rel->subplan);
} else {
/* Generate a Result plan with constant-FALSE gating qual */
return (Plan*)make_result(root, tlist, (Node*)list_make1(makeBoolConst(false, false)), NULL);
}
}
/* Normal case with multiple subpaths */
/* Build the plan for each child */
foreach (subpaths, best_path->subpaths) {
Path* subpath = (Path*)lfirst(subpaths);
subplans = lappend(subplans, create_plan_recurse(root, subpath));
}
/*
* XXX ideally, if there's just one child, we'd not bother to generate an
* Append node but just return the single child. At the moment this does
* not work because the varno of the child scan plan won't match the
* parent-rel Vars it'll be asked to emit.
*/
plan = make_append(subplans, tlist);

View File

@ -6950,7 +6950,7 @@ bool plan_cluster_use_sort(Oid tableOid, Oid indexOid)
rte = makeNode(RangeTblEntry);
rte->rtekind = RTE_RELATION;
rte->relid = tableOid;
rte->relkind = RELKIND_RELATION;
rte->relkind = RELKIND_RELATION; /* Don't be too picky. */
rte->inh = false;
rte->inFromCl = true;
query->rtable = list_make1(rte);

View File

@ -51,7 +51,7 @@
#include "utils/syscache.h"
#include "utils/tqual.h"
static void checkRuleResultList(List* targetList, TupleDesc resultDesc, bool isSelect);
static void checkRuleResultList(List* targetList, TupleDesc resultDesc, bool isSelect, bool requireColumnNameMatch);
static bool setRuleCheckAsUser_walker(Node* node, Oid* context);
static void setRuleCheckAsUser_Query(Query* qry, Oid userid);
@ -236,8 +236,11 @@ void DefineQueryRewrite(
/*
* Verify relation is of a type that rules can sensibly be applied to.
* Internal callers can target materialized views, but transformRuleStmt()
* blocks them for users. Don't mention them in the error message.
*/
if (event_relation->rd_rel->relkind != RELKIND_RELATION && event_relation->rd_rel->relkind != RELKIND_VIEW)
if (event_relation->rd_rel->relkind != RELKIND_RELATION && event_relation->rd_rel->relkind != RELKIND_MATVIEW &&
event_relation->rd_rel->relkind != RELKIND_VIEW)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table or view", RelationGetRelationName(event_relation))));
@ -324,7 +327,8 @@ void DefineQueryRewrite(
* ... the targetlist of the SELECT action must exactly match the
* event relation, ...
*/
checkRuleResultList(query->targetList, RelationGetDescr(event_relation), true);
checkRuleResultList(query->targetList, RelationGetDescr(event_relation),
true, event_relation->rd_rel->relkind != RELKIND_MATVIEW);
/*
* ... there must not be another ON SELECT rule already ...
@ -377,7 +381,7 @@ void DefineQueryRewrite(
* business of converting relations to views is just a kluge to allow
* loading ancient pg_dump files.)
*/
if (event_relation->rd_rel->relkind != RELKIND_VIEW) {
if (event_relation->rd_rel->relkind != RELKIND_VIEW && event_relation->rd_rel->relkind != RELKIND_MATVIEW) {
HeapScanDesc scanDesc;
if (RELATION_IS_PARTITIONED(event_relation)) {
ereport(ERROR,
@ -455,7 +459,7 @@ void DefineQueryRewrite(
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("RETURNING lists are not supported in non-INSTEAD rules")));
checkRuleResultList(query->returningList, RelationGetDescr(event_relation), false);
checkRuleResultList(query->returningList, RelationGetDescr(event_relation), false, false);
}
}
@ -611,10 +615,11 @@ void DefineQueryRewrite(
* Verify that targetList produces output compatible with a tupledesc
*
* The targetList might be either a SELECT targetlist, or a RETURNING list;
* isSelect tells which. (This is mostly used for choosing error messages,
* but also we don't enforce column name matching for RETURNING.)
* isSelect tells which. This is used for choosing error messages.
*
* A SELECT targetlist may optionally require that column names match.
*/
static void checkRuleResultList(List* targetList, TupleDesc resultDesc, bool isSelect)
static void checkRuleResultList(List* targetList, TupleDesc resultDesc, bool isSelect, bool requireColumnNameMatch)
{
ListCell* tllist = NULL;
int i;
@ -652,7 +657,7 @@ static void checkRuleResultList(List* targetList, TupleDesc resultDesc, bool isS
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot convert relation containing dropped columns to view")));
if (isSelect && strcmp(tle->resname, attname) != 0)
if (requireColumnNameMatch && strcmp(tle->resname, attname) != 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("SELECT rule's target entry %d has different column name from \"%s\"", i, attname)));

View File

@ -1229,7 +1229,7 @@ static void rewriteTargetListUD(Query* parsetree, RangeTblEntry* target_rte, Rel
}
#endif
if (target_relation->rd_rel->relkind == RELKIND_RELATION) {
if (target_relation->rd_rel->relkind == RELKIND_RELATION || target_relation->rd_rel->relkind == RELKIND_MATVIEW) {
/*
* Emit CTID so that executor can find the row to update or delete.
*/
@ -1705,6 +1705,19 @@ static Query* fireRIRrules(Query* parsetree, List* activeRIRs, bool forUpdatePus
continue;
}
/*
* Always ignore RIR rules for materialized views referenced in
* queries. (This does not prevent refreshing MVs, since they aren't
* referenced in their own query definitions.)
*
* Note: in the future we might want to allow MVs to be conditionally
* expanded as if they were regular views, if they are not scannable.
* In that case this test would need to be postponed till after we've
* opened the rel, so that we could check its state.
*/
if (rte->relkind == RELKIND_MATVIEW)
continue;
/*
* If the table is not referenced in the query, then we ignore it.
* This prevents infinite expansion loop due to new rtable entries

View File

@ -526,6 +526,7 @@ void estimate_rel_size(Relation rel, int32* attr_widths, RelPageType* pages, dou
#endif
/* fall through */
case RELKIND_INDEX:
case RELKIND_MATVIEW:
/* fall through */
case RELKIND_TOASTVALUE:
/*

View File

@ -2140,19 +2140,17 @@ static void do_autovacuum(void)
* Scan pg_class to determine which tables to vacuum.
*
* We do this in two passes: on the first one we collect the list of plain
* relations, and on the second one we collect TOAST tables. The reason
* for doing the second pass is that during it we want to use the main
* relation's pg_class.reloptions entry if the TOAST table does not have
* any, and we cannot obtain it unless we know beforehand what's the main
* table OID.
* relations and materialized views, and on the second one we collect
* TOAST tables. The reason for doing the second pass is that during it we
* want to use the main relation's pg_class.reloptions entry if the TOAST
* table does not have any, and we cannot obtain it unless we know
* beforehand what's the main table OID.
*
* We need to check TOAST tables separately because in cases with short,
* wide tables there might be proportionally much more activity in the
* TOAST table than in its parent.
*/
ScanKeyInit(&key[0], Anum_pg_class_relkind, BTEqualStrategyNumber, F_CHAREQ, CharGetDatum(RELKIND_RELATION));
relScan = heap_beginscan(classRel, SnapshotNow, 1, key);
relScan = heap_beginscan(classRel, SnapshotNow, 0, NULL);
/*
* On the first pass, we collect main tables to vacuum, and also the main
@ -2169,6 +2167,11 @@ static void do_autovacuum(void)
bool enable_analyze = false;
bool enable_vacuum = false;
/* Only autovacuum table and materialized view */
if (classForm->relkind != RELKIND_RELATION && classForm->relkind != RELKIND_MATVIEW) {
continue;
}
/* We cannot safely process other backends' temp tables, so skip 'em. */
if (classForm->relpersistence == RELPERSISTENCE_TEMP ||
classForm->relpersistence == RELPERSISTENCE_GLOBAL_TEMP) {
@ -2808,7 +2811,8 @@ AutoVacOpts* extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc)
int rc = 0;
Assert(((Form_pg_class)GETSTRUCT(tup))->relkind == RELKIND_RELATION ||
((Form_pg_class)GETSTRUCT(tup))->relkind == RELKIND_TOASTVALUE);
((Form_pg_class)GETSTRUCT(tup))->relkind == RELKIND_TOASTVALUE ||
((Form_pg_class)GETSTRUCT(tup))->relkind == RELKIND_MATVIEW);
relopts = extractRelOptions(tup, pg_class_desc, InvalidOid);
if (relopts == NULL)

View File

@ -1824,8 +1824,8 @@ void pgstat_initstats(Relation rel)
char relkind = rel->rd_rel->relkind;
/* We only count stats for things that have storage */
if (!(relkind == RELKIND_RELATION || relkind == RELKIND_INDEX || relkind == RELKIND_TOASTVALUE ||
relkind == RELKIND_SEQUENCE)) {
if (!(relkind == RELKIND_RELATION || relkind == RELKIND_MATVIEW || relkind == RELKIND_INDEX ||
relkind == RELKIND_TOASTVALUE || relkind == RELKIND_SEQUENCE)) {
rel->pgstat_info = NULL;
return;
}
@ -2001,7 +2001,7 @@ static void add_tabstat_xact_level(PgStat_TableStatus* pgstat_info, int nest_lev
/*
* pgstat_count_heap_insert - count a tuple insertion of n tuples
*/
void pgstat_count_heap_insert(Relation rel, int n)
void pgstat_count_heap_insert(Relation rel, PgStat_Counter n)
{
PgStat_TableStatus* pgstat_info = rel->pgstat_info;

View File

@ -33,6 +33,7 @@
#include "access/xact.h"
#include "commands/copy.h"
#include "commands/createas.h"
#include "commands/matview.h"
#include "executor/functions.h"
#include "executor/spi.h"
#include "executor/tstoreReceiver.h"
@ -133,6 +134,8 @@ DestReceiver* CreateDestReceiver(CommandDest dest)
case DestSQLFunction:
return CreateSQLFunctionDestReceiver();
case DestTransientRel:
return CreateTransientRelDestReceiver(InvalidOid);
case DestTupleBroadCast:
case DestTupleLocalBroadCast:
case DestTupleRedistribute:
@ -183,6 +186,7 @@ void EndCommand(const char* commandTag, CommandDest dest)
case DestIntoRel:
case DestCopyOut:
case DestSQLFunction:
case DestTransientRel:
default:
break;
}
@ -259,6 +263,7 @@ void NullCommand(CommandDest dest)
case DestIntoRel:
case DestCopyOut:
case DestSQLFunction:
case DestTransientRel:
default:
break;
}

View File

@ -909,7 +909,9 @@ static List* pg_rewrite_query(Query* query)
PGSTAT_START_TIME_RECORD();
#ifdef PGXC
if (query->commandType == CMD_UTILITY && IsA(query->utilityStmt, CreateTableAsStmt)) {
if (query->commandType == CMD_UTILITY && IsA(query->utilityStmt, CreateTableAsStmt) &&
((CreateTableAsStmt*)query->utilityStmt)->relkind != OBJECT_MATVIEW &&
u_sess->cmd_cxt.isUnderRefreshMatview == false) {
/*
* CREATE TABLE AS SELECT and SELECT INTO are rewritten so that the
* target table is created first. The SELECT query is then transformed
@ -7637,6 +7639,7 @@ int PostgresMain(int argc, char* argv[], const char* dbname, const char* usernam
t_thrd.postgres_cxt.debug_query_string = NULL;
t_thrd.postgres_cxt.g_NoAnalyzeRelNameList = NIL;
u_sess->analyze_cxt.is_under_analyze = false;
u_sess->cmd_cxt.isUnderRefreshMatview = false;
t_thrd.postgres_cxt.mark_explain_analyze = false;
t_thrd.postgres_cxt.mark_explain_only = false;
if (unlikely(t_thrd.log_cxt.msgbuf->data != NULL)) {

View File

@ -46,6 +46,7 @@
#include "commands/discard.h"
#include "commands/explain.h"
#include "commands/extension.h"
#include "commands/matview.h"
#include "commands/lockcmds.h"
#include "commands/portalcmds.h"
#include "commands/prepare.h"
@ -362,6 +363,7 @@ static void check_xact_readonly(Node* parse_tree)
case T_CreateSeqStmt:
case T_CreateStmt:
case T_CreateTableAsStmt:
case T_RefreshMatViewStmt:
case T_CreateTableSpaceStmt:
case T_CreateTrigStmt:
case T_CompositeTypeStmt:
@ -2076,6 +2078,7 @@ void ReindexCommand(ReindexStmt* stmt, bool is_top_level)
ReindexIndex(stmt->relation, (const char*)stmt->name, &stmt->memUsage);
break;
case OBJECT_TABLE:
case OBJECT_MATVIEW:
case OBJECT_TABLE_PARTITION:
ReindexTable(stmt->relation, (const char*)stmt->name, &stmt->memUsage);
break;
@ -2661,8 +2664,8 @@ void standard_ProcessUtility(Node* parse_tree, const char* query_string, ParamLi
#else
AlterTableSpaceOptions((AlterTableSpaceOptionsStmt*)parse_tree);
#endif
break;
break;
case T_CreateExtensionStmt:
CreateExtension((CreateExtensionStmt*)parse_tree);
#ifdef PGXC
@ -2823,6 +2826,7 @@ void standard_ProcessUtility(Node* parse_tree, const char* query_string, ParamLi
}
/* fall through */
case OBJECT_SEQUENCE:
case OBJECT_MATVIEW:
case OBJECT_VIEW:
#ifdef PGXC
{
@ -4786,6 +4790,9 @@ void standard_ProcessUtility(Node* parse_tree, const char* query_string, ParamLi
case T_CreateTableAsStmt:
ExecCreateTableAs((CreateTableAsStmt*)parse_tree, query_string, params, completion_tag);
break;
case T_RefreshMatViewStmt:
ExecRefreshMatView((RefreshMatViewStmt*)parse_tree, query_string, params, completion_tag);
break;
case T_AlterSystemStmt:
PreventTransactionChain(is_top_level, "ALTER SYSTEM SET");
@ -6531,9 +6538,10 @@ bool QueryReturnsTuples(Query* parse_tree)
* We assume it is invoked only on already-parse-analyzed statements
* (else the contained parse_tree isn't a Query yet).
*
* In some cases (currently, only EXPLAIN of CREATE TABLE AS/SELECT INTO),
* potentially Query-containing utility statements can be nested. This
* function will drill down to a non-utility Query, or return NULL if none.
* In some cases (currently, only EXPLAIN of CREATE TABLE AS/SELECT INTO and
* CREATE MATERIALIZED VIEW), potentially Query-containing utility statements
* can be nested. This function will drill down to a non-utility Query, or
* return NULL if none.
*/
Query* UtilityContainsQuery(Node* parse_tree)
{
@ -6681,6 +6689,9 @@ static const char* AlterObjectTypeCommandTag(ObjectType obj_type)
case OBJECT_VIEW:
tag = "ALTER VIEW";
break;
case OBJECT_MATVIEW:
tag = "ALTER MATERIALIZED VIEW";
break;
case OBJECT_DATA_SOURCE:
tag = "ALTER DATA SOURCE";
break;
@ -6905,6 +6916,9 @@ const char* CreateCommandTag(Node* parse_tree)
case OBJECT_VIEW:
tag = "DROP VIEW";
break;
case OBJECT_MATVIEW:
tag = "DROP MATERIALIZED VIEW";
break;
case OBJECT_INDEX:
tag = "DROP INDEX";
break;
@ -7005,7 +7019,10 @@ const char* CreateCommandTag(Node* parse_tree)
break;
case T_RenameStmt:
tag = AlterObjectTypeCommandTag(((RenameStmt*)parse_tree)->renameType);
tag = AlterObjectTypeCommandTag(
((RenameStmt*)parse_tree)->renameType == OBJECT_COLUMN ?
((RenameStmt*)parse_tree)->relationType :
((RenameStmt*)parse_tree)->renameType);
break;
case T_AlterObjectSchemaStmt:
@ -7174,10 +7191,23 @@ const char* CreateCommandTag(Node* parse_tree)
break;
case T_CreateTableAsStmt:
if (((CreateTableAsStmt*)parse_tree)->is_select_into)
tag = "SELECT INTO";
else
tag = "CREATE TABLE AS";
switch (((CreateTableAsStmt*)parse_tree)->relkind)
{
case OBJECT_TABLE:
if (((CreateTableAsStmt*)parse_tree)->is_select_into)
tag = "SELECT INTO";
else
tag = "CREATE TABLE AS";
break;
case OBJECT_MATVIEW:
tag = "CREATE MATERIALIZED VIEW";
break;
default:
tag = "?\?\?";
}
break;
case T_RefreshMatViewStmt:
tag = "REFRESH MATERIALIZED VIEW";
break;
case T_AlterSystemStmt:
@ -8029,6 +8059,7 @@ LogStmtLevel GetCommandLogLevel(Node* parse_tree)
lev = LOGSTMT_DDL;
break;
case T_RefreshMatViewStmt:
case T_AlterSystemStmt:
lev = LOGSTMT_DDL;
break;

View File

@ -465,6 +465,7 @@ void knl_u_commands_init(knl_u_commands_context* cmd_cxt)
{
cmd_cxt->TableSpaceUsageArray = NULL;
cmd_cxt->isUnderCreateForeignTable = false;
cmd_cxt->isUnderRefreshMatview = false;
cmd_cxt->CurrentExtensionObject = InvalidOid;
cmd_cxt->PendingLibraryDeletes = NIL;

View File

@ -48,6 +48,7 @@
#include "catalog/pg_statistic.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/namespace.h"
#include "commands/matview.h"
#include "commands/trigger.h"
#include "executor/execdebug.h"
#include "executor/nodeRecursiveunion.h"
@ -1318,7 +1319,7 @@ static void InitPlan(QueryDesc *queryDesc, int eflags)
* it is a parameterless subplan (not initplan), we suggest that it be
* prepared to handle REWIND efficiently; otherwise there is no need.
*/
sp_eflags = eflags & EXEC_FLAG_EXPLAIN_ONLY;
sp_eflags = eflags & (EXEC_FLAG_EXPLAIN_ONLY | EXEC_FLAG_WITH_NO_DATA);
if (bms_is_member(i, plannedstmt->rewindPlanIDs)) {
sp_eflags |= EXEC_FLAG_REWIND;
}
@ -1463,6 +1464,11 @@ void CheckValidResultRel(Relation resultRel, CmdType operation)
break;
}
break;
case RELKIND_MATVIEW:
if (!MatViewIncrementalMaintenanceIsEnabled())
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot change materialized view \"%s\"", RelationGetRelationName(resultRel))));
break;
case RELKIND_FOREIGN_TABLE:
/* Okay only if the FDW supports it */
fdwroutine = GetFdwRoutineForRelation(resultRel, false);
@ -1536,9 +1542,17 @@ static void CheckValidRowMarkRel(Relation rel, RowMarkType markType)
errmsg("cannot lock rows in TOAST relation \"%s\"", RelationGetRelationName(rel))));
break;
case RELKIND_VIEW:
/* Should not get here */
/* Allow referencing a matview, but not actual locking clauses */
if (markType != ROW_MARK_REFERENCE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot lock rows in materialized view \"%s\"",
RelationGetRelationName(rel))));
break;
case RELKIND_MATVIEW:
/* Should not get here; planner should have used ROW_MARK_COPY */
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot lock rows in view \"%s\"", RelationGetRelationName(rel))));
errmsg("cannot lock rows in materialized view \"%s\"", RelationGetRelationName(rel))));
break;
case RELKIND_FOREIGN_TABLE:
/* Should not get here; planner should have used ROW_MARK_COPY */

View File

@ -1031,7 +1031,7 @@ Relation ExecOpenScanRelation(EState* estate, Index scanrelid)
}
}
/* OK, open the relation and acquire lock as needed */
/* Open the relation and acquire lock as needed */
reloid = getrelid(scanrelid, estate->es_range_table);
rel = heap_open(reloid, lockmode);

View File

@ -1866,7 +1866,7 @@ TupleTableSlot* ExecModifyTable(ModifyTableState* node)
bool isNull = false;
relkind = result_rel_info->ri_RelationDesc->rd_rel->relkind;
if (relkind == RELKIND_RELATION || relkind == RELKIND_SEQUENCE) {
if (relkind == RELKIND_RELATION || relkind == RELKIND_SEQUENCE || relkind == RELKIND_MATVIEW) {
datum = ExecGetJunkAttribute(slot, junk_filter->jf_junkAttNo, &isNull);
/* shouldn't ever get a null result... */
if (isNull) {
@ -2404,7 +2404,7 @@ ModifyTableState* ExecInitModifyTable(ModifyTable* node, EState* estate, int efl
char relkind;
relkind = result_rel_info->ri_RelationDesc->rd_rel->relkind;
if (relkind == RELKIND_RELATION) {
if (relkind == RELKIND_RELATION || relkind == RELKIND_MATVIEW) {
j->jf_junkAttNo = ExecFindJunkAttribute(j, "ctid");
if (!AttributeNumberIsValid(j->jf_junkAttNo)) {
ereport(ERROR,

View File

@ -322,8 +322,7 @@ static AbsTblScanDesc InitBeginScan(SeqScanState* node, Relation current_relatio
/* ----------------------------------------------------------------
* InitScanRelation
*
* This does the initialization for scan relations and
* subplans of scans.
* Set up to access the scan relation.
* ----------------------------------------------------------------
*/
void InitScanRelation(SeqScanState* node, EState* estate)
@ -410,6 +409,7 @@ void InitScanRelation(SeqScanState* node, EState* estate)
node->ss_currentRelation = current_relation;
node->ss_currentScanDesc = current_scan_desc;
/* and report the scan tuple slot's rowtype */
ExecAssignScanType(node, RelationGetDescr(current_relation));
}
static inline void InitSeqNextMtd(SeqScan* node, SeqScanState* scanstate)

View File

@ -2069,9 +2069,19 @@ static int _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI, Snapshot sn
* SELECT INTO.
*/
if (IsA(stmt, CreateTableAsStmt)) {
Assert(strncmp(completionTag, "SELECT ", 7) == 0);
u_sess->SPI_cxt._current->processed = strtoul(completionTag + 7, NULL, 10);
if (((CreateTableAsStmt *)stmt)->is_select_into) {
CreateTableAsStmt *ctastmt = (CreateTableAsStmt *) stmt;
if (strncmp(completionTag, "SELECT ", 7) == 0) {
u_sess->SPI_cxt._current->processed = strtoul(completionTag + 7, NULL, 10);
} else {
/*
* Must be an IF NOT EXISTS that did nothing, or a
* CREATE ... WITH NO DATA.
*/
Assert(ctastmt->into->skipData);
u_sess->SPI_cxt._current->processed = 0;
}
if (ctastmt->is_select_into) {
res = SPI_OK_SELINTO;
} else {
res = SPI_OK_UTILITY;

View File

@ -863,6 +863,7 @@ bytea* extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
case RELKIND_RELATION:
case RELKIND_TOASTVALUE:
case RELKIND_VIEW:
case RELKIND_MATVIEW:
options = heap_reloptions(classForm->relkind, datum, false);
break;
case RELKIND_INDEX:
@ -1531,6 +1532,7 @@ bytea* heap_reloptions(char relkind, Datum reloptions, bool validate)
}
return (bytea*)rdopts;
case RELKIND_RELATION:
case RELKIND_MATVIEW:
return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
case RELKIND_VIEW:
return default_reloptions(reloptions, validate, RELOPT_KIND_VIEW);

View File

@ -3189,7 +3189,8 @@ static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup, CommandId
* If the new tuple is too big for storage or contains already toasted
* out-of-line attributes from some other relation, invoke the toaster.
*/
if (relation->rd_rel->relkind != RELKIND_RELATION) {
if (relation->rd_rel->relkind != RELKIND_RELATION &&
relation->rd_rel->relkind != RELKIND_MATVIEW) {
/* toast table entries should never be recursively toasted */
Assert(!HeapTupleHasExternal(tup));
return tup;
@ -3934,7 +3935,8 @@ l1:
* because we need to look at the contents of the tuple, but it's OK to
* release the content lock on the buffer first.
*/
if (relation->rd_rel->relkind != RELKIND_RELATION) {
if (relation->rd_rel->relkind != RELKIND_RELATION &&
relation->rd_rel->relkind != RELKIND_MATVIEW) {
/* toast table entries should never be recursively toasted */
Assert(!HeapTupleHasExternal(&tp));
} else if (HeapTupleHasExternal(&tp))
@ -4346,7 +4348,8 @@ l2:
* We need to invoke the toaster if there are already any out-of-line
* toasted values present, or if the new tuple is over-threshold.
*/
if (relation->rd_rel->relkind != RELKIND_RELATION) {
if (relation->rd_rel->relkind != RELKIND_RELATION &&
relation->rd_rel->relkind != RELKIND_MATVIEW) {
/* toast table entries should never be recursively toasted */
Assert(!HeapTupleHasExternal(&oldtup));
Assert(!HeapTupleHasExternal(newtup));

View File

@ -342,10 +342,10 @@ void toast_delete(Relation rel, HeapTuple oldtup)
bool toast_isnull[MaxHeapAttributeNumber];
/*
* We should only ever be called for tuples of plain relations ---
* recursing on a toast rel is bad news.
* We should only ever be called for tuples of plain relations or
* materialized views --- recursing on a toast rel is bad news.
*/
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
Assert(rel->rd_rel->relkind == RELKIND_RELATION || rel->rd_rel->relkind == RELKIND_MATVIEW);
/*
* Get the tuple descriptor and break down the tuple into fields.

View File

@ -378,11 +378,11 @@ static void OnConflict_CheckForSerializationFailure(const SERIALIZABLEXACT *read
/* ------------------------------------------------------------------------ */
/*
* Does this relation participate in predicate locking? Temporary and system
* relations are exempt.
* relations are exempt, as are materialized views.
*/
static inline bool PredicateLockingNeededForRelation(Relation relation)
{
return !(relation->rd_id < FirstBootstrapObjectId);
return !(relation->rd_id < FirstBootstrapObjectId || relation->rd_rel->relkind == RELKIND_MATVIEW);
}
/*

View File

@ -149,4 +149,7 @@ extern bool tupledesc_have_pck(TupleConstr* constr);
extern void copyDroppedAttribute(Form_pg_attribute target, Form_pg_attribute source);
/* Accessor for the i'th attribute of tupdesc. */
#define TupleDescAttr(tupdesc, i) (&(tupdesc)->attrs[(i)])
#endif /* TUPDESC_H */

View File

@ -508,6 +508,16 @@ DATA(insert ( 2994 2249 2249 3 s 2988 403 0 ));
DATA(insert ( 2994 2249 2249 4 s 2993 403 0 ));
DATA(insert ( 2994 2249 2249 5 s 2991 403 0 ));
/*
* btree record_image_ops
*/
DATA(insert ( 3194 2249 2249 1 s 3190 403 0 ));
DATA(insert ( 3194 2249 2249 2 s 3192 403 0 ));
DATA(insert ( 3194 2249 2249 3 s 3188 403 0 ));
DATA(insert ( 3194 2249 2249 4 s 3193 403 0 ));
DATA(insert ( 3194 2249 2249 5 s 3191 403 0 ));
/*
* btree uuid_ops
*/

View File

@ -126,6 +126,7 @@ DATA(insert ( 1989 26 26 1 356 ));
DATA(insert ( 1989 26 26 2 3134 ));
DATA(insert ( 1991 30 30 1 404 ));
DATA(insert ( 2994 2249 2249 1 2987 ));
DATA(insert ( 3194 2249 2249 1 3997 ));
DATA(insert ( 1994 25 25 1 360 ));
DATA(insert ( 1994 25 25 2 3255 ));
DATA(insert ( 1996 1083 1083 1 1107 ));

View File

@ -168,6 +168,7 @@ DESCR("");
#define RELKIND_VIEW 'v' /* view */
#define RELKIND_COMPOSITE_TYPE 'c' /* composite type */
#define RELKIND_FOREIGN_TABLE 'f' /* foreign table */
#define RELKIND_MATVIEW 'm' /* materialized view */
#define PARTTYPE_PARTITIONED_RELATION 'p' /* partitioned relation */
#define PARTTYPE_VALUE_PARTITIONED_RELATION 'v' /* value partitioned relation */
#define PARTTYPE_NON_PARTITIONED_RELATION 'n' /* non-partitioned relation */

View File

@ -143,6 +143,7 @@ DATA(insert ( 405 oid_ops PGNSP PGUID 1990 26 t 0 ));
DATA(insert ( 403 oidvector_ops PGNSP PGUID 1991 30 t 0 ));
DATA(insert ( 405 oidvector_ops PGNSP PGUID 1992 30 t 0 ));
DATA(insert ( 403 record_ops PGNSP PGUID 2994 2249 t 0 ));
DATA(insert ( 403 record_image_ops PGNSP PGUID 3194 2249 f 0 ));
DATA(insert OID = 3126 ( 403 text_ops PGNSP PGUID 1994 25 t 0 ));
#define TEXT_BTREE_OPS_OID 3126
DATA(insert ( 405 text_ops PGNSP PGUID 1995 25 t 0 ));

View File

@ -1861,6 +1861,20 @@ DESCR("less than or equal");
DATA(insert OID = 2993 (">=" PGNSP PGUID b f f 2249 2249 16 2992 2990 record_ge scalargtsel scalargtjoinsel));
DESCR("greater than or equal");
/* byte-oriented tests for identical rows and fast sorting */
DATA(insert OID = 3188 ( "*=" PGNSP PGUID b t f 2249 2249 16 3188 3189 record_image_eq eqsel eqjoinsel));
DESCR("identical");
DATA(insert OID = 3189 ( "*<>" PGNSP PGUID b f f 2249 2249 16 3189 3188 record_image_ne neqsel neqjoinsel));
DESCR("not identical");
DATA(insert OID = 3190 ( "*<" PGNSP PGUID b f f 2249 2249 16 3191 3193 record_image_lt scalarltsel scalarltjoinsel));
DESCR("less than");
DATA(insert OID = 3191 ( "*>" PGNSP PGUID b f f 2249 2249 16 3190 3192 record_image_gt scalargtsel scalargtjoinsel));
DESCR("greater than");
DATA(insert OID = 3192 ( "*<=" PGNSP PGUID b f f 2249 2249 16 3193 3191 record_image_le scalarltsel scalarltjoinsel));
DESCR("less than or equal");
DATA(insert OID = 3193 ( "*>=" PGNSP PGUID b f f 2249 2249 16 3192 3190 record_image_ge scalargtsel scalargtjoinsel));
DESCR("greater than or equal");
/* generic range type operators */
DATA(insert OID = 3882 ("=" PGNSP PGUID b t t 3831 3831 16 3882 3883 range_eq eqsel eqjoinsel));
DESCR("equal");

View File

@ -96,6 +96,7 @@ DATA(insert OID = 1990 (405 oid_ops PGNSP PGUID));
DATA(insert OID = 1991 (403 oidvector_ops PGNSP PGUID));
DATA(insert OID = 1992 (405 oidvector_ops PGNSP PGUID));
DATA(insert OID = 2994 (403 record_ops PGNSP PGUID));
DATA(insert OID = 3194 (403 record_image_ops PGNSP PGUID));
DATA(insert OID = 1994 (403 text_ops PGNSP PGUID));
#define TEXT_BTREE_FAM_OID 1994
DATA(insert OID = 1995 (405 text_ops PGNSP PGUID));

View File

@ -26,6 +26,7 @@ extern bool createToastTableForPartition(Oid relOid,
Datum reloptions,
List *relfilenode);
extern void NewRelationCreateToastTable(Oid relOid, Datum reloptions);
/*
* This macro is just to keep the C compiler from spitting up on the
* upcoming commands for genbki.pl.

View File

@ -0,0 +1,30 @@
/* -------------------------------------------------------------------------
*
* matview.h
* prototypes for matview.c.
*
*
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/include/commands/matview.h
*
* -------------------------------------------------------------------------
*/
#ifndef MATVIEW_H
#define MATVIEW_H
#include "nodes/params.h"
#include "nodes/parsenodes.h"
#include "tcop/dest.h"
#include "utils/relcache.h"
extern void SetMatViewPopulatedState(const Relation relation, bool newstate);
extern void ExecRefreshMatView(const RefreshMatViewStmt* stmt, const char* queryString, ParamListInfo params,
char* completionTag);
extern DestReceiver *CreateTransientRelDestReceiver(Oid oid);
extern bool MatViewIncrementalMaintenanceIsEnabled(void);
#endif /* MATVIEW_H */

View File

@ -66,7 +66,7 @@ int64 SearchAllAccounts();
void InitAccountLockHashTable();
extern USER_STATUS GetAccountLockedStatusFromHashTable(Oid roleid);
extern void UpdateAccountInfoFromHashTable();
extern List* roleNamesToIds(const List* memberNames);
extern inline void str_reset(char* str)
{

View File

@ -18,5 +18,6 @@
extern void DefineView(ViewStmt* stmt, const char* queryString, bool isFirstNode = true);
extern bool IsViewTemp(ViewStmt* stmt, const char* queryString);
extern void StoreViewQuery(Oid viewOid, Query* viewParse, bool replace);
#endif /* VIEW_H */

View File

@ -67,6 +67,7 @@
#define EXEC_FLAG_SKIP_TRIGGERS 0x0010 /* skip AfterTrigger calls */
#define EXEC_FLAG_WITH_OIDS 0x0020 /* force OIDs in returned tuples */
#define EXEC_FLAG_WITHOUT_OIDS 0x0040 /* force no OIDs in returned tuples */
#define EXEC_FLAG_WITH_NO_DATA 0x0080 /* rel scannability doesn't matter */
extern inline bool is_errmodule_enable(int elevel, ModuleId mod_id);

View File

@ -539,6 +539,7 @@ typedef struct knl_u_commands_context {
/* Tablespace usage information management struct */
struct TableSpaceUsageStruct* TableSpaceUsageArray;
bool isUnderCreateForeignTable;
bool isUnderRefreshMatview;
Oid CurrentExtensionObject;

View File

@ -71,6 +71,7 @@ extern Node* makeTidConst(ItemPointer item);
extern FuncCall* makeFuncCall(List* funcname, List* args, int location);
extern Param* makeParam(
ParamKind paramkind, int paramid, Oid paramtype, int32 paramtypmod, Oid paramcollid, int location);
extern ColumnDef* makeColumnDef(const char* colname, Oid typeOid, int32 typmod, Oid collOid);
extern IndexInfo* makeIndexInfo(int numattrs, List *expressions, List *predicates,
bool unique, bool isready, bool concurrent);
#endif /* MAKEFUNC_H */

View File

@ -452,6 +452,7 @@ typedef enum NodeTag {
T_DropDirectoryStmt,
T_CreateRlsPolicyStmt,
T_AlterRlsPolicyStmt,
T_RefreshMatViewStmt,
T_AlterSystemStmt,
/*

View File

@ -1406,6 +1406,7 @@ typedef enum ObjectType {
OBJECT_INTERNAL_PARTITION,
OBJECT_LANGUAGE,
OBJECT_LARGEOBJECT,
OBJECT_MATVIEW,
OBJECT_OPCLASS,
OBJECT_OPERATOR,
OBJECT_OPFAMILY,
@ -3291,6 +3292,9 @@ typedef struct ExplainStmt {
* A query written as SELECT ... INTO will be transformed to this form during
* parse analysis.
*
* A query written as CREATE MATERIALIZED view will produce this node type,
* during parse analysis, since it needs all the same data.
*
* The "query" field is handled similarly to EXPLAIN, though note that it
* can be a SELECT or an EXECUTE, but not other DML statements.
* ----------------------
@ -3299,6 +3303,7 @@ typedef struct CreateTableAsStmt {
NodeTag type;
Node* query; /* the query (see comments above) */
IntoClause* into; /* destination table */
ObjectType relkind; /* OBJECT_TABLE or OBJECT_MATVIEW */
bool is_select_into; /* it was written as SELECT INTO */
#ifdef PGXC
void* parserSetup;
@ -3306,6 +3311,17 @@ typedef struct CreateTableAsStmt {
#endif
} CreateTableAsStmt;
/* ----------------------
* REFRESH MATERIALIZED VIEW Statement
* ----------------------
*/
typedef struct RefreshMatViewStmt
{
NodeTag type;
bool skipData; /* true for WITH NO DATA */
RangeVar *relation; /* relation to insert into */
} RefreshMatViewStmt;
/* ----------------------
* Checkpoint Statement
* ----------------------
@ -3352,7 +3368,7 @@ typedef struct ConstraintsSetStmt {
*/
typedef struct ReindexStmt {
NodeTag type;
ObjectType kind; /* OBJECT_INDEX, OBJECT_TABLE, OBJECT_INTERNAL, OBJECT_DATABASE */
ObjectType kind; /* OBJECT_INDEX, OBJECT_TABLE, OBJECT_INTERNAL, etc */
RangeVar* relation; /* Table or index to reindex */
const char* name; /* name of database to reindex */
bool do_system; /* include system tables in database case */

View File

@ -93,7 +93,11 @@ typedef struct RangeVar {
} RangeVar;
/*
* IntoClause - target information for SELECT INTO and CREATE TABLE AS
* IntoClause - target information for SELECT INTO, CREATE MATERIALIZED VIEW and CREATE TABLE AS
*
* For CREATE MATERIALIZED VIEW, viewQuery is the parsed-but-not-rewritten
* SELECT Query for the view; otherwise it's NULL. (Although it's actually
* Query*, we declare it as Node* to avoid a forward reference.)
*/
typedef struct IntoClause {
NodeTag type;
@ -104,6 +108,7 @@ typedef struct IntoClause {
OnCommitAction onCommit; /* what do we do at COMMIT? */
int8 row_compress; /* row compression flag */
char* tableSpaceName; /* table space to use, or NULL */
Node* viewQuery; /* materialized view's SELECT query */
bool skipData; /* true for WITH NO DATA */
#ifdef PGXC
struct DistributeBy* distributeby; /* distribution to use, or NULL */

Some files were not shown because too many files have changed in this diff Show More