This is an automated email from the ASF dual-hosted git repository. tuhaihe pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/cloudberry.git
commit cdf09de0e92741ef8ac1a23c0e9d1e395179e3df Author: Vladislav Shchetinin <[email protected]> AuthorDate: Mon Jun 22 11:25:51 2026 +0300 Feat: Adapt gp_relsizes_stats for Cloudberry --- .github/workflows/build-cloudberry-rocky8.yml | 19 ++ .github/workflows/build-cloudberry.yml | 19 ++ gpcontrib/Makefile | 2 + gpcontrib/gp_relsizes_stats/LICENCE | 201 ------------- gpcontrib/gp_relsizes_stats/Makefile | 62 +++- gpcontrib/gp_relsizes_stats/README.md | 51 +++- .../gp_relsizes_stats/gp_relsizes_stats.control | 4 +- .../sql/gp_relsizes_stats--1.0--1.1.sql | 12 - .../sql/gp_relsizes_stats--1.0.sql | 158 ++++++++++ .../sql/gp_relsizes_stats--1.1--1.2.sql | 56 ---- .../sql/gp_relsizes_stats--1.2--1.3.sql | 9 - .../sql/gp_relsizes_stats--1.3.sql | 96 ------ .../gp_relsizes_stats/src/gp_relsizes_stats.c | 322 ++++++++++++++------- .../test/expected/gp_relsizes_stats.out | 96 +++++- .../gp_relsizes_stats/test/expected/grants.out | 5 +- .../gp_relsizes_stats/test/postgresql.conf.add | 1 + .../test/sql/gp_relsizes_stats.sql | 32 ++ gpcontrib/gp_relsizes_stats/test/sql/grants.sql | 5 +- pom.xml | 5 + 19 files changed, 630 insertions(+), 525 deletions(-) diff --git a/.github/workflows/build-cloudberry-rocky8.yml b/.github/workflows/build-cloudberry-rocky8.yml index 4986eae11b2..8ad7bbe6f57 100644 --- a/.github/workflows/build-cloudberry-rocky8.yml +++ b/.github/workflows/build-cloudberry-rocky8.yml @@ -324,6 +324,11 @@ jobs: "make_configs":["gpcontrib/gp_stats_collector:installcheck"], "extension":"gp_stats_collector" }, + {"test":"gpcontrib-gp-relsizes-stats", + "make_configs":["gpcontrib/gp_relsizes_stats:installcheck"], + "extension":"gp_relsizes_stats", + "shared_preload_libraries":"gp_relsizes_stats" + }, {"test":"ic-fixme", "make_configs":["src/test/regress:installcheck-fixme"], "enable_core_check":false @@ -1445,6 +1450,20 @@ jobs: exit 1 fi ;; + gp_relsizes_stats) + if ! su - gpadmin -c "source ${BUILD_DESTINATION}/cloudberry-env.sh && \ + source ${SRC_DIR}/gpAux/gpdemo/gpdemo-env.sh && \ + gpconfig -c shared_preload_libraries -v 'gp_relsizes_stats' && \ + gpstop -ra && \ + echo 'CREATE EXTENSION IF NOT EXISTS gp_relsizes_stats; \ + SHOW shared_preload_libraries; \ + TABLE pg_extension;' | \ + psql postgres" + then + echo "Error creating gp_relsizes_stats extension" + exit 1 + fi + ;; *) echo "Unknown extension: ${{ matrix.extension }}" exit 1 diff --git a/.github/workflows/build-cloudberry.yml b/.github/workflows/build-cloudberry.yml index 0d76fa0da51..242a89a746b 100644 --- a/.github/workflows/build-cloudberry.yml +++ b/.github/workflows/build-cloudberry.yml @@ -275,6 +275,11 @@ jobs: "make_configs":["gpcontrib/gp_stats_collector:installcheck"], "extension":"gp_stats_collector" }, + {"test":"gpcontrib-gp-relsizes-stats", + "make_configs":["gpcontrib/gp_relsizes_stats:installcheck"], + "extension":"gp_relsizes_stats", + "shared_preload_libraries":"gp_relsizes_stats" + }, {"test":"ic-expandshrink", "make_configs":["src/test/isolation2:installcheck-expandshrink"] }, @@ -1458,6 +1463,20 @@ jobs: exit 1 fi ;; + gp_relsizes_stats) + if ! su - gpadmin -c "source ${BUILD_DESTINATION}/cloudberry-env.sh && \ + source ${SRC_DIR}/gpAux/gpdemo/gpdemo-env.sh && \ + gpconfig -c shared_preload_libraries -v 'gp_relsizes_stats' && \ + gpstop -ra && \ + echo 'CREATE EXTENSION IF NOT EXISTS gp_relsizes_stats; \ + SHOW shared_preload_libraries; \ + TABLE pg_extension;' | \ + psql postgres" + then + echo "Error creating gp_relsizes_stats extension" + exit 1 + fi + ;; *) echo "Unknown extension: ${{ matrix.extension }}" exit 1 diff --git a/gpcontrib/Makefile b/gpcontrib/Makefile index 27a55e39731..32c134c95e6 100644 --- a/gpcontrib/Makefile +++ b/gpcontrib/Makefile @@ -22,6 +22,7 @@ ifeq "$(enable_debug_extensions)" "yes" gp_inject_fault \ gp_exttable_fdw \ gp_legacy_string_agg \ + gp_relsizes_stats \ gp_replica_check \ gp_toolkit \ pg_hint_plan \ @@ -31,6 +32,7 @@ else gp_distribution_policy \ gp_internal_tools \ gp_legacy_string_agg \ + gp_relsizes_stats \ gp_exttable_fdw \ gp_toolkit \ pg_hint_plan diff --git a/gpcontrib/gp_relsizes_stats/LICENCE b/gpcontrib/gp_relsizes_stats/LICENCE deleted file mode 100644 index 8dada3edaf5..00000000000 --- a/gpcontrib/gp_relsizes_stats/LICENCE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/gpcontrib/gp_relsizes_stats/Makefile b/gpcontrib/gp_relsizes_stats/Makefile index cd2c1df1e00..fdb8d22cae5 100644 --- a/gpcontrib/gp_relsizes_stats/Makefile +++ b/gpcontrib/gp_relsizes_stats/Makefile @@ -1,12 +1,62 @@ +#------------------------------------------------------------------------- +# +# Makefile for gp_relsizes_stats extension. +# +# By default this Makefile is intended to be included from the surrounding +# Cloudberry source tree (in-tree build); pass USE_PGXS=1 to build against an +# already-installed Cloudberry/PostgreSQL using pg_config. +# +# IDENTIFICATION +# gpcontrib/gp_relsizes_stats/Makefile +# +#------------------------------------------------------------------------- + MODULE_big = gp_relsizes_stats OBJS = ./src/gp_relsizes_stats.o EXTENSION = gp_relsizes_stats -EXTVERSION = 1.3 +EXTVERSION = 1.0 DATA = $(wildcard sql/*--*.sql) REGRESS = grants gp_relsizes_stats -REGRESS_OPTS = --inputdir=test/ -PGFILEDESC = "gp_relsizes_stats - an extension to track table on-disc sizes in greenplum" +REGRESS_OPTS = --inputdir=test/ --temp-config=$(srcdir)/test/postgresql.conf.add +PGFILEDESC = "gp_relsizes_stats - an extension to track table on-disc sizes in Cloudberry" PG_CXXFLAGS += $(COMMON_CPP_FLAGS) -PG_CONFIG = pg_config -PGXS := $(shell $(PG_CONFIG) --pgxs) -include $(PGXS) + +# Auto-detect build mode: if we are sitting inside a Cloudberry source tree +# (i.e. ../../src/Makefile.global is reachable), build in-tree; otherwise fall +# back to a standalone PGXS build driven by pg_config. USE_PGXS=1 forces PGXS +# even when an in-tree Makefile.global is available. +ifeq ($(USE_PGXS),) + ifeq ($(wildcard ../../src/Makefile.global),) + USE_PGXS = 1 + endif +endif + +ifdef USE_PGXS + PG_CONFIG ?= pg_config + PGXS := $(shell $(PG_CONFIG) --pgxs) + include $(PGXS) +else + subdir = gpcontrib/gp_relsizes_stats + top_builddir = ../.. + include $(top_builddir)/src/Makefile.global + include $(top_srcdir)/contrib/contrib-global.mk +endif + +# The extension registers its background worker only when loaded +# via shared_preload_libraries (see _PG_init in src/gp_relsizes_stats.c). +# Without that, relsizes_collect_stats_once() silently does nothing and the +# regression tests get (0 rows) everywhere. Ensure the preload is set up +# before installcheck runs. +installcheck: preload-bgworker + +.PHONY: preload-bgworker +preload-bgworker: + @if [ -z "$$COORDINATOR_DATA_DIRECTORY$$MASTER_DATA_DIRECTORY" ]; then \ + echo "ERROR: COORDINATOR_DATA_DIRECTORY (or MASTER_DATA_DIRECTORY) is not set;" >&2; \ + echo " source cloudberry-env.sh and gpdemo-env.sh before running installcheck." >&2; \ + exit 1; \ + fi + @echo "==> Ensuring shared_preload_libraries contains gp_relsizes_stats and restarting cluster" + gpconfig -c shared_preload_libraries -v "'gp_relsizes_stats'" --skipvalidation + gpstop -ra + psql -d postgres -c "SHOW shared_preload_libraries;" diff --git a/gpcontrib/gp_relsizes_stats/README.md b/gpcontrib/gp_relsizes_stats/README.md index 350e7b32be4..04209704b0f 100644 --- a/gpcontrib/gp_relsizes_stats/README.md +++ b/gpcontrib/gp_relsizes_stats/README.md @@ -1,33 +1,52 @@ -# gp_relsizes_stats: Table sizes monitoring tool for Greenplum +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--> + +# gp_relsizes_stats: Table sizes monitoring tool for Cloudberry ### Features -gp_relsizes_stats is an extension for the Greenplum database that calculates and stores statistics on the size of files and tables, occupied space on the disks of the master and segment hosts. +gp_relsizes_stats is an extension for the Cloudberry database that calculates and stores statistics on the size of files and tables, occupied space on the disks of the master and segment hosts. #### Features include - BackgroundWorker support for collecting statistics automatically - the ability to fine-tune the timeout values between actions, for example, between launches for different databases, or during file processing to distribute the load over time ### Supported versions and platforms -At the moment, the program is being tested only for GP6 and Linux. +At the moment, the program is being tested only for Cloudberry and Linux. ### Installation -Install from source: -``` -git clone [email protected]:open-gpdb/gp_relsizes_stats.git -cd gp_relsizes_stats -# Build it. Building would require GP installed nearby and sourcing greenplum_path.sh -source <path_to_gp>/greenplum_path.sh -make && make install +This extension is part of the Cloudberry monorepo under `gpcontrib/gp_relsizes_stats`. + +Build and install from the Cloudberry monorepo root: +```bash +make -C gpcontrib/gp_relsizes_stats +sudo make -C gpcontrib/gp_relsizes_stats install ``` -### Confguration +### Configuration gp_relsizes_stats configuration parameters: -| **Parameter** | **Type** | **Default** | **Default** | +| **Parameter** | **Type** | **Default** | **Description** | | ---------------- | --------------- | ------------ | ------------ | -| `gp_relsizes_stats.enabled` | bool | false | Using `gp_relsizes_stats.enabled` you can enable/disable background stats collection for database where extension installed (actually enable/disable background worker which collecting stats).| -| `gp_relsizes_stats.restart_naptime` | int | 21600000 | Using `gp_relsizes_stats.restart_naptime` you can set naptime between each startup of collecting process. Value set time in milliseconds. Default is equal to 6 hours.| -| `gp_relsizes_stats.database_naptime` | int | 0 | Using `gp_relsizes_stats.database_naptime` you can set naptime between collecting stats for each databases. Value set time in milliseconds. Default is equal to 0 milliseconds.| -| `gp_relsizes_stats.file_naptime` | int | 1 | Using `gp_relsizes_stats.file_naptime` you can set naptime between each file stats calculating. Value set time in milliseconds. Default is equal to 1 millisecond.| +| `gp_relsizes_stats.enabled` | bool | false | You can enable/disable background stats collection for database where extension installed (actually enable/disable background worker which collecting stats).| +| `gp_relsizes_stats.save_history` | bool | true | You can disable the collection of statistics records in the history table (table_sizes_history).| +| `gp_relsizes_stats.restart_naptime` | int | 21600000 | You can set naptime between each startup of collecting process. Value set time in milliseconds. Default is equal to 6 hours.| +| `gp_relsizes_stats.database_naptime` | int | 0 | You can set naptime between collecting stats for each databases. Value set time in milliseconds. Default is equal to 0 milliseconds.| +| `gp_relsizes_stats.file_naptime` | int | 1 | You can set naptime between each file stats calculating. Value set time in milliseconds. Default is equal to 1 millisecond.| ### Usage You can use a background worker to collect statistics, but if you sometimes need to change the format of the settings or if you don't want to collect statistics on a regular basis, you can do so. In these situations, you could set diff --git a/gpcontrib/gp_relsizes_stats/gp_relsizes_stats.control b/gpcontrib/gp_relsizes_stats/gp_relsizes_stats.control index c5a28f4e55d..e28a3526b30 100644 --- a/gpcontrib/gp_relsizes_stats/gp_relsizes_stats.control +++ b/gpcontrib/gp_relsizes_stats/gp_relsizes_stats.control @@ -1,5 +1,5 @@ # gp_relsizes_stats extension -comment = 'gp_relsizes_stats - an extension to track table on-disc sizes in greenplum' -default_version = '1.3' +comment = 'gp_relsizes_stats - an extension to track table on-disc sizes in cloudberry' +default_version = '1.0' module_pathname = '$libdir/gp_relsizes_stats' trusted = true diff --git a/gpcontrib/gp_relsizes_stats/sql/gp_relsizes_stats--1.0--1.1.sql b/gpcontrib/gp_relsizes_stats/sql/gp_relsizes_stats--1.0--1.1.sql deleted file mode 100644 index 15dd1e7e5a8..00000000000 --- a/gpcontrib/gp_relsizes_stats/sql/gp_relsizes_stats--1.0--1.1.sql +++ /dev/null @@ -1,12 +0,0 @@ -/* gp_relsizes_stats--1.0--1.1.sql */ - --- complain if script is sourced in psql, rather than via CREATE EXTENSION -\echo Use "CREATE EXTENSION gp_relsizes_stats" to load this file. \quit - - -DROP FUNCTION relsizes_stats_schema.get_stats_for_database(dboid INTEGER); - -CREATE FUNCTION relsizes_stats_schema.get_stats_for_database(dboid OID, fast BOOL) -RETURNS TABLE (segment INTEGER, relfilenode OID, filepath TEXT, size BIGINT, mtime BIGINT) -AS 'MODULE_PATHNAME', 'get_stats_for_database' -LANGUAGE C STRICT EXECUTE ON ALL SEGMENTS; diff --git a/gpcontrib/gp_relsizes_stats/sql/gp_relsizes_stats--1.0.sql b/gpcontrib/gp_relsizes_stats/sql/gp_relsizes_stats--1.0.sql new file mode 100644 index 00000000000..4886bf77f89 --- /dev/null +++ b/gpcontrib/gp_relsizes_stats/sql/gp_relsizes_stats--1.0.sql @@ -0,0 +1,158 @@ +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION gp_relsizes_stats" to load this file. \quit + + +-- CREATE TABLE IF NOT EXISTS ... (....) DISTRIBUTED BY ... +CREATE SCHEMA IF NOT EXISTS relsizes_stats_schema; + +-- create table +CREATE TABLE IF NOT EXISTS relsizes_stats_schema.segment_file_map + (segment INTEGER, reloid OID, relfilenode OID) + WITH (appendonly=true) DISTRIBUTED RANDOMLY; +-- create table +CREATE TABLE IF NOT EXISTS relsizes_stats_schema.segment_file_sizes + (segment INTEGER, relfilenode OID, filepath TEXT, size BIGINT, mtime BIGINT) + WITH (appendonly=true) DISTRIBUTED RANDOMLY; +-- create table for backup info +CREATE TABLE IF NOT EXISTS relsizes_stats_schema.table_sizes_history + (insert_date date NOT NULL, nspname text NOT NULL, relname text NOT NULL, size bigint NOT NULL, mtime timestamp NOT NULL) + DISTRIBUTED RANDOMLY; + + +CREATE OR REPLACE VIEW relsizes_stats_schema.table_files AS + /* + * Recursive enumeration of all relations together with their partition + * trees, preserving the original GP6 view semantics: + * + * - Every relation appears as a "self" row with own_oid = true + * (relname carries its own name). + * - Additionally, for every relation that is a "root" of a partition + * tree (has no parent in pg_inherits), we walk the entire tree of + * descendants and add a row per descendant with own_oid = false; + * the descendant inherits the relname of the root (so SUMs in + * table_sizes / namespace_sizes attribute leaf data to the root + * partitioned table). + * + * - Intermediate partitioned tables (which are themselves children + * of another partitioned table) only contribute a self-row; they + * do NOT roll up their own leaves — those are already counted + * under the root. + * + * On Cloudberry / PG14 partition root and intermediate partitioned + * tables have relfilenode = 0 — no physical storage. To keep them + * visible in the view (with size = 0) we LEFT JOIN segment_file_sizes + * instead of using INNER JOIN. + */ + WITH RECURSIVE all_rels AS ( + SELECT n.nspname, c.relname, c.oid + FROM pg_class c + JOIN pg_namespace n ON c.relnamespace = n.oid + WHERE c.relkind IN ('r', 'p') + AND c.reltablespace != (SELECT oid FROM pg_tablespace WHERE spcname = 'pg_global') + ), + /* "Roots" — relations that do not inherit from any table/partitioned table. */ + roots AS ( + SELECT ar.nspname, ar.relname, ar.oid + FROM all_rels ar + WHERE NOT EXISTS ( + SELECT 1 + FROM pg_inherits pi + JOIN pg_class pc ON pi.inhparent = pc.oid + WHERE pi.inhrelid = ar.oid + AND pc.relkind IN ('r', 'p') + ) + ), + /* Walk the entire partition tree starting from each root. */ + descendants AS ( + SELECT r.nspname, r.relname AS root_relname, r.oid AS cur_oid, 0 AS depth + FROM roots r + UNION ALL + SELECT d.nspname, d.root_relname, c2.oid, d.depth + 1 + FROM descendants d + JOIN pg_inherits pi ON d.cur_oid = pi.inhparent + JOIN pg_class c2 ON pi.inhrelid = c2.oid + WHERE c2.relkind IN ('r', 'p') + ), + part_oids AS ( + /* Self-row for every relation. */ + SELECT nspname, relname, oid, true AS own_oid + FROM all_rels + UNION ALL + /* Descendant rows for every root (excluding the root itself). */ + SELECT nspname, root_relname AS relname, cur_oid AS oid, false AS own_oid + FROM descendants + WHERE depth > 0 + ), + table_oids AS ( + SELECT po.nspname, po.relname, po.oid, po.own_oid, 'main' AS kind + FROM part_oids po + UNION ALL + SELECT po.nspname, po.relname, t.reltoastrelid, po.own_oid, 'toast' AS kind + FROM part_oids po + JOIN pg_class t ON po.oid = t.oid + WHERE t.reltoastrelid > 0 + UNION ALL + SELECT po.nspname, po.relname, ti.indexrelid, po.own_oid, 'toast_idx' AS kind + FROM part_oids po + JOIN pg_class t ON po.oid = t.oid + JOIN pg_index ti ON t.reltoastrelid = ti.indrelid + WHERE t.reltoastrelid > 0 + UNION ALL + SELECT po.nspname, po.relname, ao.segrelid, po.own_oid, 'ao' AS kind + FROM part_oids po + JOIN pg_appendonly ao ON po.oid = ao.relid + UNION ALL + SELECT po.nspname, po.relname, ao.visimaprelid, po.own_oid, 'ao_vm' AS kind + FROM part_oids po + JOIN pg_appendonly ao ON po.oid = ao.relid + UNION ALL + SELECT po.nspname, po.relname, ao.visimapidxid, po.own_oid, 'ao_vm_idx' AS kind + FROM part_oids po + JOIN pg_appendonly ao ON po.oid = ao.relid + ) + SELECT table_oids.nspname, + table_oids.relname, + m.segment, + m.relfilenode, + fs.filepath, + kind, + COALESCE(fs.size, 0) AS size, + COALESCE(fs.mtime, 0) AS mtime, + table_oids.own_oid AS own_file + FROM table_oids + JOIN relsizes_stats_schema.segment_file_map m + ON table_oids.oid = m.reloid + /* + * LEFT JOIN, not INNER JOIN: partitioned (root + intermediate) tables + * on Cloudberry have no physical file, so segment_file_sizes does not contain + * a matching row. We still want to surface them with size = 0. + */ + LEFT JOIN relsizes_stats_schema.segment_file_sizes fs + ON m.segment = fs.segment AND m.relfilenode = fs.relfilenode; +CREATE OR REPLACE VIEW relsizes_stats_schema.table_sizes AS + SELECT nspname, relname, sum(size) AS size, to_timestamp(MAX(mtime)) AS mtime FROM relsizes_stats_schema.table_files + GROUP BY nspname, relname; +CREATE OR REPLACE VIEW relsizes_stats_schema.namespace_sizes AS + SELECT nspname, sum(size) AS size FROM relsizes_stats_schema.table_files + WHERE own_file + GROUP BY nspname; +-- Here go any C or PL/SQL functions, table or view definitions etc +-- for example: + +CREATE FUNCTION relsizes_stats_schema.get_stats_for_database(dboid OID, fast BOOL) +RETURNS TABLE (segment INTEGER, relfilenode OID, filepath TEXT, size BIGINT, mtime BIGINT) +AS 'MODULE_PATHNAME', 'get_stats_for_database' +LANGUAGE C STRICT EXECUTE ON ALL SEGMENTS; + +CREATE FUNCTION relsizes_stats_schema.relsizes_collect_stats_once() +RETURNS void +AS 'MODULE_PATHNAME', 'relsizes_collect_stats_once' +LANGUAGE C STRICT; + + +DO $$ +BEGIN + EXECUTE 'GRANT USAGE ON SCHEMA relsizes_stats_schema TO "' || session_user || '" WITH GRANT OPTION'; + EXECUTE 'GRANT SELECT ON ALL TABLES IN SCHEMA relsizes_stats_schema TO "' || session_user || '" WITH GRANT OPTION'; +END +$$; diff --git a/gpcontrib/gp_relsizes_stats/sql/gp_relsizes_stats--1.1--1.2.sql b/gpcontrib/gp_relsizes_stats/sql/gp_relsizes_stats--1.1--1.2.sql deleted file mode 100644 index 300a17ffc92..00000000000 --- a/gpcontrib/gp_relsizes_stats/sql/gp_relsizes_stats--1.1--1.2.sql +++ /dev/null @@ -1,56 +0,0 @@ -/* gp_relsizes_stats--1.1--1.2.sql */ - --- complain if script is sourced in psql, rather than via ALTER EXTENSION -\echo Use "ALTER EXTENSION gp_relsizes_stats" to load this file. \quit - -CREATE OR REPLACE VIEW relsizes_stats_schema.table_files AS - WITH part_oids AS ( - SELECT n.nspname, c1.relname, c1.oid, true own_oid - FROM pg_class c1 - JOIN pg_namespace n ON c1.relnamespace = n.oid - WHERE c1.reltablespace != (SELECT oid FROM pg_tablespace WHERE spcname = 'pg_global') - UNION ALL - SELECT n.nspname, c1.relname, c2.oid, false own_oid - FROM pg_class c1 - JOIN pg_namespace n ON c1.relnamespace = n.oid - JOIN pg_partition pp ON c1.oid = pp.parrelid - JOIN pg_partition_rule pr ON pp.oid = pr.paroid - JOIN pg_class c2 ON pr.parchildrelid = c2.oid - WHERE c1.reltablespace != (SELECT oid FROM pg_tablespace WHERE spcname = 'pg_global') - ), - table_oids AS ( - SELECT po.nspname, po.relname, po.oid, po.own_oid, 'main' AS kind - FROM part_oids po - UNION ALL - SELECT po.nspname, po.relname, t.reltoastrelid, po.own_oid, 'toast' AS kind - FROM part_oids po - JOIN pg_class t ON po.oid = t.oid - WHERE t.reltoastrelid > 0 - UNION ALL - SELECT po.nspname, po.relname, ti.indexrelid, po.own_oid, 'toast_idx' AS kind - FROM part_oids po - JOIN pg_class t ON po.oid = t.oid - JOIN pg_index ti ON t.reltoastrelid = ti.indrelid - WHERE t.reltoastrelid > 0 - UNION ALL - SELECT po.nspname, po.relname, ao.segrelid, po.own_oid, 'ao' AS kind - FROM part_oids po - JOIN pg_appendonly ao ON po.oid = ao.relid - UNION ALL - SELECT po.nspname, po.relname, ao.visimaprelid, po.own_oid, 'ao_vm' AS kind - FROM part_oids po - JOIN pg_appendonly ao ON po.oid = ao.relid - UNION ALL - SELECT po.nspname, po.relname, ao.visimapidxid, po.own_oid, 'ao_vm_idx' AS kind - FROM part_oids po - JOIN pg_appendonly ao ON po.oid = ao.relid - ) - SELECT table_oids.nspname, table_oids.relname, m.segment, m.relfilenode, fs.filepath, kind, size, mtime, table_oids.own_oid own_file - FROM table_oids - JOIN relsizes_stats_schema.segment_file_map m ON table_oids.oid = m.reloid - JOIN relsizes_stats_schema.segment_file_sizes fs ON m.segment = fs.segment AND m.relfilenode = fs.relfilenode; - -CREATE OR REPLACE VIEW relsizes_stats_schema.namespace_sizes AS - SELECT nspname, sum(size) AS size FROM relsizes_stats_schema.table_files - WHERE own_file - GROUP BY nspname; diff --git a/gpcontrib/gp_relsizes_stats/sql/gp_relsizes_stats--1.2--1.3.sql b/gpcontrib/gp_relsizes_stats/sql/gp_relsizes_stats--1.2--1.3.sql deleted file mode 100644 index 1416af82262..00000000000 --- a/gpcontrib/gp_relsizes_stats/sql/gp_relsizes_stats--1.2--1.3.sql +++ /dev/null @@ -1,9 +0,0 @@ --- complain if script is sourced in psql, rather than via CREATE EXTENSION -\echo Use "CREATE EXTENSION gp_relsizes_stats" to load this file. \quit - -DO $$ -BEGIN - EXECUTE 'GRANT USAGE ON SCHEMA relsizes_stats_schema TO "' || session_user || '" WITH GRANT OPTION'; - EXECUTE 'GRANT SELECT ON ALL TABLES IN SCHEMA relsizes_stats_schema TO "' || session_user || '" WITH GRANT OPTION'; -END -$$; diff --git a/gpcontrib/gp_relsizes_stats/sql/gp_relsizes_stats--1.3.sql b/gpcontrib/gp_relsizes_stats/sql/gp_relsizes_stats--1.3.sql deleted file mode 100644 index 6a56aef77b9..00000000000 --- a/gpcontrib/gp_relsizes_stats/sql/gp_relsizes_stats--1.3.sql +++ /dev/null @@ -1,96 +0,0 @@ --- complain if script is sourced in psql, rather than via CREATE EXTENSION -\echo Use "CREATE EXTENSION gp_relsizes_stats" to load this file. \quit - - --- CREATE TABLE IF NOT EXISTS ... (....) DISTRIBUTED BY ... -CREATE SCHEMA IF NOT EXISTS relsizes_stats_schema; - --- create table -CREATE TABLE IF NOT EXISTS relsizes_stats_schema.segment_file_map - (segment INTEGER, reloid OID, relfilenode OID) - WITH (appendonly=true) DISTRIBUTED RANDOMLY; --- create table -CREATE TABLE IF NOT EXISTS relsizes_stats_schema.segment_file_sizes - (segment INTEGER, relfilenode OID, filepath TEXT, size BIGINT, mtime BIGINT) - WITH (appendonly=true, OIDS=FALSE) DISTRIBUTED RANDOMLY; -TRUNCATE TABLE relsizes_stats_schema.segment_file_sizes; --- create table for backup info -CREATE TABLE IF NOT EXISTS relsizes_stats_schema.table_sizes_history - (insert_date date NOT NULL, nspname text NOT NULL, relname text NOT NULL, size bigint NOT NULL, mtime timestamp NOT NULL) - DISTRIBUTED RANDOMLY; -TRUNCATE TABLE relsizes_stats_schema.table_sizes_history; - - -CREATE OR REPLACE VIEW relsizes_stats_schema.table_files AS - WITH part_oids AS ( - SELECT n.nspname, c1.relname, c1.oid, true own_oid - FROM pg_class c1 - JOIN pg_namespace n ON c1.relnamespace = n.oid - WHERE c1.reltablespace != (SELECT oid FROM pg_tablespace WHERE spcname = 'pg_global') - UNION ALL - SELECT n.nspname, c1.relname, c2.oid, false own_oid - FROM pg_class c1 - JOIN pg_namespace n ON c1.relnamespace = n.oid - JOIN pg_partition pp ON c1.oid = pp.parrelid - JOIN pg_partition_rule pr ON pp.oid = pr.paroid - JOIN pg_class c2 ON pr.parchildrelid = c2.oid - WHERE c1.reltablespace != (SELECT oid FROM pg_tablespace WHERE spcname = 'pg_global') - ), - table_oids AS ( - SELECT po.nspname, po.relname, po.oid, po.own_oid, 'main' AS kind - FROM part_oids po - UNION ALL - SELECT po.nspname, po.relname, t.reltoastrelid, po.own_oid, 'toast' AS kind - FROM part_oids po - JOIN pg_class t ON po.oid = t.oid - WHERE t.reltoastrelid > 0 - UNION ALL - SELECT po.nspname, po.relname, ti.indexrelid, po.own_oid, 'toast_idx' AS kind - FROM part_oids po - JOIN pg_class t ON po.oid = t.oid - JOIN pg_index ti ON t.reltoastrelid = ti.indrelid - WHERE t.reltoastrelid > 0 - UNION ALL - SELECT po.nspname, po.relname, ao.segrelid, po.own_oid, 'ao' AS kind - FROM part_oids po - JOIN pg_appendonly ao ON po.oid = ao.relid - UNION ALL - SELECT po.nspname, po.relname, ao.visimaprelid, po.own_oid, 'ao_vm' AS kind - FROM part_oids po - JOIN pg_appendonly ao ON po.oid = ao.relid - UNION ALL - SELECT po.nspname, po.relname, ao.visimapidxid, po.own_oid, 'ao_vm_idx' AS kind - FROM part_oids po - JOIN pg_appendonly ao ON po.oid = ao.relid - ) - SELECT table_oids.nspname, table_oids.relname, m.segment, m.relfilenode, fs.filepath, kind, size, mtime, table_oids.own_oid own_file - FROM table_oids - JOIN relsizes_stats_schema.segment_file_map m ON table_oids.oid = m.reloid - JOIN relsizes_stats_schema.segment_file_sizes fs ON m.segment = fs.segment AND m.relfilenode = fs.relfilenode; -CREATE OR REPLACE VIEW relsizes_stats_schema.table_sizes AS - SELECT nspname, relname, sum(size) AS size, to_timestamp(MAX(mtime)) AS mtime FROM relsizes_stats_schema.table_files - GROUP BY nspname, relname; -CREATE OR REPLACE VIEW relsizes_stats_schema.namespace_sizes AS - SELECT nspname, sum(size) AS size FROM relsizes_stats_schema.table_files - WHERE own_file - GROUP BY nspname; --- Here go any C or PL/SQL functions, table or view definitions etc --- for example: - -CREATE FUNCTION relsizes_stats_schema.get_stats_for_database(dboid OID, fast BOOL) -RETURNS TABLE (segment INTEGER, relfilenode OID, filepath TEXT, size BIGINT, mtime BIGINT) -AS 'MODULE_PATHNAME', 'get_stats_for_database' -LANGUAGE C STRICT EXECUTE ON ALL SEGMENTS; - -CREATE FUNCTION relsizes_stats_schema.relsizes_collect_stats_once() -RETURNS void -AS 'MODULE_PATHNAME', 'relsizes_collect_stats_once' -LANGUAGE C STRICT EXECUTE ON MASTER; - - -DO $$ -BEGIN - EXECUTE 'GRANT USAGE ON SCHEMA relsizes_stats_schema TO "' || session_user || '" WITH GRANT OPTION'; - EXECUTE 'GRANT SELECT ON ALL TABLES IN SCHEMA relsizes_stats_schema TO "' || session_user || '" WITH GRANT OPTION'; -END -$$; diff --git a/gpcontrib/gp_relsizes_stats/src/gp_relsizes_stats.c b/gpcontrib/gp_relsizes_stats/src/gp_relsizes_stats.c old mode 100644 new mode 100755 index fb1b3d77231..bc176d0b0ad --- a/gpcontrib/gp_relsizes_stats/src/gp_relsizes_stats.c +++ b/gpcontrib/gp_relsizes_stats/src/gp_relsizes_stats.c @@ -1,3 +1,30 @@ +/*------------------------------------------------------------------------- + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * gp_relsizes_stats.c + * + * IDENTIFICATION + * gpcontrib/gp_relsizes_stats/src/gp_relsizes_stats.c + * + *------------------------------------------------------------------------- + */ + #include "postgres.h" /* Required headers for background workers */ @@ -49,27 +76,30 @@ Datum get_stats_for_database(PG_FUNCTION_ARGS); Datum relsizes_collect_stats_once(PG_FUNCTION_ARGS); static void worker_sigterm(SIGNAL_ARGS); +static void worker_sighup(SIGNAL_ARGS); static Oid *get_databases_oids(int *databases_cnt, MemoryContext ctx, bool create_transaction); static int update_segment_file_map_table(void); static int update_table_sizes_history(void); static void get_stats_for_databases(Oid *databases_oids, int databases_cnt, bool fast); static void run_database_stats_worker(bool fast, Oid db); static int plugin_created(void); -static BgwHandleStatus WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle); +static BgwHandleStatus WaitForBackgroundWorkerShutdownSafely(BackgroundWorkerHandle *handle); static int delete_data_in_history(void); static int put_data_into_history(void); void _PG_init(void); -void relsizes_collect_stats(Datum main_arg); -void relsizes_database_stats_job(Datum args); +PGDLLEXPORT void relsizes_collect_stats(Datum main_arg); +PGDLLEXPORT void relsizes_database_stats_job(Datum args); /* Global variables */ static int worker_restart_naptime = 0; static int worker_database_naptime = 0; static int worker_file_naptime = 0; static bool enabled = false; +static bool save_history = true; static volatile sig_atomic_t got_sigterm = false; +static volatile sig_atomic_t got_sighup = false; typedef union DbWorkerArg { Datum d; @@ -79,19 +109,13 @@ typedef union DbWorkerArg { } s; } DbWorkerArg; -static_assert(sizeof(Datum) == sizeof(DbWorkerArg), "Invalid size of structure in DbWorkerArg"); +StaticAssertDecl(sizeof(Datum) == sizeof(DbWorkerArg), + "Invalid size of structure in DbWorkerArg"); /* - * Signal handler for SIGTERM in background worker processes. - * - * This handler is called when the postmaster requests the background worker - * to shut down. It sets the got_sigterm flag and wakes up the main worker - * loop by setting the process latch. - * - * The function follows PostgreSQL signal handling conventions: - * - Saves and restores errno - * - Uses only async-signal-safe operations - * - Sets a flag that the main loop can check + * Signal handler for SIGTERM + * Set a flag to let the main loop to terminate, and set our latch to wake + * it up. */ static void worker_sigterm(SIGNAL_ARGS) { int save_errno = errno; @@ -102,6 +126,20 @@ static void worker_sigterm(SIGNAL_ARGS) { errno = save_errno; } +/* + * Signal handler for SIGHUP + * Set a flag to tell the main loop to reread the config file, and set + * our latch to wake it up. + */ +static void worker_sighup(SIGNAL_ARGS) { + int save_errno = errno; + got_sighup = true; + if (MyProc) { + SetLatch(&MyProc->procLatch); + } + errno = save_errno; +} + /* * Wait for a background worker to stop with timeout and error handling. * @@ -109,16 +147,12 @@ static void worker_sigterm(SIGNAL_ARGS) { * error handling to prevent infinite loops in case of hung workers. * Returns BGWH_STOPPED on success, BGWH_POSTMASTER_DIED on error/timeout. */ -static BgwHandleStatus WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle) { - BgwHandleStatus status; +static BgwHandleStatus WaitForBackgroundWorkerShutdownSafely(BackgroundWorkerHandle *handle) { + BgwHandleStatus status = BGWH_NOT_YET_STARTED; int rc; - bool save_set_latch_on_sigusr1; int attempts = 0; const int max_attempts = 5 * HOUR_TIME / 100; /* maximum 5 hours wait time */ - save_set_latch_on_sigusr1 = set_latch_on_sigusr1; - set_latch_on_sigusr1 = true; - PG_TRY(); { while (attempts < max_attempts) { @@ -126,12 +160,11 @@ static BgwHandleStatus WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *h status = GetBackgroundWorkerPid(handle, &pid); if (status == BGWH_STOPPED) { - set_latch_on_sigusr1 = save_set_latch_on_sigusr1; return status; } /* Add 100ms timeout instead of infinite wait */ - rc = WaitLatch(&MyProc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, 100L); + rc = WaitLatch(&MyProc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, 100L, WAIT_EVENT_BGWORKER_SHUTDOWN); ResetLatch(&MyProc->procLatch); @@ -142,7 +175,7 @@ static BgwHandleStatus WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *h /* Check for interrupts but don't let them break the entire process */ if (QueryCancelPending || ProcDiePending) { - ereport(WARNING, (errmsg("WaitForBackgroundWorkerShutdown: received interrupt signal, stopping wait"))); + ereport(WARNING, (errmsg("WaitForBackgroundWorkerShutdownSafely: received interrupt signal, stopping wait"))); status = BGWH_POSTMASTER_DIED; /* Return status as if postmaster died */ break; } @@ -152,21 +185,18 @@ static BgwHandleStatus WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *h /* If maximum attempts reached */ if (attempts >= max_attempts) { - ereport(WARNING, (errmsg("WaitForBackgroundWorkerShutdown: timeout after %d attempts", max_attempts))); + ereport(WARNING, (errmsg("WaitForBackgroundWorkerShutdownSafely: timeout after %d attempts", max_attempts))); status = BGWH_POSTMASTER_DIED; /* Return error status */ } } PG_CATCH(); { /* Log error but do NOT re-throw exception */ - ereport(WARNING, (errmsg("WaitForBackgroundWorkerShutdown: caught exception, returning error status"))); - set_latch_on_sigusr1 = save_set_latch_on_sigusr1; + ereport(WARNING, (errmsg("WaitForBackgroundWorkerShutdownSafely: caught exception, returning error status"))); /* Return error status instead of PG_RE_THROW() */ return BGWH_POSTMASTER_DIED; } PG_END_TRY(); - - set_latch_on_sigusr1 = save_set_latch_on_sigusr1; return status; } @@ -200,16 +230,14 @@ static Oid *get_databases_oids(int *databases_cnt, MemoryContext ctx, bool creat if (create_transaction) { SetCurrentStatementStartTimestamp(); StartTransactionCommand(); + PushActiveSnapshot(GetTransactionSnapshot()); + pgstat_report_activity(STATE_RUNNING, sql); } if (SPI_connect() < 0) { error = "get_databases_oids: SPI_connect failed"; goto finish_transaction; } - if (create_transaction) { - PushActiveSnapshot(GetTransactionSnapshot()); - pgstat_report_activity(STATE_RUNNING, sql); - } if (SPI_execute(sql, true, 0) != SPI_OK_SELECT) { error = "get_databases_oids: SPI_execute failed (select datname, oid)"; @@ -225,9 +253,9 @@ static Oid *get_databases_oids(int *databases_cnt, MemoryContext ctx, bool creat for (int i = 0; i < SPI_processed; ++i) { Datum oid_datum; - bool oid_nullable; + bool oid_isnull; - heap_deform_tuple(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, &oid_datum, &oid_nullable); + oid_datum = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &oid_isnull); databases_oids[i] = DatumGetObjectId(oid_datum); } @@ -347,16 +375,20 @@ static unsigned int fill_relfilenode(char *name) { * Note: This function is called via the background worker framework and * should not be called directly. */ -void relsizes_database_stats_job(Datum args) { +PGDLLEXPORT void relsizes_database_stats_job(Datum args) { int retcode = 0; char *error = NULL; DbWorkerArg wa = { .d = args }; optimizer = false; pqsignal(SIGTERM, worker_sigterm); + pqsignal(SIGHUP, worker_sighup); BackgroundWorkerUnblockSignals(); - BackgroundWorkerInitializeConnectionByOid(wa.s.db, InvalidOid); + BackgroundWorkerInitializeConnectionByOid(wa.s.db, InvalidOid, 0); + + if (IS_QUERY_DISPATCHER() && !IS_SINGLENODE()) + Gp_role = GP_ROLE_DISPATCH; SetCurrentStatementStartTimestamp(); StartTransactionCommand(); @@ -394,7 +426,7 @@ void relsizes_database_stats_job(Datum args) { /* Remove this condition after decision how to upgrade extensions is made. */ if (SearchSysCacheExists3(PROCNAMEARGSNSP, CStringGetDatum("get_stats_for_database"), - PointerGetDatum((&(oidvector){ .dim1 = 1, .values = { INT4OID } })), + PointerGetDatum(buildoidvector((Oid[]){INT4OID}, 1)), ObjectIdGetDatum(get_namespace_oid("relsizes_stats_schema", true)))) { const char* sql_get_stats = @@ -403,7 +435,7 @@ void relsizes_database_stats_job(Datum args) { pgstat_report_activity(STATE_RUNNING, sql_get_stats); retcode = SPI_execute_with_args(sql_get_stats, 1, (Oid[]){INT4OID}, - (Datum[]){ObjectIdGetDatum(MyDatabaseId)}, + (Datum[]){Int32GetDatum((int32) MyDatabaseId)}, NULL, false, 0); } else { const char* sql_get_stats = @@ -412,7 +444,7 @@ void relsizes_database_stats_job(Datum args) { pgstat_report_activity(STATE_RUNNING, sql_get_stats); retcode = SPI_execute_with_args(sql_get_stats, 2, (Oid[]){OIDOID, BOOLOID}, - (Datum[]){ObjectIdGetDatum(MyDatabaseId), BoolGetDatum(wa.s.fast)}, + (Datum[]){Int32GetDatum((int32) MyDatabaseId), BoolGetDatum(wa.s.fast)}, NULL, false, 0); } if (retcode != SPI_OK_INSERT) { @@ -420,10 +452,12 @@ void relsizes_database_stats_job(Datum args) { goto finish_spi; } - retcode = update_table_sizes_history(); - if (retcode < 0) { - error = "relsizes_database_stats_job: updating tables sizes history table failed"; - goto finish_spi; + if (save_history) { + retcode = update_table_sizes_history(); + if (retcode < 0) { + error = "relsizes_database_stats_job: updating tables sizes history table failed"; + goto finish_spi; + } } finish_spi: @@ -433,7 +467,8 @@ finish_spi: } SPI_finish(); finish_transaction: - PopActiveSnapshot(); + if (ActiveSnapshotSet()) + PopActiveSnapshot(); CommitTransactionCommand(); pgstat_report_stat(false); pgstat_report_activity(STATE_IDLE, NULL); @@ -495,7 +530,7 @@ static void run_database_stats_worker(bool fast, Oid db) { ereport(WARNING, (errmsg("Failed to start background worker [%s], skipping", database_worker.bgw_name))); return; } - status = WaitForBackgroundWorkerShutdown(handle); + status = WaitForBackgroundWorkerShutdownSafely(handle); if (status != BGWH_STOPPED) { ereport(WARNING, (errmsg("Failure during background worker execution [%s], continuing", database_worker.bgw_name))); /* Don't abort execution, just log and continue */ @@ -531,21 +566,88 @@ static void run_database_stats_worker(bool fast, Oid db) { * * Note: Includes configurable delays between file processing to reduce I/O load */ +/* + * Scan a single directory and add file stats to the tuple store. + * Returns false if the directory could not be opened (non-fatal). + */ +static void +scan_db_dir(const char *dir_path, int segment_id, bool fast, + TupleDesc tupdesc, Tuplestorestate *tupstore) +{ + DIR *current_dir = AllocateDir(dir_path); + if (!current_dir) + { + ereport(WARNING, + (errmsg("get_stats_for_database: could not open directory \"%s\": %m", + dir_path))); + return; + } + + struct dirent *file; + while ((file = ReadDir(current_dir, dir_path)) != NULL) + { + char *filename = file->d_name; + if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) + continue; + + char *file_path = psprintf("%s/%s", dir_path, filename); + struct stat stb; + if (lstat(file_path, &stb) < 0) + { + ereport(WARNING, + (errmsg("get_stats_for_database: lstat failed for \"%s\" (unexpected behavior)", + file_path))); + pfree(file_path); + continue; + } + + if (S_ISREG(stb.st_mode)) + { + unsigned int relfilenode = fill_relfilenode(filename); + if (relfilenode == 0) + { + /* Skip non-relation files (PG_VERSION, pg_filenode.map, etc.) */ + pfree(file_path); + continue; + } + + Datum outputValues[FILEINFO_ARGS_CNT]; + bool outputNulls[FILEINFO_ARGS_CNT] = { false }; + + outputValues[0] = Int32GetDatum(segment_id); + outputValues[1] = ObjectIdGetDatum(relfilenode); + outputValues[2] = CStringGetTextDatum(file_path); + outputValues[3] = Int64GetDatum(stb.st_size); + outputValues[4] = Int64GetDatum(stb.st_mtime); + + tuplestore_putvalues(tupstore, tupdesc, outputValues, outputNulls); + + if (fast) + CHECK_FOR_INTERRUPTS(); + else + { + int retcode = WaitLatch(&MyProc->procLatch, + WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, + worker_file_naptime, WAIT_EVENT_BUFFER_IO); + ResetLatch(&MyProc->procLatch); + CHECK_FOR_INTERRUPTS(); + if (retcode & WL_POSTMASTER_DEATH) + proc_exit(1); + } + } + pfree(file_path); + } + + FreeDir(current_dir); +} + Datum get_stats_for_database(PG_FUNCTION_ARGS) { int segment_id = GpIdentity.segindex; Oid dboid = PG_GETARG_OID(0); bool fast = (PG_NARGS() < 2) ? false : PG_GETARG_BOOL(1); - char cwd[PATH_MAX]; - char *data_dir = NULL; - char *error = NULL; - char *file_path = NULL; + const char *error = NULL; - if (getcwd(cwd, sizeof(cwd)) == NULL) { - error = "get_stats_for_database: failed to get current working directory"; - goto finish_data; - } - data_dir = psprintf("%s/base/%u", cwd, dboid); ReturnSetInfo *rsinfo = (ReturnSetInfo *)fcinfo->resultinfo; /* Validate function call context */ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) { @@ -569,71 +671,63 @@ Datum get_stats_for_database(PG_FUNCTION_ARGS) { bool randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0; Tuplestorestate *tupstore = tuplestore_begin_heap(randomAccess, false, work_mem); - rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; - Datum outputValues[FILEINFO_ARGS_CNT]; - bool outputNulls[FILEINFO_ARGS_CNT] = { false }; - MemoryContextSwitchTo(oldcontext); - /* Scan database directory for files */ - DIR *current_dir = AllocateDir(data_dir); - if (!current_dir) { - error = "get_stats_for_database: failed to allocate current directory"; - goto finish_data; + /* Scan default tablespace: $DataDir/base/<dboid> */ + { + char *default_dir = psprintf("%s/base/%u", DataDir, dboid); + scan_db_dir(default_dir, segment_id, fast, tupdesc, tupstore); + pfree(default_dir); } - struct dirent *file; - while ((file = ReadDir(current_dir, data_dir)) != NULL) { - char *filename = file->d_name; - if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) { - continue; - } - - file_path = psprintf("%s/%s", data_dir, filename); - struct stat stb; - if (lstat(file_path, &stb) < 0) { - ereport(WARNING, - (errmsg("get_stats_for_database: lstat failed with %s file (unexpected behavior)", file_path))); - pfree(file_path); - continue; - } - - if (S_ISREG(stb.st_mode)) { - /* Process regular files and collect size statistics */ - outputValues[0] = Int32GetDatum(segment_id); - outputValues[1] = ObjectIdGetDatum(fill_relfilenode(filename)); - outputValues[2] = CStringGetTextDatum(file_path); - outputValues[3] = Int64GetDatum(stb.st_size); - outputValues[4] = Int64GetDatum(stb.st_mtime); - - tuplestore_putvalues(tupstore, tupdesc, outputValues, outputNulls); - - if (fast) - CHECK_FOR_INTERRUPTS(); - else { - /* Brief pause between file processing to reduce system load */ - int retcode = WaitLatch(&MyProc->procLatch, - WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, - worker_file_naptime); - ResetLatch(&MyProc->procLatch); - - CHECK_FOR_INTERRUPTS(); - - if (retcode & WL_POSTMASTER_DEATH) { - proc_exit(1); + /* Scan non-default tablespaces: $DataDir/pg_tblspc/<spcoid>/<version>/<dboid> */ + { + char *tblspc_base = psprintf("%s/pg_tblspc", DataDir); + DIR *tblspc_dir = AllocateDir(tblspc_base); + if (tblspc_dir) + { + struct dirent *spc_entry; + while ((spc_entry = ReadDir(tblspc_dir, tblspc_base)) != NULL) + { + if (strcmp(spc_entry->d_name, ".") == 0 || + strcmp(spc_entry->d_name, "..") == 0) + continue; + + /* + * Each entry is a symlink to the tablespace directory. + * Inside it there is a version subdirectory (e.g. PG_14_202107181) + * and then per-database subdirectories named by dboid. + */ + char *spc_path = psprintf("%s/%s", tblspc_base, spc_entry->d_name); + DIR *ver_dir = AllocateDir(spc_path); + if (ver_dir) + { + struct dirent *ver_entry; + while ((ver_entry = ReadDir(ver_dir, spc_path)) != NULL) + { + if (strcmp(ver_entry->d_name, ".") == 0 || + strcmp(ver_entry->d_name, "..") == 0) + continue; + + char *db_dir = psprintf("%s/%s/%u", + spc_path, ver_entry->d_name, dboid); + scan_db_dir(db_dir, segment_id, fast, tupdesc, tupstore); + pfree(db_dir); + } + FreeDir(ver_dir); } + pfree(spc_path); } + FreeDir(tblspc_dir); } - pfree(file_path); + pfree(tblspc_base); } - FreeDir(current_dir); finish_data: - pfree(data_dir); if (error != NULL) { ereport(WARNING, (errmsg("%s: %m", error))); /* Don't abort execution, return result */ @@ -671,7 +765,7 @@ static void get_stats_for_databases(Oid *databases_oids, int databases_cnt, bool CHECK_FOR_INTERRUPTS(); else { int naptime = (databases_cnt > 0) ? (worker_database_naptime / databases_cnt) : worker_database_naptime; - int retcode = WaitLatch(&MyProc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, naptime); + int retcode = WaitLatch(&MyProc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, naptime, WAIT_EVENT_BGWORKER_STARTUP); ResetLatch(&MyProc->procLatch); CHECK_FOR_INTERRUPTS(); /* emergency bailout if postmaster has died */ @@ -835,18 +929,23 @@ static void relsizes_collect_stats_once_internal(bool from_worker) { * Note: This function should only be called via the background worker * framework and runs in the "postgres" database context. */ -void relsizes_collect_stats(Datum main_arg) { +PGDLLEXPORT void relsizes_collect_stats(Datum main_arg) { optimizer = false; pqsignal(SIGTERM, worker_sigterm); + pqsignal(SIGHUP, worker_sighup); BackgroundWorkerUnblockSignals(); - BackgroundWorkerInitializeConnection("postgres", NULL); + BackgroundWorkerInitializeConnection("postgres", NULL, 0); while (!got_sigterm) { + if (got_sighup) { + got_sighup = false; + ProcessConfigFile(PGC_SIGHUP); + } if (enabled) relsizes_collect_stats_once_internal(true); int retcode = - WaitLatch(&MyProc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, worker_restart_naptime); + WaitLatch(&MyProc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, worker_restart_naptime, WAIT_EVENT_BGWORKER_STARTUP); ResetLatch(&MyProc->procLatch); CHECK_FOR_INTERRUPTS(); if (retcode & WL_POSTMASTER_DEATH) { @@ -892,6 +991,7 @@ Datum relsizes_collect_stats_once(PG_FUNCTION_ARGS) { * * GUC Parameters defined: * - gp_relsizes_stats.enabled: Enable/disable the background worker + * - gp_relsizes_stats.save_history: Enable saving table sizes to history table * - gp_relsizes_stats.restart_naptime: Delay between collection cycles (ms) * - gp_relsizes_stats.database_naptime: Delay between database processing (ms) * - gp_relsizes_stats.file_naptime: Delay between file processing (ms) @@ -913,6 +1013,10 @@ void _PG_init(void) { /* Define GUC variables */ DefineCustomBoolVariable("gp_relsizes_stats.enabled", "Enable main background worker flag", NULL, &enabled, false, PGC_SIGHUP, GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); + DefineCustomBoolVariable("gp_relsizes_stats.save_history", + "Enable saving table sizes to history table.", + NULL, &save_history, true, + PGC_SIGHUP, 0, NULL, NULL, NULL); DefineCustomIntVariable("gp_relsizes_stats.restart_naptime", "Duration between every collect-phases (in ms).", NULL, &worker_restart_naptime, 6 * HOUR_TIME, /* 6 hours delay between collect-phases */ diff --git a/gpcontrib/gp_relsizes_stats/test/expected/gp_relsizes_stats.out b/gpcontrib/gp_relsizes_stats/test/expected/gp_relsizes_stats.out index eeb3cfea1b7..0e00389d6f2 100644 --- a/gpcontrib/gp_relsizes_stats/test/expected/gp_relsizes_stats.out +++ b/gpcontrib/gp_relsizes_stats/test/expected/gp_relsizes_stats.out @@ -1,4 +1,8 @@ +\set QUIET off +SET client_min_messages TO error; +SET CREATE EXTENSION gp_relsizes_stats; +CREATE EXTENSION CREATE TABLE employees ( employee_id SERIAL PRIMARY KEY, first_name VARCHAR(50) NOT NULL, @@ -6,10 +10,13 @@ CREATE TABLE employees ( department_id INT, date_of_birth DATE ); +CREATE TABLE INSERT INTO employees (first_name, last_name, department_id, date_of_birth) VALUES ('John', 'Doe', 1, '1988-06-15'), ('Jane', 'Smith', 2, '1990-07-20'), ('Emily', 'Jones', 1, '1985-08-30'); +INSERT 0 3 +-- Default is save_history = on — history write SELECT relsizes_stats_schema.relsizes_collect_stats_once(); relsizes_collect_stats_once ----------------------------- @@ -26,6 +33,7 @@ SELECT size FROM relsizes_stats_schema.table_sizes_history WHERE relname = 'empl insert into employees (first_name, last_name, department_id, date_of_birth) select 'First' || i, 'Last' || i, (i % 10) + 1, DATE '1980-01-01' + (i % 365 * 365 / 30) from generate_series(1, 10001)i; +INSERT 0 10001 SELECT relsizes_stats_schema.relsizes_collect_stats_once(); relsizes_collect_stats_once ----------------------------- @@ -54,6 +62,7 @@ SELECT size FROM relsizes_stats_schema.table_sizes_history WHERE relname = 'empl -- Cleanup DROP TABLE employees; +DROP TABLE -- -- relsizes_collect_stats_once should collect files sizes without pauses -- The naptime value is 1ms, so the pauses take at least 10s to process 10k files. @@ -73,32 +82,24 @@ SELECT (EXTRACT(EPOCH FROM LOCALTIMESTAMP(0)) - :t1) < 5; -- Cleanup DROP TABLE t; +DROP TABLE -- -- Check that schema size is calculated correctly when the schema -- contains partitioned tables and ordinary ones. --- start_ignore -DROP SCHEMA IF EXISTS test CASCADE; -NOTICE: schema "test" does not exist, skipping --- end_ignore CREATE SCHEMA test; +CREATE SCHEMA CREATE TABLE test.t1 (i INT, j INT) DISTRIBUTED BY (i) PARTITION BY RANGE (i) SUBPARTITION BY RANGE (j) SUBPARTITION TEMPLATE (SUBPARTITION sp START (0) END (2) EVERY(1)) (PARTITION p START (0) END (3) EVERY(1)); -NOTICE: CREATE TABLE will create partition "t1_1_prt_p_1" for table "t1" -NOTICE: CREATE TABLE will create partition "t1_1_prt_p_1_2_prt_sp_1" for table "t1_1_prt_p_1" -NOTICE: CREATE TABLE will create partition "t1_1_prt_p_1_2_prt_sp_2" for table "t1_1_prt_p_1" -NOTICE: CREATE TABLE will create partition "t1_1_prt_p_2" for table "t1" -NOTICE: CREATE TABLE will create partition "t1_1_prt_p_2_2_prt_sp_1" for table "t1_1_prt_p_2" -NOTICE: CREATE TABLE will create partition "t1_1_prt_p_2_2_prt_sp_2" for table "t1_1_prt_p_2" -NOTICE: CREATE TABLE will create partition "t1_1_prt_p_3" for table "t1" -NOTICE: CREATE TABLE will create partition "t1_1_prt_p_3_2_prt_sp_1" for table "t1_1_prt_p_3" -NOTICE: CREATE TABLE will create partition "t1_1_prt_p_3_2_prt_sp_2" for table "t1_1_prt_p_3" +CREATE TABLE INSERT INTO test.t1 (i, j) SELECT a % 3, a % 2 FROM generate_series(0, 2 * 3 - 1) a; +INSERT 0 6 CREATE TABLE test.t2 AS SELECT 1 i DISTRIBUTED BY(i); +SELECT 1 SELECT relsizes_stats_schema.relsizes_collect_stats_once(); relsizes_collect_stats_once ----------------------------- @@ -182,6 +183,69 @@ ORDER BY relname, segment, own_file; (60 rows) DROP SCHEMA test CASCADE; -NOTICE: drop cascades to 2 other objects -DETAIL: drop cascades to table test.t1 -drop cascades to table test.t2 +DROP SCHEMA +-- +-- Check that save_history option controls writing to table_sizes_history +-- start_ignore +DROP TABLE IF EXISTS t_history_test; +DROP TABLE +-- end_ignore +CREATE TABLE t_history_test (i INT) DISTRIBUTED BY (i); +CREATE TABLE +INSERT INTO t_history_test VALUES (1); +INSERT 0 1 +-- Disable option - history should not write +ALTER SYSTEM SET gp_relsizes_stats.save_history = off; +ALTER SYSTEM +SELECT pg_reload_conf(); + pg_reload_conf +---------------- + t +(1 row) + +SELECT relsizes_stats_schema.relsizes_collect_stats_once(); + relsizes_collect_stats_once +----------------------------- + +(1 row) + +SELECT count(*) FROM relsizes_stats_schema.table_sizes_history + WHERE relname = 't_history_test'; + count +------- + 0 +(1 row) + +-- Enable option - history should write +ALTER SYSTEM SET gp_relsizes_stats.save_history = on; +ALTER SYSTEM +SELECT pg_reload_conf(); + pg_reload_conf +---------------- + t +(1 row) + +SELECT relsizes_stats_schema.relsizes_collect_stats_once(); + relsizes_collect_stats_once +----------------------------- + +(1 row) + +SELECT count(*) FROM relsizes_stats_schema.table_sizes_history + WHERE relname = 't_history_test'; + count +------- + 1 +(1 row) + +-- Cleanup +ALTER SYSTEM RESET gp_relsizes_stats.save_history; +ALTER SYSTEM +SELECT pg_reload_conf(); + pg_reload_conf +---------------- + t +(1 row) + +DROP TABLE t_history_test; +DROP TABLE diff --git a/gpcontrib/gp_relsizes_stats/test/expected/grants.out b/gpcontrib/gp_relsizes_stats/test/expected/grants.out index 16d77930c0c..c04cc1e8306 100644 --- a/gpcontrib/gp_relsizes_stats/test/expected/grants.out +++ b/gpcontrib/gp_relsizes_stats/test/expected/grants.out @@ -5,7 +5,10 @@ SELECT '\! cp "' || setting || '/pg_hba.conf" "' || setting || '/pg_hba.conf.ba FROM pg_settings WHERE name = 'data_directory' \gset :cp_backup -SELECT '\! echo "local all user1,user2 trust" >> ' || setting || '/pg_hba.conf' as add_users +SELECT '\! { echo "local all user1,user2 trust"; ' + || 'echo "host all user1,user2 127.0.0.1/32 trust"; ' + || 'echo "host all user1,user2 ::1/128 trust"; ' + || '} >> ' || setting || '/pg_hba.conf' as add_users FROM pg_settings WHERE name = 'data_directory' \gset :add_users diff --git a/gpcontrib/gp_relsizes_stats/test/postgresql.conf.add b/gpcontrib/gp_relsizes_stats/test/postgresql.conf.add new file mode 100644 index 00000000000..3c2492efd93 --- /dev/null +++ b/gpcontrib/gp_relsizes_stats/test/postgresql.conf.add @@ -0,0 +1 @@ +shared_preload_libraries = 'gp_relsizes_stats' diff --git a/gpcontrib/gp_relsizes_stats/test/sql/gp_relsizes_stats.sql b/gpcontrib/gp_relsizes_stats/test/sql/gp_relsizes_stats.sql index 9d5d0cd89af..5ff0c1c8bf8 100644 --- a/gpcontrib/gp_relsizes_stats/test/sql/gp_relsizes_stats.sql +++ b/gpcontrib/gp_relsizes_stats/test/sql/gp_relsizes_stats.sql @@ -1,3 +1,5 @@ +\set QUIET off +SET client_min_messages TO error; CREATE EXTENSION gp_relsizes_stats; -- start_ignore @@ -16,6 +18,7 @@ INSERT INTO employees (first_name, last_name, department_id, date_of_birth) VALU ('Jane', 'Smith', 2, '1990-07-20'), ('Emily', 'Jones', 1, '1985-08-30'); +-- Default is save_history = on — history write SELECT relsizes_stats_schema.relsizes_collect_stats_once(); SELECT size FROM relsizes_stats_schema.table_sizes_history WHERE relname = 'employees'; @@ -94,3 +97,32 @@ SELECT relname, segment, own_file, size ORDER BY relname, segment, own_file; DROP SCHEMA test CASCADE; + + +-- +-- Check that save_history option controls writing to table_sizes_history + +-- start_ignore +DROP TABLE IF EXISTS t_history_test; +-- end_ignore +CREATE TABLE t_history_test (i INT) DISTRIBUTED BY (i); +INSERT INTO t_history_test VALUES (1); + +-- Disable option - history should not write +ALTER SYSTEM SET gp_relsizes_stats.save_history = off; +SELECT pg_reload_conf(); +SELECT relsizes_stats_schema.relsizes_collect_stats_once(); +SELECT count(*) FROM relsizes_stats_schema.table_sizes_history + WHERE relname = 't_history_test'; + +-- Enable option - history should write +ALTER SYSTEM SET gp_relsizes_stats.save_history = on; +SELECT pg_reload_conf(); +SELECT relsizes_stats_schema.relsizes_collect_stats_once(); +SELECT count(*) FROM relsizes_stats_schema.table_sizes_history + WHERE relname = 't_history_test'; + +-- Cleanup +ALTER SYSTEM RESET gp_relsizes_stats.save_history; +SELECT pg_reload_conf(); +DROP TABLE t_history_test; diff --git a/gpcontrib/gp_relsizes_stats/test/sql/grants.sql b/gpcontrib/gp_relsizes_stats/test/sql/grants.sql index 3bff541c242..46fe68ccbfd 100644 --- a/gpcontrib/gp_relsizes_stats/test/sql/grants.sql +++ b/gpcontrib/gp_relsizes_stats/test/sql/grants.sql @@ -13,7 +13,10 @@ WHERE name = 'data_directory' \gset :cp_backup -SELECT '\! echo "local all user1,user2 trust" >> ' || setting || '/pg_hba.conf' as add_users +SELECT '\! { echo "local all user1,user2 trust"; ' + || 'echo "host all user1,user2 127.0.0.1/32 trust"; ' + || 'echo "host all user1,user2 ::1/128 trust"; ' + || '} >> ' || setting || '/pg_hba.conf' as add_users FROM pg_settings WHERE name = 'data_directory' \gset diff --git a/pom.xml b/pom.xml index 587e223fec6..cb2c25c20c4 100644 --- a/pom.xml +++ b/pom.xml @@ -1276,6 +1276,11 @@ code or new licensing patterns. <exclude>gpcontrib/gp_stats_collector/.clang-format</exclude> <exclude>gpcontrib/gp_stats_collector/Makefile</exclude> + <exclude>gpcontrib/gp_relsizes_stats/Makefile</exclude> + <exclude>gpcontrib/gp_relsizes_stats/.clang-format</exclude> + <exclude>gpcontrib/gp_relsizes_stats/gp_relsizes_stats.control</exclude> + <exclude>gpcontrib/gp_relsizes_stats/test/postgresql.conf.add</exclude> + <exclude>gpcontrib/reject_partition_fullscan/Makefile</exclude> <exclude>gpcontrib/reject_partition_fullscan/reject_partition_fullscan.control</exclude> --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
