diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml index 4a2b6f0dae..88c635f82f 100644 --- a/doc/src/sgml/ref/create_table.sgml +++ b/doc/src/sgml/ref/create_table.sgml @@ -1310,8 +1310,6 @@ WITH ( MODULUS numeric_literal, REM If a table parameter value is set and the equivalent toast. parameter is not, the TOAST table will use the table's parameter value. - Specifying these parameters for partitioned tables is not supported, - but you may specify them for individual leaf partitions. diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c index 79430d2b7b..20183a96a4 100644 --- a/src/backend/access/common/reloptions.c +++ b/src/backend/access/common/reloptions.c @@ -108,7 +108,7 @@ static relopt_bool boolRelOpts[] = { "autovacuum_enabled", "Enables autovacuum in this relation", - RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, + RELOPT_KIND_HEAP | RELOPT_KIND_TOAST | RELOPT_KIND_PARTITIONED, ShareUpdateExclusiveLock }, true @@ -227,7 +227,7 @@ static relopt_int intRelOpts[] = { "autovacuum_analyze_threshold", "Minimum number of tuple inserts, updates or deletes prior to analyze", - RELOPT_KIND_HEAP, + RELOPT_KIND_HEAP | RELOPT_KIND_PARTITIONED, ShareUpdateExclusiveLock }, -1, 0, INT_MAX @@ -379,7 +379,7 @@ static relopt_real realRelOpts[] = { "autovacuum_analyze_scale_factor", "Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples", - RELOPT_KIND_HEAP, + RELOPT_KIND_HEAP | RELOPT_KIND_PARTITIONED, ShareUpdateExclusiveLock }, -1, 0.0, 100.0 @@ -1586,13 +1586,12 @@ build_reloptions(Datum reloptions, bool validate, bytea * partitioned_table_reloptions(Datum reloptions, bool validate) { + /* - * There are no options for partitioned tables yet, but this is able to do - * some validation. + * autovacuum_enabled, autovacuum_analyze_threshold and + * autovacuum_analyze_scale_factor are supported for partitioned tables. */ - return (bytea *) build_reloptions(reloptions, validate, - RELOPT_KIND_PARTITIONED, - 0, NULL, 0); + return default_reloptions(reloptions, validate, RELOPT_KIND_PARTITIONED); } /* diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index f681aafcf9..161abb6450 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -584,7 +584,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', 'm') + WHERE C.relkind IN ('r', 't', 'm', 'p') GROUP BY C.oid, N.nspname, C.relname; CREATE VIEW pg_stat_xact_all_tables AS @@ -604,7 +604,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', 'm') + WHERE C.relkind IN ('r', 't', 'm', 'p') GROUP BY C.oid, N.nspname, C.relname; CREATE VIEW pg_stat_sys_tables AS diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index c4420ddd7f..df3d93ea5d 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -655,15 +655,14 @@ do_analyze_rel(Relation onerel, VacuumParams *params, } /* - * Report ANALYZE to the stats collector, too. However, if doing - * inherited stats we shouldn't report, because the stats collector only - * tracks per-table stats. Reset the changes_since_analyze counter only - * if we analyzed all columns; otherwise, there is still work for - * auto-analyze to do. + * Report ANALYZE to the stats collector, too. If the table is a + * partition, report changes_since_analyze of its parent because + * autovacuum process for partitioned tables needs it. Reset the + * changes_since_analyze counter only if we analyzed all columns; + * otherwise, there is still work for auto-analyze to do. */ - if (!inh) - pgstat_report_analyze(onerel, totalrows, totaldeadrows, - (va_cols == NIL)); + pgstat_report_analyze(onerel, totalrows, totaldeadrows, + (va_cols == NIL)); /* If this isn't part of VACUUM ANALYZE, let index AMs do cleanup */ if (!(params->options & VACOPT_VACUUM)) diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index 6d1f28c327..7d0a5ce30d 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -75,6 +75,7 @@ #include "catalog/dependency.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" +#include "catalog/pg_inherits.h" #include "commands/dbcommands.h" #include "commands/vacuum.h" #include "lib/ilist.h" @@ -2031,11 +2032,11 @@ 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 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. + * relations, materialized views and partitioned tables, 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 @@ -2058,7 +2059,8 @@ do_autovacuum(void) bool wraparound; if (classForm->relkind != RELKIND_RELATION && - classForm->relkind != RELKIND_MATVIEW) + classForm->relkind != RELKIND_MATVIEW && + classForm->relkind != RELKIND_PARTITIONED_TABLE) continue; relid = classForm->oid; @@ -2087,19 +2089,103 @@ do_autovacuum(void) continue; } - /* Fetch reloptions and the pgstat entry for this table */ - relopts = extract_autovac_opts(tuple, pg_class_desc); - tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared, - shared, dbentry); + if (classForm->relkind == RELKIND_RELATION || + classForm->relkind == RELKIND_MATVIEW) + { + /* Fetch reloptions and the pgstat entry for this table */ + relopts = extract_autovac_opts(tuple, pg_class_desc); + tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared, + shared, dbentry); + + /* Check if it needs vacuum or analyze */ + relation_needs_vacanalyze(relid, relopts, classForm, tabentry, + effective_multixact_freeze_max_age, + &dovacuum, &doanalyze, &wraparound); + + /* Relations that need work are added to table_oids */ + if (dovacuum || doanalyze) + table_oids = lappend_oid(table_oids, relid); + } + else + { + /* + * If the relation is a partitioned table, we check it using reltuples + * added up childrens' and changes_since_analyze tracked by stats collector. + * We check only auto analyze because partitioned tables don't need to vacuum. + */ + List *tableOIDs; + ListCell *lc; + bool av_enabled; + int anl_base_thresh; + float4 all_reltuples = 0, + anl_scale_factor, + anlthresh, + reltuples, + anltuples; + + /* Find all members of inheritance set taking AccessShareLock */ + tableOIDs = find_all_inheritors(relid, AccessShareLock, NULL); + + foreach(lc, tableOIDs) + { + Oid childOID = lfirst_oid(lc); + HeapTuple childtuple; + Form_pg_class childclassForm; - /* Check if it needs vacuum or analyze */ - relation_needs_vacanalyze(relid, relopts, classForm, tabentry, - effective_multixact_freeze_max_age, - &dovacuum, &doanalyze, &wraparound); + /* Ignore the parent table */ + if (childOID == relid) + continue; - /* Relations that need work are added to table_oids */ - if (dovacuum || doanalyze) - table_oids = lappend_oid(table_oids, relid); + childtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(childOID)); + childclassForm = (Form_pg_class) GETSTRUCT(childtuple); + + /* Skip foreign partitions */ + if (childclassForm->relkind == RELKIND_FOREIGN_TABLE) + continue; + + /* Sum up the child's reltuples for its parent table */ + all_reltuples += childclassForm->reltuples; + elog(NOTICE, "[parent:%s] child%s has %.0f tuples", NameStr(classForm->relname),NameStr(childclassForm->relname), childclassForm->reltuples); + } + + + /* Fetch reloptions and the pgstat entry for the partitioned table */ + relopts = extract_autovac_opts(tuple, pg_class_desc); + tabentry = get_pgstat_tabentry_relid(relid, + classForm->relisshared, + shared, dbentry); + + /* Check if it needs auto analyze */ + av_enabled = (relopts ? relopts->enabled : true); + + if (av_enabled) + { + anl_scale_factor = (relopts && relopts->analyze_scale_factor >= 0) + ? relopts->analyze_scale_factor + : autovacuum_anl_scale; + + anl_base_thresh = (relopts && relopts->analyze_threshold >= 0) + ? relopts->analyze_threshold + : autovacuum_anl_thresh; + + elog(NOTICE, "[parent:%s] has %.0f tuples", NameStr(classForm->relname), all_reltuples); + if (PointerIsValid(tabentry)) + { + reltuples = all_reltuples; + anltuples = tabentry->changes_since_analyze; + anlthresh = (float4) anl_base_thresh + anl_scale_factor * reltuples; + + elog(DEBUG3, "%s: anl: %.0f (threshold %.0f)", + NameStr(classForm->relname), + anltuples, anlthresh); + + /* Determine if this table needs analyze. */ + doanalyze = (anltuples > anlthresh); + } + if (doanalyze) + table_oids = lappend_oid(table_oids, relid); + } + } /* * Remember TOAST associations for the second pass. Note: we must do @@ -2720,6 +2806,7 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc) Assert(((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_RELATION || ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_MATVIEW || + ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_PARTITIONED_TABLE || ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_TOASTVALUE); relopts = extractRelOptions(tup, pg_class_desc, NULL); @@ -2793,33 +2880,105 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, return NULL; classForm = (Form_pg_class) GETSTRUCT(classTup); - /* - * Get the applicable reloptions. If it is a TOAST table, try to get the - * main table reloptions if the toast table itself doesn't have. - */ - avopts = extract_autovac_opts(classTup, pg_class_desc); - if (classForm->relkind == RELKIND_TOASTVALUE && - avopts == NULL && table_toast_map != NULL) + if (classForm->relkind != RELKIND_PARTITIONED_TABLE) { - av_relation *hentry; - bool found; + /* + * Get the applicable reloptions. If it is a TOAST table, try to get the + * main table reloptions if the toast table itself doesn't have. + */ + avopts = extract_autovac_opts(classTup, pg_class_desc); + if (classForm->relkind == RELKIND_TOASTVALUE && + avopts == NULL && table_toast_map != NULL) + { + av_relation *hentry; + bool found; - hentry = hash_search(table_toast_map, &relid, HASH_FIND, &found); - if (found && hentry->ar_hasrelopts) - avopts = &hentry->ar_reloptions; + hentry = hash_search(table_toast_map, &relid, HASH_FIND, &found); + if (found && hentry->ar_hasrelopts) + avopts = &hentry->ar_reloptions; + } + + /* fetch the pgstat table entry */ + tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared, + shared, dbentry); + + relation_needs_vacanalyze(relid, avopts, classForm, tabentry, + effective_multixact_freeze_max_age, + &dovacuum, &doanalyze, &wraparound); + + /* ignore ANALYZE for toast tables */ + if (classForm->relkind == RELKIND_TOASTVALUE) + doanalyze = false; } + else + { + List *tableOIDs; + ListCell *lc; + bool av_enabled; + int anl_base_thresh; + float4 all_reltuples, + anl_scale_factor, + anlthresh, + reltuples, + anltuples; + + /* Find all members of inheritance set taking AccessShareLock */ + tableOIDs = find_all_inheritors(relid, AccessShareLock, NULL); + + foreach(lc, tableOIDs) + { + Oid childOID = lfirst_oid(lc); + HeapTuple childtuple; + Form_pg_class childclassForm; - /* fetch the pgstat table entry */ - tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared, - shared, dbentry); + /* Ignore the parent table */ + if (childOID == relid) + continue; + + childtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(childOID)); + childclassForm = (Form_pg_class) GETSTRUCT(childtuple); - relation_needs_vacanalyze(relid, avopts, classForm, tabentry, - effective_multixact_freeze_max_age, - &dovacuum, &doanalyze, &wraparound); + /* Skip foreign partitions */ + if (childclassForm->relkind == RELKIND_FOREIGN_TABLE) + continue; + + /* Sum up the child's reltuples for the partitioned table */ + all_reltuples += childclassForm->reltuples; + } - /* ignore ANALYZE for toast tables */ - if (classForm->relkind == RELKIND_TOASTVALUE) - doanalyze = false; + /* Fetch reloptions and the pgstat entry for the partitioned table */ + avopts = extract_autovac_opts(classTup, pg_class_desc); + tabentry = get_pgstat_tabentry_relid(relid, + classForm->relisshared, + shared, dbentry); + + /* Check if it needs auto analyze */ + av_enabled = (avopts ? avopts->enabled : true); + + if (av_enabled) + { + anl_scale_factor = (avopts && avopts->analyze_scale_factor >= 0) + ? avopts->analyze_scale_factor + : autovacuum_anl_scale; + + anl_base_thresh = (avopts && avopts->analyze_threshold >= 0) + ? avopts->analyze_threshold + : autovacuum_anl_thresh; + + if (PointerIsValid(tabentry)) + { + reltuples = all_reltuples; + anltuples = tabentry->changes_since_analyze; + anlthresh = (float4) anl_base_thresh + anl_scale_factor * reltuples; + + elog(DEBUG3, "%s: anl: %.0f (threshold %.0f)", + NameStr(classForm->relname), + anltuples, anlthresh); + + doanalyze = (anltuples > anlthresh); + } + } + } /* OK, it needs something done */ if (doanalyze || dovacuum) diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index 59dc4f31ab..1933da145a 100644 --- a/src/backend/postmaster/pgstat.c +++ b/src/backend/postmaster/pgstat.c @@ -38,6 +38,7 @@ #include "access/transam.h" #include "access/twophase_rmgr.h" #include "access/xact.h" +#include "catalog/partition.h" #include "catalog/pg_database.h" #include "catalog/pg_proc.h" #include "common/ip.h" @@ -67,6 +68,7 @@ #include "utils/ps_status.h" #include "utils/rel.h" #include "utils/snapmgr.h" +#include "utils/syscache.h" #include "utils/timestamp.h" /* ---------- @@ -322,6 +324,7 @@ static void pgstat_recv_resetsinglecounter(PgStat_MsgResetsinglecounter *msg, in static void pgstat_recv_autovac(PgStat_MsgAutovacStart *msg, int len); static void pgstat_recv_vacuum(PgStat_MsgVacuum *msg, int len); static void pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len); +static void pgstat_recv_partanalyze(PgStat_MsgPartAnalyze *msg, int len); static void pgstat_recv_archiver(PgStat_MsgArchiver *msg, int len); static void pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len); static void pgstat_recv_funcstat(PgStat_MsgFuncstat *msg, int len); @@ -1463,6 +1466,32 @@ pgstat_report_analyze(Relation rel, deadtuples = Max(deadtuples, 0); } + /* + * If the table is a leaf partition, tell the stats collector its parent's + * changes_since_analyze for auto analyze + */ + if (rel->rd_rel->relispartition && + !(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)) + { + Oid parentoid; + Relation parentrel; + PgStat_StatDBEntry *dbentry; + PgStat_StatTabEntry *tabentry; + + /* Get its parent table's Oid and relation */ + parentoid = get_partition_parent(RelationGetRelid(rel)); + parentrel = table_open(parentoid, AccessShareLock); + + /* Fetch the pgstat for this table */ + dbentry = pgstat_fetch_stat_dbentry(MyDatabaseId); + tabentry = pgstat_get_tab_entry(dbentry, RelationGetRelid(rel), true); + + /* Report changes_since_analyze to the stats collector */ + pgstat_report_partanalyze(parentrel, tabentry->changes_since_analyze); + + table_close(parentrel, AccessShareLock); + } + pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_ANALYZE); msg.m_databaseid = rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId; msg.m_tableoid = RelationGetRelid(rel); @@ -1475,6 +1504,49 @@ pgstat_report_analyze(Relation rel, } /* -------- + * pgstat_report_partanalyze() - + * + * Tell the collector about the parent table of which partition just analyzed. + * + * Caller must provide a child's changes_since_analyze as a parents. + * -------- + */ +void +pgstat_report_partanalyze(Relation rel, PgStat_Counter changes_tuples) +{ + PgStat_MsgPartAnalyze msg; + + if (pgStatSock == PGINVALID_SOCKET || !pgstat_track_counts) + return; + + /* + * If the partitioned table is also a partition, tell the stats collector + * its parent's changes_since_analyze for auto analyze + */ + if (rel->rd_rel->relispartition) + { + Oid parentoid; + Relation parentrel; + + /* Get its parent table's Oid and relation */ + parentoid = get_partition_parent(RelationGetRelid(rel)); + parentrel = table_open(parentoid, AccessShareLock); + + /* Report changes_since_analyze to the stats collector */ + pgstat_report_partanalyze(parentrel, changes_tuples); + + table_close(parentrel, AccessShareLock); + } + + pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_PARTANALYZE); + msg.m_databaseid = rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId; + msg.m_tableoid = RelationGetRelid(rel); + msg.m_changes_tuples = changes_tuples; + pgstat_send(&msg, sizeof(msg)); +} + + +/* -------- * pgstat_report_recovery_conflict() - * * Tell the collector about a Hot Standby recovery conflict. @@ -1749,6 +1821,7 @@ pgstat_initstats(Relation rel) /* We only count stats for things that have storage */ if (!(relkind == RELKIND_RELATION || relkind == RELKIND_MATVIEW || + relkind == RELKIND_PARTITIONED_TABLE || relkind == RELKIND_INDEX || relkind == RELKIND_TOASTVALUE || relkind == RELKIND_SEQUENCE)) @@ -4592,6 +4665,10 @@ PgstatCollectorMain(int argc, char *argv[]) pgstat_recv_analyze(&msg.msg_analyze, len); break; + case PGSTAT_MTYPE_PARTANALYZE: + pgstat_recv_partanalyze(&msg.msg_partanalyze, len); + break; + case PGSTAT_MTYPE_ARCHIVER: pgstat_recv_archiver(&msg.msg_archiver, len); break; @@ -6239,6 +6316,18 @@ pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len) } } +static void +pgstat_recv_partanalyze(PgStat_MsgPartAnalyze *msg, int len) +{ + PgStat_StatDBEntry *dbentry; + PgStat_StatTabEntry *tabentry; + + dbentry = pgstat_get_db_entry(msg->m_databaseid, true); + + tabentry = pgstat_get_tab_entry(dbentry, msg->m_tableoid, true); + + tabentry->changes_since_analyze += msg->m_changes_tuples; +} /* ---------- * pgstat_recv_archiver() - diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 3a65a51696..590885d7e8 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -57,6 +57,7 @@ typedef enum StatMsgType PGSTAT_MTYPE_AUTOVAC_START, PGSTAT_MTYPE_VACUUM, PGSTAT_MTYPE_ANALYZE, + PGSTAT_MTYPE_PARTANALYZE, PGSTAT_MTYPE_ARCHIVER, PGSTAT_MTYPE_BGWRITER, PGSTAT_MTYPE_FUNCSTAT, @@ -389,6 +390,18 @@ typedef struct PgStat_MsgAnalyze PgStat_Counter m_dead_tuples; } PgStat_MsgAnalyze; +/* ---------- + * PgStat_MsgPartAnalyze Sent by the backend or autovacuum daemon + * after ANALYZE for partitioned tables + * ---------- + */ +typedef struct PgStat_MsgPartAnalyze +{ + PgStat_MsgHdr m_hdr; + Oid m_databaseid; + Oid m_tableoid; + PgStat_Counter m_changes_tuples; +} PgStat_MsgPartAnalyze; /* ---------- * PgStat_MsgArchiver Sent by the archiver to update statistics. @@ -562,6 +575,7 @@ typedef union PgStat_Msg PgStat_MsgAutovacStart msg_autovacuum_start; PgStat_MsgVacuum msg_vacuum; PgStat_MsgAnalyze msg_analyze; + PgStat_MsgPartAnalyze msg_partanalyze; PgStat_MsgArchiver msg_archiver; PgStat_MsgBgWriter msg_bgwriter; PgStat_MsgFuncstat msg_funcstat; @@ -1267,7 +1281,7 @@ extern void pgstat_report_vacuum(Oid tableoid, bool shared, extern void pgstat_report_analyze(Relation rel, PgStat_Counter livetuples, PgStat_Counter deadtuples, bool resetcounter); - +extern void pgstat_report_partanalyze(Relation rel, PgStat_Counter changes_tuples); extern void pgstat_report_recovery_conflict(int reason); extern void pgstat_report_deadlock(void); extern void pgstat_report_checksum_failures_in_db(Oid dboid, int failurecount); diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 634f8256f7..7e9f6de9cb 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1789,7 +1789,7 @@ pg_stat_all_tables| SELECT c.oid AS relid, 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 = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])) + WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; pg_stat_archiver| SELECT s.archived_count, s.last_archived_wal, @@ -2117,7 +2117,7 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, 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 = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])) + WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid, pg_stat_xact_all_tables.schemaname,