forked from huawei/openGauss-server
add materialized view feature to opengauss
This commit is contained in:
parent
3eade03f9c
commit
d5337ceca7
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 " : "");
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 "
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ");");
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
|
||||
/*
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
/*
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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 ));
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 ));
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 */
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -539,6 +539,7 @@ typedef struct knl_u_commands_context {
|
|||
/* Tablespace usage information management struct */
|
||||
struct TableSpaceUsageStruct* TableSpaceUsageArray;
|
||||
bool isUnderCreateForeignTable;
|
||||
bool isUnderRefreshMatview;
|
||||
|
||||
Oid CurrentExtensionObject;
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -452,6 +452,7 @@ typedef enum NodeTag {
|
|||
T_DropDirectoryStmt,
|
||||
T_CreateRlsPolicyStmt,
|
||||
T_AlterRlsPolicyStmt,
|
||||
T_RefreshMatViewStmt,
|
||||
T_AlterSystemStmt,
|
||||
|
||||
/*
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue