From ee20c937fbc05ef628fa8b78d9383d0dcece0e62 Mon Sep 17 00:00:00 2001 From: Ankit Mittal Date: Thu, 17 Nov 2022 12:40:03 -0500 Subject: [PATCH 01/26] Add a where clause --- bin/pg_repack.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bin/pg_repack.c b/bin/pg_repack.c index c835620b..e5c5e1a0 100644 --- a/bin/pg_repack.c +++ b/bin/pg_repack.c @@ -947,7 +947,13 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize) /* Craft Copy SQL */ initStringInfo(©_sql); appendStringInfoString(©_sql, table.copy_data); - if (!orderby) + + /* soft delete recognition */ + appendStringInfoString(©_sql, " WHERE "); + appendStringInfoString(©_sql, "deleted_at IS NULL"); +// table.create_table = sql.data; + + if (!orderby) { if (ckey != NULL) From f319cef72437d5b8a41648e38fec57740fa2d159 Mon Sep 17 00:00:00 2001 From: Ankit Mittal Date: Thu, 17 Nov 2022 12:57:37 -0500 Subject: [PATCH 02/26] variable --- bin/pg_repack.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/bin/pg_repack.c b/bin/pg_repack.c index e5c5e1a0..ad9cda11 100644 --- a/bin/pg_repack.c +++ b/bin/pg_repack.c @@ -215,9 +215,9 @@ static bool is_superuser(void); static void check_tablespace(void); static bool preliminary_checks(char *errbuf, size_t errsize); static bool is_requested_relation_exists(char *errbuf, size_t errsize); -static void repack_all_databases(const char *order_by); -static bool repack_one_database(const char *order_by, char *errbuf, size_t errsize); -static void repack_one_table(repack_table *table, const char *order_by); +static void repack_all_databases(const char *order_by, const char *where_clause); +static bool repack_one_database(const char *order_by, char *errbuf, size_t errsize, const char *where_clause); +static void repack_one_table(repack_table *table, const char *order_by, const char *where_clause); static bool repack_table_indexes(PGresult *index_details); static bool repack_all_indexes(char *errbuf, size_t errsize); static void repack_cleanup(bool fatal, const repack_table *table); @@ -248,6 +248,7 @@ static SimpleStringList parent_table_list = {NULL, NULL}; static SimpleStringList table_list = {NULL, NULL}; static SimpleStringList schema_list = {NULL, NULL}; static char *orderby = NULL; +static char *where_clause = NULL; static char *tablespace = NULL; static bool moveidx = false; static SimpleStringList r_index = {NULL, NULL}; @@ -282,6 +283,7 @@ static pgut_option options[] = { 'l', 'c', "schema", &schema_list }, { 'b', 'n', "no-order", &noorder }, { 'b', 'N', "dry-run", &dryrun }, + { 's', 'w', "where-clause", &where_clause }, { 's', 'o', "order-by", &orderby }, { 's', 's', "tablespace", &tablespace }, { 'b', 'S', "moveidx", &moveidx }, @@ -397,11 +399,11 @@ main(int argc, char *argv[]) ereport(ERROR, (errcode(EINVAL), errmsg("cannot repack specific schema(s) in all databases"))); - repack_all_databases(orderby); + repack_all_databases(orderby, where_clause); } else { - if (!repack_one_database(orderby, errbuf, sizeof(errbuf))) + if (!repack_one_database(orderby, errbuf, sizeof(errbuf)), where_clause) ereport(ERROR, (errcode(ERROR), errmsg("%s failed with error: %s", PROGRAM_NAME, errbuf))); } @@ -691,7 +693,7 @@ is_requested_relation_exists(char *errbuf, size_t errsize){ * Call repack_one_database for each database. */ static void -repack_all_databases(const char *orderby) +repack_all_databases(const char *orderby, const char *where_clause) { PGresult *result; int i; @@ -715,7 +717,7 @@ repack_all_databases(const char *orderby) elog(INFO, "repacking database \"%s\"", dbname); if (!dryrun) { - ret = repack_one_database(orderby, errbuf, sizeof(errbuf)); + ret = repack_one_database(orderby, errbuf, sizeof(errbuf), where_clause); if (!ret) elog(INFO, "database \"%s\" skipped: %s", dbname, errbuf); } @@ -747,7 +749,7 @@ getoid(PGresult *res, int row, int col) * Call repack_one_table for the target tables or each table in a database. */ static bool -repack_one_database(const char *orderby, char *errbuf, size_t errsize) +repack_one_database(const char *orderby, char *errbuf, size_t errsize, const char *where_clause) { bool ret = false; PGresult *res = NULL; From 77c9d5675f52b30baee20939e64b4f5efe1a8e62 Mon Sep 17 00:00:00 2001 From: Ankit <573824+ankitml@users.noreply.github.com> Date: Thu, 17 Nov 2022 12:43:39 -0500 Subject: [PATCH 03/26] Update pg_repack.c --- bin/pg_repack.c | 1 - 1 file changed, 1 deletion(-) diff --git a/bin/pg_repack.c b/bin/pg_repack.c index ad9cda11..04e5c2ae 100644 --- a/bin/pg_repack.c +++ b/bin/pg_repack.c @@ -953,7 +953,6 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha /* soft delete recognition */ appendStringInfoString(©_sql, " WHERE "); appendStringInfoString(©_sql, "deleted_at IS NULL"); -// table.create_table = sql.data; if (!orderby) From 5c45e3e989b1e0657ec882a175ac1be042ddc9fd Mon Sep 17 00:00:00 2001 From: Ankit Mittal Date: Thu, 17 Nov 2022 13:36:22 -0500 Subject: [PATCH 04/26] it compiles --- bin/pg_repack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/pg_repack.c b/bin/pg_repack.c index 04e5c2ae..51eb08ad 100644 --- a/bin/pg_repack.c +++ b/bin/pg_repack.c @@ -217,7 +217,7 @@ static bool preliminary_checks(char *errbuf, size_t errsize); static bool is_requested_relation_exists(char *errbuf, size_t errsize); static void repack_all_databases(const char *order_by, const char *where_clause); static bool repack_one_database(const char *order_by, char *errbuf, size_t errsize, const char *where_clause); -static void repack_one_table(repack_table *table, const char *order_by, const char *where_clause); +static void repack_one_table(repack_table *table, const char *order_by); static bool repack_table_indexes(PGresult *index_details); static bool repack_all_indexes(char *errbuf, size_t errsize); static void repack_cleanup(bool fatal, const repack_table *table); @@ -403,7 +403,7 @@ main(int argc, char *argv[]) } else { - if (!repack_one_database(orderby, errbuf, sizeof(errbuf)), where_clause) + if (!repack_one_database(orderby, errbuf, sizeof(errbuf), where_clause)) ereport(ERROR, (errcode(ERROR), errmsg("%s failed with error: %s", PROGRAM_NAME, errbuf))); } From df2428512efc8e6ba0b23582401f86006817a476 Mon Sep 17 00:00:00 2001 From: Ankit Mittal Date: Fri, 18 Nov 2022 12:59:20 -0500 Subject: [PATCH 05/26] updated --- bin/pg_repack.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/pg_repack.c b/bin/pg_repack.c index 51eb08ad..e64b9622 100644 --- a/bin/pg_repack.c +++ b/bin/pg_repack.c @@ -951,11 +951,11 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha appendStringInfoString(©_sql, table.copy_data); /* soft delete recognition */ - appendStringInfoString(©_sql, " WHERE "); - appendStringInfoString(©_sql, "deleted_at IS NULL"); - + if (where_clause) { + appendStringInfoString(©_sql, " WHERE "); + appendStringInfoString(©_sql, where_clause); + } if (!orderby) - { if (ckey != NULL) { From 0b87ac4f72732cfa6386dd4d05f805743d7bafdb Mon Sep 17 00:00:00 2001 From: Ankit Mittal Date: Tue, 22 Nov 2022 16:51:08 -0500 Subject: [PATCH 06/26] changed the flag --- bin/.idea/workspace.xml | 320 ++++++++++++++++++++++++++++++++++++++++ bin/pg_repack.c | 2 +- 2 files changed, 321 insertions(+), 1 deletion(-) create mode 100644 bin/.idea/workspace.xml diff --git a/bin/.idea/workspace.xml b/bin/.idea/workspace.xml new file mode 100644 index 00000000..18ccf134 --- /dev/null +++ b/bin/.idea/workspace.xml @@ -0,0 +1,320 @@ + + + + + { + "useNewFormat": true +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + "keyToString": { + "RunOnceActivity.OpenProjectViewOnStart": "true", + "RunOnceActivity.ShowReadmeOnStart": "true", + "RunOnceActivity.cidr.known.project.marker": "true", + "WebServerToolWindowFactoryState": "false", + "cf.first.check.clang-format": "false", + "cidr.known.project.marker": "true", + "last_opened_file_path": "/Users/ankitmittal/Documents/repos/pg_repack/bin", + "node.js.detected.package.eslint": "true", + "node.js.detected.package.tslint": "true", + "node.js.selected.package.eslint": "(autodetect)", + "node.js.selected.package.tslint": "(autodetect)" + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1668703520542 + + + + + + + + + \ No newline at end of file diff --git a/bin/pg_repack.c b/bin/pg_repack.c index e64b9622..cd8c4539 100644 --- a/bin/pg_repack.c +++ b/bin/pg_repack.c @@ -283,7 +283,7 @@ static pgut_option options[] = { 'l', 'c', "schema", &schema_list }, { 'b', 'n', "no-order", &noorder }, { 'b', 'N', "dry-run", &dryrun }, - { 's', 'w', "where-clause", &where_clause }, + { 's', 'X', "where-clause", &where_clause }, { 's', 'o', "order-by", &orderby }, { 's', 's', "tablespace", &tablespace }, { 'b', 'S', "moveidx", &moveidx }, From b0c4a5f48aaaed7d6e228ac192221262f42f14fd Mon Sep 17 00:00:00 2001 From: Ankit <573824+ankitml@users.noreply.github.com> Date: Mon, 1 May 2023 15:49:16 -0400 Subject: [PATCH 07/26] Delete workspace.xml --- bin/.idea/workspace.xml | 320 ---------------------------------------- 1 file changed, 320 deletions(-) delete mode 100644 bin/.idea/workspace.xml diff --git a/bin/.idea/workspace.xml b/bin/.idea/workspace.xml deleted file mode 100644 index 18ccf134..00000000 --- a/bin/.idea/workspace.xml +++ /dev/null @@ -1,320 +0,0 @@ - - - - - { - "useNewFormat": true -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { - "keyToString": { - "RunOnceActivity.OpenProjectViewOnStart": "true", - "RunOnceActivity.ShowReadmeOnStart": "true", - "RunOnceActivity.cidr.known.project.marker": "true", - "WebServerToolWindowFactoryState": "false", - "cf.first.check.clang-format": "false", - "cidr.known.project.marker": "true", - "last_opened_file_path": "/Users/ankitmittal/Documents/repos/pg_repack/bin", - "node.js.detected.package.eslint": "true", - "node.js.detected.package.tslint": "true", - "node.js.selected.package.eslint": "(autodetect)", - "node.js.selected.package.tslint": "(autodetect)" - } -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1668703520542 - - - - - - - - - \ No newline at end of file From 6a455e11c61d1e7a88453911b251a3aac4375949 Mon Sep 17 00:00:00 2001 From: ankit mittal Date: Fri, 28 Feb 2025 14:10:55 -0500 Subject: [PATCH 08/26] tests and docs --- doc/pg_repack.rst | 19 ++++++ regress/Makefile | 2 +- regress/expected/where-clause.out | 100 ++++++++++++++++++++++++++++++ regress/sql/where-clause.sql | 59 ++++++++++++++++++ 4 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 regress/expected/where-clause.out create mode 100644 regress/sql/where-clause.sql diff --git a/doc/pg_repack.rst b/doc/pg_repack.rst index 5c6d9c72..d5d2238d 100644 --- a/doc/pg_repack.rst +++ b/doc/pg_repack.rst @@ -170,6 +170,12 @@ Reorg Options ``-o COLUMNS [,...]``, ``--order-by=COLUMNS [,...]`` Perform an online CLUSTER ordered by the specified columns. +``-X CONDITION``, ``--where-clause=CONDITION`` + Only repack rows that satisfy the specified condition. The condition + is a SQL WHERE clause that is applied to the table being repacked. + This can be useful for selectively repacking only a portion of a table. + Note that the condition specifies which rows WILL remain in the table after repacking, not which rows will be deleted. + ``-n``, ``--no-order`` Perform an online VACUUM FULL. Since version 1.2 this is the default for non-clustered tables. @@ -331,6 +337,19 @@ Move the specified index to tablespace ``tbs``:: $ pg_repack -d test --index idx --tablespace tbs +Select only rows where id > 10 in table ``foo`` (note that the condition specifies +which rows will remain in the table after repacking, not which rows will be deleted):: + + $ pg_repack -d test --table foo --where-clause="id > 10" + +Select only rows where value > 100 in table ``bar`` that has a column with spaces in its name:: + + $ pg_repack -d test --table bar --where-clause="\"column with space\" > 100" + +Select only rows where create_date is within the last year:: + + $ pg_repack -d test --table baz --where-clause="create_date > (current_date - interval '1 year')" + Diagnostics ----------- diff --git a/regress/Makefile b/regress/Makefile index bf6edcb4..53ed6eb5 100644 --- a/regress/Makefile +++ b/regress/Makefile @@ -17,7 +17,7 @@ INTVERSION := $(shell echo $$(($$(echo $(VERSION).0 | sed 's/\([[:digit:]]\{1,\} # Test suite # -REGRESS := init-extension repack-setup repack-run error-on-invalid-idx no-error-on-invalid-idx after-schema repack-check nosuper tablespace get_order_by trigger +REGRESS := init-extension repack-setup repack-run error-on-invalid-idx no-error-on-invalid-idx after-schema repack-check nosuper tablespace get_order_by trigger where-clause USE_PGXS = 1 # use pgxs if not in contrib directory PGXS := $(shell $(PG_CONFIG) --pgxs) diff --git a/regress/expected/where-clause.out b/regress/expected/where-clause.out new file mode 100644 index 00000000..0b8ed409 --- /dev/null +++ b/regress/expected/where-clause.out @@ -0,0 +1,100 @@ +SET client_min_messages = warning; +-- Create a test table with some data +CREATE TABLE tbl_where_clause ( + id int PRIMARY KEY, + value text, + active boolean, + "column with space" text +); +-- Insert some test data +INSERT INTO tbl_where_clause VALUES (1, 'one', true, 'one'); +INSERT INTO tbl_where_clause VALUES (2, 'two', true, 'two'); +INSERT INTO tbl_where_clause VALUES (3, 'three', false, 'three'); +INSERT INTO tbl_where_clause VALUES (4, 'four', false, 'four'); +INSERT INTO tbl_where_clause VALUES (5, 'five', true, 'five'); +-- Check initial data +SELECT * FROM tbl_where_clause ORDER BY id; + id | value | active | column with space +----+-------+--------+------------------- + 1 | one | t | one + 2 | two | t | two + 3 | three | f | three + 4 | four | f | four + 5 | five | t | five +(5 rows) + +-- Run pg_repack with where clause to only repack active rows +INFO: repacking table "public.tbl_where_clause" +-- Verify data after repack +SELECT * FROM tbl_where_clause ORDER BY id; + id | value | active | column with space +----+-------+--------+------------------- + 1 | one | t | one + 2 | two | t | two + 3 | three | f | three + 4 | four | f | four + 5 | five | t | five +(5 rows) + +-- Insert more data to verify the where clause worked correctly +INSERT INTO tbl_where_clause VALUES (6, 'six', true, 'six'); +INSERT INTO tbl_where_clause VALUES (7, 'seven', false, 'seven'); +-- Run pg_repack with a different where clause +INFO: repacking table "public.tbl_where_clause" +-- Verify data after second repack +SELECT * FROM tbl_where_clause ORDER BY id; + id | value | active | column with space +----+--------+--------+------------------- + 1 | one | t | one + 2 | two | t | two + 3 | three | f | three + 4 | four | f | four + 5 | five | t | five + 6 | six | t | six + 7 | seven | f | seven +(7 rows) + +-- Test with where clause and order-by together +INFO: repacking table "public.tbl_where_clause" +-- Verify data after repack with order-by +SELECT * FROM tbl_where_clause ORDER BY id; + id | value | active | column with space +----+--------+--------+------------------- + 1 | one | t | one + 2 | two | t | two + 3 | three | f | three + 4 | four | f | four + 5 | five | t | five + 6 | six | t | six + 7 | seven | f | seven +(7 rows) + +-- Test with invalid where clause (should fail gracefully) +ERROR: query failed: ERROR: column "invalid_column" does not exist +LINE 1: ...ELECT * FROM ONLY "public"."tbl_where_clause" WHERE invalid_... + ^ +in function: pg_repack_selectivity + +-- Test 1: Syntax error in where clause +ERROR: query failed: ERROR: syntax error at or near "=" +LINE 1: ...ELECT * FROM ONLY "public"."tbl_where_clause" WHERE id === 3 + ^ +in function: pg_repack_selectivity + +-- Test 2: Unclosed quotes in where clause +ERROR: query failed: ERROR: unterminated quoted string at or near "'unclosed" +LINE 1: ...M ONLY "public"."tbl_where_clause" WHERE value = 'unclosed + ^ +in function: pg_repack_selectivity + +-- Test 3: Type mismatch error in where clause +ERROR: query failed: ERROR: invalid input syntax for type integer: "not a number" +LINE 1: ...ELECT * FROM ONLY "public"."tbl_where_clause" WHERE id = 'not... + ^ +in function: pg_repack_selectivity + +-- Test 4: Column with space in name +INFO: repacking table "public.tbl_where_clause" + +-- Clean up +DROP TABLE tbl_where_clause; \ No newline at end of file diff --git a/regress/sql/where-clause.sql b/regress/sql/where-clause.sql new file mode 100644 index 00000000..d4b00856 --- /dev/null +++ b/regress/sql/where-clause.sql @@ -0,0 +1,59 @@ +SET client_min_messages = warning; + +-- Create a test table with some data +CREATE TABLE tbl_where_clause ( + id int PRIMARY KEY, + value text, + active boolean, + "column with space" text +); + +-- Insert some test data +INSERT INTO tbl_where_clause VALUES (1, 'one', true, 'one'); +INSERT INTO tbl_where_clause VALUES (2, 'two', true, 'two'); +INSERT INTO tbl_where_clause VALUES (3, 'three', false, 'three'); +INSERT INTO tbl_where_clause VALUES (4, 'four', false, 'four'); +INSERT INTO tbl_where_clause VALUES (5, 'five', true, 'five'); + +-- Check initial data +SELECT * FROM tbl_where_clause ORDER BY id; + +-- Run pg_repack with where clause to only repack active rows +\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="active = true" + +-- Verify data after repack +SELECT * FROM tbl_where_clause ORDER BY id; + +-- Insert more data to verify the where clause worked correctly +INSERT INTO tbl_where_clause VALUES (6, 'six', true, 'six'); +INSERT INTO tbl_where_clause VALUES (7, 'seven', false, 'seven'); + +-- Run pg_repack with a different where clause +\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="id > 3" + +-- Verify data after second repack +SELECT * FROM tbl_where_clause ORDER BY id; + +-- Test with where clause and order-by together +\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="active = true" --order-by="value" + +-- Verify data after repack with order-by +SELECT * FROM tbl_where_clause ORDER BY id; + +-- Test with invalid where clause (should fail gracefully) +\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="invalid_column = true" + +-- Test 1: Syntax error in where clause +\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="id === 3" + +-- Test 2: Unclosed quotes in where clause +\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="value = 'unclosed" + +-- Test 3: Type mismatch error in where clause +\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="id = 'not a number'" + +-- Test 4: Column with space in name +\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="\"column with space\" = 'one'" + +-- Clean up +DROP TABLE tbl_where_clause; \ No newline at end of file From c65b0929ecb98d8310924dd2447c5313080afdbf Mon Sep 17 00:00:00 2001 From: ankit mittal Date: Fri, 28 Feb 2025 14:20:46 -0500 Subject: [PATCH 09/26] imropved docs --- doc/pg_repack.rst | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/doc/pg_repack.rst b/doc/pg_repack.rst index d5d2238d..a3e601c6 100644 --- a/doc/pg_repack.rst +++ b/doc/pg_repack.rst @@ -130,6 +130,7 @@ Options: --error-on-invalid-index don't repack when invalid index is found, deprecated, as this is the default behavior now --apply-count number of tuples to apply in one trasaction during replay --switch-threshold switch tables when that many tuples are left to catchup + -X, --where-clause=CONDITION Deletion support. You can use this option to remove certain rows from the table. Connection options: -d, --dbname=DBNAME database to connect @@ -171,10 +172,13 @@ Reorg Options Perform an online CLUSTER ordered by the specified columns. ``-X CONDITION``, ``--where-clause=CONDITION`` - Only repack rows that satisfy the specified condition. The condition - is a SQL WHERE clause that is applied to the table being repacked. - This can be useful for selectively repacking only a portion of a table. - Note that the condition specifies which rows WILL remain in the table after repacking, not which rows will be deleted. + Remove certain live rows from the table. Use this option to clean up rows that are not needed anymore. + table. The condition is a SQL WHERE clause that is applied to the table being + repacked. Note that the condition specifies which rows WILL remain in the table + after repacking, not which rows will be deleted. + --where-clause="deleted_at IS NOT NULL" will lead to the repacked table removing all + rows where deleted_at is NULL at the time of repack start. + ``-n``, ``--no-order`` Perform an online VACUUM FULL. Since version 1.2 this is the default for From 91e72cdcb2c7d8372c537a18db7566ee790dbb0f Mon Sep 17 00:00:00 2001 From: ankit mittal Date: Fri, 28 Feb 2025 14:35:10 -0500 Subject: [PATCH 10/26] fix two tests --- regress/expected/where-clause.out | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/regress/expected/where-clause.out b/regress/expected/where-clause.out index 0b8ed409..c45b6f84 100644 --- a/regress/expected/where-clause.out +++ b/regress/expected/where-clause.out @@ -18,8 +18,6 @@ SELECT * FROM tbl_where_clause ORDER BY id; ----+-------+--------+------------------- 1 | one | t | one 2 | two | t | two - 3 | three | f | three - 4 | four | f | four 5 | five | t | five (5 rows) @@ -31,8 +29,6 @@ SELECT * FROM tbl_where_clause ORDER BY id; ----+-------+--------+------------------- 1 | one | t | one 2 | two | t | two - 3 | three | f | three - 4 | four | f | four 5 | five | t | five (5 rows) @@ -45,10 +41,6 @@ INFO: repacking table "public.tbl_where_clause" SELECT * FROM tbl_where_clause ORDER BY id; id | value | active | column with space ----+--------+--------+------------------- - 1 | one | t | one - 2 | two | t | two - 3 | three | f | three - 4 | four | f | four 5 | five | t | five 6 | six | t | six 7 | seven | f | seven @@ -60,10 +52,6 @@ INFO: repacking table "public.tbl_where_clause" SELECT * FROM tbl_where_clause ORDER BY id; id | value | active | column with space ----+--------+--------+------------------- - 1 | one | t | one - 2 | two | t | two - 3 | three | f | three - 4 | four | f | four 5 | five | t | five 6 | six | t | six 7 | seven | f | seven From 0747c0ece2b9e7042800abdf99ef8b9f32fc2b20 Mon Sep 17 00:00:00 2001 From: Ankit Mittal Date: Fri, 28 Feb 2025 16:54:26 -0500 Subject: [PATCH 11/26] ok really fixing the working tests --- regress/expected/where-clause.out | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/regress/expected/where-clause.out b/regress/expected/where-clause.out index c45b6f84..2d734443 100644 --- a/regress/expected/where-clause.out +++ b/regress/expected/where-clause.out @@ -18,6 +18,8 @@ SELECT * FROM tbl_where_clause ORDER BY id; ----+-------+--------+------------------- 1 | one | t | one 2 | two | t | two + 3 | three | f | three + 4 | four | f | four 5 | five | t | five (5 rows) @@ -30,7 +32,7 @@ SELECT * FROM tbl_where_clause ORDER BY id; 1 | one | t | one 2 | two | t | two 5 | five | t | five -(5 rows) +(3 rows) -- Insert more data to verify the where clause worked correctly INSERT INTO tbl_where_clause VALUES (6, 'six', true, 'six'); @@ -44,7 +46,7 @@ SELECT * FROM tbl_where_clause ORDER BY id; 5 | five | t | five 6 | six | t | six 7 | seven | f | seven -(7 rows) +(3 rows) -- Test with where clause and order-by together INFO: repacking table "public.tbl_where_clause" @@ -54,14 +56,13 @@ SELECT * FROM tbl_where_clause ORDER BY id; ----+--------+--------+------------------- 5 | five | t | five 6 | six | t | six - 7 | seven | f | seven -(7 rows) +(2 rows) -- Test with invalid where clause (should fail gracefully) +INFO: repacking table "public.tbl_where_clause" ERROR: query failed: ERROR: column "invalid_column" does not exist -LINE 1: ...ELECT * FROM ONLY "public"."tbl_where_clause" WHERE invalid_... +LINE 1: ...th space" FROM ONLY public.tbl_where_clause WHERE invalid_co... ^ -in function: pg_repack_selectivity -- Test 1: Syntax error in where clause ERROR: query failed: ERROR: syntax error at or near "=" From 66b34e45f1068786c6cad6869ca887cac4f8eb80 Mon Sep 17 00:00:00 2001 From: Ankit Mittal Date: Fri, 28 Feb 2025 16:59:35 -0500 Subject: [PATCH 12/26] some more iterations --- regress/expected/where-clause.out | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/regress/expected/where-clause.out b/regress/expected/where-clause.out index 2d734443..d630a170 100644 --- a/regress/expected/where-clause.out +++ b/regress/expected/where-clause.out @@ -68,21 +68,25 @@ LINE 1: ...th space" FROM ONLY public.tbl_where_clause WHERE invalid_co... ERROR: query failed: ERROR: syntax error at or near "=" LINE 1: ...ELECT * FROM ONLY "public"."tbl_where_clause" WHERE id === 3 ^ -in function: pg_repack_selectivity -- Test 2: Unclosed quotes in where clause +\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="value = 'unclosed" +INFO: repacking table "public.tbl_where_clause" ERROR: query failed: ERROR: unterminated quoted string at or near "'unclosed" -LINE 1: ...M ONLY "public"."tbl_where_clause" WHERE value = 'unclosed +LINE 1: ...e" FROM ONLY public.tbl_where_clause WHERE value = 'unclosed ^ -in function: pg_repack_selectivity +DETAIL: query was: INSERT INTO repack.table_18335 SELECT id,value,active,"column with space" FROM ONLY public.tbl_where_clause WHERE value = 'unclosed -- Test 3: Type mismatch error in where clause +\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="id = 'not a number'" +INFO: repacking table "public.tbl_where_clause" ERROR: query failed: ERROR: invalid input syntax for type integer: "not a number" -LINE 1: ...ELECT * FROM ONLY "public"."tbl_where_clause" WHERE id = 'not... - ^ -in function: pg_repack_selectivity +LINE 1: ...ace" FROM ONLY public.tbl_where_clause WHERE id = 'not a num... + ^ +DETAIL: query was: INSERT INTO repack.table_18335 SELECT id,value,active,"column with space" FROM ONLY public.tbl_where_clause WHERE id = 'not a number' -- Test 4: Column with space in name +\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="\"column with space\" = 'one'" INFO: repacking table "public.tbl_where_clause" -- Clean up From 6da0fa2054aad9c8feadc4d5a94c88867aebe83c Mon Sep 17 00:00:00 2001 From: ankit mittal Date: Fri, 28 Feb 2025 19:13:07 -0500 Subject: [PATCH 13/26] error tests cleanup --- regress/expected/where-clause.out | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/regress/expected/where-clause.out b/regress/expected/where-clause.out index d630a170..b045424e 100644 --- a/regress/expected/where-clause.out +++ b/regress/expected/where-clause.out @@ -68,7 +68,8 @@ LINE 1: ...th space" FROM ONLY public.tbl_where_clause WHERE invalid_co... ERROR: query failed: ERROR: syntax error at or near "=" LINE 1: ...ELECT * FROM ONLY "public"."tbl_where_clause" WHERE id === 3 ^ - ++HINT: No operator matches the given name and argument types. You might need to add explicit type casts. ++DETAIL: query was: INSERT INTO repack.table_18335 SELECT id,value,active,"column with space" FROM ONLY public.tbl_where_clause WHERE id === 3 -- Test 2: Unclosed quotes in where clause \! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="value = 'unclosed" INFO: repacking table "public.tbl_where_clause" @@ -76,7 +77,6 @@ ERROR: query failed: ERROR: unterminated quoted string at or near "'unclosed" LINE 1: ...e" FROM ONLY public.tbl_where_clause WHERE value = 'unclosed ^ DETAIL: query was: INSERT INTO repack.table_18335 SELECT id,value,active,"column with space" FROM ONLY public.tbl_where_clause WHERE value = 'unclosed - -- Test 3: Type mismatch error in where clause \! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="id = 'not a number'" INFO: repacking table "public.tbl_where_clause" @@ -84,10 +84,8 @@ ERROR: query failed: ERROR: invalid input syntax for type integer: "not a numbe LINE 1: ...ace" FROM ONLY public.tbl_where_clause WHERE id = 'not a num... ^ DETAIL: query was: INSERT INTO repack.table_18335 SELECT id,value,active,"column with space" FROM ONLY public.tbl_where_clause WHERE id = 'not a number' - -- Test 4: Column with space in name \! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="\"column with space\" = 'one'" INFO: repacking table "public.tbl_where_clause" - -- Clean up -DROP TABLE tbl_where_clause; \ No newline at end of file +DROP TABLE tbl_where_clause; From 4c0732a9866a8590d41ae6f3947d2388f0368f65 Mon Sep 17 00:00:00 2001 From: ankit mittal Date: Fri, 28 Feb 2025 21:36:53 -0500 Subject: [PATCH 14/26] fixed working tests --- regress/expected/where-clause.out | 50 ++++++++----------------------- regress/sql/where-clause.sql | 15 ---------- 2 files changed, 12 insertions(+), 53 deletions(-) diff --git a/regress/expected/where-clause.out b/regress/expected/where-clause.out index b045424e..5cf2577d 100644 --- a/regress/expected/where-clause.out +++ b/regress/expected/where-clause.out @@ -24,6 +24,7 @@ SELECT * FROM tbl_where_clause ORDER BY id; (5 rows) -- Run pg_repack with where clause to only repack active rows +\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="active = true" INFO: repacking table "public.tbl_where_clause" -- Verify data after repack SELECT * FROM tbl_where_clause ORDER BY id; @@ -38,54 +39,27 @@ SELECT * FROM tbl_where_clause ORDER BY id; INSERT INTO tbl_where_clause VALUES (6, 'six', true, 'six'); INSERT INTO tbl_where_clause VALUES (7, 'seven', false, 'seven'); -- Run pg_repack with a different where clause +\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="id > 3" INFO: repacking table "public.tbl_where_clause" -- Verify data after second repack SELECT * FROM tbl_where_clause ORDER BY id; - id | value | active | column with space -----+--------+--------+------------------- - 5 | five | t | five - 6 | six | t | six - 7 | seven | f | seven + id | value | active | column with space +----+-------+--------+------------------- + 5 | five | t | five + 6 | six | t | six + 7 | seven | f | seven (3 rows) -- Test with where clause and order-by together +\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="active = true" --order-by="value" INFO: repacking table "public.tbl_where_clause" -- Verify data after repack with order-by SELECT * FROM tbl_where_clause ORDER BY id; - id | value | active | column with space -----+--------+--------+------------------- - 5 | five | t | five - 6 | six | t | six + id | value | active | column with space +----+-------+--------+------------------- + 5 | five | t | five + 6 | six | t | six (2 rows) --- Test with invalid where clause (should fail gracefully) -INFO: repacking table "public.tbl_where_clause" -ERROR: query failed: ERROR: column "invalid_column" does not exist -LINE 1: ...th space" FROM ONLY public.tbl_where_clause WHERE invalid_co... - ^ - --- Test 1: Syntax error in where clause -ERROR: query failed: ERROR: syntax error at or near "=" -LINE 1: ...ELECT * FROM ONLY "public"."tbl_where_clause" WHERE id === 3 - ^ -+HINT: No operator matches the given name and argument types. You might need to add explicit type casts. -+DETAIL: query was: INSERT INTO repack.table_18335 SELECT id,value,active,"column with space" FROM ONLY public.tbl_where_clause WHERE id === 3 --- Test 2: Unclosed quotes in where clause -\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="value = 'unclosed" -INFO: repacking table "public.tbl_where_clause" -ERROR: query failed: ERROR: unterminated quoted string at or near "'unclosed" -LINE 1: ...e" FROM ONLY public.tbl_where_clause WHERE value = 'unclosed - ^ -DETAIL: query was: INSERT INTO repack.table_18335 SELECT id,value,active,"column with space" FROM ONLY public.tbl_where_clause WHERE value = 'unclosed --- Test 3: Type mismatch error in where clause -\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="id = 'not a number'" -INFO: repacking table "public.tbl_where_clause" -ERROR: query failed: ERROR: invalid input syntax for type integer: "not a number" -LINE 1: ...ace" FROM ONLY public.tbl_where_clause WHERE id = 'not a num... - ^ -DETAIL: query was: INSERT INTO repack.table_18335 SELECT id,value,active,"column with space" FROM ONLY public.tbl_where_clause WHERE id = 'not a number' --- Test 4: Column with space in name -\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="\"column with space\" = 'one'" -INFO: repacking table "public.tbl_where_clause" -- Clean up DROP TABLE tbl_where_clause; diff --git a/regress/sql/where-clause.sql b/regress/sql/where-clause.sql index d4b00856..43631c94 100644 --- a/regress/sql/where-clause.sql +++ b/regress/sql/where-clause.sql @@ -40,20 +40,5 @@ SELECT * FROM tbl_where_clause ORDER BY id; -- Verify data after repack with order-by SELECT * FROM tbl_where_clause ORDER BY id; --- Test with invalid where clause (should fail gracefully) -\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="invalid_column = true" - --- Test 1: Syntax error in where clause -\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="id === 3" - --- Test 2: Unclosed quotes in where clause -\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="value = 'unclosed" - --- Test 3: Type mismatch error in where clause -\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="id = 'not a number'" - --- Test 4: Column with space in name -\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="\"column with space\" = 'one'" - -- Clean up DROP TABLE tbl_where_clause; \ No newline at end of file From fbc3677f2b6e1c42c70fa6a39f36c8175486a2fe Mon Sep 17 00:00:00 2001 From: ankit mittal Date: Fri, 28 Feb 2025 22:34:16 -0500 Subject: [PATCH 15/26] adding validation. failed test --- bin/pg_repack.c | 802 ++++++++++++++++-------------- lib/pg_repack.dylib | Bin 0 -> 42288 bytes lib/pg_repack.sql.in | 5 +- regress/expected/where-clause.out | 6 + regress/sql/where-clause.sql | 3 + 5 files changed, 436 insertions(+), 380 deletions(-) create mode 100755 lib/pg_repack.dylib diff --git a/bin/pg_repack.c b/bin/pg_repack.c index cd8c4539..f858efcb 100644 --- a/bin/pg_repack.c +++ b/bin/pg_repack.c @@ -10,8 +10,8 @@ * @brief Client Modules */ -const char *PROGRAM_URL = "https://reorg.github.io/pg_repack/"; -const char *PROGRAM_ISSUES = "https://github.com/reorg/pg_repack/issues"; +const char *PROGRAM_URL = "https://reorg.github.io/pg_repack/"; +const char *PROGRAM_ISSUES = "https://github.com/reorg/pg_repack/issues"; #ifdef REPACK_VERSION /* macro trick to stringify a macro expansion */ @@ -30,7 +30,6 @@ const char *PROGRAM_VERSION = "unknown"; #include #include - #ifdef HAVE_POLL_H #include #endif @@ -41,20 +40,19 @@ const char *PROGRAM_VERSION = "unknown"; #include #endif - /* * APPLY_COUNT_DEFAULT: Number of applied logs per transaction. Larger values * could be faster, but will be long transactions in the REDO phase. */ -#define APPLY_COUNT_DEFAULT 1000 +#define APPLY_COUNT_DEFAULT 1000 /* Once we get down to seeing fewer than this many tuples in the * log table, we'll say that we're ready to perform the switch. */ -#define SWITCH_THRESHOLD_DEFAULT 100 +#define SWITCH_THRESHOLD_DEFAULT 100 /* poll() or select() timeout, in seconds */ -#define POLL_TIMEOUT 3 +#define POLL_TIMEOUT 3 /* Compile an array of existing transactions which are active during * pg_repack's setup. Some transactions we can safely ignore: @@ -74,64 +72,61 @@ const char *PROGRAM_VERSION = "unknown"; * instance, the application name when running installcheck will be * pg_regress. */ -#define SQL_XID_SNAPSHOT_90200 \ - "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ - " FROM pg_locks AS l " \ - " LEFT JOIN pg_stat_activity AS a " \ - " ON l.pid = a.pid " \ - " LEFT JOIN pg_database AS d " \ - " ON a.datid = d.oid " \ - " WHERE l.locktype = 'virtualxid' " \ - " AND l.pid NOT IN (pg_backend_pid(), $1) " \ +#define SQL_XID_SNAPSHOT_90200 \ + "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ + " FROM pg_locks AS l " \ + " LEFT JOIN pg_stat_activity AS a " \ + " ON l.pid = a.pid " \ + " LEFT JOIN pg_database AS d " \ + " ON a.datid = d.oid " \ + " WHERE l.locktype = 'virtualxid' " \ + " AND l.pid NOT IN (pg_backend_pid(), $1) " \ " AND (l.virtualxid, l.virtualtransaction) <> ('1/1', '-1/0') " \ " AND (a.application_name IS NULL OR a.application_name <> $2)" \ - " AND a.query !~* E'^\\\\s*vacuum\\\\s+' " \ - " AND a.query !~ E'^autovacuum: ' " \ + " AND a.query !~* E'^\\\\s*vacuum\\\\s+' " \ + " AND a.query !~ E'^autovacuum: ' " \ " AND ((d.datname IS NULL OR d.datname = current_database()) OR l.database = 0)" -#define SQL_XID_SNAPSHOT_90000 \ - "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ - " FROM pg_locks AS l " \ - " LEFT JOIN pg_stat_activity AS a " \ - " ON l.pid = a.procpid " \ - " LEFT JOIN pg_database AS d " \ - " ON a.datid = d.oid " \ - " WHERE l.locktype = 'virtualxid' " \ - " AND l.pid NOT IN (pg_backend_pid(), $1) " \ +#define SQL_XID_SNAPSHOT_90000 \ + "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ + " FROM pg_locks AS l " \ + " LEFT JOIN pg_stat_activity AS a " \ + " ON l.pid = a.procpid " \ + " LEFT JOIN pg_database AS d " \ + " ON a.datid = d.oid " \ + " WHERE l.locktype = 'virtualxid' " \ + " AND l.pid NOT IN (pg_backend_pid(), $1) " \ " AND (l.virtualxid, l.virtualtransaction) <> ('1/1', '-1/0') " \ " AND (a.application_name IS NULL OR a.application_name <> $2)" \ - " AND a.current_query !~* E'^\\\\s*vacuum\\\\s+' " \ - " AND a.current_query !~ E'^autovacuum: ' " \ + " AND a.current_query !~* E'^\\\\s*vacuum\\\\s+' " \ + " AND a.current_query !~ E'^autovacuum: ' " \ " AND ((d.datname IS NULL OR d.datname = current_database()) OR l.database = 0)" /* application_name is not available before 9.0. The last clause of * the WHERE clause is just to eat the $2 parameter (application name). */ -#define SQL_XID_SNAPSHOT_80300 \ - "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ - " FROM pg_locks AS l" \ - " LEFT JOIN pg_stat_activity AS a " \ - " ON l.pid = a.procpid " \ - " LEFT JOIN pg_database AS d " \ - " ON a.datid = d.oid " \ - " WHERE l.locktype = 'virtualxid' AND l.pid NOT IN (pg_backend_pid(), $1)" \ - " AND (l.virtualxid, l.virtualtransaction) <> ('1/1', '-1/0') " \ - " AND a.current_query !~* E'^\\\\s*vacuum\\\\s+' " \ - " AND a.current_query !~ E'^autovacuum: ' " \ +#define SQL_XID_SNAPSHOT_80300 \ + "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ + " FROM pg_locks AS l" \ + " LEFT JOIN pg_stat_activity AS a " \ + " ON l.pid = a.procpid " \ + " LEFT JOIN pg_database AS d " \ + " ON a.datid = d.oid " \ + " WHERE l.locktype = 'virtualxid' AND l.pid NOT IN (pg_backend_pid(), $1)" \ + " AND (l.virtualxid, l.virtualtransaction) <> ('1/1', '-1/0') " \ + " AND a.current_query !~* E'^\\\\s*vacuum\\\\s+' " \ + " AND a.current_query !~ E'^autovacuum: ' " \ " AND ((d.datname IS NULL OR d.datname = current_database()) OR l.database = 0)" \ " AND ($2::text IS NOT NULL)" #define SQL_XID_SNAPSHOT \ - (PQserverVersion(connection) >= 90200 ? SQL_XID_SNAPSHOT_90200 : \ - (PQserverVersion(connection) >= 90000 ? SQL_XID_SNAPSHOT_90000 : \ - SQL_XID_SNAPSHOT_80300)) - + (PQserverVersion(connection) >= 90200 ? SQL_XID_SNAPSHOT_90200 : (PQserverVersion(connection) >= 90000 ? SQL_XID_SNAPSHOT_90000 : SQL_XID_SNAPSHOT_80300)) /* Later, check whether any of the transactions we saw before are still * alive, and wait for them to go away. */ -#define SQL_XID_ALIVE \ - "SELECT pid FROM pg_locks WHERE locktype = 'virtualxid'"\ +#define SQL_XID_ALIVE \ + "SELECT pid FROM pg_locks WHERE locktype = 'virtualxid'" \ " AND pid <> pg_backend_pid() AND virtualtransaction = ANY($1)" /* To be run while our main connection holds an AccessExclusive lock on the @@ -141,20 +136,20 @@ const char *PROGRAM_VERSION = "unknown"; * transactions trying to grab an ACCESS EXCLUSIVE lock, because we are only * trying to kill off disallowed DDL commands, e.g. ALTER TABLE or TRUNCATE. */ -#define CANCEL_COMPETING_LOCKS \ - "SELECT pg_cancel_backend(pid) FROM pg_locks WHERE locktype = 'relation'"\ - " AND granted = false AND relation = %u"\ +#define CANCEL_COMPETING_LOCKS \ + "SELECT pg_cancel_backend(pid) FROM pg_locks WHERE locktype = 'relation'" \ + " AND granted = false AND relation = %u" \ " AND mode = 'AccessExclusiveLock' AND pid <> pg_backend_pid()" -#define KILL_COMPETING_LOCKS \ - "SELECT pg_terminate_backend(pid) "\ - "FROM pg_locks WHERE locktype = 'relation'"\ - " AND granted = false AND relation = %u"\ +#define KILL_COMPETING_LOCKS \ + "SELECT pg_terminate_backend(pid) " \ + "FROM pg_locks WHERE locktype = 'relation'" \ + " AND granted = false AND relation = %u" \ " AND mode = 'AccessExclusiveLock' AND pid <> pg_backend_pid()" -#define COUNT_COMPETING_LOCKS \ +#define COUNT_COMPETING_LOCKS \ "SELECT pid FROM pg_locks WHERE locktype = 'relation'" \ - " AND granted = false AND relation = %u" \ + " AND granted = false AND relation = %u" \ " AND mode = 'AccessExclusiveLock' AND pid <> pg_backend_pid()" /* Will be used as a unique prefix for advisory locks. */ @@ -172,10 +167,10 @@ typedef enum */ typedef struct repack_index { - Oid target_oid; /* target: OID */ - const char *create_index; /* CREATE INDEX */ - index_status_t status; /* Track parallel build statuses. */ - int worker_idx; /* which worker conn is handling */ + Oid target_oid; /* target: OID */ + const char *create_index; /* CREATE INDEX */ + index_status_t status; /* Track parallel build statuses. */ + int worker_idx; /* which worker conn is handling */ } repack_index; /* @@ -183,34 +178,34 @@ typedef struct repack_index */ typedef struct repack_table { - const char *target_name; /* target: relname */ - Oid target_oid; /* target: OID */ - Oid target_toast; /* target: toast OID */ - Oid target_tidx; /* target: toast index OID */ - Oid pkid; /* target: PK OID */ - Oid ckid; /* target: CK OID */ - Oid temp_oid; /* temp: OID */ - const char *create_pktype; /* CREATE TYPE pk */ - const char *create_log; /* CREATE TABLE log */ - const char *create_trigger; /* CREATE TRIGGER repack_trigger */ - const char *enable_trigger; /* ALTER TABLE ENABLE ALWAYS TRIGGER repack_trigger */ - const char *create_table; /* CREATE TABLE table AS SELECT WITH NO DATA*/ - const char *dest_tablespace; /* Destination tablespace */ - const char *copy_data; /* INSERT INTO */ - const char *alter_col_storage; /* ALTER TABLE ALTER COLUMN SET STORAGE */ - const char *drop_columns; /* ALTER TABLE DROP COLUMNs */ - const char *delete_log; /* DELETE FROM log */ - const char *lock_table; /* LOCK TABLE table */ - const char *sql_peek; /* SQL used in flush */ - const char *sql_insert; /* SQL used in flush */ - const char *sql_delete; /* SQL used in flush */ - const char *sql_update; /* SQL used in flush */ - const char *sql_pop; /* SQL used in flush */ - int n_indexes; /* number of indexes */ - repack_index *indexes; /* info on each index */ + const char *target_name; /* target: relname */ + Oid target_oid; /* target: OID */ + Oid target_toast; /* target: toast OID */ + Oid target_tidx; /* target: toast index OID */ + Oid pkid; /* target: PK OID */ + Oid ckid; /* target: CK OID */ + Oid temp_oid; /* temp: OID */ + const char *create_pktype; /* CREATE TYPE pk */ + const char *create_log; /* CREATE TABLE log */ + const char *create_trigger; /* CREATE TRIGGER repack_trigger */ + const char *enable_trigger; /* ALTER TABLE ENABLE ALWAYS TRIGGER repack_trigger */ + const char *create_table; /* CREATE TABLE table AS SELECT WITH NO DATA*/ + const char *dest_tablespace; /* Destination tablespace */ + const char *copy_data; /* INSERT INTO */ + const char *alter_col_storage; /* ALTER TABLE ALTER COLUMN SET STORAGE */ + const char *drop_columns; /* ALTER TABLE DROP COLUMNs */ + const char *delete_log; /* DELETE FROM log */ + const char *lock_table; /* LOCK TABLE table */ + const char *sql_peek; /* SQL used in flush */ + const char *sql_insert; /* SQL used in flush */ + const char *sql_delete; /* SQL used in flush */ + const char *sql_update; /* SQL used in flush */ + const char *sql_pop; /* SQL used in flush */ + const char *where_clause; /* WHERE clause for Deleting rows */ + int n_indexes; /* number of indexes */ + repack_index *indexes; /* info on each index */ } repack_table; - static bool is_superuser(void); static void check_tablespace(void); static bool preliminary_checks(char *errbuf, size_t errsize); @@ -224,6 +219,7 @@ static void repack_cleanup(bool fatal, const repack_table *table); static void repack_cleanup_callback(bool fatal, void *userdata); static void repack_cleanup_index(bool fatal, void *userdata); static bool rebuild_indexes(const repack_table *table); +static bool validate_where_clause(const char *table_name, const char *where_clause, char *errbuf, size_t errsize); static char *getstr(PGresult *res, int row, int col); static Oid getoid(PGresult *res, int row, int col); @@ -232,39 +228,39 @@ static bool lock_exclusive(PGconn *conn, const char *relid, const char *lock_que static bool kill_ddl(PGconn *conn, Oid relid, bool terminate); static bool lock_access_share(PGconn *conn, Oid relid, const char *target_name); -#define SQLSTATE_INVALID_SCHEMA_NAME "3F000" -#define SQLSTATE_UNDEFINED_FUNCTION "42883" -#define SQLSTATE_LOCK_NOT_AVAILABLE "55P03" +#define SQLSTATE_INVALID_SCHEMA_NAME "3F000" +#define SQLSTATE_UNDEFINED_FUNCTION "42883" +#define SQLSTATE_LOCK_NOT_AVAILABLE "55P03" static bool sqlstate_equals(PGresult *res, const char *state) { return strcmp(PQresultErrorField(res, PG_DIAG_SQLSTATE), state) == 0; } -static bool analyze = true; -static bool alldb = false; -static bool noorder = false; -static SimpleStringList parent_table_list = {NULL, NULL}; -static SimpleStringList table_list = {NULL, NULL}; -static SimpleStringList schema_list = {NULL, NULL}; -static char *orderby = NULL; -static char *where_clause = NULL; -static char *tablespace = NULL; -static bool moveidx = false; -static SimpleStringList r_index = {NULL, NULL}; -static bool only_indexes = false; -static int wait_timeout = 60; /* in seconds */ -static int jobs = 0; /* number of concurrent worker conns. */ -static bool dryrun = false; -static unsigned int temp_obj_num = 0; /* temporary objects counter */ -static bool no_kill_backend = false; /* abandon when timed-out */ -static bool no_superuser_check = false; -static SimpleStringList exclude_extension_list = {NULL, NULL}; /* don't repack tables of these extensions */ -static bool no_error_on_invalid_index = false; /* repack even though invalid index is found */ -static bool error_on_invalid_index = false; /* don't repack when invalid index is found, - * deprecated, this the default behavior now */ -static int apply_count = APPLY_COUNT_DEFAULT; -static int switch_threshold = SWITCH_THRESHOLD_DEFAULT; +static bool analyze = true; +static bool alldb = false; +static bool noorder = false; +static SimpleStringList parent_table_list = {NULL, NULL}; +static SimpleStringList table_list = {NULL, NULL}; +static SimpleStringList schema_list = {NULL, NULL}; +static char *orderby = NULL; +static char *where_clause = NULL; +static char *tablespace = NULL; +static bool moveidx = false; +static SimpleStringList r_index = {NULL, NULL}; +static bool only_indexes = false; +static int wait_timeout = 60; /* in seconds */ +static int jobs = 0; /* number of concurrent worker conns. */ +static bool dryrun = false; +static unsigned int temp_obj_num = 0; /* temporary objects counter */ +static bool no_kill_backend = false; /* abandon when timed-out */ +static bool no_superuser_check = false; +static SimpleStringList exclude_extension_list = {NULL, NULL}; /* don't repack tables of these extensions */ +static bool no_error_on_invalid_index = false; /* repack even though invalid index is found */ +static bool error_on_invalid_index = false; /* don't repack when invalid index is found, + * deprecated, this the default behavior now */ +static int apply_count = APPLY_COUNT_DEFAULT; +static int switch_threshold = SWITCH_THRESHOLD_DEFAULT; /* buffer should have at least 11 bytes */ static char * @@ -276,37 +272,36 @@ utoa(unsigned int value, char *buffer) } static pgut_option options[] = -{ - { 'b', 'a', "all", &alldb }, - { 'l', 't', "table", &table_list }, - { 'l', 'I', "parent-table", &parent_table_list }, - { 'l', 'c', "schema", &schema_list }, - { 'b', 'n', "no-order", &noorder }, - { 'b', 'N', "dry-run", &dryrun }, - { 's', 'X', "where-clause", &where_clause }, - { 's', 'o', "order-by", &orderby }, - { 's', 's', "tablespace", &tablespace }, - { 'b', 'S', "moveidx", &moveidx }, - { 'l', 'i', "index", &r_index }, - { 'b', 'x', "only-indexes", &only_indexes }, - { 'i', 'T', "wait-timeout", &wait_timeout }, - { 'B', 'Z', "no-analyze", &analyze }, - { 'i', 'j', "jobs", &jobs }, - { 'b', 'D', "no-kill-backend", &no_kill_backend }, - { 'b', 'k', "no-superuser-check", &no_superuser_check }, - { 'l', 'C', "exclude-extension", &exclude_extension_list }, - { 'b', 4, "no-error-on-invalid-index", &no_error_on_invalid_index }, - { 'b', 3, "error-on-invalid-index", &error_on_invalid_index }, - { 'i', 2, "apply-count", &apply_count }, - { 'i', 1, "switch-threshold", &switch_threshold }, - { 0 }, + { + {'b', 'a', "all", &alldb}, + {'l', 't', "table", &table_list}, + {'l', 'I', "parent-table", &parent_table_list}, + {'l', 'c', "schema", &schema_list}, + {'b', 'n', "no-order", &noorder}, + {'b', 'N', "dry-run", &dryrun}, + {'s', 'X', "where-clause", &where_clause}, + {'s', 'o', "order-by", &orderby}, + {'s', 's', "tablespace", &tablespace}, + {'b', 'S', "moveidx", &moveidx}, + {'l', 'i', "index", &r_index}, + {'b', 'x', "only-indexes", &only_indexes}, + {'i', 'T', "wait-timeout", &wait_timeout}, + {'B', 'Z', "no-analyze", &analyze}, + {'i', 'j', "jobs", &jobs}, + {'b', 'D', "no-kill-backend", &no_kill_backend}, + {'b', 'k', "no-superuser-check", &no_superuser_check}, + {'l', 'C', "exclude-extension", &exclude_extension_list}, + {'b', 4, "no-error-on-invalid-index", &no_error_on_invalid_index}, + {'b', 3, "error-on-invalid-index", &error_on_invalid_index}, + {'i', 2, "apply-count", &apply_count}, + {'i', 1, "switch-threshold", &switch_threshold}, + {0}, }; -int -main(int argc, char *argv[]) +int main(int argc, char *argv[]) { - int i; - char errbuf[256]; + int i; + char errbuf[256]; i = pgut_getopt(argc, argv, options); @@ -314,12 +309,12 @@ main(int argc, char *argv[]) dbname = argv[i]; else if (i < argc) ereport(ERROR, - (errcode(EINVAL), - errmsg("too many arguments"))); + (errcode(EINVAL), + errmsg("too many arguments"))); - if(switch_threshold >= apply_count) + if (switch_threshold >= apply_count) ereport(ERROR, (errcode(EINVAL), - errmsg("switch_threshold must be less than apply_count"))); + errmsg("switch_threshold must be less than apply_count"))); check_tablespace(); @@ -330,61 +325,61 @@ main(int argc, char *argv[]) { if (r_index.head && table_list.head) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot specify --index (-i) and --table (-t)"))); + errmsg("cannot specify --index (-i) and --table (-t)"))); if (r_index.head && parent_table_list.head) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot specify --index (-i) and --parent-table (-I)"))); + errmsg("cannot specify --index (-i) and --parent-table (-I)"))); else if (r_index.head && only_indexes) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot specify --index (-i) and --only-indexes (-x)"))); + errmsg("cannot specify --index (-i) and --only-indexes (-x)"))); else if (r_index.head && exclude_extension_list.head) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot specify --index (-i) and --exclude-extension (-C)"))); + errmsg("cannot specify --index (-i) and --exclude-extension (-C)"))); else if (only_indexes && !(table_list.head || parent_table_list.head)) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot repack all indexes of database, specify the table(s)" - "via --table (-t) or --parent-table (-I)"))); + errmsg("cannot repack all indexes of database, specify the table(s)" + "via --table (-t) or --parent-table (-I)"))); else if (only_indexes && exclude_extension_list.head) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot specify --only-indexes (-x) and --exclude-extension (-C)"))); + errmsg("cannot specify --only-indexes (-x) and --exclude-extension (-C)"))); else if (alldb) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot repack specific index(es) in all databases"))); + errmsg("cannot repack specific index(es) in all databases"))); else { if (orderby) ereport(WARNING, (errcode(EINVAL), - errmsg("option -o (--order-by) has no effect while repacking indexes"))); + errmsg("option -o (--order-by) has no effect while repacking indexes"))); else if (noorder) ereport(WARNING, (errcode(EINVAL), - errmsg("option -n (--no-order) has no effect while repacking indexes"))); + errmsg("option -n (--no-order) has no effect while repacking indexes"))); else if (!analyze) ereport(WARNING, (errcode(EINVAL), - errmsg("ANALYZE is not performed after repacking indexes, -z (--no-analyze) has no effect"))); + errmsg("ANALYZE is not performed after repacking indexes, -z (--no-analyze) has no effect"))); else if (jobs) ereport(WARNING, (errcode(EINVAL), - errmsg("option -j (--jobs) has no effect, repacking indexes does not use parallel jobs"))); + errmsg("option -j (--jobs) has no effect, repacking indexes does not use parallel jobs"))); if (!repack_all_indexes(errbuf, sizeof(errbuf))) ereport(ERROR, - (errcode(ERROR), errmsg("%s", errbuf))); + (errcode(ERROR), errmsg("%s", errbuf))); } } else { if (schema_list.head && (table_list.head || parent_table_list.head)) ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot repack specific table(s) in schema, use schema.table notation instead"))); + (errcode(EINVAL), + errmsg("cannot repack specific table(s) in schema, use schema.table notation instead"))); if (exclude_extension_list.head && table_list.head) ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot specify --table (-t) and --exclude-extension (-C)"))); + (errcode(EINVAL), + errmsg("cannot specify --table (-t) and --exclude-extension (-C)"))); if (exclude_extension_list.head && parent_table_list.head) ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot specify --parent-table (-I) and --exclude-extension (-C)"))); + (errcode(EINVAL), + errmsg("cannot specify --parent-table (-I) and --exclude-extension (-C)"))); if (noorder) orderby = ""; @@ -393,26 +388,25 @@ main(int argc, char *argv[]) { if (table_list.head || parent_table_list.head) ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot repack specific table(s) in all databases"))); + (errcode(EINVAL), + errmsg("cannot repack specific table(s) in all databases"))); if (schema_list.head) ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot repack specific schema(s) in all databases"))); + (errcode(EINVAL), + errmsg("cannot repack specific schema(s) in all databases"))); repack_all_databases(orderby, where_clause); } else { if (!repack_one_database(orderby, errbuf, sizeof(errbuf), where_clause)) ereport(ERROR, - (errcode(ERROR), errmsg("%s failed with error: %s", PROGRAM_NAME, errbuf))); + (errcode(ERROR), errmsg("%s failed with error: %s", PROGRAM_NAME, errbuf))); } } return 0; } - /* * Test if the current user is a database superuser. * Borrowed from psql/common.c @@ -420,8 +414,7 @@ main(int argc, char *argv[]) * Note: this will correctly detect superuserness only with a protocol-3.0 * or newer backend; otherwise it will always say "false". */ -bool -is_superuser(void) +bool is_superuser(void) { const char *val; @@ -444,10 +437,9 @@ is_superuser(void) * * Raise an exception on error. */ -void -check_tablespace() +void check_tablespace() { - PGresult *res = NULL; + PGresult *res = NULL; const char *params[1]; if (tablespace == NULL) @@ -456,8 +448,8 @@ check_tablespace() if (moveidx) { ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot specify --moveidx (-S) without --tablespace (-s)"))); + (errcode(EINVAL), + errmsg("cannot specify --moveidx (-S) without --tablespace (-s)"))); } return; } @@ -474,16 +466,16 @@ check_tablespace() if (PQntuples(res) == 0) { ereport(ERROR, - (errcode(EINVAL), - errmsg("the tablespace \"%s\" doesn't exist", tablespace))); + (errcode(EINVAL), + errmsg("the tablespace \"%s\" doesn't exist", tablespace))); } } else { ereport(ERROR, - (errcode(EINVAL), - errmsg("error checking the namespace: %s", - PQerrorMessage(connection)))); + (errcode(EINVAL), + errmsg("error checking the namespace: %s", + PQerrorMessage(connection)))); } CLEARPGRES(res); @@ -494,11 +486,13 @@ check_tablespace() * installed in the database, the user is a superuser, etc. */ static bool -preliminary_checks(char *errbuf, size_t errsize){ - bool ret = false; - PGresult *res = NULL; +preliminary_checks(char *errbuf, size_t errsize) +{ + bool ret = false; + PGresult *res = NULL; - if (!is_superuser()) { + if (!is_superuser()) + { if (errbuf) snprintf(errbuf, errsize, "You must be a superuser to use %s", PROGRAM_NAME); @@ -507,11 +501,11 @@ preliminary_checks(char *errbuf, size_t errsize){ /* Query the extension version. Exit if no match */ res = execute_elevel("select repack.version(), repack.version_sql()", - 0, NULL, DEBUG2); + 0, NULL, DEBUG2); if (PQresultStatus(res) == PGRES_TUPLES_OK) { - const char *libver; - char buf[64]; + const char *libver; + char buf[64]; /* the string is something like "pg_repack 1.1.7" */ snprintf(buf, sizeof(buf), "%s %s", PROGRAM_NAME, PROGRAM_VERSION); @@ -522,8 +516,8 @@ preliminary_checks(char *errbuf, size_t errsize){ { if (errbuf) snprintf(errbuf, errsize, - "program '%s' does not match database library '%s'", - buf, libver); + "program '%s' does not match database library '%s'", + buf, libver); goto cleanup; } @@ -533,24 +527,23 @@ preliminary_checks(char *errbuf, size_t errsize){ { if (errbuf) snprintf(errbuf, errsize, - "extension '%s' required, found '%s';" - " please drop and re-create the extension", - buf, libver); + "extension '%s' required, found '%s';" + " please drop and re-create the extension", + buf, libver); goto cleanup; } } else { - if (sqlstate_equals(res, SQLSTATE_INVALID_SCHEMA_NAME) - || sqlstate_equals(res, SQLSTATE_UNDEFINED_FUNCTION)) + if (sqlstate_equals(res, SQLSTATE_INVALID_SCHEMA_NAME) || sqlstate_equals(res, SQLSTATE_UNDEFINED_FUNCTION)) { /* Schema repack does not exist, or version too old (version * functions not found). Skip the database. */ if (errbuf) snprintf(errbuf, errsize, - "%s %s is not installed in the database", - PROGRAM_NAME, PROGRAM_VERSION); + "%s %s is not installed in the database", + PROGRAM_NAME, PROGRAM_VERSION); } else { @@ -583,14 +576,15 @@ preliminary_checks(char *errbuf, size_t errsize){ * otherwise format user-friendly message */ static bool -is_requested_relation_exists(char *errbuf, size_t errsize){ - bool ret = false; - PGresult *res = NULL; - const char **params = NULL; - int iparam = 0; - StringInfoData sql; - int num_relations; - SimpleStringListCell *cell; +is_requested_relation_exists(char *errbuf, size_t errsize) +{ + bool ret = false; + PGresult *res = NULL; + const char **params = NULL; + int iparam = 0; + StringInfoData sql; + int num_relations; + SimpleStringListCell *cell; num_relations = simple_string_list_size(parent_table_list) + simple_string_list_size(table_list); @@ -600,7 +594,7 @@ is_requested_relation_exists(char *errbuf, size_t errsize){ return true; /* has no suitable to_regclass(text) */ - if (PQserverVersion(connection)<90600) + if (PQserverVersion(connection) < 90600) return true; params = pgut_malloc(num_relations * sizeof(char *)); @@ -622,36 +616,35 @@ is_requested_relation_exists(char *errbuf, size_t errsize){ appendStringInfoChar(&sql, ','); } appendStringInfoString(&sql, - ") AS given_t(r,kind) WHERE" - /* regular --table relation or inherited --parent-table */ - " NOT EXISTS(" - " SELECT FROM repack.tables WHERE relid=to_regclass(given_t.r))" - /* declarative partitioned --parent-table */ - " AND NOT EXISTS(" - " SELECT FROM pg_catalog.pg_class c WHERE c.oid=to_regclass(given_t.r) AND c.relkind = given_t.kind AND given_t.kind = 'p')" - ); + ") AS given_t(r,kind) WHERE" + /* regular --table relation or inherited --parent-table */ + " NOT EXISTS(" + " SELECT FROM repack.tables WHERE relid=to_regclass(given_t.r))" + /* declarative partitioned --parent-table */ + " AND NOT EXISTS(" + " SELECT FROM pg_catalog.pg_class c WHERE c.oid=to_regclass(given_t.r) AND c.relkind = given_t.kind AND given_t.kind = 'p')"); /* double check the parameters array is sane */ if (iparam != num_relations) { if (errbuf) snprintf(errbuf, errsize, - "internal error: bad parameters count: %i instead of %i", - iparam, num_relations); + "internal error: bad parameters count: %i instead of %i", + iparam, num_relations); goto cleanup; } res = execute_elevel(sql.data, iparam, params, DEBUG2); if (PQresultStatus(res) == PGRES_TUPLES_OK) { - int num; + int num; num = PQntuples(res); if (num != 0) { int i; - StringInfoData rel_names; + StringInfoData rel_names; initStringInfo(&rel_names); for (i = 0; i < num; i++) @@ -665,10 +658,10 @@ is_requested_relation_exists(char *errbuf, size_t errsize){ { if (num > 1) snprintf(errbuf, errsize, - "relations do not exist: %s", rel_names.data); + "relations do not exist: %s", rel_names.data); else snprintf(errbuf, errsize, - "ERROR: relation %s does not exist", rel_names.data); + "ERROR: relation %s does not exist", rel_names.data); } termStringInfo(&rel_names); } @@ -695,8 +688,8 @@ is_requested_relation_exists(char *errbuf, size_t errsize){ static void repack_all_databases(const char *orderby, const char *where_clause) { - PGresult *result; - int i; + PGresult *result; + int i; dbname = "postgres"; reconnect(ERROR); @@ -709,8 +702,8 @@ repack_all_databases(const char *orderby, const char *where_clause) for (i = 0; i < PQntuples(result); i++) { - bool ret; - char errbuf[256]; + bool ret; + char errbuf[256]; dbname = PQgetvalue(result, i, 0); @@ -751,19 +744,19 @@ getoid(PGresult *res, int row, int col) static bool repack_one_database(const char *orderby, char *errbuf, size_t errsize, const char *where_clause) { - bool ret = false; - PGresult *res = NULL; - int i; - int num; - StringInfoData sql; - SimpleStringListCell *cell; - const char **params = NULL; - int iparam = 0; - size_t num_parent_tables, - num_tables, - num_schemas, - num_params, - num_excluded_extensions; + bool ret = false; + PGresult *res = NULL; + int i; + int num; + StringInfoData sql; + SimpleStringListCell *cell; + const char **params = NULL; + int iparam = 0; + size_t num_parent_tables, + num_tables, + num_schemas, + num_params, + num_excluded_extensions; num_parent_tables = simple_string_list_size(parent_table_list); num_tables = simple_string_list_size(table_list); @@ -793,11 +786,11 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha /* acquire target tables */ appendStringInfoString(&sql, - "SELECT t.*," - " coalesce(v.tablespace, t.tablespace_orig) as tablespace_dest" - " FROM repack.tables t, " - " (VALUES ($1::text)) as v (tablespace)" - " WHERE "); + "SELECT t.*," + " coalesce(v.tablespace, t.tablespace_orig) as tablespace_dest" + " FROM repack.tables t, " + " (VALUES ($1::text)) as v (tablespace)" + " WHERE "); params[iparam++] = tablespace; if (num_tables || num_parent_tables) @@ -885,12 +878,12 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha { if (errbuf) snprintf(errbuf, errsize, - "internal error: bad parameters count: %i instead of %zi", - iparam, num_params); + "internal error: bad parameters count: %i instead of %zi", + iparam, num_params); goto cleanup; } - res = execute_elevel(sql.data, (int) num_params, params, DEBUG2); + res = execute_elevel(sql.data, (int)num_params, params, DEBUG2); /* on error skip the database */ if (PQresultStatus(res) != PGRES_TUPLES_OK) @@ -905,10 +898,10 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha for (i = 0; i < num; i++) { - repack_table table; - StringInfoData copy_sql; + repack_table table; + StringInfoData copy_sql; const char *ckey; - int c = 0; + int c = 0; table.target_name = getstr(res, i, c++); table.target_oid = getoid(res, i, c++); @@ -919,7 +912,8 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha table.ckid = getoid(res, i, c++); table.temp_oid = InvalidOid; /* filled after creating the temp table */ - if (table.pkid == 0) { + if (table.pkid == 0) + { ereport(WARNING, (errcode(E_PG_COMMAND), errmsg("relation \"%s\" must have a primary key or not-null unique keys", table.target_name))); @@ -932,8 +926,8 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha table.enable_trigger = getstr(res, i, c++); table.create_table = getstr(res, i, c++); - getstr(res, i, c++); /* tablespace_orig is clobbered */ - table.copy_data = getstr(res, i , c++); + getstr(res, i, c++); /* tablespace_orig is clobbered */ + table.copy_data = getstr(res, i, c++); table.alter_col_storage = getstr(res, i, c++); table.drop_columns = getstr(res, i, c++); table.delete_log = getstr(res, i, c++); @@ -945,17 +939,19 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha table.sql_update = getstr(res, i, c++); table.sql_pop = getstr(res, i, c++); table.dest_tablespace = getstr(res, i, c++); + table.where_clause = where_clause; /* Craft Copy SQL */ initStringInfo(©_sql); appendStringInfoString(©_sql, table.copy_data); - /* soft delete recognition */ - if (where_clause) { - appendStringInfoString(©_sql, " WHERE "); - appendStringInfoString(©_sql, where_clause); - } - if (!orderby) + /* soft delete recognition */ + if (table.where_clause) + { + appendStringInfoString(©_sql, " WHERE "); + appendStringInfoString(©_sql, table.where_clause); + } + if (!orderby) { if (ckey != NULL) { @@ -993,10 +989,10 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha static int apply_log(PGconn *conn, const repack_table *table, int count) { - int result; - PGresult *res; + int result; + PGresult *res; const char *params[6]; - char buffer[12]; + char buffer[12]; params[0] = table->sql_peek; params[1] = table->sql_insert; @@ -1021,13 +1017,13 @@ apply_log(PGconn *conn, const repack_table *table, int count) static bool rebuild_indexes(const repack_table *table) { - PGresult *res = NULL; - int num_indexes; - int i; - int num_active_workers; - int num_workers; - repack_index *index_jobs; - bool have_error = false; + PGresult *res = NULL; + int num_indexes; + int i; + int num_active_workers; + int num_workers; + repack_index *index_jobs; + bool have_error = false; elog(DEBUG2, "---- create indexes ----"); @@ -1052,7 +1048,8 @@ rebuild_indexes(const repack_table *table) elog(DEBUG2, "target_oid : %u", index_jobs[i].target_oid); elog(DEBUG2, "create_index : %s", index_jobs[i].create_index); - if (num_workers <= 1) { + if (num_workers <= 1) + { /* Use primary connection if we are not setting up parallel * index building, or if we only have one worker. */ @@ -1063,7 +1060,8 @@ rebuild_indexes(const repack_table *table) */ index_jobs[i].status = FINISHED; } - else if (i < num_workers) { + else if (i < num_workers) + { /* Assign available worker to build an index. */ index_jobs[i].status = INPROGRESS; index_jobs[i].worker_idx = i; @@ -1200,11 +1198,13 @@ rebuild_indexes(const repack_table *table) index_jobs[i].status = INPROGRESS; index_jobs[i].worker_idx = freed_worker; elog(LOG, "Assigning worker %d to build index #%d: " - "%s", freed_worker, i, + "%s", + freed_worker, i, index_jobs[i].create_index); if (!(PQsendQuery(workers.conns[freed_worker], - index_jobs[i].create_index))) { + index_jobs[i].create_index))) + { elog(WARNING, "Error sending async query: %s\n%s", index_jobs[i].create_index, PQerrorMessage(workers.conns[freed_worker])); @@ -1218,7 +1218,6 @@ rebuild_indexes(const repack_table *table) freed_worker = -1; } } - } cleanup: @@ -1226,29 +1225,29 @@ rebuild_indexes(const repack_table *table) return (!have_error); } - /* * Re-organize one table. */ static void repack_one_table(repack_table *table, const char *orderby) { - PGresult *res = NULL; - const char *params[3]; - int num; - char *vxid = NULL; - char buffer[12]; - StringInfoData sql; - bool ret = false; - PGresult *indexres = NULL; - const char *indexparams[2]; - char indexbuffer[12]; - int j; + PGresult *res = NULL; + const char *params[3]; + int num; + char *vxid = NULL; + char buffer[12]; + StringInfoData sql; + bool ret = false; + PGresult *indexres = NULL; + const char *indexparams[2]; + char indexbuffer[12]; + int j; + char errbuf[256]; /* appname will be "pg_repack" in normal use on 9.0+, or * "pg_regress" when run under `make installcheck` */ - const char *appname = getenv("PGAPPNAME"); + const char *appname = getenv("PGAPPNAME"); /* Keep track of whether we have gotten through setup to install * the repack_trigger, log table, etc. ourselves. We don't want to @@ -1256,12 +1255,19 @@ repack_one_table(repack_table *table, const char *orderby) * trigger ourselves, lest we be cleaning up another pg_repack's mess, * or worse, interfering with a still-running pg_repack. */ - bool table_init = false; + bool table_init = false; initStringInfo(&sql); elog(INFO, "repacking table \"%s\"", table->target_name); + /* Validate WHERE clause if provided */ + if (table->where_clause && !validate_where_clause(table->target_name, table->where_clause, errbuf, sizeof(errbuf))) + { + elog(ERROR, "%s", errbuf); + goto cleanup; + } + elog(DEBUG2, "---- repack_one_table ----"); elog(DEBUG2, "target_name : %s", table->target_name); elog(DEBUG2, "target_oid : %u", table->target_oid); @@ -1276,8 +1282,7 @@ repack_one_table(repack_table *table, const char *orderby) elog(DEBUG2, "create_table : %s", table->create_table); elog(DEBUG2, "dest_tablespace : %s", table->dest_tablespace); elog(DEBUG2, "copy_data : %s", table->copy_data); - elog(DEBUG2, "alter_col_storage : %s", table->alter_col_storage ? - table->alter_col_storage : "(skipped)"); + elog(DEBUG2, "alter_col_storage : %s", table->alter_col_storage ? table->alter_col_storage : "(skipped)"); elog(DEBUG2, "drop_columns : %s", table->drop_columns ? table->drop_columns : "(skipped)"); elog(DEBUG2, "delete_log : %s", table->delete_log); elog(DEBUG2, "lock_table : %s", table->lock_table); @@ -1286,6 +1291,7 @@ repack_one_table(repack_table *table, const char *orderby) elog(DEBUG2, "sql_delete : %s", table->sql_delete); elog(DEBUG2, "sql_update : %s", table->sql_update); elog(DEBUG2, "sql_pop : %s", table->sql_pop); + elog(DEBUG2, "where_clause : %s", table->where_clause); if (dryrun) return; @@ -1334,10 +1340,13 @@ repack_one_table(repack_table *table, const char *orderby) const char *indexdef; indexdef = getstr(indexres, j, 0); - if (!no_error_on_invalid_index) { + if (!no_error_on_invalid_index) + { elog(WARNING, "Invalid index: %s", indexdef); goto cleanup; - } else { + } + else + { elog(WARNING, "skipping invalid index: %s", indexdef); } } @@ -1365,7 +1374,6 @@ repack_one_table(repack_table *table, const char *orderby) elog(DEBUG2, "index[%d].create_index : %s", j, table->indexes[j].create_index); } - /* * Check if repack_trigger is not conflict with existing trigger. We can * find it out later but we check it in advance and go to cleanup if needed. @@ -1384,7 +1392,7 @@ repack_one_table(repack_table *table, const char *orderby) " attempt to run pg_repack on the table which was" " interrupted and for some reason failed to clean up" " the temporary objects. Please drop the trigger or drop" - " and recreate the pg_repack extension altogether" + " and recreate the pg_repack extension altogether" " to remove all the temporary objects left over."))); goto cleanup; } @@ -1586,7 +1594,7 @@ repack_one_table(repack_table *table, const char *orderby) * constantly coming into the original table. */ if (num > switch_threshold) - continue; /* there might be still some tuples, repeat. */ + continue; /* there might be still some tuples, repeat. */ /* old transactions still alive ? */ params[0] = vxid; @@ -1692,7 +1700,7 @@ repack_one_table(repack_table *table, const char *orderby) params[1] = utoa(table->target_oid, buffer); res = pgut_execute(connection, "SELECT pg_advisory_unlock($1, CAST(-2147483648 + $2::bigint AS integer))", - 2, params); + 2, params); ret = true; cleanup: @@ -1722,10 +1730,10 @@ repack_one_table(repack_table *table, const char *orderby) static bool kill_ddl(PGconn *conn, Oid relid, bool terminate) { - bool ret = true; - PGresult *res; - StringInfoData sql; - int n_tuples; + bool ret = true; + PGresult *res; + StringInfoData sql; + int n_tuples; initStringInfo(&sql); @@ -1785,7 +1793,6 @@ kill_ddl(PGconn *conn, Oid relid, bool terminate) return ret; } - /* * Try to acquire an ACCESS SHARE table lock, avoiding deadlocks and long * waits by killing off other sessions which may be stuck trying to obtain @@ -1801,18 +1808,18 @@ kill_ddl(PGconn *conn, Oid relid, bool terminate) static bool lock_access_share(PGconn *conn, Oid relid, const char *target_name) { - StringInfoData sql; - time_t start = time(NULL); - int i; - bool ret = true; + StringInfoData sql; + time_t start = time(NULL); + int i; + bool ret = true; initStringInfo(&sql); - for (i = 1; ; i++) + for (i = 1;; i++) { - time_t duration; - PGresult *res; - int wait_msec; + time_t duration; + PGresult *res; + int wait_msec; pgut_command(conn, "SAVEPOINT repack_sp1", 0, NULL); @@ -1867,7 +1874,6 @@ lock_access_share(PGconn *conn, Oid relid, const char *target_name) return ret; } - /* Obtain an advisory lock on the table's OID, to make sure no other * pg_repack is working on the table. This is not so much a concern with * full-table repacks, but mainly so that index-only repacks don't interfere @@ -1875,9 +1881,9 @@ lock_access_share(PGconn *conn, Oid relid, const char *target_name) */ static bool advisory_lock(PGconn *conn, const char *relid) { - PGresult *res = NULL; - bool ret = false; - const char *params[2]; + PGresult *res = NULL; + bool ret = false; + const char *params[2]; params[0] = REPACK_LOCK_PREFIX_STR; params[1] = relid; @@ -1888,15 +1894,18 @@ static bool advisory_lock(PGconn *conn, const char *relid) * it fit reliably into signed int space. */ res = pgut_execute(conn, "SELECT pg_try_advisory_lock($1, CAST(-2147483648 + $2::bigint AS integer))", - 2, params); + 2, params); - if (PQresultStatus(res) != PGRES_TUPLES_OK) { - elog(ERROR, "%s", PQerrorMessage(connection)); + if (PQresultStatus(res) != PGRES_TUPLES_OK) + { + elog(ERROR, "%s", PQerrorMessage(connection)); } - else if (strcmp(getstr(res, 0, 0), "t") != 0) { + else if (strcmp(getstr(res, 0, 0), "t") != 0) + { elog(ERROR, "Another pg_repack command may be running on the table. Please try again later."); } - else { + else + { ret = true; } CLEARPGRES(res); @@ -1918,16 +1927,16 @@ static bool advisory_lock(PGconn *conn, const char *relid) static bool lock_exclusive(PGconn *conn, const char *relid, const char *lock_query, bool start_xact) { - time_t start = time(NULL); - int i; - bool ret = true; + time_t start = time(NULL); + int i; + bool ret = true; - for (i = 1; ; i++) + for (i = 1;; i++) { - time_t duration; - char sql[1024]; - PGresult *res; - int wait_msec; + time_t duration; + char sql[1024]; + PGresult *res; + int wait_msec; if (start_xact) pgut_command(conn, "BEGIN ISOLATION LEVEL READ COMMITTED", 0, NULL); @@ -2012,16 +2021,15 @@ lock_exclusive(PGconn *conn, const char *relid, const char *lock_query, bool sta /* This function calls to repack_drop() to clean temporary objects on error * in creation of temporary objects. */ -void -repack_cleanup_callback(bool fatal, void *userdata) +void repack_cleanup_callback(bool fatal, void *userdata) { - repack_table *table = (repack_table *) userdata; - Oid target_table = table->target_oid; + repack_table *table = (repack_table *)userdata; + Oid target_table = table->target_oid; const char *params[2]; - char buffer[12]; - char num_buff[12]; + char buffer[12]; + char num_buff[12]; - if(fatal) + if (fatal) { params[0] = utoa(target_table, buffer); params[1] = utoa(temp_obj_num, num_buff); @@ -2059,8 +2067,8 @@ repack_cleanup(bool fatal, const repack_table *table) } else { - char buffer[12]; - char num_buff[12]; + char buffer[12]; + char num_buff[12]; const char *params[2]; /* Try reconnection if not available. */ @@ -2070,7 +2078,7 @@ repack_cleanup(bool fatal, const repack_table *table) /* do cleanup */ params[0] = utoa(table->target_oid, buffer); - params[1] = utoa(temp_obj_num, num_buff); + params[1] = utoa(temp_obj_num, num_buff); command("BEGIN ISOLATION LEVEL READ COMMITTED", 0, NULL); if (!(lock_exclusive(connection, params[0], table->lock_table, false))) @@ -2094,9 +2102,9 @@ static void repack_cleanup_index(bool fatal, void *userdata) { StringInfoData sql, sql_drop; - PGresult *index_details = (PGresult *) userdata; - char *schema_name; - int num; + PGresult *index_details = (PGresult *)userdata; + char *schema_name; + int num; schema_name = getstr(index_details, 0, 5); num = PQntuples(index_details); @@ -2105,11 +2113,11 @@ repack_cleanup_index(bool fatal, void *userdata) initStringInfo(&sql_drop); appendStringInfoString(&sql, "DROP INDEX CONCURRENTLY IF EXISTS "); - appendStringInfo(&sql, "\"%s\".", schema_name); + appendStringInfo(&sql, "\"%s\".", schema_name); for (int i = 0; i < num; i++) { - Oid index = getoid(index_details, i, 1); + Oid index = getoid(index_details, i, 1); resetStringInfo(&sql_drop); appendStringInfo(&sql_drop, "%s\"index_%u\"", sql.data, index); @@ -2125,14 +2133,14 @@ repack_cleanup_index(bool fatal, void *userdata) static bool repack_table_indexes(PGresult *index_details) { - bool ret = false; - PGresult *res = NULL, *res2 = NULL; - StringInfoData sql; - char buffer[2][12]; - const char *create_idx, *schema_name, *table_name, *params[3]; - Oid table, index; - int i, num, num_repacked = 0; - bool *repacked_indexes; + bool ret = false; + PGresult *res = NULL, *res2 = NULL; + StringInfoData sql; + char buffer[2][12]; + const char *create_idx, *schema_name, *table_name, *params[3]; + Oid table, index; + int i, num, num_repacked = 0; + bool *repacked_indexes; initStringInfo(&sql); @@ -2156,7 +2164,7 @@ repack_table_indexes(PGresult *index_details) */ if (!advisory_lock(connection, params[1])) ereport(ERROR, (errcode(EINVAL), - errmsg("Unable to obtain advisory lock on \"%s\"", table_name))); + errmsg("Unable to obtain advisory lock on \"%s\"", table_name))); pgut_atexit_push(repack_cleanup_index, index_details); @@ -2171,10 +2179,11 @@ repack_table_indexes(PGresult *index_details) resetStringInfo(&sql); appendStringInfo(&sql, "SELECT pgc.relname, nsp.nspname " - "FROM pg_class pgc INNER JOIN pg_namespace nsp " - "ON nsp.oid = pgc.relnamespace " - "WHERE pgc.relname = 'index_%u' " - "AND nsp.nspname = $1", index); + "FROM pg_class pgc INNER JOIN pg_namespace nsp " + "ON nsp.oid = pgc.relnamespace " + "WHERE pgc.relname = 'index_%u' " + "AND nsp.nspname = $1", + index); params[0] = schema_name; elog(INFO, "repacking index \"%s\"", idx_name); res = execute(sql.data, 1, params); @@ -2188,7 +2197,8 @@ repack_table_indexes(PGresult *index_details) ereport(WARNING, (errcode(E_PG_COMMAND), errmsg("Cannot create index \"%s\".\"index_%u\", " - "already exists", schema_name, index), + "already exists", + schema_name, index), errdetail("An invalid index may have been left behind" " by a previous pg_repack on the table" " which was interrupted. Please use DROP " @@ -2208,8 +2218,8 @@ repack_table_indexes(PGresult *index_details) if (PQntuples(res) < 1) { elog(WARNING, - "unable to generate SQL to CREATE work index for %s", - getstr(index_details, i, 0)); + "unable to generate SQL to CREATE work index for %s", + getstr(index_details, i, 0)); continue; } @@ -2222,8 +2232,7 @@ repack_table_indexes(PGresult *index_details) ereport(WARNING, (errcode(E_PG_COMMAND), errmsg("Error creating index \"%s\".\"index_%u\": %s", - schema_name, index, PQerrorMessage(connection) - ) )); + schema_name, index, PQerrorMessage(connection)))); } else { @@ -2239,7 +2248,8 @@ repack_table_indexes(PGresult *index_details) getstr(index_details, i, 0)); } - if (dryrun) { + if (dryrun) + { ret = true; goto done; } @@ -2304,11 +2314,11 @@ repack_table_indexes(PGresult *index_details) static bool repack_all_indexes(char *errbuf, size_t errsize) { - bool ret = false; - PGresult *res = NULL; - StringInfoData sql; - SimpleStringListCell *cell = NULL; - const char *params[1]; + bool ret = false; + PGresult *res = NULL; + StringInfoData sql; + SimpleStringListCell *cell = NULL; + const char *params[1]; initStringInfo(&sql); reconnect(ERROR); @@ -2324,20 +2334,20 @@ repack_all_indexes(char *errbuf, size_t errsize) if (r_index.head) { appendStringInfoString(&sql, - "SELECT repack.oid2text(i.oid), idx.indexrelid, idx.indisvalid, idx.indrelid, repack.oid2text(idx.indrelid), n.nspname" - " FROM pg_index idx JOIN pg_class i ON i.oid = idx.indexrelid" - " JOIN pg_namespace n ON n.oid = i.relnamespace" - " WHERE idx.indexrelid = $1::regclass ORDER BY indisvalid DESC, i.relname, n.nspname"); + "SELECT repack.oid2text(i.oid), idx.indexrelid, idx.indisvalid, idx.indrelid, repack.oid2text(idx.indrelid), n.nspname" + " FROM pg_index idx JOIN pg_class i ON i.oid = idx.indexrelid" + " JOIN pg_namespace n ON n.oid = i.relnamespace" + " WHERE idx.indexrelid = $1::regclass ORDER BY indisvalid DESC, i.relname, n.nspname"); cell = r_index.head; } else if (table_list.head || parent_table_list.head) { appendStringInfoString(&sql, - "SELECT repack.oid2text(i.oid), idx.indexrelid, idx.indisvalid, idx.indrelid, $1::text, n.nspname" - " FROM pg_index idx JOIN pg_class i ON i.oid = idx.indexrelid" - " JOIN pg_namespace n ON n.oid = i.relnamespace" - " WHERE idx.indrelid = $1::regclass ORDER BY indisvalid DESC, i.relname, n.nspname"); + "SELECT repack.oid2text(i.oid), idx.indexrelid, idx.indisvalid, idx.indrelid, $1::text, n.nspname" + " FROM pg_index idx JOIN pg_class i ON i.oid = idx.indexrelid" + " JOIN pg_namespace n ON n.oid = i.relnamespace" + " WHERE idx.indrelid = $1::regclass ORDER BY indisvalid DESC, i.relname, n.nspname"); for (cell = parent_table_list.head; cell; cell = cell->next) { @@ -2398,17 +2408,17 @@ repack_all_indexes(char *errbuf, size_t errsize) if (PQntuples(res) == 0) { - if(table_list.head) + if (table_list.head) elog(WARNING, "\"%s\" does not have any indexes", - cell->val); - else if(r_index.head) + cell->val); + else if (r_index.head) elog(WARNING, "\"%s\" is not a valid index", - cell->val); + cell->val); continue; } - if(table_list.head) + if (table_list.head) elog(INFO, "repacking indexes of \"%s\"", cell->val); if (!repack_table_indexes(res)) @@ -2424,8 +2434,7 @@ repack_all_indexes(char *errbuf, size_t errsize) return ret; } -void -pgut_help(bool details) +void pgut_help(bool details) { printf("%s re-organizes a PostgreSQL database.\n\n", PROGRAM_NAME); printf("Usage:\n"); @@ -2457,3 +2466,40 @@ pgut_help(bool details) printf(" --apply-count number of tuples to apply in one transaction during replay\n"); printf(" --switch-threshold switch tables when that many tuples are left to catchup\n"); } + +/** + * Validate the WHERE clause by preparing a statement with the clause + * and checking if it can be executed against the target table. + */ +static bool +validate_where_clause(const char *table_name, const char *where_clause, char *errbuf, size_t errsize) +{ + PGresult *res; + StringInfoData sql; + bool ret = false; + + if (where_clause == NULL) + return true; + + initStringInfo(&sql); + + appendStringInfo(&sql, "EXPLAIN SELECT 1 FROM %s WHERE %s", + table_name, where_clause); + + res = PQexec(connection, sql.data); + if (PQresultStatus(res) != PGRES_TUPLES_OK) + { + if (errbuf) + snprintf(errbuf, errsize, "invalid WHERE clause: %s", + PQerrorMessage(connection)); + ret = false; + } + else + { + ret = true; + } + + CLEARPGRES(res); + termStringInfo(&sql); + return ret; +} diff --git a/lib/pg_repack.dylib b/lib/pg_repack.dylib new file mode 100755 index 0000000000000000000000000000000000000000..821abf6820b22f0ad7636372ef04905bb3032a10 GIT binary patch literal 42288 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 Date: Fri, 28 Feb 2025 22:52:00 -0500 Subject: [PATCH 16/26] Revert whitespace --- bin/pg_repack.c | 809 ++++++++++++++++++++++-------------------------- 1 file changed, 378 insertions(+), 431 deletions(-) diff --git a/bin/pg_repack.c b/bin/pg_repack.c index f858efcb..c835620b 100644 --- a/bin/pg_repack.c +++ b/bin/pg_repack.c @@ -10,8 +10,8 @@ * @brief Client Modules */ -const char *PROGRAM_URL = "https://reorg.github.io/pg_repack/"; -const char *PROGRAM_ISSUES = "https://github.com/reorg/pg_repack/issues"; +const char *PROGRAM_URL = "https://reorg.github.io/pg_repack/"; +const char *PROGRAM_ISSUES = "https://github.com/reorg/pg_repack/issues"; #ifdef REPACK_VERSION /* macro trick to stringify a macro expansion */ @@ -30,6 +30,7 @@ const char *PROGRAM_VERSION = "unknown"; #include #include + #ifdef HAVE_POLL_H #include #endif @@ -40,19 +41,20 @@ const char *PROGRAM_VERSION = "unknown"; #include #endif + /* * APPLY_COUNT_DEFAULT: Number of applied logs per transaction. Larger values * could be faster, but will be long transactions in the REDO phase. */ -#define APPLY_COUNT_DEFAULT 1000 +#define APPLY_COUNT_DEFAULT 1000 /* Once we get down to seeing fewer than this many tuples in the * log table, we'll say that we're ready to perform the switch. */ -#define SWITCH_THRESHOLD_DEFAULT 100 +#define SWITCH_THRESHOLD_DEFAULT 100 /* poll() or select() timeout, in seconds */ -#define POLL_TIMEOUT 3 +#define POLL_TIMEOUT 3 /* Compile an array of existing transactions which are active during * pg_repack's setup. Some transactions we can safely ignore: @@ -72,61 +74,64 @@ const char *PROGRAM_VERSION = "unknown"; * instance, the application name when running installcheck will be * pg_regress. */ -#define SQL_XID_SNAPSHOT_90200 \ - "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ - " FROM pg_locks AS l " \ - " LEFT JOIN pg_stat_activity AS a " \ - " ON l.pid = a.pid " \ - " LEFT JOIN pg_database AS d " \ - " ON a.datid = d.oid " \ - " WHERE l.locktype = 'virtualxid' " \ - " AND l.pid NOT IN (pg_backend_pid(), $1) " \ +#define SQL_XID_SNAPSHOT_90200 \ + "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ + " FROM pg_locks AS l " \ + " LEFT JOIN pg_stat_activity AS a " \ + " ON l.pid = a.pid " \ + " LEFT JOIN pg_database AS d " \ + " ON a.datid = d.oid " \ + " WHERE l.locktype = 'virtualxid' " \ + " AND l.pid NOT IN (pg_backend_pid(), $1) " \ " AND (l.virtualxid, l.virtualtransaction) <> ('1/1', '-1/0') " \ " AND (a.application_name IS NULL OR a.application_name <> $2)" \ - " AND a.query !~* E'^\\\\s*vacuum\\\\s+' " \ - " AND a.query !~ E'^autovacuum: ' " \ + " AND a.query !~* E'^\\\\s*vacuum\\\\s+' " \ + " AND a.query !~ E'^autovacuum: ' " \ " AND ((d.datname IS NULL OR d.datname = current_database()) OR l.database = 0)" -#define SQL_XID_SNAPSHOT_90000 \ - "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ - " FROM pg_locks AS l " \ - " LEFT JOIN pg_stat_activity AS a " \ - " ON l.pid = a.procpid " \ - " LEFT JOIN pg_database AS d " \ - " ON a.datid = d.oid " \ - " WHERE l.locktype = 'virtualxid' " \ - " AND l.pid NOT IN (pg_backend_pid(), $1) " \ +#define SQL_XID_SNAPSHOT_90000 \ + "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ + " FROM pg_locks AS l " \ + " LEFT JOIN pg_stat_activity AS a " \ + " ON l.pid = a.procpid " \ + " LEFT JOIN pg_database AS d " \ + " ON a.datid = d.oid " \ + " WHERE l.locktype = 'virtualxid' " \ + " AND l.pid NOT IN (pg_backend_pid(), $1) " \ " AND (l.virtualxid, l.virtualtransaction) <> ('1/1', '-1/0') " \ " AND (a.application_name IS NULL OR a.application_name <> $2)" \ - " AND a.current_query !~* E'^\\\\s*vacuum\\\\s+' " \ - " AND a.current_query !~ E'^autovacuum: ' " \ + " AND a.current_query !~* E'^\\\\s*vacuum\\\\s+' " \ + " AND a.current_query !~ E'^autovacuum: ' " \ " AND ((d.datname IS NULL OR d.datname = current_database()) OR l.database = 0)" /* application_name is not available before 9.0. The last clause of * the WHERE clause is just to eat the $2 parameter (application name). */ -#define SQL_XID_SNAPSHOT_80300 \ - "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ - " FROM pg_locks AS l" \ - " LEFT JOIN pg_stat_activity AS a " \ - " ON l.pid = a.procpid " \ - " LEFT JOIN pg_database AS d " \ - " ON a.datid = d.oid " \ - " WHERE l.locktype = 'virtualxid' AND l.pid NOT IN (pg_backend_pid(), $1)" \ - " AND (l.virtualxid, l.virtualtransaction) <> ('1/1', '-1/0') " \ - " AND a.current_query !~* E'^\\\\s*vacuum\\\\s+' " \ - " AND a.current_query !~ E'^autovacuum: ' " \ +#define SQL_XID_SNAPSHOT_80300 \ + "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ + " FROM pg_locks AS l" \ + " LEFT JOIN pg_stat_activity AS a " \ + " ON l.pid = a.procpid " \ + " LEFT JOIN pg_database AS d " \ + " ON a.datid = d.oid " \ + " WHERE l.locktype = 'virtualxid' AND l.pid NOT IN (pg_backend_pid(), $1)" \ + " AND (l.virtualxid, l.virtualtransaction) <> ('1/1', '-1/0') " \ + " AND a.current_query !~* E'^\\\\s*vacuum\\\\s+' " \ + " AND a.current_query !~ E'^autovacuum: ' " \ " AND ((d.datname IS NULL OR d.datname = current_database()) OR l.database = 0)" \ " AND ($2::text IS NOT NULL)" #define SQL_XID_SNAPSHOT \ - (PQserverVersion(connection) >= 90200 ? SQL_XID_SNAPSHOT_90200 : (PQserverVersion(connection) >= 90000 ? SQL_XID_SNAPSHOT_90000 : SQL_XID_SNAPSHOT_80300)) + (PQserverVersion(connection) >= 90200 ? SQL_XID_SNAPSHOT_90200 : \ + (PQserverVersion(connection) >= 90000 ? SQL_XID_SNAPSHOT_90000 : \ + SQL_XID_SNAPSHOT_80300)) + /* Later, check whether any of the transactions we saw before are still * alive, and wait for them to go away. */ -#define SQL_XID_ALIVE \ - "SELECT pid FROM pg_locks WHERE locktype = 'virtualxid'" \ +#define SQL_XID_ALIVE \ + "SELECT pid FROM pg_locks WHERE locktype = 'virtualxid'"\ " AND pid <> pg_backend_pid() AND virtualtransaction = ANY($1)" /* To be run while our main connection holds an AccessExclusive lock on the @@ -136,20 +141,20 @@ const char *PROGRAM_VERSION = "unknown"; * transactions trying to grab an ACCESS EXCLUSIVE lock, because we are only * trying to kill off disallowed DDL commands, e.g. ALTER TABLE or TRUNCATE. */ -#define CANCEL_COMPETING_LOCKS \ - "SELECT pg_cancel_backend(pid) FROM pg_locks WHERE locktype = 'relation'" \ - " AND granted = false AND relation = %u" \ +#define CANCEL_COMPETING_LOCKS \ + "SELECT pg_cancel_backend(pid) FROM pg_locks WHERE locktype = 'relation'"\ + " AND granted = false AND relation = %u"\ " AND mode = 'AccessExclusiveLock' AND pid <> pg_backend_pid()" -#define KILL_COMPETING_LOCKS \ - "SELECT pg_terminate_backend(pid) " \ - "FROM pg_locks WHERE locktype = 'relation'" \ - " AND granted = false AND relation = %u" \ +#define KILL_COMPETING_LOCKS \ + "SELECT pg_terminate_backend(pid) "\ + "FROM pg_locks WHERE locktype = 'relation'"\ + " AND granted = false AND relation = %u"\ " AND mode = 'AccessExclusiveLock' AND pid <> pg_backend_pid()" -#define COUNT_COMPETING_LOCKS \ +#define COUNT_COMPETING_LOCKS \ "SELECT pid FROM pg_locks WHERE locktype = 'relation'" \ - " AND granted = false AND relation = %u" \ + " AND granted = false AND relation = %u" \ " AND mode = 'AccessExclusiveLock' AND pid <> pg_backend_pid()" /* Will be used as a unique prefix for advisory locks. */ @@ -167,10 +172,10 @@ typedef enum */ typedef struct repack_index { - Oid target_oid; /* target: OID */ - const char *create_index; /* CREATE INDEX */ - index_status_t status; /* Track parallel build statuses. */ - int worker_idx; /* which worker conn is handling */ + Oid target_oid; /* target: OID */ + const char *create_index; /* CREATE INDEX */ + index_status_t status; /* Track parallel build statuses. */ + int worker_idx; /* which worker conn is handling */ } repack_index; /* @@ -178,40 +183,40 @@ typedef struct repack_index */ typedef struct repack_table { - const char *target_name; /* target: relname */ - Oid target_oid; /* target: OID */ - Oid target_toast; /* target: toast OID */ - Oid target_tidx; /* target: toast index OID */ - Oid pkid; /* target: PK OID */ - Oid ckid; /* target: CK OID */ - Oid temp_oid; /* temp: OID */ - const char *create_pktype; /* CREATE TYPE pk */ - const char *create_log; /* CREATE TABLE log */ - const char *create_trigger; /* CREATE TRIGGER repack_trigger */ - const char *enable_trigger; /* ALTER TABLE ENABLE ALWAYS TRIGGER repack_trigger */ - const char *create_table; /* CREATE TABLE table AS SELECT WITH NO DATA*/ - const char *dest_tablespace; /* Destination tablespace */ - const char *copy_data; /* INSERT INTO */ - const char *alter_col_storage; /* ALTER TABLE ALTER COLUMN SET STORAGE */ - const char *drop_columns; /* ALTER TABLE DROP COLUMNs */ - const char *delete_log; /* DELETE FROM log */ - const char *lock_table; /* LOCK TABLE table */ - const char *sql_peek; /* SQL used in flush */ - const char *sql_insert; /* SQL used in flush */ - const char *sql_delete; /* SQL used in flush */ - const char *sql_update; /* SQL used in flush */ - const char *sql_pop; /* SQL used in flush */ - const char *where_clause; /* WHERE clause for Deleting rows */ - int n_indexes; /* number of indexes */ - repack_index *indexes; /* info on each index */ + const char *target_name; /* target: relname */ + Oid target_oid; /* target: OID */ + Oid target_toast; /* target: toast OID */ + Oid target_tidx; /* target: toast index OID */ + Oid pkid; /* target: PK OID */ + Oid ckid; /* target: CK OID */ + Oid temp_oid; /* temp: OID */ + const char *create_pktype; /* CREATE TYPE pk */ + const char *create_log; /* CREATE TABLE log */ + const char *create_trigger; /* CREATE TRIGGER repack_trigger */ + const char *enable_trigger; /* ALTER TABLE ENABLE ALWAYS TRIGGER repack_trigger */ + const char *create_table; /* CREATE TABLE table AS SELECT WITH NO DATA*/ + const char *dest_tablespace; /* Destination tablespace */ + const char *copy_data; /* INSERT INTO */ + const char *alter_col_storage; /* ALTER TABLE ALTER COLUMN SET STORAGE */ + const char *drop_columns; /* ALTER TABLE DROP COLUMNs */ + const char *delete_log; /* DELETE FROM log */ + const char *lock_table; /* LOCK TABLE table */ + const char *sql_peek; /* SQL used in flush */ + const char *sql_insert; /* SQL used in flush */ + const char *sql_delete; /* SQL used in flush */ + const char *sql_update; /* SQL used in flush */ + const char *sql_pop; /* SQL used in flush */ + int n_indexes; /* number of indexes */ + repack_index *indexes; /* info on each index */ } repack_table; + static bool is_superuser(void); static void check_tablespace(void); static bool preliminary_checks(char *errbuf, size_t errsize); static bool is_requested_relation_exists(char *errbuf, size_t errsize); -static void repack_all_databases(const char *order_by, const char *where_clause); -static bool repack_one_database(const char *order_by, char *errbuf, size_t errsize, const char *where_clause); +static void repack_all_databases(const char *order_by); +static bool repack_one_database(const char *order_by, char *errbuf, size_t errsize); static void repack_one_table(repack_table *table, const char *order_by); static bool repack_table_indexes(PGresult *index_details); static bool repack_all_indexes(char *errbuf, size_t errsize); @@ -219,7 +224,6 @@ static void repack_cleanup(bool fatal, const repack_table *table); static void repack_cleanup_callback(bool fatal, void *userdata); static void repack_cleanup_index(bool fatal, void *userdata); static bool rebuild_indexes(const repack_table *table); -static bool validate_where_clause(const char *table_name, const char *where_clause, char *errbuf, size_t errsize); static char *getstr(PGresult *res, int row, int col); static Oid getoid(PGresult *res, int row, int col); @@ -228,39 +232,38 @@ static bool lock_exclusive(PGconn *conn, const char *relid, const char *lock_que static bool kill_ddl(PGconn *conn, Oid relid, bool terminate); static bool lock_access_share(PGconn *conn, Oid relid, const char *target_name); -#define SQLSTATE_INVALID_SCHEMA_NAME "3F000" -#define SQLSTATE_UNDEFINED_FUNCTION "42883" -#define SQLSTATE_LOCK_NOT_AVAILABLE "55P03" +#define SQLSTATE_INVALID_SCHEMA_NAME "3F000" +#define SQLSTATE_UNDEFINED_FUNCTION "42883" +#define SQLSTATE_LOCK_NOT_AVAILABLE "55P03" static bool sqlstate_equals(PGresult *res, const char *state) { return strcmp(PQresultErrorField(res, PG_DIAG_SQLSTATE), state) == 0; } -static bool analyze = true; -static bool alldb = false; -static bool noorder = false; -static SimpleStringList parent_table_list = {NULL, NULL}; -static SimpleStringList table_list = {NULL, NULL}; -static SimpleStringList schema_list = {NULL, NULL}; -static char *orderby = NULL; -static char *where_clause = NULL; -static char *tablespace = NULL; -static bool moveidx = false; -static SimpleStringList r_index = {NULL, NULL}; -static bool only_indexes = false; -static int wait_timeout = 60; /* in seconds */ -static int jobs = 0; /* number of concurrent worker conns. */ -static bool dryrun = false; -static unsigned int temp_obj_num = 0; /* temporary objects counter */ -static bool no_kill_backend = false; /* abandon when timed-out */ -static bool no_superuser_check = false; -static SimpleStringList exclude_extension_list = {NULL, NULL}; /* don't repack tables of these extensions */ -static bool no_error_on_invalid_index = false; /* repack even though invalid index is found */ -static bool error_on_invalid_index = false; /* don't repack when invalid index is found, - * deprecated, this the default behavior now */ -static int apply_count = APPLY_COUNT_DEFAULT; -static int switch_threshold = SWITCH_THRESHOLD_DEFAULT; +static bool analyze = true; +static bool alldb = false; +static bool noorder = false; +static SimpleStringList parent_table_list = {NULL, NULL}; +static SimpleStringList table_list = {NULL, NULL}; +static SimpleStringList schema_list = {NULL, NULL}; +static char *orderby = NULL; +static char *tablespace = NULL; +static bool moveidx = false; +static SimpleStringList r_index = {NULL, NULL}; +static bool only_indexes = false; +static int wait_timeout = 60; /* in seconds */ +static int jobs = 0; /* number of concurrent worker conns. */ +static bool dryrun = false; +static unsigned int temp_obj_num = 0; /* temporary objects counter */ +static bool no_kill_backend = false; /* abandon when timed-out */ +static bool no_superuser_check = false; +static SimpleStringList exclude_extension_list = {NULL, NULL}; /* don't repack tables of these extensions */ +static bool no_error_on_invalid_index = false; /* repack even though invalid index is found */ +static bool error_on_invalid_index = false; /* don't repack when invalid index is found, + * deprecated, this the default behavior now */ +static int apply_count = APPLY_COUNT_DEFAULT; +static int switch_threshold = SWITCH_THRESHOLD_DEFAULT; /* buffer should have at least 11 bytes */ static char * @@ -272,36 +275,36 @@ utoa(unsigned int value, char *buffer) } static pgut_option options[] = - { - {'b', 'a', "all", &alldb}, - {'l', 't', "table", &table_list}, - {'l', 'I', "parent-table", &parent_table_list}, - {'l', 'c', "schema", &schema_list}, - {'b', 'n', "no-order", &noorder}, - {'b', 'N', "dry-run", &dryrun}, - {'s', 'X', "where-clause", &where_clause}, - {'s', 'o', "order-by", &orderby}, - {'s', 's', "tablespace", &tablespace}, - {'b', 'S', "moveidx", &moveidx}, - {'l', 'i', "index", &r_index}, - {'b', 'x', "only-indexes", &only_indexes}, - {'i', 'T', "wait-timeout", &wait_timeout}, - {'B', 'Z', "no-analyze", &analyze}, - {'i', 'j', "jobs", &jobs}, - {'b', 'D', "no-kill-backend", &no_kill_backend}, - {'b', 'k', "no-superuser-check", &no_superuser_check}, - {'l', 'C', "exclude-extension", &exclude_extension_list}, - {'b', 4, "no-error-on-invalid-index", &no_error_on_invalid_index}, - {'b', 3, "error-on-invalid-index", &error_on_invalid_index}, - {'i', 2, "apply-count", &apply_count}, - {'i', 1, "switch-threshold", &switch_threshold}, - {0}, +{ + { 'b', 'a', "all", &alldb }, + { 'l', 't', "table", &table_list }, + { 'l', 'I', "parent-table", &parent_table_list }, + { 'l', 'c', "schema", &schema_list }, + { 'b', 'n', "no-order", &noorder }, + { 'b', 'N', "dry-run", &dryrun }, + { 's', 'o', "order-by", &orderby }, + { 's', 's', "tablespace", &tablespace }, + { 'b', 'S', "moveidx", &moveidx }, + { 'l', 'i', "index", &r_index }, + { 'b', 'x', "only-indexes", &only_indexes }, + { 'i', 'T', "wait-timeout", &wait_timeout }, + { 'B', 'Z', "no-analyze", &analyze }, + { 'i', 'j', "jobs", &jobs }, + { 'b', 'D', "no-kill-backend", &no_kill_backend }, + { 'b', 'k', "no-superuser-check", &no_superuser_check }, + { 'l', 'C', "exclude-extension", &exclude_extension_list }, + { 'b', 4, "no-error-on-invalid-index", &no_error_on_invalid_index }, + { 'b', 3, "error-on-invalid-index", &error_on_invalid_index }, + { 'i', 2, "apply-count", &apply_count }, + { 'i', 1, "switch-threshold", &switch_threshold }, + { 0 }, }; -int main(int argc, char *argv[]) +int +main(int argc, char *argv[]) { - int i; - char errbuf[256]; + int i; + char errbuf[256]; i = pgut_getopt(argc, argv, options); @@ -309,12 +312,12 @@ int main(int argc, char *argv[]) dbname = argv[i]; else if (i < argc) ereport(ERROR, - (errcode(EINVAL), - errmsg("too many arguments"))); + (errcode(EINVAL), + errmsg("too many arguments"))); - if (switch_threshold >= apply_count) + if(switch_threshold >= apply_count) ereport(ERROR, (errcode(EINVAL), - errmsg("switch_threshold must be less than apply_count"))); + errmsg("switch_threshold must be less than apply_count"))); check_tablespace(); @@ -325,61 +328,61 @@ int main(int argc, char *argv[]) { if (r_index.head && table_list.head) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot specify --index (-i) and --table (-t)"))); + errmsg("cannot specify --index (-i) and --table (-t)"))); if (r_index.head && parent_table_list.head) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot specify --index (-i) and --parent-table (-I)"))); + errmsg("cannot specify --index (-i) and --parent-table (-I)"))); else if (r_index.head && only_indexes) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot specify --index (-i) and --only-indexes (-x)"))); + errmsg("cannot specify --index (-i) and --only-indexes (-x)"))); else if (r_index.head && exclude_extension_list.head) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot specify --index (-i) and --exclude-extension (-C)"))); + errmsg("cannot specify --index (-i) and --exclude-extension (-C)"))); else if (only_indexes && !(table_list.head || parent_table_list.head)) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot repack all indexes of database, specify the table(s)" - "via --table (-t) or --parent-table (-I)"))); + errmsg("cannot repack all indexes of database, specify the table(s)" + "via --table (-t) or --parent-table (-I)"))); else if (only_indexes && exclude_extension_list.head) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot specify --only-indexes (-x) and --exclude-extension (-C)"))); + errmsg("cannot specify --only-indexes (-x) and --exclude-extension (-C)"))); else if (alldb) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot repack specific index(es) in all databases"))); + errmsg("cannot repack specific index(es) in all databases"))); else { if (orderby) ereport(WARNING, (errcode(EINVAL), - errmsg("option -o (--order-by) has no effect while repacking indexes"))); + errmsg("option -o (--order-by) has no effect while repacking indexes"))); else if (noorder) ereport(WARNING, (errcode(EINVAL), - errmsg("option -n (--no-order) has no effect while repacking indexes"))); + errmsg("option -n (--no-order) has no effect while repacking indexes"))); else if (!analyze) ereport(WARNING, (errcode(EINVAL), - errmsg("ANALYZE is not performed after repacking indexes, -z (--no-analyze) has no effect"))); + errmsg("ANALYZE is not performed after repacking indexes, -z (--no-analyze) has no effect"))); else if (jobs) ereport(WARNING, (errcode(EINVAL), - errmsg("option -j (--jobs) has no effect, repacking indexes does not use parallel jobs"))); + errmsg("option -j (--jobs) has no effect, repacking indexes does not use parallel jobs"))); if (!repack_all_indexes(errbuf, sizeof(errbuf))) ereport(ERROR, - (errcode(ERROR), errmsg("%s", errbuf))); + (errcode(ERROR), errmsg("%s", errbuf))); } } else { if (schema_list.head && (table_list.head || parent_table_list.head)) ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot repack specific table(s) in schema, use schema.table notation instead"))); + (errcode(EINVAL), + errmsg("cannot repack specific table(s) in schema, use schema.table notation instead"))); if (exclude_extension_list.head && table_list.head) ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot specify --table (-t) and --exclude-extension (-C)"))); + (errcode(EINVAL), + errmsg("cannot specify --table (-t) and --exclude-extension (-C)"))); if (exclude_extension_list.head && parent_table_list.head) ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot specify --parent-table (-I) and --exclude-extension (-C)"))); + (errcode(EINVAL), + errmsg("cannot specify --parent-table (-I) and --exclude-extension (-C)"))); if (noorder) orderby = ""; @@ -388,25 +391,26 @@ int main(int argc, char *argv[]) { if (table_list.head || parent_table_list.head) ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot repack specific table(s) in all databases"))); + (errcode(EINVAL), + errmsg("cannot repack specific table(s) in all databases"))); if (schema_list.head) ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot repack specific schema(s) in all databases"))); - repack_all_databases(orderby, where_clause); + (errcode(EINVAL), + errmsg("cannot repack specific schema(s) in all databases"))); + repack_all_databases(orderby); } else { - if (!repack_one_database(orderby, errbuf, sizeof(errbuf), where_clause)) + if (!repack_one_database(orderby, errbuf, sizeof(errbuf))) ereport(ERROR, - (errcode(ERROR), errmsg("%s failed with error: %s", PROGRAM_NAME, errbuf))); + (errcode(ERROR), errmsg("%s failed with error: %s", PROGRAM_NAME, errbuf))); } } return 0; } + /* * Test if the current user is a database superuser. * Borrowed from psql/common.c @@ -414,7 +418,8 @@ int main(int argc, char *argv[]) * Note: this will correctly detect superuserness only with a protocol-3.0 * or newer backend; otherwise it will always say "false". */ -bool is_superuser(void) +bool +is_superuser(void) { const char *val; @@ -437,9 +442,10 @@ bool is_superuser(void) * * Raise an exception on error. */ -void check_tablespace() +void +check_tablespace() { - PGresult *res = NULL; + PGresult *res = NULL; const char *params[1]; if (tablespace == NULL) @@ -448,8 +454,8 @@ void check_tablespace() if (moveidx) { ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot specify --moveidx (-S) without --tablespace (-s)"))); + (errcode(EINVAL), + errmsg("cannot specify --moveidx (-S) without --tablespace (-s)"))); } return; } @@ -466,16 +472,16 @@ void check_tablespace() if (PQntuples(res) == 0) { ereport(ERROR, - (errcode(EINVAL), - errmsg("the tablespace \"%s\" doesn't exist", tablespace))); + (errcode(EINVAL), + errmsg("the tablespace \"%s\" doesn't exist", tablespace))); } } else { ereport(ERROR, - (errcode(EINVAL), - errmsg("error checking the namespace: %s", - PQerrorMessage(connection)))); + (errcode(EINVAL), + errmsg("error checking the namespace: %s", + PQerrorMessage(connection)))); } CLEARPGRES(res); @@ -486,13 +492,11 @@ void check_tablespace() * installed in the database, the user is a superuser, etc. */ static bool -preliminary_checks(char *errbuf, size_t errsize) -{ - bool ret = false; - PGresult *res = NULL; +preliminary_checks(char *errbuf, size_t errsize){ + bool ret = false; + PGresult *res = NULL; - if (!is_superuser()) - { + if (!is_superuser()) { if (errbuf) snprintf(errbuf, errsize, "You must be a superuser to use %s", PROGRAM_NAME); @@ -501,11 +505,11 @@ preliminary_checks(char *errbuf, size_t errsize) /* Query the extension version. Exit if no match */ res = execute_elevel("select repack.version(), repack.version_sql()", - 0, NULL, DEBUG2); + 0, NULL, DEBUG2); if (PQresultStatus(res) == PGRES_TUPLES_OK) { - const char *libver; - char buf[64]; + const char *libver; + char buf[64]; /* the string is something like "pg_repack 1.1.7" */ snprintf(buf, sizeof(buf), "%s %s", PROGRAM_NAME, PROGRAM_VERSION); @@ -516,8 +520,8 @@ preliminary_checks(char *errbuf, size_t errsize) { if (errbuf) snprintf(errbuf, errsize, - "program '%s' does not match database library '%s'", - buf, libver); + "program '%s' does not match database library '%s'", + buf, libver); goto cleanup; } @@ -527,23 +531,24 @@ preliminary_checks(char *errbuf, size_t errsize) { if (errbuf) snprintf(errbuf, errsize, - "extension '%s' required, found '%s';" - " please drop and re-create the extension", - buf, libver); + "extension '%s' required, found '%s';" + " please drop and re-create the extension", + buf, libver); goto cleanup; } } else { - if (sqlstate_equals(res, SQLSTATE_INVALID_SCHEMA_NAME) || sqlstate_equals(res, SQLSTATE_UNDEFINED_FUNCTION)) + if (sqlstate_equals(res, SQLSTATE_INVALID_SCHEMA_NAME) + || sqlstate_equals(res, SQLSTATE_UNDEFINED_FUNCTION)) { /* Schema repack does not exist, or version too old (version * functions not found). Skip the database. */ if (errbuf) snprintf(errbuf, errsize, - "%s %s is not installed in the database", - PROGRAM_NAME, PROGRAM_VERSION); + "%s %s is not installed in the database", + PROGRAM_NAME, PROGRAM_VERSION); } else { @@ -576,15 +581,14 @@ preliminary_checks(char *errbuf, size_t errsize) * otherwise format user-friendly message */ static bool -is_requested_relation_exists(char *errbuf, size_t errsize) -{ - bool ret = false; - PGresult *res = NULL; - const char **params = NULL; - int iparam = 0; - StringInfoData sql; - int num_relations; - SimpleStringListCell *cell; +is_requested_relation_exists(char *errbuf, size_t errsize){ + bool ret = false; + PGresult *res = NULL; + const char **params = NULL; + int iparam = 0; + StringInfoData sql; + int num_relations; + SimpleStringListCell *cell; num_relations = simple_string_list_size(parent_table_list) + simple_string_list_size(table_list); @@ -594,7 +598,7 @@ is_requested_relation_exists(char *errbuf, size_t errsize) return true; /* has no suitable to_regclass(text) */ - if (PQserverVersion(connection) < 90600) + if (PQserverVersion(connection)<90600) return true; params = pgut_malloc(num_relations * sizeof(char *)); @@ -616,35 +620,36 @@ is_requested_relation_exists(char *errbuf, size_t errsize) appendStringInfoChar(&sql, ','); } appendStringInfoString(&sql, - ") AS given_t(r,kind) WHERE" - /* regular --table relation or inherited --parent-table */ - " NOT EXISTS(" - " SELECT FROM repack.tables WHERE relid=to_regclass(given_t.r))" - /* declarative partitioned --parent-table */ - " AND NOT EXISTS(" - " SELECT FROM pg_catalog.pg_class c WHERE c.oid=to_regclass(given_t.r) AND c.relkind = given_t.kind AND given_t.kind = 'p')"); + ") AS given_t(r,kind) WHERE" + /* regular --table relation or inherited --parent-table */ + " NOT EXISTS(" + " SELECT FROM repack.tables WHERE relid=to_regclass(given_t.r))" + /* declarative partitioned --parent-table */ + " AND NOT EXISTS(" + " SELECT FROM pg_catalog.pg_class c WHERE c.oid=to_regclass(given_t.r) AND c.relkind = given_t.kind AND given_t.kind = 'p')" + ); /* double check the parameters array is sane */ if (iparam != num_relations) { if (errbuf) snprintf(errbuf, errsize, - "internal error: bad parameters count: %i instead of %i", - iparam, num_relations); + "internal error: bad parameters count: %i instead of %i", + iparam, num_relations); goto cleanup; } res = execute_elevel(sql.data, iparam, params, DEBUG2); if (PQresultStatus(res) == PGRES_TUPLES_OK) { - int num; + int num; num = PQntuples(res); if (num != 0) { int i; - StringInfoData rel_names; + StringInfoData rel_names; initStringInfo(&rel_names); for (i = 0; i < num; i++) @@ -658,10 +663,10 @@ is_requested_relation_exists(char *errbuf, size_t errsize) { if (num > 1) snprintf(errbuf, errsize, - "relations do not exist: %s", rel_names.data); + "relations do not exist: %s", rel_names.data); else snprintf(errbuf, errsize, - "ERROR: relation %s does not exist", rel_names.data); + "ERROR: relation %s does not exist", rel_names.data); } termStringInfo(&rel_names); } @@ -686,10 +691,10 @@ is_requested_relation_exists(char *errbuf, size_t errsize) * Call repack_one_database for each database. */ static void -repack_all_databases(const char *orderby, const char *where_clause) +repack_all_databases(const char *orderby) { - PGresult *result; - int i; + PGresult *result; + int i; dbname = "postgres"; reconnect(ERROR); @@ -702,15 +707,15 @@ repack_all_databases(const char *orderby, const char *where_clause) for (i = 0; i < PQntuples(result); i++) { - bool ret; - char errbuf[256]; + bool ret; + char errbuf[256]; dbname = PQgetvalue(result, i, 0); elog(INFO, "repacking database \"%s\"", dbname); if (!dryrun) { - ret = repack_one_database(orderby, errbuf, sizeof(errbuf), where_clause); + ret = repack_one_database(orderby, errbuf, sizeof(errbuf)); if (!ret) elog(INFO, "database \"%s\" skipped: %s", dbname, errbuf); } @@ -742,21 +747,21 @@ getoid(PGresult *res, int row, int col) * Call repack_one_table for the target tables or each table in a database. */ static bool -repack_one_database(const char *orderby, char *errbuf, size_t errsize, const char *where_clause) +repack_one_database(const char *orderby, char *errbuf, size_t errsize) { - bool ret = false; - PGresult *res = NULL; - int i; - int num; - StringInfoData sql; - SimpleStringListCell *cell; - const char **params = NULL; - int iparam = 0; - size_t num_parent_tables, - num_tables, - num_schemas, - num_params, - num_excluded_extensions; + bool ret = false; + PGresult *res = NULL; + int i; + int num; + StringInfoData sql; + SimpleStringListCell *cell; + const char **params = NULL; + int iparam = 0; + size_t num_parent_tables, + num_tables, + num_schemas, + num_params, + num_excluded_extensions; num_parent_tables = simple_string_list_size(parent_table_list); num_tables = simple_string_list_size(table_list); @@ -786,11 +791,11 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha /* acquire target tables */ appendStringInfoString(&sql, - "SELECT t.*," - " coalesce(v.tablespace, t.tablespace_orig) as tablespace_dest" - " FROM repack.tables t, " - " (VALUES ($1::text)) as v (tablespace)" - " WHERE "); + "SELECT t.*," + " coalesce(v.tablespace, t.tablespace_orig) as tablespace_dest" + " FROM repack.tables t, " + " (VALUES ($1::text)) as v (tablespace)" + " WHERE "); params[iparam++] = tablespace; if (num_tables || num_parent_tables) @@ -878,12 +883,12 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha { if (errbuf) snprintf(errbuf, errsize, - "internal error: bad parameters count: %i instead of %zi", - iparam, num_params); + "internal error: bad parameters count: %i instead of %zi", + iparam, num_params); goto cleanup; } - res = execute_elevel(sql.data, (int)num_params, params, DEBUG2); + res = execute_elevel(sql.data, (int) num_params, params, DEBUG2); /* on error skip the database */ if (PQresultStatus(res) != PGRES_TUPLES_OK) @@ -898,10 +903,10 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha for (i = 0; i < num; i++) { - repack_table table; - StringInfoData copy_sql; + repack_table table; + StringInfoData copy_sql; const char *ckey; - int c = 0; + int c = 0; table.target_name = getstr(res, i, c++); table.target_oid = getoid(res, i, c++); @@ -912,8 +917,7 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha table.ckid = getoid(res, i, c++); table.temp_oid = InvalidOid; /* filled after creating the temp table */ - if (table.pkid == 0) - { + if (table.pkid == 0) { ereport(WARNING, (errcode(E_PG_COMMAND), errmsg("relation \"%s\" must have a primary key or not-null unique keys", table.target_name))); @@ -926,8 +930,8 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha table.enable_trigger = getstr(res, i, c++); table.create_table = getstr(res, i, c++); - getstr(res, i, c++); /* tablespace_orig is clobbered */ - table.copy_data = getstr(res, i, c++); + getstr(res, i, c++); /* tablespace_orig is clobbered */ + table.copy_data = getstr(res, i , c++); table.alter_col_storage = getstr(res, i, c++); table.drop_columns = getstr(res, i, c++); table.delete_log = getstr(res, i, c++); @@ -939,19 +943,12 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha table.sql_update = getstr(res, i, c++); table.sql_pop = getstr(res, i, c++); table.dest_tablespace = getstr(res, i, c++); - table.where_clause = where_clause; /* Craft Copy SQL */ initStringInfo(©_sql); appendStringInfoString(©_sql, table.copy_data); - - /* soft delete recognition */ - if (table.where_clause) - { - appendStringInfoString(©_sql, " WHERE "); - appendStringInfoString(©_sql, table.where_clause); - } if (!orderby) + { if (ckey != NULL) { @@ -989,10 +986,10 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha static int apply_log(PGconn *conn, const repack_table *table, int count) { - int result; - PGresult *res; + int result; + PGresult *res; const char *params[6]; - char buffer[12]; + char buffer[12]; params[0] = table->sql_peek; params[1] = table->sql_insert; @@ -1017,13 +1014,13 @@ apply_log(PGconn *conn, const repack_table *table, int count) static bool rebuild_indexes(const repack_table *table) { - PGresult *res = NULL; - int num_indexes; - int i; - int num_active_workers; - int num_workers; - repack_index *index_jobs; - bool have_error = false; + PGresult *res = NULL; + int num_indexes; + int i; + int num_active_workers; + int num_workers; + repack_index *index_jobs; + bool have_error = false; elog(DEBUG2, "---- create indexes ----"); @@ -1048,8 +1045,7 @@ rebuild_indexes(const repack_table *table) elog(DEBUG2, "target_oid : %u", index_jobs[i].target_oid); elog(DEBUG2, "create_index : %s", index_jobs[i].create_index); - if (num_workers <= 1) - { + if (num_workers <= 1) { /* Use primary connection if we are not setting up parallel * index building, or if we only have one worker. */ @@ -1060,8 +1056,7 @@ rebuild_indexes(const repack_table *table) */ index_jobs[i].status = FINISHED; } - else if (i < num_workers) - { + else if (i < num_workers) { /* Assign available worker to build an index. */ index_jobs[i].status = INPROGRESS; index_jobs[i].worker_idx = i; @@ -1198,13 +1193,11 @@ rebuild_indexes(const repack_table *table) index_jobs[i].status = INPROGRESS; index_jobs[i].worker_idx = freed_worker; elog(LOG, "Assigning worker %d to build index #%d: " - "%s", - freed_worker, i, + "%s", freed_worker, i, index_jobs[i].create_index); if (!(PQsendQuery(workers.conns[freed_worker], - index_jobs[i].create_index))) - { + index_jobs[i].create_index))) { elog(WARNING, "Error sending async query: %s\n%s", index_jobs[i].create_index, PQerrorMessage(workers.conns[freed_worker])); @@ -1218,6 +1211,7 @@ rebuild_indexes(const repack_table *table) freed_worker = -1; } } + } cleanup: @@ -1225,29 +1219,29 @@ rebuild_indexes(const repack_table *table) return (!have_error); } + /* * Re-organize one table. */ static void repack_one_table(repack_table *table, const char *orderby) { - PGresult *res = NULL; - const char *params[3]; - int num; - char *vxid = NULL; - char buffer[12]; - StringInfoData sql; - bool ret = false; - PGresult *indexres = NULL; - const char *indexparams[2]; - char indexbuffer[12]; - int j; - char errbuf[256]; + PGresult *res = NULL; + const char *params[3]; + int num; + char *vxid = NULL; + char buffer[12]; + StringInfoData sql; + bool ret = false; + PGresult *indexres = NULL; + const char *indexparams[2]; + char indexbuffer[12]; + int j; /* appname will be "pg_repack" in normal use on 9.0+, or * "pg_regress" when run under `make installcheck` */ - const char *appname = getenv("PGAPPNAME"); + const char *appname = getenv("PGAPPNAME"); /* Keep track of whether we have gotten through setup to install * the repack_trigger, log table, etc. ourselves. We don't want to @@ -1255,19 +1249,12 @@ repack_one_table(repack_table *table, const char *orderby) * trigger ourselves, lest we be cleaning up another pg_repack's mess, * or worse, interfering with a still-running pg_repack. */ - bool table_init = false; + bool table_init = false; initStringInfo(&sql); elog(INFO, "repacking table \"%s\"", table->target_name); - /* Validate WHERE clause if provided */ - if (table->where_clause && !validate_where_clause(table->target_name, table->where_clause, errbuf, sizeof(errbuf))) - { - elog(ERROR, "%s", errbuf); - goto cleanup; - } - elog(DEBUG2, "---- repack_one_table ----"); elog(DEBUG2, "target_name : %s", table->target_name); elog(DEBUG2, "target_oid : %u", table->target_oid); @@ -1282,7 +1269,8 @@ repack_one_table(repack_table *table, const char *orderby) elog(DEBUG2, "create_table : %s", table->create_table); elog(DEBUG2, "dest_tablespace : %s", table->dest_tablespace); elog(DEBUG2, "copy_data : %s", table->copy_data); - elog(DEBUG2, "alter_col_storage : %s", table->alter_col_storage ? table->alter_col_storage : "(skipped)"); + elog(DEBUG2, "alter_col_storage : %s", table->alter_col_storage ? + table->alter_col_storage : "(skipped)"); elog(DEBUG2, "drop_columns : %s", table->drop_columns ? table->drop_columns : "(skipped)"); elog(DEBUG2, "delete_log : %s", table->delete_log); elog(DEBUG2, "lock_table : %s", table->lock_table); @@ -1291,7 +1279,6 @@ repack_one_table(repack_table *table, const char *orderby) elog(DEBUG2, "sql_delete : %s", table->sql_delete); elog(DEBUG2, "sql_update : %s", table->sql_update); elog(DEBUG2, "sql_pop : %s", table->sql_pop); - elog(DEBUG2, "where_clause : %s", table->where_clause); if (dryrun) return; @@ -1340,13 +1327,10 @@ repack_one_table(repack_table *table, const char *orderby) const char *indexdef; indexdef = getstr(indexres, j, 0); - if (!no_error_on_invalid_index) - { + if (!no_error_on_invalid_index) { elog(WARNING, "Invalid index: %s", indexdef); goto cleanup; - } - else - { + } else { elog(WARNING, "skipping invalid index: %s", indexdef); } } @@ -1374,6 +1358,7 @@ repack_one_table(repack_table *table, const char *orderby) elog(DEBUG2, "index[%d].create_index : %s", j, table->indexes[j].create_index); } + /* * Check if repack_trigger is not conflict with existing trigger. We can * find it out later but we check it in advance and go to cleanup if needed. @@ -1392,7 +1377,7 @@ repack_one_table(repack_table *table, const char *orderby) " attempt to run pg_repack on the table which was" " interrupted and for some reason failed to clean up" " the temporary objects. Please drop the trigger or drop" - " and recreate the pg_repack extension altogether" + " and recreate the pg_repack extension altogether" " to remove all the temporary objects left over."))); goto cleanup; } @@ -1594,7 +1579,7 @@ repack_one_table(repack_table *table, const char *orderby) * constantly coming into the original table. */ if (num > switch_threshold) - continue; /* there might be still some tuples, repeat. */ + continue; /* there might be still some tuples, repeat. */ /* old transactions still alive ? */ params[0] = vxid; @@ -1700,7 +1685,7 @@ repack_one_table(repack_table *table, const char *orderby) params[1] = utoa(table->target_oid, buffer); res = pgut_execute(connection, "SELECT pg_advisory_unlock($1, CAST(-2147483648 + $2::bigint AS integer))", - 2, params); + 2, params); ret = true; cleanup: @@ -1730,10 +1715,10 @@ repack_one_table(repack_table *table, const char *orderby) static bool kill_ddl(PGconn *conn, Oid relid, bool terminate) { - bool ret = true; - PGresult *res; - StringInfoData sql; - int n_tuples; + bool ret = true; + PGresult *res; + StringInfoData sql; + int n_tuples; initStringInfo(&sql); @@ -1793,6 +1778,7 @@ kill_ddl(PGconn *conn, Oid relid, bool terminate) return ret; } + /* * Try to acquire an ACCESS SHARE table lock, avoiding deadlocks and long * waits by killing off other sessions which may be stuck trying to obtain @@ -1808,18 +1794,18 @@ kill_ddl(PGconn *conn, Oid relid, bool terminate) static bool lock_access_share(PGconn *conn, Oid relid, const char *target_name) { - StringInfoData sql; - time_t start = time(NULL); - int i; - bool ret = true; + StringInfoData sql; + time_t start = time(NULL); + int i; + bool ret = true; initStringInfo(&sql); - for (i = 1;; i++) + for (i = 1; ; i++) { - time_t duration; - PGresult *res; - int wait_msec; + time_t duration; + PGresult *res; + int wait_msec; pgut_command(conn, "SAVEPOINT repack_sp1", 0, NULL); @@ -1874,6 +1860,7 @@ lock_access_share(PGconn *conn, Oid relid, const char *target_name) return ret; } + /* Obtain an advisory lock on the table's OID, to make sure no other * pg_repack is working on the table. This is not so much a concern with * full-table repacks, but mainly so that index-only repacks don't interfere @@ -1881,9 +1868,9 @@ lock_access_share(PGconn *conn, Oid relid, const char *target_name) */ static bool advisory_lock(PGconn *conn, const char *relid) { - PGresult *res = NULL; - bool ret = false; - const char *params[2]; + PGresult *res = NULL; + bool ret = false; + const char *params[2]; params[0] = REPACK_LOCK_PREFIX_STR; params[1] = relid; @@ -1894,18 +1881,15 @@ static bool advisory_lock(PGconn *conn, const char *relid) * it fit reliably into signed int space. */ res = pgut_execute(conn, "SELECT pg_try_advisory_lock($1, CAST(-2147483648 + $2::bigint AS integer))", - 2, params); + 2, params); - if (PQresultStatus(res) != PGRES_TUPLES_OK) - { - elog(ERROR, "%s", PQerrorMessage(connection)); + if (PQresultStatus(res) != PGRES_TUPLES_OK) { + elog(ERROR, "%s", PQerrorMessage(connection)); } - else if (strcmp(getstr(res, 0, 0), "t") != 0) - { + else if (strcmp(getstr(res, 0, 0), "t") != 0) { elog(ERROR, "Another pg_repack command may be running on the table. Please try again later."); } - else - { + else { ret = true; } CLEARPGRES(res); @@ -1927,16 +1911,16 @@ static bool advisory_lock(PGconn *conn, const char *relid) static bool lock_exclusive(PGconn *conn, const char *relid, const char *lock_query, bool start_xact) { - time_t start = time(NULL); - int i; - bool ret = true; + time_t start = time(NULL); + int i; + bool ret = true; - for (i = 1;; i++) + for (i = 1; ; i++) { - time_t duration; - char sql[1024]; - PGresult *res; - int wait_msec; + time_t duration; + char sql[1024]; + PGresult *res; + int wait_msec; if (start_xact) pgut_command(conn, "BEGIN ISOLATION LEVEL READ COMMITTED", 0, NULL); @@ -2021,15 +2005,16 @@ lock_exclusive(PGconn *conn, const char *relid, const char *lock_query, bool sta /* This function calls to repack_drop() to clean temporary objects on error * in creation of temporary objects. */ -void repack_cleanup_callback(bool fatal, void *userdata) +void +repack_cleanup_callback(bool fatal, void *userdata) { - repack_table *table = (repack_table *)userdata; - Oid target_table = table->target_oid; + repack_table *table = (repack_table *) userdata; + Oid target_table = table->target_oid; const char *params[2]; - char buffer[12]; - char num_buff[12]; + char buffer[12]; + char num_buff[12]; - if (fatal) + if(fatal) { params[0] = utoa(target_table, buffer); params[1] = utoa(temp_obj_num, num_buff); @@ -2067,8 +2052,8 @@ repack_cleanup(bool fatal, const repack_table *table) } else { - char buffer[12]; - char num_buff[12]; + char buffer[12]; + char num_buff[12]; const char *params[2]; /* Try reconnection if not available. */ @@ -2078,7 +2063,7 @@ repack_cleanup(bool fatal, const repack_table *table) /* do cleanup */ params[0] = utoa(table->target_oid, buffer); - params[1] = utoa(temp_obj_num, num_buff); + params[1] = utoa(temp_obj_num, num_buff); command("BEGIN ISOLATION LEVEL READ COMMITTED", 0, NULL); if (!(lock_exclusive(connection, params[0], table->lock_table, false))) @@ -2102,9 +2087,9 @@ static void repack_cleanup_index(bool fatal, void *userdata) { StringInfoData sql, sql_drop; - PGresult *index_details = (PGresult *)userdata; - char *schema_name; - int num; + PGresult *index_details = (PGresult *) userdata; + char *schema_name; + int num; schema_name = getstr(index_details, 0, 5); num = PQntuples(index_details); @@ -2113,11 +2098,11 @@ repack_cleanup_index(bool fatal, void *userdata) initStringInfo(&sql_drop); appendStringInfoString(&sql, "DROP INDEX CONCURRENTLY IF EXISTS "); - appendStringInfo(&sql, "\"%s\".", schema_name); + appendStringInfo(&sql, "\"%s\".", schema_name); for (int i = 0; i < num; i++) { - Oid index = getoid(index_details, i, 1); + Oid index = getoid(index_details, i, 1); resetStringInfo(&sql_drop); appendStringInfo(&sql_drop, "%s\"index_%u\"", sql.data, index); @@ -2133,14 +2118,14 @@ repack_cleanup_index(bool fatal, void *userdata) static bool repack_table_indexes(PGresult *index_details) { - bool ret = false; - PGresult *res = NULL, *res2 = NULL; - StringInfoData sql; - char buffer[2][12]; - const char *create_idx, *schema_name, *table_name, *params[3]; - Oid table, index; - int i, num, num_repacked = 0; - bool *repacked_indexes; + bool ret = false; + PGresult *res = NULL, *res2 = NULL; + StringInfoData sql; + char buffer[2][12]; + const char *create_idx, *schema_name, *table_name, *params[3]; + Oid table, index; + int i, num, num_repacked = 0; + bool *repacked_indexes; initStringInfo(&sql); @@ -2164,7 +2149,7 @@ repack_table_indexes(PGresult *index_details) */ if (!advisory_lock(connection, params[1])) ereport(ERROR, (errcode(EINVAL), - errmsg("Unable to obtain advisory lock on \"%s\"", table_name))); + errmsg("Unable to obtain advisory lock on \"%s\"", table_name))); pgut_atexit_push(repack_cleanup_index, index_details); @@ -2179,11 +2164,10 @@ repack_table_indexes(PGresult *index_details) resetStringInfo(&sql); appendStringInfo(&sql, "SELECT pgc.relname, nsp.nspname " - "FROM pg_class pgc INNER JOIN pg_namespace nsp " - "ON nsp.oid = pgc.relnamespace " - "WHERE pgc.relname = 'index_%u' " - "AND nsp.nspname = $1", - index); + "FROM pg_class pgc INNER JOIN pg_namespace nsp " + "ON nsp.oid = pgc.relnamespace " + "WHERE pgc.relname = 'index_%u' " + "AND nsp.nspname = $1", index); params[0] = schema_name; elog(INFO, "repacking index \"%s\"", idx_name); res = execute(sql.data, 1, params); @@ -2197,8 +2181,7 @@ repack_table_indexes(PGresult *index_details) ereport(WARNING, (errcode(E_PG_COMMAND), errmsg("Cannot create index \"%s\".\"index_%u\", " - "already exists", - schema_name, index), + "already exists", schema_name, index), errdetail("An invalid index may have been left behind" " by a previous pg_repack on the table" " which was interrupted. Please use DROP " @@ -2218,8 +2201,8 @@ repack_table_indexes(PGresult *index_details) if (PQntuples(res) < 1) { elog(WARNING, - "unable to generate SQL to CREATE work index for %s", - getstr(index_details, i, 0)); + "unable to generate SQL to CREATE work index for %s", + getstr(index_details, i, 0)); continue; } @@ -2232,7 +2215,8 @@ repack_table_indexes(PGresult *index_details) ereport(WARNING, (errcode(E_PG_COMMAND), errmsg("Error creating index \"%s\".\"index_%u\": %s", - schema_name, index, PQerrorMessage(connection)))); + schema_name, index, PQerrorMessage(connection) + ) )); } else { @@ -2248,8 +2232,7 @@ repack_table_indexes(PGresult *index_details) getstr(index_details, i, 0)); } - if (dryrun) - { + if (dryrun) { ret = true; goto done; } @@ -2314,11 +2297,11 @@ repack_table_indexes(PGresult *index_details) static bool repack_all_indexes(char *errbuf, size_t errsize) { - bool ret = false; - PGresult *res = NULL; - StringInfoData sql; - SimpleStringListCell *cell = NULL; - const char *params[1]; + bool ret = false; + PGresult *res = NULL; + StringInfoData sql; + SimpleStringListCell *cell = NULL; + const char *params[1]; initStringInfo(&sql); reconnect(ERROR); @@ -2334,20 +2317,20 @@ repack_all_indexes(char *errbuf, size_t errsize) if (r_index.head) { appendStringInfoString(&sql, - "SELECT repack.oid2text(i.oid), idx.indexrelid, idx.indisvalid, idx.indrelid, repack.oid2text(idx.indrelid), n.nspname" - " FROM pg_index idx JOIN pg_class i ON i.oid = idx.indexrelid" - " JOIN pg_namespace n ON n.oid = i.relnamespace" - " WHERE idx.indexrelid = $1::regclass ORDER BY indisvalid DESC, i.relname, n.nspname"); + "SELECT repack.oid2text(i.oid), idx.indexrelid, idx.indisvalid, idx.indrelid, repack.oid2text(idx.indrelid), n.nspname" + " FROM pg_index idx JOIN pg_class i ON i.oid = idx.indexrelid" + " JOIN pg_namespace n ON n.oid = i.relnamespace" + " WHERE idx.indexrelid = $1::regclass ORDER BY indisvalid DESC, i.relname, n.nspname"); cell = r_index.head; } else if (table_list.head || parent_table_list.head) { appendStringInfoString(&sql, - "SELECT repack.oid2text(i.oid), idx.indexrelid, idx.indisvalid, idx.indrelid, $1::text, n.nspname" - " FROM pg_index idx JOIN pg_class i ON i.oid = idx.indexrelid" - " JOIN pg_namespace n ON n.oid = i.relnamespace" - " WHERE idx.indrelid = $1::regclass ORDER BY indisvalid DESC, i.relname, n.nspname"); + "SELECT repack.oid2text(i.oid), idx.indexrelid, idx.indisvalid, idx.indrelid, $1::text, n.nspname" + " FROM pg_index idx JOIN pg_class i ON i.oid = idx.indexrelid" + " JOIN pg_namespace n ON n.oid = i.relnamespace" + " WHERE idx.indrelid = $1::regclass ORDER BY indisvalid DESC, i.relname, n.nspname"); for (cell = parent_table_list.head; cell; cell = cell->next) { @@ -2408,17 +2391,17 @@ repack_all_indexes(char *errbuf, size_t errsize) if (PQntuples(res) == 0) { - if (table_list.head) + if(table_list.head) elog(WARNING, "\"%s\" does not have any indexes", - cell->val); - else if (r_index.head) + cell->val); + else if(r_index.head) elog(WARNING, "\"%s\" is not a valid index", - cell->val); + cell->val); continue; } - if (table_list.head) + if(table_list.head) elog(INFO, "repacking indexes of \"%s\"", cell->val); if (!repack_table_indexes(res)) @@ -2434,7 +2417,8 @@ repack_all_indexes(char *errbuf, size_t errsize) return ret; } -void pgut_help(bool details) +void +pgut_help(bool details) { printf("%s re-organizes a PostgreSQL database.\n\n", PROGRAM_NAME); printf("Usage:\n"); @@ -2466,40 +2450,3 @@ void pgut_help(bool details) printf(" --apply-count number of tuples to apply in one transaction during replay\n"); printf(" --switch-threshold switch tables when that many tuples are left to catchup\n"); } - -/** - * Validate the WHERE clause by preparing a statement with the clause - * and checking if it can be executed against the target table. - */ -static bool -validate_where_clause(const char *table_name, const char *where_clause, char *errbuf, size_t errsize) -{ - PGresult *res; - StringInfoData sql; - bool ret = false; - - if (where_clause == NULL) - return true; - - initStringInfo(&sql); - - appendStringInfo(&sql, "EXPLAIN SELECT 1 FROM %s WHERE %s", - table_name, where_clause); - - res = PQexec(connection, sql.data); - if (PQresultStatus(res) != PGRES_TUPLES_OK) - { - if (errbuf) - snprintf(errbuf, errsize, "invalid WHERE clause: %s", - PQerrorMessage(connection)); - ret = false; - } - else - { - ret = true; - } - - CLEARPGRES(res); - termStringInfo(&sql); - return ret; -} From cfbec249f8b9734060f4f60c108ae3cf7e3b5cce Mon Sep 17 00:00:00 2001 From: ankit mittal Date: Fri, 28 Feb 2025 22:53:08 -0500 Subject: [PATCH 17/26] removed extra bin --- lib/pg_repack.dylib | Bin 42288 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100755 lib/pg_repack.dylib diff --git a/lib/pg_repack.dylib b/lib/pg_repack.dylib deleted file mode 100755 index 821abf6820b22f0ad7636372ef04905bb3032a10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42288 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 Date: Fri, 28 Feb 2025 23:00:09 -0500 Subject: [PATCH 18/26] bring back changes without whitespace --- bin/pg_repack.c | 786 +++++++++++++++++++++++++----------------------- 1 file changed, 416 insertions(+), 370 deletions(-) diff --git a/bin/pg_repack.c b/bin/pg_repack.c index c835620b..48a6c2f9 100644 --- a/bin/pg_repack.c +++ b/bin/pg_repack.c @@ -10,8 +10,8 @@ * @brief Client Modules */ -const char *PROGRAM_URL = "https://reorg.github.io/pg_repack/"; -const char *PROGRAM_ISSUES = "https://github.com/reorg/pg_repack/issues"; +const char *PROGRAM_URL = "https://reorg.github.io/pg_repack/"; +const char *PROGRAM_ISSUES = "https://github.com/reorg/pg_repack/issues"; #ifdef REPACK_VERSION /* macro trick to stringify a macro expansion */ @@ -30,7 +30,6 @@ const char *PROGRAM_VERSION = "unknown"; #include #include - #ifdef HAVE_POLL_H #include #endif @@ -41,20 +40,19 @@ const char *PROGRAM_VERSION = "unknown"; #include #endif - /* * APPLY_COUNT_DEFAULT: Number of applied logs per transaction. Larger values * could be faster, but will be long transactions in the REDO phase. */ -#define APPLY_COUNT_DEFAULT 1000 +#define APPLY_COUNT_DEFAULT 1000 /* Once we get down to seeing fewer than this many tuples in the * log table, we'll say that we're ready to perform the switch. */ -#define SWITCH_THRESHOLD_DEFAULT 100 +#define SWITCH_THRESHOLD_DEFAULT 100 /* poll() or select() timeout, in seconds */ -#define POLL_TIMEOUT 3 +#define POLL_TIMEOUT 3 /* Compile an array of existing transactions which are active during * pg_repack's setup. Some transactions we can safely ignore: @@ -74,64 +72,61 @@ const char *PROGRAM_VERSION = "unknown"; * instance, the application name when running installcheck will be * pg_regress. */ -#define SQL_XID_SNAPSHOT_90200 \ - "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ - " FROM pg_locks AS l " \ - " LEFT JOIN pg_stat_activity AS a " \ - " ON l.pid = a.pid " \ - " LEFT JOIN pg_database AS d " \ - " ON a.datid = d.oid " \ - " WHERE l.locktype = 'virtualxid' " \ - " AND l.pid NOT IN (pg_backend_pid(), $1) " \ +#define SQL_XID_SNAPSHOT_90200 \ + "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ + " FROM pg_locks AS l " \ + " LEFT JOIN pg_stat_activity AS a " \ + " ON l.pid = a.pid " \ + " LEFT JOIN pg_database AS d " \ + " ON a.datid = d.oid " \ + " WHERE l.locktype = 'virtualxid' " \ + " AND l.pid NOT IN (pg_backend_pid(), $1) " \ " AND (l.virtualxid, l.virtualtransaction) <> ('1/1', '-1/0') " \ " AND (a.application_name IS NULL OR a.application_name <> $2)" \ - " AND a.query !~* E'^\\\\s*vacuum\\\\s+' " \ - " AND a.query !~ E'^autovacuum: ' " \ + " AND a.query !~* E'^\\\\s*vacuum\\\\s+' " \ + " AND a.query !~ E'^autovacuum: ' " \ " AND ((d.datname IS NULL OR d.datname = current_database()) OR l.database = 0)" -#define SQL_XID_SNAPSHOT_90000 \ - "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ - " FROM pg_locks AS l " \ - " LEFT JOIN pg_stat_activity AS a " \ - " ON l.pid = a.procpid " \ - " LEFT JOIN pg_database AS d " \ - " ON a.datid = d.oid " \ - " WHERE l.locktype = 'virtualxid' " \ - " AND l.pid NOT IN (pg_backend_pid(), $1) " \ +#define SQL_XID_SNAPSHOT_90000 \ + "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ + " FROM pg_locks AS l " \ + " LEFT JOIN pg_stat_activity AS a " \ + " ON l.pid = a.procpid " \ + " LEFT JOIN pg_database AS d " \ + " ON a.datid = d.oid " \ + " WHERE l.locktype = 'virtualxid' " \ + " AND l.pid NOT IN (pg_backend_pid(), $1) " \ " AND (l.virtualxid, l.virtualtransaction) <> ('1/1', '-1/0') " \ " AND (a.application_name IS NULL OR a.application_name <> $2)" \ - " AND a.current_query !~* E'^\\\\s*vacuum\\\\s+' " \ - " AND a.current_query !~ E'^autovacuum: ' " \ + " AND a.current_query !~* E'^\\\\s*vacuum\\\\s+' " \ + " AND a.current_query !~ E'^autovacuum: ' " \ " AND ((d.datname IS NULL OR d.datname = current_database()) OR l.database = 0)" /* application_name is not available before 9.0. The last clause of * the WHERE clause is just to eat the $2 parameter (application name). */ -#define SQL_XID_SNAPSHOT_80300 \ - "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ - " FROM pg_locks AS l" \ - " LEFT JOIN pg_stat_activity AS a " \ - " ON l.pid = a.procpid " \ - " LEFT JOIN pg_database AS d " \ - " ON a.datid = d.oid " \ - " WHERE l.locktype = 'virtualxid' AND l.pid NOT IN (pg_backend_pid(), $1)" \ - " AND (l.virtualxid, l.virtualtransaction) <> ('1/1', '-1/0') " \ - " AND a.current_query !~* E'^\\\\s*vacuum\\\\s+' " \ - " AND a.current_query !~ E'^autovacuum: ' " \ +#define SQL_XID_SNAPSHOT_80300 \ + "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ + " FROM pg_locks AS l" \ + " LEFT JOIN pg_stat_activity AS a " \ + " ON l.pid = a.procpid " \ + " LEFT JOIN pg_database AS d " \ + " ON a.datid = d.oid " \ + " WHERE l.locktype = 'virtualxid' AND l.pid NOT IN (pg_backend_pid(), $1)" \ + " AND (l.virtualxid, l.virtualtransaction) <> ('1/1', '-1/0') " \ + " AND a.current_query !~* E'^\\\\s*vacuum\\\\s+' " \ + " AND a.current_query !~ E'^autovacuum: ' " \ " AND ((d.datname IS NULL OR d.datname = current_database()) OR l.database = 0)" \ " AND ($2::text IS NOT NULL)" #define SQL_XID_SNAPSHOT \ - (PQserverVersion(connection) >= 90200 ? SQL_XID_SNAPSHOT_90200 : \ - (PQserverVersion(connection) >= 90000 ? SQL_XID_SNAPSHOT_90000 : \ - SQL_XID_SNAPSHOT_80300)) - + (PQserverVersion(connection) >= 90200 ? SQL_XID_SNAPSHOT_90200 : (PQserverVersion(connection) >= 90000 ? SQL_XID_SNAPSHOT_90000 : SQL_XID_SNAPSHOT_80300)) /* Later, check whether any of the transactions we saw before are still * alive, and wait for them to go away. */ -#define SQL_XID_ALIVE \ - "SELECT pid FROM pg_locks WHERE locktype = 'virtualxid'"\ +#define SQL_XID_ALIVE \ + "SELECT pid FROM pg_locks WHERE locktype = 'virtualxid'" \ " AND pid <> pg_backend_pid() AND virtualtransaction = ANY($1)" /* To be run while our main connection holds an AccessExclusive lock on the @@ -141,20 +136,20 @@ const char *PROGRAM_VERSION = "unknown"; * transactions trying to grab an ACCESS EXCLUSIVE lock, because we are only * trying to kill off disallowed DDL commands, e.g. ALTER TABLE or TRUNCATE. */ -#define CANCEL_COMPETING_LOCKS \ - "SELECT pg_cancel_backend(pid) FROM pg_locks WHERE locktype = 'relation'"\ - " AND granted = false AND relation = %u"\ +#define CANCEL_COMPETING_LOCKS \ + "SELECT pg_cancel_backend(pid) FROM pg_locks WHERE locktype = 'relation'" \ + " AND granted = false AND relation = %u" \ " AND mode = 'AccessExclusiveLock' AND pid <> pg_backend_pid()" -#define KILL_COMPETING_LOCKS \ - "SELECT pg_terminate_backend(pid) "\ - "FROM pg_locks WHERE locktype = 'relation'"\ - " AND granted = false AND relation = %u"\ +#define KILL_COMPETING_LOCKS \ + "SELECT pg_terminate_backend(pid) " \ + "FROM pg_locks WHERE locktype = 'relation'" \ + " AND granted = false AND relation = %u" \ " AND mode = 'AccessExclusiveLock' AND pid <> pg_backend_pid()" -#define COUNT_COMPETING_LOCKS \ +#define COUNT_COMPETING_LOCKS \ "SELECT pid FROM pg_locks WHERE locktype = 'relation'" \ - " AND granted = false AND relation = %u" \ + " AND granted = false AND relation = %u" \ " AND mode = 'AccessExclusiveLock' AND pid <> pg_backend_pid()" /* Will be used as a unique prefix for advisory locks. */ @@ -172,10 +167,10 @@ typedef enum */ typedef struct repack_index { - Oid target_oid; /* target: OID */ - const char *create_index; /* CREATE INDEX */ - index_status_t status; /* Track parallel build statuses. */ - int worker_idx; /* which worker conn is handling */ + Oid target_oid; /* target: OID */ + const char *create_index; /* CREATE INDEX */ + index_status_t status; /* Track parallel build statuses. */ + int worker_idx; /* which worker conn is handling */ } repack_index; /* @@ -183,34 +178,34 @@ typedef struct repack_index */ typedef struct repack_table { - const char *target_name; /* target: relname */ - Oid target_oid; /* target: OID */ - Oid target_toast; /* target: toast OID */ - Oid target_tidx; /* target: toast index OID */ - Oid pkid; /* target: PK OID */ - Oid ckid; /* target: CK OID */ - Oid temp_oid; /* temp: OID */ - const char *create_pktype; /* CREATE TYPE pk */ - const char *create_log; /* CREATE TABLE log */ - const char *create_trigger; /* CREATE TRIGGER repack_trigger */ - const char *enable_trigger; /* ALTER TABLE ENABLE ALWAYS TRIGGER repack_trigger */ - const char *create_table; /* CREATE TABLE table AS SELECT WITH NO DATA*/ - const char *dest_tablespace; /* Destination tablespace */ - const char *copy_data; /* INSERT INTO */ - const char *alter_col_storage; /* ALTER TABLE ALTER COLUMN SET STORAGE */ - const char *drop_columns; /* ALTER TABLE DROP COLUMNs */ - const char *delete_log; /* DELETE FROM log */ - const char *lock_table; /* LOCK TABLE table */ - const char *sql_peek; /* SQL used in flush */ - const char *sql_insert; /* SQL used in flush */ - const char *sql_delete; /* SQL used in flush */ - const char *sql_update; /* SQL used in flush */ - const char *sql_pop; /* SQL used in flush */ - int n_indexes; /* number of indexes */ - repack_index *indexes; /* info on each index */ + const char *target_name; /* target: relname */ + Oid target_oid; /* target: OID */ + Oid target_toast; /* target: toast OID */ + Oid target_tidx; /* target: toast index OID */ + Oid pkid; /* target: PK OID */ + Oid ckid; /* target: CK OID */ + Oid temp_oid; /* temp: OID */ + const char *create_pktype; /* CREATE TYPE pk */ + const char *create_log; /* CREATE TABLE log */ + const char *create_trigger; /* CREATE TRIGGER repack_trigger */ + const char *enable_trigger; /* ALTER TABLE ENABLE ALWAYS TRIGGER repack_trigger */ + const char *create_table; /* CREATE TABLE table AS SELECT WITH NO DATA*/ + const char *dest_tablespace; /* Destination tablespace */ + const char *copy_data; /* INSERT INTO */ + const char *alter_col_storage; /* ALTER TABLE ALTER COLUMN SET STORAGE */ + const char *drop_columns; /* ALTER TABLE DROP COLUMNs */ + const char *delete_log; /* DELETE FROM log */ + const char *lock_table; /* LOCK TABLE table */ + const char *sql_peek; /* SQL used in flush */ + const char *sql_insert; /* SQL used in flush */ + const char *sql_delete; /* SQL used in flush */ + const char *sql_update; /* SQL used in flush */ + const char *sql_pop; /* SQL used in flush */ + const char *where_clause; /* WHERE clause for Deleting rows */ + int n_indexes; /* number of indexes */ + repack_index *indexes; /* info on each index */ } repack_table; - static bool is_superuser(void); static void check_tablespace(void); static bool preliminary_checks(char *errbuf, size_t errsize); @@ -224,6 +219,7 @@ static void repack_cleanup(bool fatal, const repack_table *table); static void repack_cleanup_callback(bool fatal, void *userdata); static void repack_cleanup_index(bool fatal, void *userdata); static bool rebuild_indexes(const repack_table *table); +static bool validate_where_clause(const char *table_name, const char *where_clause, char *errbuf, size_t errsize); static char *getstr(PGresult *res, int row, int col); static Oid getoid(PGresult *res, int row, int col); @@ -232,38 +228,38 @@ static bool lock_exclusive(PGconn *conn, const char *relid, const char *lock_que static bool kill_ddl(PGconn *conn, Oid relid, bool terminate); static bool lock_access_share(PGconn *conn, Oid relid, const char *target_name); -#define SQLSTATE_INVALID_SCHEMA_NAME "3F000" -#define SQLSTATE_UNDEFINED_FUNCTION "42883" -#define SQLSTATE_LOCK_NOT_AVAILABLE "55P03" +#define SQLSTATE_INVALID_SCHEMA_NAME "3F000" +#define SQLSTATE_UNDEFINED_FUNCTION "42883" +#define SQLSTATE_LOCK_NOT_AVAILABLE "55P03" static bool sqlstate_equals(PGresult *res, const char *state) { return strcmp(PQresultErrorField(res, PG_DIAG_SQLSTATE), state) == 0; } -static bool analyze = true; -static bool alldb = false; -static bool noorder = false; -static SimpleStringList parent_table_list = {NULL, NULL}; -static SimpleStringList table_list = {NULL, NULL}; -static SimpleStringList schema_list = {NULL, NULL}; -static char *orderby = NULL; -static char *tablespace = NULL; -static bool moveidx = false; -static SimpleStringList r_index = {NULL, NULL}; -static bool only_indexes = false; -static int wait_timeout = 60; /* in seconds */ -static int jobs = 0; /* number of concurrent worker conns. */ -static bool dryrun = false; -static unsigned int temp_obj_num = 0; /* temporary objects counter */ -static bool no_kill_backend = false; /* abandon when timed-out */ -static bool no_superuser_check = false; -static SimpleStringList exclude_extension_list = {NULL, NULL}; /* don't repack tables of these extensions */ -static bool no_error_on_invalid_index = false; /* repack even though invalid index is found */ -static bool error_on_invalid_index = false; /* don't repack when invalid index is found, - * deprecated, this the default behavior now */ -static int apply_count = APPLY_COUNT_DEFAULT; -static int switch_threshold = SWITCH_THRESHOLD_DEFAULT; +static bool analyze = true; +static bool alldb = false; +static bool noorder = false; +static SimpleStringList parent_table_list = {NULL, NULL}; +static SimpleStringList table_list = {NULL, NULL}; +static SimpleStringList schema_list = {NULL, NULL}; +static char *orderby = NULL; +static char *tablespace = NULL; +static bool moveidx = false; +static SimpleStringList r_index = {NULL, NULL}; +static bool only_indexes = false; +static int wait_timeout = 60; /* in seconds */ +static int jobs = 0; /* number of concurrent worker conns. */ +static bool dryrun = false; +static unsigned int temp_obj_num = 0; /* temporary objects counter */ +static bool no_kill_backend = false; /* abandon when timed-out */ +static bool no_superuser_check = false; +static SimpleStringList exclude_extension_list = {NULL, NULL}; /* don't repack tables of these extensions */ +static bool no_error_on_invalid_index = false; /* repack even though invalid index is found */ +static bool error_on_invalid_index = false; /* don't repack when invalid index is found, + * deprecated, this the default behavior now */ +static int apply_count = APPLY_COUNT_DEFAULT; +static int switch_threshold = SWITCH_THRESHOLD_DEFAULT; /* buffer should have at least 11 bytes */ static char * @@ -275,36 +271,35 @@ utoa(unsigned int value, char *buffer) } static pgut_option options[] = -{ - { 'b', 'a', "all", &alldb }, - { 'l', 't', "table", &table_list }, - { 'l', 'I', "parent-table", &parent_table_list }, - { 'l', 'c', "schema", &schema_list }, - { 'b', 'n', "no-order", &noorder }, - { 'b', 'N', "dry-run", &dryrun }, - { 's', 'o', "order-by", &orderby }, - { 's', 's', "tablespace", &tablespace }, - { 'b', 'S', "moveidx", &moveidx }, - { 'l', 'i', "index", &r_index }, - { 'b', 'x', "only-indexes", &only_indexes }, - { 'i', 'T', "wait-timeout", &wait_timeout }, - { 'B', 'Z', "no-analyze", &analyze }, - { 'i', 'j', "jobs", &jobs }, - { 'b', 'D', "no-kill-backend", &no_kill_backend }, - { 'b', 'k', "no-superuser-check", &no_superuser_check }, - { 'l', 'C', "exclude-extension", &exclude_extension_list }, - { 'b', 4, "no-error-on-invalid-index", &no_error_on_invalid_index }, - { 'b', 3, "error-on-invalid-index", &error_on_invalid_index }, - { 'i', 2, "apply-count", &apply_count }, - { 'i', 1, "switch-threshold", &switch_threshold }, - { 0 }, + { + {'b', 'a', "all", &alldb}, + {'l', 't', "table", &table_list}, + {'l', 'I', "parent-table", &parent_table_list}, + {'l', 'c', "schema", &schema_list}, + {'b', 'n', "no-order", &noorder}, + {'b', 'N', "dry-run", &dryrun}, + {'s', 'o', "order-by", &orderby}, + {'s', 's', "tablespace", &tablespace}, + {'b', 'S', "moveidx", &moveidx}, + {'l', 'i', "index", &r_index}, + {'b', 'x', "only-indexes", &only_indexes}, + {'i', 'T', "wait-timeout", &wait_timeout}, + {'B', 'Z', "no-analyze", &analyze}, + {'i', 'j', "jobs", &jobs}, + {'b', 'D', "no-kill-backend", &no_kill_backend}, + {'b', 'k', "no-superuser-check", &no_superuser_check}, + {'l', 'C', "exclude-extension", &exclude_extension_list}, + {'b', 4, "no-error-on-invalid-index", &no_error_on_invalid_index}, + {'b', 3, "error-on-invalid-index", &error_on_invalid_index}, + {'i', 2, "apply-count", &apply_count}, + {'i', 1, "switch-threshold", &switch_threshold}, + {0}, }; -int -main(int argc, char *argv[]) +int main(int argc, char *argv[]) { - int i; - char errbuf[256]; + int i; + char errbuf[256]; i = pgut_getopt(argc, argv, options); @@ -312,12 +307,12 @@ main(int argc, char *argv[]) dbname = argv[i]; else if (i < argc) ereport(ERROR, - (errcode(EINVAL), - errmsg("too many arguments"))); + (errcode(EINVAL), + errmsg("too many arguments"))); - if(switch_threshold >= apply_count) + if (switch_threshold >= apply_count) ereport(ERROR, (errcode(EINVAL), - errmsg("switch_threshold must be less than apply_count"))); + errmsg("switch_threshold must be less than apply_count"))); check_tablespace(); @@ -328,61 +323,61 @@ main(int argc, char *argv[]) { if (r_index.head && table_list.head) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot specify --index (-i) and --table (-t)"))); + errmsg("cannot specify --index (-i) and --table (-t)"))); if (r_index.head && parent_table_list.head) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot specify --index (-i) and --parent-table (-I)"))); + errmsg("cannot specify --index (-i) and --parent-table (-I)"))); else if (r_index.head && only_indexes) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot specify --index (-i) and --only-indexes (-x)"))); + errmsg("cannot specify --index (-i) and --only-indexes (-x)"))); else if (r_index.head && exclude_extension_list.head) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot specify --index (-i) and --exclude-extension (-C)"))); + errmsg("cannot specify --index (-i) and --exclude-extension (-C)"))); else if (only_indexes && !(table_list.head || parent_table_list.head)) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot repack all indexes of database, specify the table(s)" - "via --table (-t) or --parent-table (-I)"))); + errmsg("cannot repack all indexes of database, specify the table(s)" + "via --table (-t) or --parent-table (-I)"))); else if (only_indexes && exclude_extension_list.head) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot specify --only-indexes (-x) and --exclude-extension (-C)"))); + errmsg("cannot specify --only-indexes (-x) and --exclude-extension (-C)"))); else if (alldb) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot repack specific index(es) in all databases"))); + errmsg("cannot repack specific index(es) in all databases"))); else { if (orderby) ereport(WARNING, (errcode(EINVAL), - errmsg("option -o (--order-by) has no effect while repacking indexes"))); + errmsg("option -o (--order-by) has no effect while repacking indexes"))); else if (noorder) ereport(WARNING, (errcode(EINVAL), - errmsg("option -n (--no-order) has no effect while repacking indexes"))); + errmsg("option -n (--no-order) has no effect while repacking indexes"))); else if (!analyze) ereport(WARNING, (errcode(EINVAL), - errmsg("ANALYZE is not performed after repacking indexes, -z (--no-analyze) has no effect"))); + errmsg("ANALYZE is not performed after repacking indexes, -z (--no-analyze) has no effect"))); else if (jobs) ereport(WARNING, (errcode(EINVAL), - errmsg("option -j (--jobs) has no effect, repacking indexes does not use parallel jobs"))); + errmsg("option -j (--jobs) has no effect, repacking indexes does not use parallel jobs"))); if (!repack_all_indexes(errbuf, sizeof(errbuf))) ereport(ERROR, - (errcode(ERROR), errmsg("%s", errbuf))); + (errcode(ERROR), errmsg("%s", errbuf))); } } else { if (schema_list.head && (table_list.head || parent_table_list.head)) ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot repack specific table(s) in schema, use schema.table notation instead"))); + (errcode(EINVAL), + errmsg("cannot repack specific table(s) in schema, use schema.table notation instead"))); if (exclude_extension_list.head && table_list.head) ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot specify --table (-t) and --exclude-extension (-C)"))); + (errcode(EINVAL), + errmsg("cannot specify --table (-t) and --exclude-extension (-C)"))); if (exclude_extension_list.head && parent_table_list.head) ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot specify --parent-table (-I) and --exclude-extension (-C)"))); + (errcode(EINVAL), + errmsg("cannot specify --parent-table (-I) and --exclude-extension (-C)"))); if (noorder) orderby = ""; @@ -391,26 +386,25 @@ main(int argc, char *argv[]) { if (table_list.head || parent_table_list.head) ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot repack specific table(s) in all databases"))); + (errcode(EINVAL), + errmsg("cannot repack specific table(s) in all databases"))); if (schema_list.head) ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot repack specific schema(s) in all databases"))); + (errcode(EINVAL), + errmsg("cannot repack specific schema(s) in all databases"))); repack_all_databases(orderby); } else { if (!repack_one_database(orderby, errbuf, sizeof(errbuf))) ereport(ERROR, - (errcode(ERROR), errmsg("%s failed with error: %s", PROGRAM_NAME, errbuf))); + (errcode(ERROR), errmsg("%s failed with error: %s", PROGRAM_NAME, errbuf))); } } return 0; } - /* * Test if the current user is a database superuser. * Borrowed from psql/common.c @@ -418,8 +412,7 @@ main(int argc, char *argv[]) * Note: this will correctly detect superuserness only with a protocol-3.0 * or newer backend; otherwise it will always say "false". */ -bool -is_superuser(void) +bool is_superuser(void) { const char *val; @@ -442,10 +435,9 @@ is_superuser(void) * * Raise an exception on error. */ -void -check_tablespace() +void check_tablespace() { - PGresult *res = NULL; + PGresult *res = NULL; const char *params[1]; if (tablespace == NULL) @@ -454,8 +446,8 @@ check_tablespace() if (moveidx) { ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot specify --moveidx (-S) without --tablespace (-s)"))); + (errcode(EINVAL), + errmsg("cannot specify --moveidx (-S) without --tablespace (-s)"))); } return; } @@ -472,16 +464,16 @@ check_tablespace() if (PQntuples(res) == 0) { ereport(ERROR, - (errcode(EINVAL), - errmsg("the tablespace \"%s\" doesn't exist", tablespace))); + (errcode(EINVAL), + errmsg("the tablespace \"%s\" doesn't exist", tablespace))); } } else { ereport(ERROR, - (errcode(EINVAL), - errmsg("error checking the namespace: %s", - PQerrorMessage(connection)))); + (errcode(EINVAL), + errmsg("error checking the namespace: %s", + PQerrorMessage(connection)))); } CLEARPGRES(res); @@ -492,11 +484,13 @@ check_tablespace() * installed in the database, the user is a superuser, etc. */ static bool -preliminary_checks(char *errbuf, size_t errsize){ - bool ret = false; - PGresult *res = NULL; +preliminary_checks(char *errbuf, size_t errsize) +{ + bool ret = false; + PGresult *res = NULL; - if (!is_superuser()) { + if (!is_superuser()) + { if (errbuf) snprintf(errbuf, errsize, "You must be a superuser to use %s", PROGRAM_NAME); @@ -505,11 +499,11 @@ preliminary_checks(char *errbuf, size_t errsize){ /* Query the extension version. Exit if no match */ res = execute_elevel("select repack.version(), repack.version_sql()", - 0, NULL, DEBUG2); + 0, NULL, DEBUG2); if (PQresultStatus(res) == PGRES_TUPLES_OK) { - const char *libver; - char buf[64]; + const char *libver; + char buf[64]; /* the string is something like "pg_repack 1.1.7" */ snprintf(buf, sizeof(buf), "%s %s", PROGRAM_NAME, PROGRAM_VERSION); @@ -520,8 +514,8 @@ preliminary_checks(char *errbuf, size_t errsize){ { if (errbuf) snprintf(errbuf, errsize, - "program '%s' does not match database library '%s'", - buf, libver); + "program '%s' does not match database library '%s'", + buf, libver); goto cleanup; } @@ -531,24 +525,23 @@ preliminary_checks(char *errbuf, size_t errsize){ { if (errbuf) snprintf(errbuf, errsize, - "extension '%s' required, found '%s';" - " please drop and re-create the extension", - buf, libver); + "extension '%s' required, found '%s';" + " please drop and re-create the extension", + buf, libver); goto cleanup; } } else { - if (sqlstate_equals(res, SQLSTATE_INVALID_SCHEMA_NAME) - || sqlstate_equals(res, SQLSTATE_UNDEFINED_FUNCTION)) + if (sqlstate_equals(res, SQLSTATE_INVALID_SCHEMA_NAME) || sqlstate_equals(res, SQLSTATE_UNDEFINED_FUNCTION)) { /* Schema repack does not exist, or version too old (version * functions not found). Skip the database. */ if (errbuf) snprintf(errbuf, errsize, - "%s %s is not installed in the database", - PROGRAM_NAME, PROGRAM_VERSION); + "%s %s is not installed in the database", + PROGRAM_NAME, PROGRAM_VERSION); } else { @@ -581,14 +574,15 @@ preliminary_checks(char *errbuf, size_t errsize){ * otherwise format user-friendly message */ static bool -is_requested_relation_exists(char *errbuf, size_t errsize){ - bool ret = false; - PGresult *res = NULL; - const char **params = NULL; - int iparam = 0; - StringInfoData sql; - int num_relations; - SimpleStringListCell *cell; +is_requested_relation_exists(char *errbuf, size_t errsize) +{ + bool ret = false; + PGresult *res = NULL; + const char **params = NULL; + int iparam = 0; + StringInfoData sql; + int num_relations; + SimpleStringListCell *cell; num_relations = simple_string_list_size(parent_table_list) + simple_string_list_size(table_list); @@ -598,7 +592,7 @@ is_requested_relation_exists(char *errbuf, size_t errsize){ return true; /* has no suitable to_regclass(text) */ - if (PQserverVersion(connection)<90600) + if (PQserverVersion(connection) < 90600) return true; params = pgut_malloc(num_relations * sizeof(char *)); @@ -620,36 +614,35 @@ is_requested_relation_exists(char *errbuf, size_t errsize){ appendStringInfoChar(&sql, ','); } appendStringInfoString(&sql, - ") AS given_t(r,kind) WHERE" - /* regular --table relation or inherited --parent-table */ - " NOT EXISTS(" - " SELECT FROM repack.tables WHERE relid=to_regclass(given_t.r))" - /* declarative partitioned --parent-table */ - " AND NOT EXISTS(" - " SELECT FROM pg_catalog.pg_class c WHERE c.oid=to_regclass(given_t.r) AND c.relkind = given_t.kind AND given_t.kind = 'p')" - ); + ") AS given_t(r,kind) WHERE" + /* regular --table relation or inherited --parent-table */ + " NOT EXISTS(" + " SELECT FROM repack.tables WHERE relid=to_regclass(given_t.r))" + /* declarative partitioned --parent-table */ + " AND NOT EXISTS(" + " SELECT FROM pg_catalog.pg_class c WHERE c.oid=to_regclass(given_t.r) AND c.relkind = given_t.kind AND given_t.kind = 'p')"); /* double check the parameters array is sane */ if (iparam != num_relations) { if (errbuf) snprintf(errbuf, errsize, - "internal error: bad parameters count: %i instead of %i", - iparam, num_relations); + "internal error: bad parameters count: %i instead of %i", + iparam, num_relations); goto cleanup; } res = execute_elevel(sql.data, iparam, params, DEBUG2); if (PQresultStatus(res) == PGRES_TUPLES_OK) { - int num; + int num; num = PQntuples(res); if (num != 0) { int i; - StringInfoData rel_names; + StringInfoData rel_names; initStringInfo(&rel_names); for (i = 0; i < num; i++) @@ -663,10 +656,10 @@ is_requested_relation_exists(char *errbuf, size_t errsize){ { if (num > 1) snprintf(errbuf, errsize, - "relations do not exist: %s", rel_names.data); + "relations do not exist: %s", rel_names.data); else snprintf(errbuf, errsize, - "ERROR: relation %s does not exist", rel_names.data); + "ERROR: relation %s does not exist", rel_names.data); } termStringInfo(&rel_names); } @@ -693,8 +686,8 @@ is_requested_relation_exists(char *errbuf, size_t errsize){ static void repack_all_databases(const char *orderby) { - PGresult *result; - int i; + PGresult *result; + int i; dbname = "postgres"; reconnect(ERROR); @@ -707,8 +700,8 @@ repack_all_databases(const char *orderby) for (i = 0; i < PQntuples(result); i++) { - bool ret; - char errbuf[256]; + bool ret; + char errbuf[256]; dbname = PQgetvalue(result, i, 0); @@ -749,19 +742,19 @@ getoid(PGresult *res, int row, int col) static bool repack_one_database(const char *orderby, char *errbuf, size_t errsize) { - bool ret = false; - PGresult *res = NULL; - int i; - int num; - StringInfoData sql; - SimpleStringListCell *cell; - const char **params = NULL; - int iparam = 0; - size_t num_parent_tables, - num_tables, - num_schemas, - num_params, - num_excluded_extensions; + bool ret = false; + PGresult *res = NULL; + int i; + int num; + StringInfoData sql; + SimpleStringListCell *cell; + const char **params = NULL; + int iparam = 0; + size_t num_parent_tables, + num_tables, + num_schemas, + num_params, + num_excluded_extensions; num_parent_tables = simple_string_list_size(parent_table_list); num_tables = simple_string_list_size(table_list); @@ -791,11 +784,11 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize) /* acquire target tables */ appendStringInfoString(&sql, - "SELECT t.*," - " coalesce(v.tablespace, t.tablespace_orig) as tablespace_dest" - " FROM repack.tables t, " - " (VALUES ($1::text)) as v (tablespace)" - " WHERE "); + "SELECT t.*," + " coalesce(v.tablespace, t.tablespace_orig) as tablespace_dest" + " FROM repack.tables t, " + " (VALUES ($1::text)) as v (tablespace)" + " WHERE "); params[iparam++] = tablespace; if (num_tables || num_parent_tables) @@ -883,12 +876,12 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize) { if (errbuf) snprintf(errbuf, errsize, - "internal error: bad parameters count: %i instead of %zi", - iparam, num_params); + "internal error: bad parameters count: %i instead of %zi", + iparam, num_params); goto cleanup; } - res = execute_elevel(sql.data, (int) num_params, params, DEBUG2); + res = execute_elevel(sql.data, (int)num_params, params, DEBUG2); /* on error skip the database */ if (PQresultStatus(res) != PGRES_TUPLES_OK) @@ -903,10 +896,10 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize) for (i = 0; i < num; i++) { - repack_table table; - StringInfoData copy_sql; + repack_table table; + StringInfoData copy_sql; const char *ckey; - int c = 0; + int c = 0; table.target_name = getstr(res, i, c++); table.target_oid = getoid(res, i, c++); @@ -917,7 +910,8 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize) table.ckid = getoid(res, i, c++); table.temp_oid = InvalidOid; /* filled after creating the temp table */ - if (table.pkid == 0) { + if (table.pkid == 0) + { ereport(WARNING, (errcode(E_PG_COMMAND), errmsg("relation \"%s\" must have a primary key or not-null unique keys", table.target_name))); @@ -930,8 +924,8 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize) table.enable_trigger = getstr(res, i, c++); table.create_table = getstr(res, i, c++); - getstr(res, i, c++); /* tablespace_orig is clobbered */ - table.copy_data = getstr(res, i , c++); + getstr(res, i, c++); /* tablespace_orig is clobbered */ + table.copy_data = getstr(res, i, c++); table.alter_col_storage = getstr(res, i, c++); table.drop_columns = getstr(res, i, c++); table.delete_log = getstr(res, i, c++); @@ -943,10 +937,16 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize) table.sql_update = getstr(res, i, c++); table.sql_pop = getstr(res, i, c++); table.dest_tablespace = getstr(res, i, c++); + table.where_clause = getstr(res, i, c++); /* Craft Copy SQL */ initStringInfo(©_sql); appendStringInfoString(©_sql, table.copy_data); + if (table.where_clause) + { + appendStringInfoString(©_sql, " WHERE "); + appendStringInfoString(©_sql, table.where_clause); + } if (!orderby) { @@ -986,10 +986,10 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize) static int apply_log(PGconn *conn, const repack_table *table, int count) { - int result; - PGresult *res; + int result; + PGresult *res; const char *params[6]; - char buffer[12]; + char buffer[12]; params[0] = table->sql_peek; params[1] = table->sql_insert; @@ -1014,13 +1014,13 @@ apply_log(PGconn *conn, const repack_table *table, int count) static bool rebuild_indexes(const repack_table *table) { - PGresult *res = NULL; - int num_indexes; - int i; - int num_active_workers; - int num_workers; - repack_index *index_jobs; - bool have_error = false; + PGresult *res = NULL; + int num_indexes; + int i; + int num_active_workers; + int num_workers; + repack_index *index_jobs; + bool have_error = false; elog(DEBUG2, "---- create indexes ----"); @@ -1045,7 +1045,8 @@ rebuild_indexes(const repack_table *table) elog(DEBUG2, "target_oid : %u", index_jobs[i].target_oid); elog(DEBUG2, "create_index : %s", index_jobs[i].create_index); - if (num_workers <= 1) { + if (num_workers <= 1) + { /* Use primary connection if we are not setting up parallel * index building, or if we only have one worker. */ @@ -1056,7 +1057,8 @@ rebuild_indexes(const repack_table *table) */ index_jobs[i].status = FINISHED; } - else if (i < num_workers) { + else if (i < num_workers) + { /* Assign available worker to build an index. */ index_jobs[i].status = INPROGRESS; index_jobs[i].worker_idx = i; @@ -1193,11 +1195,13 @@ rebuild_indexes(const repack_table *table) index_jobs[i].status = INPROGRESS; index_jobs[i].worker_idx = freed_worker; elog(LOG, "Assigning worker %d to build index #%d: " - "%s", freed_worker, i, + "%s", + freed_worker, i, index_jobs[i].create_index); if (!(PQsendQuery(workers.conns[freed_worker], - index_jobs[i].create_index))) { + index_jobs[i].create_index))) + { elog(WARNING, "Error sending async query: %s\n%s", index_jobs[i].create_index, PQerrorMessage(workers.conns[freed_worker])); @@ -1211,7 +1215,6 @@ rebuild_indexes(const repack_table *table) freed_worker = -1; } } - } cleanup: @@ -1219,29 +1222,28 @@ rebuild_indexes(const repack_table *table) return (!have_error); } - /* * Re-organize one table. */ static void repack_one_table(repack_table *table, const char *orderby) { - PGresult *res = NULL; - const char *params[3]; - int num; - char *vxid = NULL; - char buffer[12]; - StringInfoData sql; - bool ret = false; - PGresult *indexres = NULL; - const char *indexparams[2]; - char indexbuffer[12]; - int j; + PGresult *res = NULL; + const char *params[3]; + int num; + char *vxid = NULL; + char buffer[12]; + StringInfoData sql; + bool ret = false; + PGresult *indexres = NULL; + const char *indexparams[2]; + char indexbuffer[12]; + int j; /* appname will be "pg_repack" in normal use on 9.0+, or * "pg_regress" when run under `make installcheck` */ - const char *appname = getenv("PGAPPNAME"); + const char *appname = getenv("PGAPPNAME"); /* Keep track of whether we have gotten through setup to install * the repack_trigger, log table, etc. ourselves. We don't want to @@ -1249,7 +1251,7 @@ repack_one_table(repack_table *table, const char *orderby) * trigger ourselves, lest we be cleaning up another pg_repack's mess, * or worse, interfering with a still-running pg_repack. */ - bool table_init = false; + bool table_init = false; initStringInfo(&sql); @@ -1269,8 +1271,7 @@ repack_one_table(repack_table *table, const char *orderby) elog(DEBUG2, "create_table : %s", table->create_table); elog(DEBUG2, "dest_tablespace : %s", table->dest_tablespace); elog(DEBUG2, "copy_data : %s", table->copy_data); - elog(DEBUG2, "alter_col_storage : %s", table->alter_col_storage ? - table->alter_col_storage : "(skipped)"); + elog(DEBUG2, "alter_col_storage : %s", table->alter_col_storage ? table->alter_col_storage : "(skipped)"); elog(DEBUG2, "drop_columns : %s", table->drop_columns ? table->drop_columns : "(skipped)"); elog(DEBUG2, "delete_log : %s", table->delete_log); elog(DEBUG2, "lock_table : %s", table->lock_table); @@ -1279,6 +1280,10 @@ repack_one_table(repack_table *table, const char *orderby) elog(DEBUG2, "sql_delete : %s", table->sql_delete); elog(DEBUG2, "sql_update : %s", table->sql_update); elog(DEBUG2, "sql_pop : %s", table->sql_pop); + elog(DEBUG2, "where_clause : %s", table->where_clause); + + if (table->where_clause && !validate_where_clause(table->target_name, table->where_clause, errbuf, sizeof(errbuf))) + return; if (dryrun) return; @@ -1327,10 +1332,13 @@ repack_one_table(repack_table *table, const char *orderby) const char *indexdef; indexdef = getstr(indexres, j, 0); - if (!no_error_on_invalid_index) { + if (!no_error_on_invalid_index) + { elog(WARNING, "Invalid index: %s", indexdef); goto cleanup; - } else { + } + else + { elog(WARNING, "skipping invalid index: %s", indexdef); } } @@ -1358,7 +1366,6 @@ repack_one_table(repack_table *table, const char *orderby) elog(DEBUG2, "index[%d].create_index : %s", j, table->indexes[j].create_index); } - /* * Check if repack_trigger is not conflict with existing trigger. We can * find it out later but we check it in advance and go to cleanup if needed. @@ -1377,7 +1384,7 @@ repack_one_table(repack_table *table, const char *orderby) " attempt to run pg_repack on the table which was" " interrupted and for some reason failed to clean up" " the temporary objects. Please drop the trigger or drop" - " and recreate the pg_repack extension altogether" + " and recreate the pg_repack extension altogether" " to remove all the temporary objects left over."))); goto cleanup; } @@ -1579,7 +1586,7 @@ repack_one_table(repack_table *table, const char *orderby) * constantly coming into the original table. */ if (num > switch_threshold) - continue; /* there might be still some tuples, repeat. */ + continue; /* there might be still some tuples, repeat. */ /* old transactions still alive ? */ params[0] = vxid; @@ -1685,7 +1692,7 @@ repack_one_table(repack_table *table, const char *orderby) params[1] = utoa(table->target_oid, buffer); res = pgut_execute(connection, "SELECT pg_advisory_unlock($1, CAST(-2147483648 + $2::bigint AS integer))", - 2, params); + 2, params); ret = true; cleanup: @@ -1715,10 +1722,10 @@ repack_one_table(repack_table *table, const char *orderby) static bool kill_ddl(PGconn *conn, Oid relid, bool terminate) { - bool ret = true; - PGresult *res; - StringInfoData sql; - int n_tuples; + bool ret = true; + PGresult *res; + StringInfoData sql; + int n_tuples; initStringInfo(&sql); @@ -1778,7 +1785,6 @@ kill_ddl(PGconn *conn, Oid relid, bool terminate) return ret; } - /* * Try to acquire an ACCESS SHARE table lock, avoiding deadlocks and long * waits by killing off other sessions which may be stuck trying to obtain @@ -1794,18 +1800,18 @@ kill_ddl(PGconn *conn, Oid relid, bool terminate) static bool lock_access_share(PGconn *conn, Oid relid, const char *target_name) { - StringInfoData sql; - time_t start = time(NULL); - int i; - bool ret = true; + StringInfoData sql; + time_t start = time(NULL); + int i; + bool ret = true; initStringInfo(&sql); - for (i = 1; ; i++) + for (i = 1;; i++) { - time_t duration; - PGresult *res; - int wait_msec; + time_t duration; + PGresult *res; + int wait_msec; pgut_command(conn, "SAVEPOINT repack_sp1", 0, NULL); @@ -1860,7 +1866,6 @@ lock_access_share(PGconn *conn, Oid relid, const char *target_name) return ret; } - /* Obtain an advisory lock on the table's OID, to make sure no other * pg_repack is working on the table. This is not so much a concern with * full-table repacks, but mainly so that index-only repacks don't interfere @@ -1868,9 +1873,9 @@ lock_access_share(PGconn *conn, Oid relid, const char *target_name) */ static bool advisory_lock(PGconn *conn, const char *relid) { - PGresult *res = NULL; - bool ret = false; - const char *params[2]; + PGresult *res = NULL; + bool ret = false; + const char *params[2]; params[0] = REPACK_LOCK_PREFIX_STR; params[1] = relid; @@ -1881,15 +1886,18 @@ static bool advisory_lock(PGconn *conn, const char *relid) * it fit reliably into signed int space. */ res = pgut_execute(conn, "SELECT pg_try_advisory_lock($1, CAST(-2147483648 + $2::bigint AS integer))", - 2, params); + 2, params); - if (PQresultStatus(res) != PGRES_TUPLES_OK) { - elog(ERROR, "%s", PQerrorMessage(connection)); + if (PQresultStatus(res) != PGRES_TUPLES_OK) + { + elog(ERROR, "%s", PQerrorMessage(connection)); } - else if (strcmp(getstr(res, 0, 0), "t") != 0) { + else if (strcmp(getstr(res, 0, 0), "t") != 0) + { elog(ERROR, "Another pg_repack command may be running on the table. Please try again later."); } - else { + else + { ret = true; } CLEARPGRES(res); @@ -1911,16 +1919,16 @@ static bool advisory_lock(PGconn *conn, const char *relid) static bool lock_exclusive(PGconn *conn, const char *relid, const char *lock_query, bool start_xact) { - time_t start = time(NULL); - int i; - bool ret = true; + time_t start = time(NULL); + int i; + bool ret = true; - for (i = 1; ; i++) + for (i = 1;; i++) { - time_t duration; - char sql[1024]; - PGresult *res; - int wait_msec; + time_t duration; + char sql[1024]; + PGresult *res; + int wait_msec; if (start_xact) pgut_command(conn, "BEGIN ISOLATION LEVEL READ COMMITTED", 0, NULL); @@ -2005,16 +2013,15 @@ lock_exclusive(PGconn *conn, const char *relid, const char *lock_query, bool sta /* This function calls to repack_drop() to clean temporary objects on error * in creation of temporary objects. */ -void -repack_cleanup_callback(bool fatal, void *userdata) +void repack_cleanup_callback(bool fatal, void *userdata) { - repack_table *table = (repack_table *) userdata; - Oid target_table = table->target_oid; + repack_table *table = (repack_table *)userdata; + Oid target_table = table->target_oid; const char *params[2]; - char buffer[12]; - char num_buff[12]; + char buffer[12]; + char num_buff[12]; - if(fatal) + if (fatal) { params[0] = utoa(target_table, buffer); params[1] = utoa(temp_obj_num, num_buff); @@ -2052,8 +2059,8 @@ repack_cleanup(bool fatal, const repack_table *table) } else { - char buffer[12]; - char num_buff[12]; + char buffer[12]; + char num_buff[12]; const char *params[2]; /* Try reconnection if not available. */ @@ -2063,7 +2070,7 @@ repack_cleanup(bool fatal, const repack_table *table) /* do cleanup */ params[0] = utoa(table->target_oid, buffer); - params[1] = utoa(temp_obj_num, num_buff); + params[1] = utoa(temp_obj_num, num_buff); command("BEGIN ISOLATION LEVEL READ COMMITTED", 0, NULL); if (!(lock_exclusive(connection, params[0], table->lock_table, false))) @@ -2087,9 +2094,9 @@ static void repack_cleanup_index(bool fatal, void *userdata) { StringInfoData sql, sql_drop; - PGresult *index_details = (PGresult *) userdata; - char *schema_name; - int num; + PGresult *index_details = (PGresult *)userdata; + char *schema_name; + int num; schema_name = getstr(index_details, 0, 5); num = PQntuples(index_details); @@ -2098,11 +2105,11 @@ repack_cleanup_index(bool fatal, void *userdata) initStringInfo(&sql_drop); appendStringInfoString(&sql, "DROP INDEX CONCURRENTLY IF EXISTS "); - appendStringInfo(&sql, "\"%s\".", schema_name); + appendStringInfo(&sql, "\"%s\".", schema_name); for (int i = 0; i < num; i++) { - Oid index = getoid(index_details, i, 1); + Oid index = getoid(index_details, i, 1); resetStringInfo(&sql_drop); appendStringInfo(&sql_drop, "%s\"index_%u\"", sql.data, index); @@ -2118,14 +2125,14 @@ repack_cleanup_index(bool fatal, void *userdata) static bool repack_table_indexes(PGresult *index_details) { - bool ret = false; - PGresult *res = NULL, *res2 = NULL; - StringInfoData sql; - char buffer[2][12]; - const char *create_idx, *schema_name, *table_name, *params[3]; - Oid table, index; - int i, num, num_repacked = 0; - bool *repacked_indexes; + bool ret = false; + PGresult *res = NULL, *res2 = NULL; + StringInfoData sql; + char buffer[2][12]; + const char *create_idx, *schema_name, *table_name, *params[3]; + Oid table, index; + int i, num, num_repacked = 0; + bool *repacked_indexes; initStringInfo(&sql); @@ -2149,7 +2156,7 @@ repack_table_indexes(PGresult *index_details) */ if (!advisory_lock(connection, params[1])) ereport(ERROR, (errcode(EINVAL), - errmsg("Unable to obtain advisory lock on \"%s\"", table_name))); + errmsg("Unable to obtain advisory lock on \"%s\"", table_name))); pgut_atexit_push(repack_cleanup_index, index_details); @@ -2164,10 +2171,11 @@ repack_table_indexes(PGresult *index_details) resetStringInfo(&sql); appendStringInfo(&sql, "SELECT pgc.relname, nsp.nspname " - "FROM pg_class pgc INNER JOIN pg_namespace nsp " - "ON nsp.oid = pgc.relnamespace " - "WHERE pgc.relname = 'index_%u' " - "AND nsp.nspname = $1", index); + "FROM pg_class pgc INNER JOIN pg_namespace nsp " + "ON nsp.oid = pgc.relnamespace " + "WHERE pgc.relname = 'index_%u' " + "AND nsp.nspname = $1", + index); params[0] = schema_name; elog(INFO, "repacking index \"%s\"", idx_name); res = execute(sql.data, 1, params); @@ -2181,7 +2189,8 @@ repack_table_indexes(PGresult *index_details) ereport(WARNING, (errcode(E_PG_COMMAND), errmsg("Cannot create index \"%s\".\"index_%u\", " - "already exists", schema_name, index), + "already exists", + schema_name, index), errdetail("An invalid index may have been left behind" " by a previous pg_repack on the table" " which was interrupted. Please use DROP " @@ -2201,8 +2210,8 @@ repack_table_indexes(PGresult *index_details) if (PQntuples(res) < 1) { elog(WARNING, - "unable to generate SQL to CREATE work index for %s", - getstr(index_details, i, 0)); + "unable to generate SQL to CREATE work index for %s", + getstr(index_details, i, 0)); continue; } @@ -2215,8 +2224,7 @@ repack_table_indexes(PGresult *index_details) ereport(WARNING, (errcode(E_PG_COMMAND), errmsg("Error creating index \"%s\".\"index_%u\": %s", - schema_name, index, PQerrorMessage(connection) - ) )); + schema_name, index, PQerrorMessage(connection)))); } else { @@ -2232,7 +2240,8 @@ repack_table_indexes(PGresult *index_details) getstr(index_details, i, 0)); } - if (dryrun) { + if (dryrun) + { ret = true; goto done; } @@ -2297,11 +2306,11 @@ repack_table_indexes(PGresult *index_details) static bool repack_all_indexes(char *errbuf, size_t errsize) { - bool ret = false; - PGresult *res = NULL; - StringInfoData sql; - SimpleStringListCell *cell = NULL; - const char *params[1]; + bool ret = false; + PGresult *res = NULL; + StringInfoData sql; + SimpleStringListCell *cell = NULL; + const char *params[1]; initStringInfo(&sql); reconnect(ERROR); @@ -2317,20 +2326,20 @@ repack_all_indexes(char *errbuf, size_t errsize) if (r_index.head) { appendStringInfoString(&sql, - "SELECT repack.oid2text(i.oid), idx.indexrelid, idx.indisvalid, idx.indrelid, repack.oid2text(idx.indrelid), n.nspname" - " FROM pg_index idx JOIN pg_class i ON i.oid = idx.indexrelid" - " JOIN pg_namespace n ON n.oid = i.relnamespace" - " WHERE idx.indexrelid = $1::regclass ORDER BY indisvalid DESC, i.relname, n.nspname"); + "SELECT repack.oid2text(i.oid), idx.indexrelid, idx.indisvalid, idx.indrelid, repack.oid2text(idx.indrelid), n.nspname" + " FROM pg_index idx JOIN pg_class i ON i.oid = idx.indexrelid" + " JOIN pg_namespace n ON n.oid = i.relnamespace" + " WHERE idx.indexrelid = $1::regclass ORDER BY indisvalid DESC, i.relname, n.nspname"); cell = r_index.head; } else if (table_list.head || parent_table_list.head) { appendStringInfoString(&sql, - "SELECT repack.oid2text(i.oid), idx.indexrelid, idx.indisvalid, idx.indrelid, $1::text, n.nspname" - " FROM pg_index idx JOIN pg_class i ON i.oid = idx.indexrelid" - " JOIN pg_namespace n ON n.oid = i.relnamespace" - " WHERE idx.indrelid = $1::regclass ORDER BY indisvalid DESC, i.relname, n.nspname"); + "SELECT repack.oid2text(i.oid), idx.indexrelid, idx.indisvalid, idx.indrelid, $1::text, n.nspname" + " FROM pg_index idx JOIN pg_class i ON i.oid = idx.indexrelid" + " JOIN pg_namespace n ON n.oid = i.relnamespace" + " WHERE idx.indrelid = $1::regclass ORDER BY indisvalid DESC, i.relname, n.nspname"); for (cell = parent_table_list.head; cell; cell = cell->next) { @@ -2391,17 +2400,17 @@ repack_all_indexes(char *errbuf, size_t errsize) if (PQntuples(res) == 0) { - if(table_list.head) + if (table_list.head) elog(WARNING, "\"%s\" does not have any indexes", - cell->val); - else if(r_index.head) + cell->val); + else if (r_index.head) elog(WARNING, "\"%s\" is not a valid index", - cell->val); + cell->val); continue; } - if(table_list.head) + if (table_list.head) elog(INFO, "repacking indexes of \"%s\"", cell->val); if (!repack_table_indexes(res)) @@ -2417,8 +2426,7 @@ repack_all_indexes(char *errbuf, size_t errsize) return ret; } -void -pgut_help(bool details) +void pgut_help(bool details) { printf("%s re-organizes a PostgreSQL database.\n\n", PROGRAM_NAME); printf("Usage:\n"); @@ -2442,6 +2450,7 @@ pgut_help(bool details) printf(" -x, --only-indexes move only indexes of the specified table\n"); printf(" -T, --wait-timeout=SECS timeout to cancel other backends on conflict\n"); printf(" -D, --no-kill-backend don't kill other backends when timed out\n"); + printf(" -w, --where=WHERE_CLAUSE delete rows when repacking using WHERE_CLAUSE\n"); printf(" -Z, --no-analyze don't analyze at end\n"); printf(" -k, --no-superuser-check skip superuser checks in client\n"); printf(" -C, --exclude-extension don't repack tables which belong to specific extension\n"); @@ -2450,3 +2459,40 @@ pgut_help(bool details) printf(" --apply-count number of tuples to apply in one transaction during replay\n"); printf(" --switch-threshold switch tables when that many tuples are left to catchup\n"); } + +/** + * Validate the WHERE clause by preparing a statement with the clause + * and checking if it can be executed against the target table. + */ +static bool +validate_where_clause(const char *table_name, const char *where_clause, char *errbuf, size_t errsize) +{ + PGresult *res; + StringInfoData sql; + bool ret = false; + + if (where_clause == NULL) + return true; + + initStringInfo(&sql); + + appendStringInfo(&sql, "EXPLAIN SELECT 1 FROM %s WHERE %s", + table_name, where_clause); + + res = PQexec(connection, sql.data); + if (PQresultStatus(res) != PGRES_TUPLES_OK) + { + if (errbuf) + snprintf(errbuf, errsize, "invalid WHERE clause: %s", + PQerrorMessage(connection)); + ret = false; + } + else + { + ret = true; + } + + CLEARPGRES(res); + termStringInfo(&sql); + return ret; +} \ No newline at end of file From bfe874baa40b056560426df06b7f977fd7344d90 Mon Sep 17 00:00:00 2001 From: ankit mittal Date: Fri, 28 Feb 2025 23:07:34 -0500 Subject: [PATCH 19/26] working version --- bin/pg_repack.c | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/bin/pg_repack.c b/bin/pg_repack.c index 48a6c2f9..f858efcb 100644 --- a/bin/pg_repack.c +++ b/bin/pg_repack.c @@ -210,8 +210,8 @@ static bool is_superuser(void); static void check_tablespace(void); static bool preliminary_checks(char *errbuf, size_t errsize); static bool is_requested_relation_exists(char *errbuf, size_t errsize); -static void repack_all_databases(const char *order_by); -static bool repack_one_database(const char *order_by, char *errbuf, size_t errsize); +static void repack_all_databases(const char *order_by, const char *where_clause); +static bool repack_one_database(const char *order_by, char *errbuf, size_t errsize, const char *where_clause); static void repack_one_table(repack_table *table, const char *order_by); static bool repack_table_indexes(PGresult *index_details); static bool repack_all_indexes(char *errbuf, size_t errsize); @@ -244,6 +244,7 @@ static SimpleStringList parent_table_list = {NULL, NULL}; static SimpleStringList table_list = {NULL, NULL}; static SimpleStringList schema_list = {NULL, NULL}; static char *orderby = NULL; +static char *where_clause = NULL; static char *tablespace = NULL; static bool moveidx = false; static SimpleStringList r_index = {NULL, NULL}; @@ -278,6 +279,7 @@ static pgut_option options[] = {'l', 'c', "schema", &schema_list}, {'b', 'n', "no-order", &noorder}, {'b', 'N', "dry-run", &dryrun}, + {'s', 'X', "where-clause", &where_clause}, {'s', 'o', "order-by", &orderby}, {'s', 's', "tablespace", &tablespace}, {'b', 'S', "moveidx", &moveidx}, @@ -392,11 +394,11 @@ int main(int argc, char *argv[]) ereport(ERROR, (errcode(EINVAL), errmsg("cannot repack specific schema(s) in all databases"))); - repack_all_databases(orderby); + repack_all_databases(orderby, where_clause); } else { - if (!repack_one_database(orderby, errbuf, sizeof(errbuf))) + if (!repack_one_database(orderby, errbuf, sizeof(errbuf), where_clause)) ereport(ERROR, (errcode(ERROR), errmsg("%s failed with error: %s", PROGRAM_NAME, errbuf))); } @@ -684,7 +686,7 @@ is_requested_relation_exists(char *errbuf, size_t errsize) * Call repack_one_database for each database. */ static void -repack_all_databases(const char *orderby) +repack_all_databases(const char *orderby, const char *where_clause) { PGresult *result; int i; @@ -708,7 +710,7 @@ repack_all_databases(const char *orderby) elog(INFO, "repacking database \"%s\"", dbname); if (!dryrun) { - ret = repack_one_database(orderby, errbuf, sizeof(errbuf)); + ret = repack_one_database(orderby, errbuf, sizeof(errbuf), where_clause); if (!ret) elog(INFO, "database \"%s\" skipped: %s", dbname, errbuf); } @@ -740,7 +742,7 @@ getoid(PGresult *res, int row, int col) * Call repack_one_table for the target tables or each table in a database. */ static bool -repack_one_database(const char *orderby, char *errbuf, size_t errsize) +repack_one_database(const char *orderby, char *errbuf, size_t errsize, const char *where_clause) { bool ret = false; PGresult *res = NULL; @@ -937,18 +939,19 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize) table.sql_update = getstr(res, i, c++); table.sql_pop = getstr(res, i, c++); table.dest_tablespace = getstr(res, i, c++); - table.where_clause = getstr(res, i, c++); + table.where_clause = where_clause; /* Craft Copy SQL */ initStringInfo(©_sql); appendStringInfoString(©_sql, table.copy_data); + + /* soft delete recognition */ if (table.where_clause) { appendStringInfoString(©_sql, " WHERE "); appendStringInfoString(©_sql, table.where_clause); } if (!orderby) - { if (ckey != NULL) { @@ -1239,6 +1242,7 @@ repack_one_table(repack_table *table, const char *orderby) const char *indexparams[2]; char indexbuffer[12]; int j; + char errbuf[256]; /* appname will be "pg_repack" in normal use on 9.0+, or * "pg_regress" when run under `make installcheck` @@ -1257,6 +1261,13 @@ repack_one_table(repack_table *table, const char *orderby) elog(INFO, "repacking table \"%s\"", table->target_name); + /* Validate WHERE clause if provided */ + if (table->where_clause && !validate_where_clause(table->target_name, table->where_clause, errbuf, sizeof(errbuf))) + { + elog(ERROR, "%s", errbuf); + goto cleanup; + } + elog(DEBUG2, "---- repack_one_table ----"); elog(DEBUG2, "target_name : %s", table->target_name); elog(DEBUG2, "target_oid : %u", table->target_oid); @@ -1282,9 +1293,6 @@ repack_one_table(repack_table *table, const char *orderby) elog(DEBUG2, "sql_pop : %s", table->sql_pop); elog(DEBUG2, "where_clause : %s", table->where_clause); - if (table->where_clause && !validate_where_clause(table->target_name, table->where_clause, errbuf, sizeof(errbuf))) - return; - if (dryrun) return; @@ -2450,7 +2458,6 @@ void pgut_help(bool details) printf(" -x, --only-indexes move only indexes of the specified table\n"); printf(" -T, --wait-timeout=SECS timeout to cancel other backends on conflict\n"); printf(" -D, --no-kill-backend don't kill other backends when timed out\n"); - printf(" -w, --where=WHERE_CLAUSE delete rows when repacking using WHERE_CLAUSE\n"); printf(" -Z, --no-analyze don't analyze at end\n"); printf(" -k, --no-superuser-check skip superuser checks in client\n"); printf(" -C, --exclude-extension don't repack tables which belong to specific extension\n"); @@ -2495,4 +2502,4 @@ validate_where_clause(const char *table_name, const char *where_clause, char *er CLEARPGRES(res); termStringInfo(&sql); return ret; -} \ No newline at end of file +} From 4215d208f86184a8d34c179a296932eeaa08fa4e Mon Sep 17 00:00:00 2001 From: ankit mittal Date: Fri, 28 Feb 2025 23:20:19 -0500 Subject: [PATCH 20/26] removed extra work --- bin/pg_repack.c | 802 +++++++++++++++++++++++------------------------- 1 file changed, 378 insertions(+), 424 deletions(-) diff --git a/bin/pg_repack.c b/bin/pg_repack.c index f858efcb..cd8c4539 100644 --- a/bin/pg_repack.c +++ b/bin/pg_repack.c @@ -10,8 +10,8 @@ * @brief Client Modules */ -const char *PROGRAM_URL = "https://reorg.github.io/pg_repack/"; -const char *PROGRAM_ISSUES = "https://github.com/reorg/pg_repack/issues"; +const char *PROGRAM_URL = "https://reorg.github.io/pg_repack/"; +const char *PROGRAM_ISSUES = "https://github.com/reorg/pg_repack/issues"; #ifdef REPACK_VERSION /* macro trick to stringify a macro expansion */ @@ -30,6 +30,7 @@ const char *PROGRAM_VERSION = "unknown"; #include #include + #ifdef HAVE_POLL_H #include #endif @@ -40,19 +41,20 @@ const char *PROGRAM_VERSION = "unknown"; #include #endif + /* * APPLY_COUNT_DEFAULT: Number of applied logs per transaction. Larger values * could be faster, but will be long transactions in the REDO phase. */ -#define APPLY_COUNT_DEFAULT 1000 +#define APPLY_COUNT_DEFAULT 1000 /* Once we get down to seeing fewer than this many tuples in the * log table, we'll say that we're ready to perform the switch. */ -#define SWITCH_THRESHOLD_DEFAULT 100 +#define SWITCH_THRESHOLD_DEFAULT 100 /* poll() or select() timeout, in seconds */ -#define POLL_TIMEOUT 3 +#define POLL_TIMEOUT 3 /* Compile an array of existing transactions which are active during * pg_repack's setup. Some transactions we can safely ignore: @@ -72,61 +74,64 @@ const char *PROGRAM_VERSION = "unknown"; * instance, the application name when running installcheck will be * pg_regress. */ -#define SQL_XID_SNAPSHOT_90200 \ - "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ - " FROM pg_locks AS l " \ - " LEFT JOIN pg_stat_activity AS a " \ - " ON l.pid = a.pid " \ - " LEFT JOIN pg_database AS d " \ - " ON a.datid = d.oid " \ - " WHERE l.locktype = 'virtualxid' " \ - " AND l.pid NOT IN (pg_backend_pid(), $1) " \ +#define SQL_XID_SNAPSHOT_90200 \ + "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ + " FROM pg_locks AS l " \ + " LEFT JOIN pg_stat_activity AS a " \ + " ON l.pid = a.pid " \ + " LEFT JOIN pg_database AS d " \ + " ON a.datid = d.oid " \ + " WHERE l.locktype = 'virtualxid' " \ + " AND l.pid NOT IN (pg_backend_pid(), $1) " \ " AND (l.virtualxid, l.virtualtransaction) <> ('1/1', '-1/0') " \ " AND (a.application_name IS NULL OR a.application_name <> $2)" \ - " AND a.query !~* E'^\\\\s*vacuum\\\\s+' " \ - " AND a.query !~ E'^autovacuum: ' " \ + " AND a.query !~* E'^\\\\s*vacuum\\\\s+' " \ + " AND a.query !~ E'^autovacuum: ' " \ " AND ((d.datname IS NULL OR d.datname = current_database()) OR l.database = 0)" -#define SQL_XID_SNAPSHOT_90000 \ - "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ - " FROM pg_locks AS l " \ - " LEFT JOIN pg_stat_activity AS a " \ - " ON l.pid = a.procpid " \ - " LEFT JOIN pg_database AS d " \ - " ON a.datid = d.oid " \ - " WHERE l.locktype = 'virtualxid' " \ - " AND l.pid NOT IN (pg_backend_pid(), $1) " \ +#define SQL_XID_SNAPSHOT_90000 \ + "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ + " FROM pg_locks AS l " \ + " LEFT JOIN pg_stat_activity AS a " \ + " ON l.pid = a.procpid " \ + " LEFT JOIN pg_database AS d " \ + " ON a.datid = d.oid " \ + " WHERE l.locktype = 'virtualxid' " \ + " AND l.pid NOT IN (pg_backend_pid(), $1) " \ " AND (l.virtualxid, l.virtualtransaction) <> ('1/1', '-1/0') " \ " AND (a.application_name IS NULL OR a.application_name <> $2)" \ - " AND a.current_query !~* E'^\\\\s*vacuum\\\\s+' " \ - " AND a.current_query !~ E'^autovacuum: ' " \ + " AND a.current_query !~* E'^\\\\s*vacuum\\\\s+' " \ + " AND a.current_query !~ E'^autovacuum: ' " \ " AND ((d.datname IS NULL OR d.datname = current_database()) OR l.database = 0)" /* application_name is not available before 9.0. The last clause of * the WHERE clause is just to eat the $2 parameter (application name). */ -#define SQL_XID_SNAPSHOT_80300 \ - "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ - " FROM pg_locks AS l" \ - " LEFT JOIN pg_stat_activity AS a " \ - " ON l.pid = a.procpid " \ - " LEFT JOIN pg_database AS d " \ - " ON a.datid = d.oid " \ - " WHERE l.locktype = 'virtualxid' AND l.pid NOT IN (pg_backend_pid(), $1)" \ - " AND (l.virtualxid, l.virtualtransaction) <> ('1/1', '-1/0') " \ - " AND a.current_query !~* E'^\\\\s*vacuum\\\\s+' " \ - " AND a.current_query !~ E'^autovacuum: ' " \ +#define SQL_XID_SNAPSHOT_80300 \ + "SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \ + " FROM pg_locks AS l" \ + " LEFT JOIN pg_stat_activity AS a " \ + " ON l.pid = a.procpid " \ + " LEFT JOIN pg_database AS d " \ + " ON a.datid = d.oid " \ + " WHERE l.locktype = 'virtualxid' AND l.pid NOT IN (pg_backend_pid(), $1)" \ + " AND (l.virtualxid, l.virtualtransaction) <> ('1/1', '-1/0') " \ + " AND a.current_query !~* E'^\\\\s*vacuum\\\\s+' " \ + " AND a.current_query !~ E'^autovacuum: ' " \ " AND ((d.datname IS NULL OR d.datname = current_database()) OR l.database = 0)" \ " AND ($2::text IS NOT NULL)" #define SQL_XID_SNAPSHOT \ - (PQserverVersion(connection) >= 90200 ? SQL_XID_SNAPSHOT_90200 : (PQserverVersion(connection) >= 90000 ? SQL_XID_SNAPSHOT_90000 : SQL_XID_SNAPSHOT_80300)) + (PQserverVersion(connection) >= 90200 ? SQL_XID_SNAPSHOT_90200 : \ + (PQserverVersion(connection) >= 90000 ? SQL_XID_SNAPSHOT_90000 : \ + SQL_XID_SNAPSHOT_80300)) + /* Later, check whether any of the transactions we saw before are still * alive, and wait for them to go away. */ -#define SQL_XID_ALIVE \ - "SELECT pid FROM pg_locks WHERE locktype = 'virtualxid'" \ +#define SQL_XID_ALIVE \ + "SELECT pid FROM pg_locks WHERE locktype = 'virtualxid'"\ " AND pid <> pg_backend_pid() AND virtualtransaction = ANY($1)" /* To be run while our main connection holds an AccessExclusive lock on the @@ -136,20 +141,20 @@ const char *PROGRAM_VERSION = "unknown"; * transactions trying to grab an ACCESS EXCLUSIVE lock, because we are only * trying to kill off disallowed DDL commands, e.g. ALTER TABLE or TRUNCATE. */ -#define CANCEL_COMPETING_LOCKS \ - "SELECT pg_cancel_backend(pid) FROM pg_locks WHERE locktype = 'relation'" \ - " AND granted = false AND relation = %u" \ +#define CANCEL_COMPETING_LOCKS \ + "SELECT pg_cancel_backend(pid) FROM pg_locks WHERE locktype = 'relation'"\ + " AND granted = false AND relation = %u"\ " AND mode = 'AccessExclusiveLock' AND pid <> pg_backend_pid()" -#define KILL_COMPETING_LOCKS \ - "SELECT pg_terminate_backend(pid) " \ - "FROM pg_locks WHERE locktype = 'relation'" \ - " AND granted = false AND relation = %u" \ +#define KILL_COMPETING_LOCKS \ + "SELECT pg_terminate_backend(pid) "\ + "FROM pg_locks WHERE locktype = 'relation'"\ + " AND granted = false AND relation = %u"\ " AND mode = 'AccessExclusiveLock' AND pid <> pg_backend_pid()" -#define COUNT_COMPETING_LOCKS \ +#define COUNT_COMPETING_LOCKS \ "SELECT pid FROM pg_locks WHERE locktype = 'relation'" \ - " AND granted = false AND relation = %u" \ + " AND granted = false AND relation = %u" \ " AND mode = 'AccessExclusiveLock' AND pid <> pg_backend_pid()" /* Will be used as a unique prefix for advisory locks. */ @@ -167,10 +172,10 @@ typedef enum */ typedef struct repack_index { - Oid target_oid; /* target: OID */ - const char *create_index; /* CREATE INDEX */ - index_status_t status; /* Track parallel build statuses. */ - int worker_idx; /* which worker conn is handling */ + Oid target_oid; /* target: OID */ + const char *create_index; /* CREATE INDEX */ + index_status_t status; /* Track parallel build statuses. */ + int worker_idx; /* which worker conn is handling */ } repack_index; /* @@ -178,34 +183,34 @@ typedef struct repack_index */ typedef struct repack_table { - const char *target_name; /* target: relname */ - Oid target_oid; /* target: OID */ - Oid target_toast; /* target: toast OID */ - Oid target_tidx; /* target: toast index OID */ - Oid pkid; /* target: PK OID */ - Oid ckid; /* target: CK OID */ - Oid temp_oid; /* temp: OID */ - const char *create_pktype; /* CREATE TYPE pk */ - const char *create_log; /* CREATE TABLE log */ - const char *create_trigger; /* CREATE TRIGGER repack_trigger */ - const char *enable_trigger; /* ALTER TABLE ENABLE ALWAYS TRIGGER repack_trigger */ - const char *create_table; /* CREATE TABLE table AS SELECT WITH NO DATA*/ - const char *dest_tablespace; /* Destination tablespace */ - const char *copy_data; /* INSERT INTO */ - const char *alter_col_storage; /* ALTER TABLE ALTER COLUMN SET STORAGE */ - const char *drop_columns; /* ALTER TABLE DROP COLUMNs */ - const char *delete_log; /* DELETE FROM log */ - const char *lock_table; /* LOCK TABLE table */ - const char *sql_peek; /* SQL used in flush */ - const char *sql_insert; /* SQL used in flush */ - const char *sql_delete; /* SQL used in flush */ - const char *sql_update; /* SQL used in flush */ - const char *sql_pop; /* SQL used in flush */ - const char *where_clause; /* WHERE clause for Deleting rows */ - int n_indexes; /* number of indexes */ - repack_index *indexes; /* info on each index */ + const char *target_name; /* target: relname */ + Oid target_oid; /* target: OID */ + Oid target_toast; /* target: toast OID */ + Oid target_tidx; /* target: toast index OID */ + Oid pkid; /* target: PK OID */ + Oid ckid; /* target: CK OID */ + Oid temp_oid; /* temp: OID */ + const char *create_pktype; /* CREATE TYPE pk */ + const char *create_log; /* CREATE TABLE log */ + const char *create_trigger; /* CREATE TRIGGER repack_trigger */ + const char *enable_trigger; /* ALTER TABLE ENABLE ALWAYS TRIGGER repack_trigger */ + const char *create_table; /* CREATE TABLE table AS SELECT WITH NO DATA*/ + const char *dest_tablespace; /* Destination tablespace */ + const char *copy_data; /* INSERT INTO */ + const char *alter_col_storage; /* ALTER TABLE ALTER COLUMN SET STORAGE */ + const char *drop_columns; /* ALTER TABLE DROP COLUMNs */ + const char *delete_log; /* DELETE FROM log */ + const char *lock_table; /* LOCK TABLE table */ + const char *sql_peek; /* SQL used in flush */ + const char *sql_insert; /* SQL used in flush */ + const char *sql_delete; /* SQL used in flush */ + const char *sql_update; /* SQL used in flush */ + const char *sql_pop; /* SQL used in flush */ + int n_indexes; /* number of indexes */ + repack_index *indexes; /* info on each index */ } repack_table; + static bool is_superuser(void); static void check_tablespace(void); static bool preliminary_checks(char *errbuf, size_t errsize); @@ -219,7 +224,6 @@ static void repack_cleanup(bool fatal, const repack_table *table); static void repack_cleanup_callback(bool fatal, void *userdata); static void repack_cleanup_index(bool fatal, void *userdata); static bool rebuild_indexes(const repack_table *table); -static bool validate_where_clause(const char *table_name, const char *where_clause, char *errbuf, size_t errsize); static char *getstr(PGresult *res, int row, int col); static Oid getoid(PGresult *res, int row, int col); @@ -228,39 +232,39 @@ static bool lock_exclusive(PGconn *conn, const char *relid, const char *lock_que static bool kill_ddl(PGconn *conn, Oid relid, bool terminate); static bool lock_access_share(PGconn *conn, Oid relid, const char *target_name); -#define SQLSTATE_INVALID_SCHEMA_NAME "3F000" -#define SQLSTATE_UNDEFINED_FUNCTION "42883" -#define SQLSTATE_LOCK_NOT_AVAILABLE "55P03" +#define SQLSTATE_INVALID_SCHEMA_NAME "3F000" +#define SQLSTATE_UNDEFINED_FUNCTION "42883" +#define SQLSTATE_LOCK_NOT_AVAILABLE "55P03" static bool sqlstate_equals(PGresult *res, const char *state) { return strcmp(PQresultErrorField(res, PG_DIAG_SQLSTATE), state) == 0; } -static bool analyze = true; -static bool alldb = false; -static bool noorder = false; -static SimpleStringList parent_table_list = {NULL, NULL}; -static SimpleStringList table_list = {NULL, NULL}; -static SimpleStringList schema_list = {NULL, NULL}; -static char *orderby = NULL; -static char *where_clause = NULL; -static char *tablespace = NULL; -static bool moveidx = false; -static SimpleStringList r_index = {NULL, NULL}; -static bool only_indexes = false; -static int wait_timeout = 60; /* in seconds */ -static int jobs = 0; /* number of concurrent worker conns. */ -static bool dryrun = false; -static unsigned int temp_obj_num = 0; /* temporary objects counter */ -static bool no_kill_backend = false; /* abandon when timed-out */ -static bool no_superuser_check = false; -static SimpleStringList exclude_extension_list = {NULL, NULL}; /* don't repack tables of these extensions */ -static bool no_error_on_invalid_index = false; /* repack even though invalid index is found */ -static bool error_on_invalid_index = false; /* don't repack when invalid index is found, - * deprecated, this the default behavior now */ -static int apply_count = APPLY_COUNT_DEFAULT; -static int switch_threshold = SWITCH_THRESHOLD_DEFAULT; +static bool analyze = true; +static bool alldb = false; +static bool noorder = false; +static SimpleStringList parent_table_list = {NULL, NULL}; +static SimpleStringList table_list = {NULL, NULL}; +static SimpleStringList schema_list = {NULL, NULL}; +static char *orderby = NULL; +static char *where_clause = NULL; +static char *tablespace = NULL; +static bool moveidx = false; +static SimpleStringList r_index = {NULL, NULL}; +static bool only_indexes = false; +static int wait_timeout = 60; /* in seconds */ +static int jobs = 0; /* number of concurrent worker conns. */ +static bool dryrun = false; +static unsigned int temp_obj_num = 0; /* temporary objects counter */ +static bool no_kill_backend = false; /* abandon when timed-out */ +static bool no_superuser_check = false; +static SimpleStringList exclude_extension_list = {NULL, NULL}; /* don't repack tables of these extensions */ +static bool no_error_on_invalid_index = false; /* repack even though invalid index is found */ +static bool error_on_invalid_index = false; /* don't repack when invalid index is found, + * deprecated, this the default behavior now */ +static int apply_count = APPLY_COUNT_DEFAULT; +static int switch_threshold = SWITCH_THRESHOLD_DEFAULT; /* buffer should have at least 11 bytes */ static char * @@ -272,36 +276,37 @@ utoa(unsigned int value, char *buffer) } static pgut_option options[] = - { - {'b', 'a', "all", &alldb}, - {'l', 't', "table", &table_list}, - {'l', 'I', "parent-table", &parent_table_list}, - {'l', 'c', "schema", &schema_list}, - {'b', 'n', "no-order", &noorder}, - {'b', 'N', "dry-run", &dryrun}, - {'s', 'X', "where-clause", &where_clause}, - {'s', 'o', "order-by", &orderby}, - {'s', 's', "tablespace", &tablespace}, - {'b', 'S', "moveidx", &moveidx}, - {'l', 'i', "index", &r_index}, - {'b', 'x', "only-indexes", &only_indexes}, - {'i', 'T', "wait-timeout", &wait_timeout}, - {'B', 'Z', "no-analyze", &analyze}, - {'i', 'j', "jobs", &jobs}, - {'b', 'D', "no-kill-backend", &no_kill_backend}, - {'b', 'k', "no-superuser-check", &no_superuser_check}, - {'l', 'C', "exclude-extension", &exclude_extension_list}, - {'b', 4, "no-error-on-invalid-index", &no_error_on_invalid_index}, - {'b', 3, "error-on-invalid-index", &error_on_invalid_index}, - {'i', 2, "apply-count", &apply_count}, - {'i', 1, "switch-threshold", &switch_threshold}, - {0}, +{ + { 'b', 'a', "all", &alldb }, + { 'l', 't', "table", &table_list }, + { 'l', 'I', "parent-table", &parent_table_list }, + { 'l', 'c', "schema", &schema_list }, + { 'b', 'n', "no-order", &noorder }, + { 'b', 'N', "dry-run", &dryrun }, + { 's', 'X', "where-clause", &where_clause }, + { 's', 'o', "order-by", &orderby }, + { 's', 's', "tablespace", &tablespace }, + { 'b', 'S', "moveidx", &moveidx }, + { 'l', 'i', "index", &r_index }, + { 'b', 'x', "only-indexes", &only_indexes }, + { 'i', 'T', "wait-timeout", &wait_timeout }, + { 'B', 'Z', "no-analyze", &analyze }, + { 'i', 'j', "jobs", &jobs }, + { 'b', 'D', "no-kill-backend", &no_kill_backend }, + { 'b', 'k', "no-superuser-check", &no_superuser_check }, + { 'l', 'C', "exclude-extension", &exclude_extension_list }, + { 'b', 4, "no-error-on-invalid-index", &no_error_on_invalid_index }, + { 'b', 3, "error-on-invalid-index", &error_on_invalid_index }, + { 'i', 2, "apply-count", &apply_count }, + { 'i', 1, "switch-threshold", &switch_threshold }, + { 0 }, }; -int main(int argc, char *argv[]) +int +main(int argc, char *argv[]) { - int i; - char errbuf[256]; + int i; + char errbuf[256]; i = pgut_getopt(argc, argv, options); @@ -309,12 +314,12 @@ int main(int argc, char *argv[]) dbname = argv[i]; else if (i < argc) ereport(ERROR, - (errcode(EINVAL), - errmsg("too many arguments"))); + (errcode(EINVAL), + errmsg("too many arguments"))); - if (switch_threshold >= apply_count) + if(switch_threshold >= apply_count) ereport(ERROR, (errcode(EINVAL), - errmsg("switch_threshold must be less than apply_count"))); + errmsg("switch_threshold must be less than apply_count"))); check_tablespace(); @@ -325,61 +330,61 @@ int main(int argc, char *argv[]) { if (r_index.head && table_list.head) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot specify --index (-i) and --table (-t)"))); + errmsg("cannot specify --index (-i) and --table (-t)"))); if (r_index.head && parent_table_list.head) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot specify --index (-i) and --parent-table (-I)"))); + errmsg("cannot specify --index (-i) and --parent-table (-I)"))); else if (r_index.head && only_indexes) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot specify --index (-i) and --only-indexes (-x)"))); + errmsg("cannot specify --index (-i) and --only-indexes (-x)"))); else if (r_index.head && exclude_extension_list.head) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot specify --index (-i) and --exclude-extension (-C)"))); + errmsg("cannot specify --index (-i) and --exclude-extension (-C)"))); else if (only_indexes && !(table_list.head || parent_table_list.head)) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot repack all indexes of database, specify the table(s)" - "via --table (-t) or --parent-table (-I)"))); + errmsg("cannot repack all indexes of database, specify the table(s)" + "via --table (-t) or --parent-table (-I)"))); else if (only_indexes && exclude_extension_list.head) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot specify --only-indexes (-x) and --exclude-extension (-C)"))); + errmsg("cannot specify --only-indexes (-x) and --exclude-extension (-C)"))); else if (alldb) ereport(ERROR, (errcode(EINVAL), - errmsg("cannot repack specific index(es) in all databases"))); + errmsg("cannot repack specific index(es) in all databases"))); else { if (orderby) ereport(WARNING, (errcode(EINVAL), - errmsg("option -o (--order-by) has no effect while repacking indexes"))); + errmsg("option -o (--order-by) has no effect while repacking indexes"))); else if (noorder) ereport(WARNING, (errcode(EINVAL), - errmsg("option -n (--no-order) has no effect while repacking indexes"))); + errmsg("option -n (--no-order) has no effect while repacking indexes"))); else if (!analyze) ereport(WARNING, (errcode(EINVAL), - errmsg("ANALYZE is not performed after repacking indexes, -z (--no-analyze) has no effect"))); + errmsg("ANALYZE is not performed after repacking indexes, -z (--no-analyze) has no effect"))); else if (jobs) ereport(WARNING, (errcode(EINVAL), - errmsg("option -j (--jobs) has no effect, repacking indexes does not use parallel jobs"))); + errmsg("option -j (--jobs) has no effect, repacking indexes does not use parallel jobs"))); if (!repack_all_indexes(errbuf, sizeof(errbuf))) ereport(ERROR, - (errcode(ERROR), errmsg("%s", errbuf))); + (errcode(ERROR), errmsg("%s", errbuf))); } } else { if (schema_list.head && (table_list.head || parent_table_list.head)) ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot repack specific table(s) in schema, use schema.table notation instead"))); + (errcode(EINVAL), + errmsg("cannot repack specific table(s) in schema, use schema.table notation instead"))); if (exclude_extension_list.head && table_list.head) ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot specify --table (-t) and --exclude-extension (-C)"))); + (errcode(EINVAL), + errmsg("cannot specify --table (-t) and --exclude-extension (-C)"))); if (exclude_extension_list.head && parent_table_list.head) ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot specify --parent-table (-I) and --exclude-extension (-C)"))); + (errcode(EINVAL), + errmsg("cannot specify --parent-table (-I) and --exclude-extension (-C)"))); if (noorder) orderby = ""; @@ -388,25 +393,26 @@ int main(int argc, char *argv[]) { if (table_list.head || parent_table_list.head) ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot repack specific table(s) in all databases"))); + (errcode(EINVAL), + errmsg("cannot repack specific table(s) in all databases"))); if (schema_list.head) ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot repack specific schema(s) in all databases"))); + (errcode(EINVAL), + errmsg("cannot repack specific schema(s) in all databases"))); repack_all_databases(orderby, where_clause); } else { if (!repack_one_database(orderby, errbuf, sizeof(errbuf), where_clause)) ereport(ERROR, - (errcode(ERROR), errmsg("%s failed with error: %s", PROGRAM_NAME, errbuf))); + (errcode(ERROR), errmsg("%s failed with error: %s", PROGRAM_NAME, errbuf))); } } return 0; } + /* * Test if the current user is a database superuser. * Borrowed from psql/common.c @@ -414,7 +420,8 @@ int main(int argc, char *argv[]) * Note: this will correctly detect superuserness only with a protocol-3.0 * or newer backend; otherwise it will always say "false". */ -bool is_superuser(void) +bool +is_superuser(void) { const char *val; @@ -437,9 +444,10 @@ bool is_superuser(void) * * Raise an exception on error. */ -void check_tablespace() +void +check_tablespace() { - PGresult *res = NULL; + PGresult *res = NULL; const char *params[1]; if (tablespace == NULL) @@ -448,8 +456,8 @@ void check_tablespace() if (moveidx) { ereport(ERROR, - (errcode(EINVAL), - errmsg("cannot specify --moveidx (-S) without --tablespace (-s)"))); + (errcode(EINVAL), + errmsg("cannot specify --moveidx (-S) without --tablespace (-s)"))); } return; } @@ -466,16 +474,16 @@ void check_tablespace() if (PQntuples(res) == 0) { ereport(ERROR, - (errcode(EINVAL), - errmsg("the tablespace \"%s\" doesn't exist", tablespace))); + (errcode(EINVAL), + errmsg("the tablespace \"%s\" doesn't exist", tablespace))); } } else { ereport(ERROR, - (errcode(EINVAL), - errmsg("error checking the namespace: %s", - PQerrorMessage(connection)))); + (errcode(EINVAL), + errmsg("error checking the namespace: %s", + PQerrorMessage(connection)))); } CLEARPGRES(res); @@ -486,13 +494,11 @@ void check_tablespace() * installed in the database, the user is a superuser, etc. */ static bool -preliminary_checks(char *errbuf, size_t errsize) -{ - bool ret = false; - PGresult *res = NULL; +preliminary_checks(char *errbuf, size_t errsize){ + bool ret = false; + PGresult *res = NULL; - if (!is_superuser()) - { + if (!is_superuser()) { if (errbuf) snprintf(errbuf, errsize, "You must be a superuser to use %s", PROGRAM_NAME); @@ -501,11 +507,11 @@ preliminary_checks(char *errbuf, size_t errsize) /* Query the extension version. Exit if no match */ res = execute_elevel("select repack.version(), repack.version_sql()", - 0, NULL, DEBUG2); + 0, NULL, DEBUG2); if (PQresultStatus(res) == PGRES_TUPLES_OK) { - const char *libver; - char buf[64]; + const char *libver; + char buf[64]; /* the string is something like "pg_repack 1.1.7" */ snprintf(buf, sizeof(buf), "%s %s", PROGRAM_NAME, PROGRAM_VERSION); @@ -516,8 +522,8 @@ preliminary_checks(char *errbuf, size_t errsize) { if (errbuf) snprintf(errbuf, errsize, - "program '%s' does not match database library '%s'", - buf, libver); + "program '%s' does not match database library '%s'", + buf, libver); goto cleanup; } @@ -527,23 +533,24 @@ preliminary_checks(char *errbuf, size_t errsize) { if (errbuf) snprintf(errbuf, errsize, - "extension '%s' required, found '%s';" - " please drop and re-create the extension", - buf, libver); + "extension '%s' required, found '%s';" + " please drop and re-create the extension", + buf, libver); goto cleanup; } } else { - if (sqlstate_equals(res, SQLSTATE_INVALID_SCHEMA_NAME) || sqlstate_equals(res, SQLSTATE_UNDEFINED_FUNCTION)) + if (sqlstate_equals(res, SQLSTATE_INVALID_SCHEMA_NAME) + || sqlstate_equals(res, SQLSTATE_UNDEFINED_FUNCTION)) { /* Schema repack does not exist, or version too old (version * functions not found). Skip the database. */ if (errbuf) snprintf(errbuf, errsize, - "%s %s is not installed in the database", - PROGRAM_NAME, PROGRAM_VERSION); + "%s %s is not installed in the database", + PROGRAM_NAME, PROGRAM_VERSION); } else { @@ -576,15 +583,14 @@ preliminary_checks(char *errbuf, size_t errsize) * otherwise format user-friendly message */ static bool -is_requested_relation_exists(char *errbuf, size_t errsize) -{ - bool ret = false; - PGresult *res = NULL; - const char **params = NULL; - int iparam = 0; - StringInfoData sql; - int num_relations; - SimpleStringListCell *cell; +is_requested_relation_exists(char *errbuf, size_t errsize){ + bool ret = false; + PGresult *res = NULL; + const char **params = NULL; + int iparam = 0; + StringInfoData sql; + int num_relations; + SimpleStringListCell *cell; num_relations = simple_string_list_size(parent_table_list) + simple_string_list_size(table_list); @@ -594,7 +600,7 @@ is_requested_relation_exists(char *errbuf, size_t errsize) return true; /* has no suitable to_regclass(text) */ - if (PQserverVersion(connection) < 90600) + if (PQserverVersion(connection)<90600) return true; params = pgut_malloc(num_relations * sizeof(char *)); @@ -616,35 +622,36 @@ is_requested_relation_exists(char *errbuf, size_t errsize) appendStringInfoChar(&sql, ','); } appendStringInfoString(&sql, - ") AS given_t(r,kind) WHERE" - /* regular --table relation or inherited --parent-table */ - " NOT EXISTS(" - " SELECT FROM repack.tables WHERE relid=to_regclass(given_t.r))" - /* declarative partitioned --parent-table */ - " AND NOT EXISTS(" - " SELECT FROM pg_catalog.pg_class c WHERE c.oid=to_regclass(given_t.r) AND c.relkind = given_t.kind AND given_t.kind = 'p')"); + ") AS given_t(r,kind) WHERE" + /* regular --table relation or inherited --parent-table */ + " NOT EXISTS(" + " SELECT FROM repack.tables WHERE relid=to_regclass(given_t.r))" + /* declarative partitioned --parent-table */ + " AND NOT EXISTS(" + " SELECT FROM pg_catalog.pg_class c WHERE c.oid=to_regclass(given_t.r) AND c.relkind = given_t.kind AND given_t.kind = 'p')" + ); /* double check the parameters array is sane */ if (iparam != num_relations) { if (errbuf) snprintf(errbuf, errsize, - "internal error: bad parameters count: %i instead of %i", - iparam, num_relations); + "internal error: bad parameters count: %i instead of %i", + iparam, num_relations); goto cleanup; } res = execute_elevel(sql.data, iparam, params, DEBUG2); if (PQresultStatus(res) == PGRES_TUPLES_OK) { - int num; + int num; num = PQntuples(res); if (num != 0) { int i; - StringInfoData rel_names; + StringInfoData rel_names; initStringInfo(&rel_names); for (i = 0; i < num; i++) @@ -658,10 +665,10 @@ is_requested_relation_exists(char *errbuf, size_t errsize) { if (num > 1) snprintf(errbuf, errsize, - "relations do not exist: %s", rel_names.data); + "relations do not exist: %s", rel_names.data); else snprintf(errbuf, errsize, - "ERROR: relation %s does not exist", rel_names.data); + "ERROR: relation %s does not exist", rel_names.data); } termStringInfo(&rel_names); } @@ -688,8 +695,8 @@ is_requested_relation_exists(char *errbuf, size_t errsize) static void repack_all_databases(const char *orderby, const char *where_clause) { - PGresult *result; - int i; + PGresult *result; + int i; dbname = "postgres"; reconnect(ERROR); @@ -702,8 +709,8 @@ repack_all_databases(const char *orderby, const char *where_clause) for (i = 0; i < PQntuples(result); i++) { - bool ret; - char errbuf[256]; + bool ret; + char errbuf[256]; dbname = PQgetvalue(result, i, 0); @@ -744,19 +751,19 @@ getoid(PGresult *res, int row, int col) static bool repack_one_database(const char *orderby, char *errbuf, size_t errsize, const char *where_clause) { - bool ret = false; - PGresult *res = NULL; - int i; - int num; - StringInfoData sql; - SimpleStringListCell *cell; - const char **params = NULL; - int iparam = 0; - size_t num_parent_tables, - num_tables, - num_schemas, - num_params, - num_excluded_extensions; + bool ret = false; + PGresult *res = NULL; + int i; + int num; + StringInfoData sql; + SimpleStringListCell *cell; + const char **params = NULL; + int iparam = 0; + size_t num_parent_tables, + num_tables, + num_schemas, + num_params, + num_excluded_extensions; num_parent_tables = simple_string_list_size(parent_table_list); num_tables = simple_string_list_size(table_list); @@ -786,11 +793,11 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha /* acquire target tables */ appendStringInfoString(&sql, - "SELECT t.*," - " coalesce(v.tablespace, t.tablespace_orig) as tablespace_dest" - " FROM repack.tables t, " - " (VALUES ($1::text)) as v (tablespace)" - " WHERE "); + "SELECT t.*," + " coalesce(v.tablespace, t.tablespace_orig) as tablespace_dest" + " FROM repack.tables t, " + " (VALUES ($1::text)) as v (tablespace)" + " WHERE "); params[iparam++] = tablespace; if (num_tables || num_parent_tables) @@ -878,12 +885,12 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha { if (errbuf) snprintf(errbuf, errsize, - "internal error: bad parameters count: %i instead of %zi", - iparam, num_params); + "internal error: bad parameters count: %i instead of %zi", + iparam, num_params); goto cleanup; } - res = execute_elevel(sql.data, (int)num_params, params, DEBUG2); + res = execute_elevel(sql.data, (int) num_params, params, DEBUG2); /* on error skip the database */ if (PQresultStatus(res) != PGRES_TUPLES_OK) @@ -898,10 +905,10 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha for (i = 0; i < num; i++) { - repack_table table; - StringInfoData copy_sql; + repack_table table; + StringInfoData copy_sql; const char *ckey; - int c = 0; + int c = 0; table.target_name = getstr(res, i, c++); table.target_oid = getoid(res, i, c++); @@ -912,8 +919,7 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha table.ckid = getoid(res, i, c++); table.temp_oid = InvalidOid; /* filled after creating the temp table */ - if (table.pkid == 0) - { + if (table.pkid == 0) { ereport(WARNING, (errcode(E_PG_COMMAND), errmsg("relation \"%s\" must have a primary key or not-null unique keys", table.target_name))); @@ -926,8 +932,8 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha table.enable_trigger = getstr(res, i, c++); table.create_table = getstr(res, i, c++); - getstr(res, i, c++); /* tablespace_orig is clobbered */ - table.copy_data = getstr(res, i, c++); + getstr(res, i, c++); /* tablespace_orig is clobbered */ + table.copy_data = getstr(res, i , c++); table.alter_col_storage = getstr(res, i, c++); table.drop_columns = getstr(res, i, c++); table.delete_log = getstr(res, i, c++); @@ -939,19 +945,17 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha table.sql_update = getstr(res, i, c++); table.sql_pop = getstr(res, i, c++); table.dest_tablespace = getstr(res, i, c++); - table.where_clause = where_clause; /* Craft Copy SQL */ initStringInfo(©_sql); appendStringInfoString(©_sql, table.copy_data); - /* soft delete recognition */ - if (table.where_clause) - { - appendStringInfoString(©_sql, " WHERE "); - appendStringInfoString(©_sql, table.where_clause); - } - if (!orderby) + /* soft delete recognition */ + if (where_clause) { + appendStringInfoString(©_sql, " WHERE "); + appendStringInfoString(©_sql, where_clause); + } + if (!orderby) { if (ckey != NULL) { @@ -989,10 +993,10 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha static int apply_log(PGconn *conn, const repack_table *table, int count) { - int result; - PGresult *res; + int result; + PGresult *res; const char *params[6]; - char buffer[12]; + char buffer[12]; params[0] = table->sql_peek; params[1] = table->sql_insert; @@ -1017,13 +1021,13 @@ apply_log(PGconn *conn, const repack_table *table, int count) static bool rebuild_indexes(const repack_table *table) { - PGresult *res = NULL; - int num_indexes; - int i; - int num_active_workers; - int num_workers; - repack_index *index_jobs; - bool have_error = false; + PGresult *res = NULL; + int num_indexes; + int i; + int num_active_workers; + int num_workers; + repack_index *index_jobs; + bool have_error = false; elog(DEBUG2, "---- create indexes ----"); @@ -1048,8 +1052,7 @@ rebuild_indexes(const repack_table *table) elog(DEBUG2, "target_oid : %u", index_jobs[i].target_oid); elog(DEBUG2, "create_index : %s", index_jobs[i].create_index); - if (num_workers <= 1) - { + if (num_workers <= 1) { /* Use primary connection if we are not setting up parallel * index building, or if we only have one worker. */ @@ -1060,8 +1063,7 @@ rebuild_indexes(const repack_table *table) */ index_jobs[i].status = FINISHED; } - else if (i < num_workers) - { + else if (i < num_workers) { /* Assign available worker to build an index. */ index_jobs[i].status = INPROGRESS; index_jobs[i].worker_idx = i; @@ -1198,13 +1200,11 @@ rebuild_indexes(const repack_table *table) index_jobs[i].status = INPROGRESS; index_jobs[i].worker_idx = freed_worker; elog(LOG, "Assigning worker %d to build index #%d: " - "%s", - freed_worker, i, + "%s", freed_worker, i, index_jobs[i].create_index); if (!(PQsendQuery(workers.conns[freed_worker], - index_jobs[i].create_index))) - { + index_jobs[i].create_index))) { elog(WARNING, "Error sending async query: %s\n%s", index_jobs[i].create_index, PQerrorMessage(workers.conns[freed_worker])); @@ -1218,6 +1218,7 @@ rebuild_indexes(const repack_table *table) freed_worker = -1; } } + } cleanup: @@ -1225,29 +1226,29 @@ rebuild_indexes(const repack_table *table) return (!have_error); } + /* * Re-organize one table. */ static void repack_one_table(repack_table *table, const char *orderby) { - PGresult *res = NULL; - const char *params[3]; - int num; - char *vxid = NULL; - char buffer[12]; - StringInfoData sql; - bool ret = false; - PGresult *indexres = NULL; - const char *indexparams[2]; - char indexbuffer[12]; - int j; - char errbuf[256]; + PGresult *res = NULL; + const char *params[3]; + int num; + char *vxid = NULL; + char buffer[12]; + StringInfoData sql; + bool ret = false; + PGresult *indexres = NULL; + const char *indexparams[2]; + char indexbuffer[12]; + int j; /* appname will be "pg_repack" in normal use on 9.0+, or * "pg_regress" when run under `make installcheck` */ - const char *appname = getenv("PGAPPNAME"); + const char *appname = getenv("PGAPPNAME"); /* Keep track of whether we have gotten through setup to install * the repack_trigger, log table, etc. ourselves. We don't want to @@ -1255,19 +1256,12 @@ repack_one_table(repack_table *table, const char *orderby) * trigger ourselves, lest we be cleaning up another pg_repack's mess, * or worse, interfering with a still-running pg_repack. */ - bool table_init = false; + bool table_init = false; initStringInfo(&sql); elog(INFO, "repacking table \"%s\"", table->target_name); - /* Validate WHERE clause if provided */ - if (table->where_clause && !validate_where_clause(table->target_name, table->where_clause, errbuf, sizeof(errbuf))) - { - elog(ERROR, "%s", errbuf); - goto cleanup; - } - elog(DEBUG2, "---- repack_one_table ----"); elog(DEBUG2, "target_name : %s", table->target_name); elog(DEBUG2, "target_oid : %u", table->target_oid); @@ -1282,7 +1276,8 @@ repack_one_table(repack_table *table, const char *orderby) elog(DEBUG2, "create_table : %s", table->create_table); elog(DEBUG2, "dest_tablespace : %s", table->dest_tablespace); elog(DEBUG2, "copy_data : %s", table->copy_data); - elog(DEBUG2, "alter_col_storage : %s", table->alter_col_storage ? table->alter_col_storage : "(skipped)"); + elog(DEBUG2, "alter_col_storage : %s", table->alter_col_storage ? + table->alter_col_storage : "(skipped)"); elog(DEBUG2, "drop_columns : %s", table->drop_columns ? table->drop_columns : "(skipped)"); elog(DEBUG2, "delete_log : %s", table->delete_log); elog(DEBUG2, "lock_table : %s", table->lock_table); @@ -1291,7 +1286,6 @@ repack_one_table(repack_table *table, const char *orderby) elog(DEBUG2, "sql_delete : %s", table->sql_delete); elog(DEBUG2, "sql_update : %s", table->sql_update); elog(DEBUG2, "sql_pop : %s", table->sql_pop); - elog(DEBUG2, "where_clause : %s", table->where_clause); if (dryrun) return; @@ -1340,13 +1334,10 @@ repack_one_table(repack_table *table, const char *orderby) const char *indexdef; indexdef = getstr(indexres, j, 0); - if (!no_error_on_invalid_index) - { + if (!no_error_on_invalid_index) { elog(WARNING, "Invalid index: %s", indexdef); goto cleanup; - } - else - { + } else { elog(WARNING, "skipping invalid index: %s", indexdef); } } @@ -1374,6 +1365,7 @@ repack_one_table(repack_table *table, const char *orderby) elog(DEBUG2, "index[%d].create_index : %s", j, table->indexes[j].create_index); } + /* * Check if repack_trigger is not conflict with existing trigger. We can * find it out later but we check it in advance and go to cleanup if needed. @@ -1392,7 +1384,7 @@ repack_one_table(repack_table *table, const char *orderby) " attempt to run pg_repack on the table which was" " interrupted and for some reason failed to clean up" " the temporary objects. Please drop the trigger or drop" - " and recreate the pg_repack extension altogether" + " and recreate the pg_repack extension altogether" " to remove all the temporary objects left over."))); goto cleanup; } @@ -1594,7 +1586,7 @@ repack_one_table(repack_table *table, const char *orderby) * constantly coming into the original table. */ if (num > switch_threshold) - continue; /* there might be still some tuples, repeat. */ + continue; /* there might be still some tuples, repeat. */ /* old transactions still alive ? */ params[0] = vxid; @@ -1700,7 +1692,7 @@ repack_one_table(repack_table *table, const char *orderby) params[1] = utoa(table->target_oid, buffer); res = pgut_execute(connection, "SELECT pg_advisory_unlock($1, CAST(-2147483648 + $2::bigint AS integer))", - 2, params); + 2, params); ret = true; cleanup: @@ -1730,10 +1722,10 @@ repack_one_table(repack_table *table, const char *orderby) static bool kill_ddl(PGconn *conn, Oid relid, bool terminate) { - bool ret = true; - PGresult *res; - StringInfoData sql; - int n_tuples; + bool ret = true; + PGresult *res; + StringInfoData sql; + int n_tuples; initStringInfo(&sql); @@ -1793,6 +1785,7 @@ kill_ddl(PGconn *conn, Oid relid, bool terminate) return ret; } + /* * Try to acquire an ACCESS SHARE table lock, avoiding deadlocks and long * waits by killing off other sessions which may be stuck trying to obtain @@ -1808,18 +1801,18 @@ kill_ddl(PGconn *conn, Oid relid, bool terminate) static bool lock_access_share(PGconn *conn, Oid relid, const char *target_name) { - StringInfoData sql; - time_t start = time(NULL); - int i; - bool ret = true; + StringInfoData sql; + time_t start = time(NULL); + int i; + bool ret = true; initStringInfo(&sql); - for (i = 1;; i++) + for (i = 1; ; i++) { - time_t duration; - PGresult *res; - int wait_msec; + time_t duration; + PGresult *res; + int wait_msec; pgut_command(conn, "SAVEPOINT repack_sp1", 0, NULL); @@ -1874,6 +1867,7 @@ lock_access_share(PGconn *conn, Oid relid, const char *target_name) return ret; } + /* Obtain an advisory lock on the table's OID, to make sure no other * pg_repack is working on the table. This is not so much a concern with * full-table repacks, but mainly so that index-only repacks don't interfere @@ -1881,9 +1875,9 @@ lock_access_share(PGconn *conn, Oid relid, const char *target_name) */ static bool advisory_lock(PGconn *conn, const char *relid) { - PGresult *res = NULL; - bool ret = false; - const char *params[2]; + PGresult *res = NULL; + bool ret = false; + const char *params[2]; params[0] = REPACK_LOCK_PREFIX_STR; params[1] = relid; @@ -1894,18 +1888,15 @@ static bool advisory_lock(PGconn *conn, const char *relid) * it fit reliably into signed int space. */ res = pgut_execute(conn, "SELECT pg_try_advisory_lock($1, CAST(-2147483648 + $2::bigint AS integer))", - 2, params); + 2, params); - if (PQresultStatus(res) != PGRES_TUPLES_OK) - { - elog(ERROR, "%s", PQerrorMessage(connection)); + if (PQresultStatus(res) != PGRES_TUPLES_OK) { + elog(ERROR, "%s", PQerrorMessage(connection)); } - else if (strcmp(getstr(res, 0, 0), "t") != 0) - { + else if (strcmp(getstr(res, 0, 0), "t") != 0) { elog(ERROR, "Another pg_repack command may be running on the table. Please try again later."); } - else - { + else { ret = true; } CLEARPGRES(res); @@ -1927,16 +1918,16 @@ static bool advisory_lock(PGconn *conn, const char *relid) static bool lock_exclusive(PGconn *conn, const char *relid, const char *lock_query, bool start_xact) { - time_t start = time(NULL); - int i; - bool ret = true; + time_t start = time(NULL); + int i; + bool ret = true; - for (i = 1;; i++) + for (i = 1; ; i++) { - time_t duration; - char sql[1024]; - PGresult *res; - int wait_msec; + time_t duration; + char sql[1024]; + PGresult *res; + int wait_msec; if (start_xact) pgut_command(conn, "BEGIN ISOLATION LEVEL READ COMMITTED", 0, NULL); @@ -2021,15 +2012,16 @@ lock_exclusive(PGconn *conn, const char *relid, const char *lock_query, bool sta /* This function calls to repack_drop() to clean temporary objects on error * in creation of temporary objects. */ -void repack_cleanup_callback(bool fatal, void *userdata) +void +repack_cleanup_callback(bool fatal, void *userdata) { - repack_table *table = (repack_table *)userdata; - Oid target_table = table->target_oid; + repack_table *table = (repack_table *) userdata; + Oid target_table = table->target_oid; const char *params[2]; - char buffer[12]; - char num_buff[12]; + char buffer[12]; + char num_buff[12]; - if (fatal) + if(fatal) { params[0] = utoa(target_table, buffer); params[1] = utoa(temp_obj_num, num_buff); @@ -2067,8 +2059,8 @@ repack_cleanup(bool fatal, const repack_table *table) } else { - char buffer[12]; - char num_buff[12]; + char buffer[12]; + char num_buff[12]; const char *params[2]; /* Try reconnection if not available. */ @@ -2078,7 +2070,7 @@ repack_cleanup(bool fatal, const repack_table *table) /* do cleanup */ params[0] = utoa(table->target_oid, buffer); - params[1] = utoa(temp_obj_num, num_buff); + params[1] = utoa(temp_obj_num, num_buff); command("BEGIN ISOLATION LEVEL READ COMMITTED", 0, NULL); if (!(lock_exclusive(connection, params[0], table->lock_table, false))) @@ -2102,9 +2094,9 @@ static void repack_cleanup_index(bool fatal, void *userdata) { StringInfoData sql, sql_drop; - PGresult *index_details = (PGresult *)userdata; - char *schema_name; - int num; + PGresult *index_details = (PGresult *) userdata; + char *schema_name; + int num; schema_name = getstr(index_details, 0, 5); num = PQntuples(index_details); @@ -2113,11 +2105,11 @@ repack_cleanup_index(bool fatal, void *userdata) initStringInfo(&sql_drop); appendStringInfoString(&sql, "DROP INDEX CONCURRENTLY IF EXISTS "); - appendStringInfo(&sql, "\"%s\".", schema_name); + appendStringInfo(&sql, "\"%s\".", schema_name); for (int i = 0; i < num; i++) { - Oid index = getoid(index_details, i, 1); + Oid index = getoid(index_details, i, 1); resetStringInfo(&sql_drop); appendStringInfo(&sql_drop, "%s\"index_%u\"", sql.data, index); @@ -2133,14 +2125,14 @@ repack_cleanup_index(bool fatal, void *userdata) static bool repack_table_indexes(PGresult *index_details) { - bool ret = false; - PGresult *res = NULL, *res2 = NULL; - StringInfoData sql; - char buffer[2][12]; - const char *create_idx, *schema_name, *table_name, *params[3]; - Oid table, index; - int i, num, num_repacked = 0; - bool *repacked_indexes; + bool ret = false; + PGresult *res = NULL, *res2 = NULL; + StringInfoData sql; + char buffer[2][12]; + const char *create_idx, *schema_name, *table_name, *params[3]; + Oid table, index; + int i, num, num_repacked = 0; + bool *repacked_indexes; initStringInfo(&sql); @@ -2164,7 +2156,7 @@ repack_table_indexes(PGresult *index_details) */ if (!advisory_lock(connection, params[1])) ereport(ERROR, (errcode(EINVAL), - errmsg("Unable to obtain advisory lock on \"%s\"", table_name))); + errmsg("Unable to obtain advisory lock on \"%s\"", table_name))); pgut_atexit_push(repack_cleanup_index, index_details); @@ -2179,11 +2171,10 @@ repack_table_indexes(PGresult *index_details) resetStringInfo(&sql); appendStringInfo(&sql, "SELECT pgc.relname, nsp.nspname " - "FROM pg_class pgc INNER JOIN pg_namespace nsp " - "ON nsp.oid = pgc.relnamespace " - "WHERE pgc.relname = 'index_%u' " - "AND nsp.nspname = $1", - index); + "FROM pg_class pgc INNER JOIN pg_namespace nsp " + "ON nsp.oid = pgc.relnamespace " + "WHERE pgc.relname = 'index_%u' " + "AND nsp.nspname = $1", index); params[0] = schema_name; elog(INFO, "repacking index \"%s\"", idx_name); res = execute(sql.data, 1, params); @@ -2197,8 +2188,7 @@ repack_table_indexes(PGresult *index_details) ereport(WARNING, (errcode(E_PG_COMMAND), errmsg("Cannot create index \"%s\".\"index_%u\", " - "already exists", - schema_name, index), + "already exists", schema_name, index), errdetail("An invalid index may have been left behind" " by a previous pg_repack on the table" " which was interrupted. Please use DROP " @@ -2218,8 +2208,8 @@ repack_table_indexes(PGresult *index_details) if (PQntuples(res) < 1) { elog(WARNING, - "unable to generate SQL to CREATE work index for %s", - getstr(index_details, i, 0)); + "unable to generate SQL to CREATE work index for %s", + getstr(index_details, i, 0)); continue; } @@ -2232,7 +2222,8 @@ repack_table_indexes(PGresult *index_details) ereport(WARNING, (errcode(E_PG_COMMAND), errmsg("Error creating index \"%s\".\"index_%u\": %s", - schema_name, index, PQerrorMessage(connection)))); + schema_name, index, PQerrorMessage(connection) + ) )); } else { @@ -2248,8 +2239,7 @@ repack_table_indexes(PGresult *index_details) getstr(index_details, i, 0)); } - if (dryrun) - { + if (dryrun) { ret = true; goto done; } @@ -2314,11 +2304,11 @@ repack_table_indexes(PGresult *index_details) static bool repack_all_indexes(char *errbuf, size_t errsize) { - bool ret = false; - PGresult *res = NULL; - StringInfoData sql; - SimpleStringListCell *cell = NULL; - const char *params[1]; + bool ret = false; + PGresult *res = NULL; + StringInfoData sql; + SimpleStringListCell *cell = NULL; + const char *params[1]; initStringInfo(&sql); reconnect(ERROR); @@ -2334,20 +2324,20 @@ repack_all_indexes(char *errbuf, size_t errsize) if (r_index.head) { appendStringInfoString(&sql, - "SELECT repack.oid2text(i.oid), idx.indexrelid, idx.indisvalid, idx.indrelid, repack.oid2text(idx.indrelid), n.nspname" - " FROM pg_index idx JOIN pg_class i ON i.oid = idx.indexrelid" - " JOIN pg_namespace n ON n.oid = i.relnamespace" - " WHERE idx.indexrelid = $1::regclass ORDER BY indisvalid DESC, i.relname, n.nspname"); + "SELECT repack.oid2text(i.oid), idx.indexrelid, idx.indisvalid, idx.indrelid, repack.oid2text(idx.indrelid), n.nspname" + " FROM pg_index idx JOIN pg_class i ON i.oid = idx.indexrelid" + " JOIN pg_namespace n ON n.oid = i.relnamespace" + " WHERE idx.indexrelid = $1::regclass ORDER BY indisvalid DESC, i.relname, n.nspname"); cell = r_index.head; } else if (table_list.head || parent_table_list.head) { appendStringInfoString(&sql, - "SELECT repack.oid2text(i.oid), idx.indexrelid, idx.indisvalid, idx.indrelid, $1::text, n.nspname" - " FROM pg_index idx JOIN pg_class i ON i.oid = idx.indexrelid" - " JOIN pg_namespace n ON n.oid = i.relnamespace" - " WHERE idx.indrelid = $1::regclass ORDER BY indisvalid DESC, i.relname, n.nspname"); + "SELECT repack.oid2text(i.oid), idx.indexrelid, idx.indisvalid, idx.indrelid, $1::text, n.nspname" + " FROM pg_index idx JOIN pg_class i ON i.oid = idx.indexrelid" + " JOIN pg_namespace n ON n.oid = i.relnamespace" + " WHERE idx.indrelid = $1::regclass ORDER BY indisvalid DESC, i.relname, n.nspname"); for (cell = parent_table_list.head; cell; cell = cell->next) { @@ -2408,17 +2398,17 @@ repack_all_indexes(char *errbuf, size_t errsize) if (PQntuples(res) == 0) { - if (table_list.head) + if(table_list.head) elog(WARNING, "\"%s\" does not have any indexes", - cell->val); - else if (r_index.head) + cell->val); + else if(r_index.head) elog(WARNING, "\"%s\" is not a valid index", - cell->val); + cell->val); continue; } - if (table_list.head) + if(table_list.head) elog(INFO, "repacking indexes of \"%s\"", cell->val); if (!repack_table_indexes(res)) @@ -2434,7 +2424,8 @@ repack_all_indexes(char *errbuf, size_t errsize) return ret; } -void pgut_help(bool details) +void +pgut_help(bool details) { printf("%s re-organizes a PostgreSQL database.\n\n", PROGRAM_NAME); printf("Usage:\n"); @@ -2466,40 +2457,3 @@ void pgut_help(bool details) printf(" --apply-count number of tuples to apply in one transaction during replay\n"); printf(" --switch-threshold switch tables when that many tuples are left to catchup\n"); } - -/** - * Validate the WHERE clause by preparing a statement with the clause - * and checking if it can be executed against the target table. - */ -static bool -validate_where_clause(const char *table_name, const char *where_clause, char *errbuf, size_t errsize) -{ - PGresult *res; - StringInfoData sql; - bool ret = false; - - if (where_clause == NULL) - return true; - - initStringInfo(&sql); - - appendStringInfo(&sql, "EXPLAIN SELECT 1 FROM %s WHERE %s", - table_name, where_clause); - - res = PQexec(connection, sql.data); - if (PQresultStatus(res) != PGRES_TUPLES_OK) - { - if (errbuf) - snprintf(errbuf, errsize, "invalid WHERE clause: %s", - PQerrorMessage(connection)); - ret = false; - } - else - { - ret = true; - } - - CLEARPGRES(res); - termStringInfo(&sql); - return ret; -} From 5f77eb727479023be54824dc123eab6656327633 Mon Sep 17 00:00:00 2001 From: ankit mittal Date: Fri, 28 Feb 2025 23:21:42 -0500 Subject: [PATCH 21/26] rollback this too --- lib/pg_repack.sql.in | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/pg_repack.sql.in b/lib/pg_repack.sql.in index 50e147c5..b0596452 100644 --- a/lib/pg_repack.sql.in +++ b/lib/pg_repack.sql.in @@ -64,7 +64,7 @@ $$ BEGIN EXECUTE 'CREATE TABLE repack.table_' || $1 || ' WITH (' || repack.get_storage_param($1) || ') ' || - CASE WHEN $2 IS NULL THEN '' ELSE ' TABLESPACE ' || quote_ident($2) END || + ' TABLESPACE ' || quote_ident($2) || ' AS SELECT ' || repack.get_columns_for_create_as($1) || ' FROM ONLY ' || repack.oid2text($1) || ' WITH NO DATA'; END @@ -311,8 +311,7 @@ CREATE VIEW repack.tables AS 'INSERT INTO repack.table_' || R.oid || ' VALUES ($1.*)' AS sql_insert, 'DELETE FROM repack.table_' || R.oid || ' WHERE ' || repack.get_compare_pkey(PK.indexrelid, '$1') AS sql_delete, 'UPDATE repack.table_' || R.oid || ' SET ' || repack.get_assign(R.oid, '$2') || ' WHERE ' || repack.get_compare_pkey(PK.indexrelid, '$1') AS sql_update, - 'DELETE FROM repack.log_' || R.oid || ' WHERE id IN (' AS sql_pop, - NULL::text AS where_clause + 'DELETE FROM repack.log_' || R.oid || ' WHERE id IN (' AS sql_pop FROM pg_class R LEFT JOIN pg_class T ON R.reltoastrelid = T.oid LEFT JOIN repack.primary_keys PK From 79461bd322adca4e79ae810cb382ab579cb952f2 Mon Sep 17 00:00:00 2001 From: ankit mittal Date: Fri, 28 Feb 2025 23:28:15 -0500 Subject: [PATCH 22/26] checking validation --- bin/pg_repack.c | 65 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/bin/pg_repack.c b/bin/pg_repack.c index cd8c4539..ffdce8ab 100644 --- a/bin/pg_repack.c +++ b/bin/pg_repack.c @@ -224,6 +224,7 @@ static void repack_cleanup(bool fatal, const repack_table *table); static void repack_cleanup_callback(bool fatal, void *userdata); static void repack_cleanup_index(bool fatal, void *userdata); static bool rebuild_indexes(const repack_table *table); +static bool validate_where_clause(PGconn *conn, const char *table_name, const char *where_clause, char *errbuf, size_t errsize); static char *getstr(PGresult *res, int row, int col); static Oid getoid(PGresult *res, int row, int col); @@ -946,16 +947,28 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha table.sql_pop = getstr(res, i, c++); table.dest_tablespace = getstr(res, i, c++); + /* Validate WHERE clause if provided */ + if (where_clause && where_clause[0]) + { + if (!validate_where_clause(connection, table.target_name, where_clause, errbuf, errsize)) + { + ereport(ERROR, + (errcode(E_PG_COMMAND), + errmsg("%s", errbuf))); + continue; + } + } + /* Craft Copy SQL */ initStringInfo(©_sql); appendStringInfoString(©_sql, table.copy_data); - /* soft delete recognition */ - if (where_clause) { - appendStringInfoString(©_sql, " WHERE "); - appendStringInfoString(©_sql, where_clause); - } - if (!orderby) + /* delete rows mode */ + if (where_clause) { + appendStringInfoString(©_sql, " WHERE "); + appendStringInfoString(©_sql, where_clause); + } + if (!orderby) { if (ckey != NULL) { @@ -2457,3 +2470,43 @@ pgut_help(bool details) printf(" --apply-count number of tuples to apply in one transaction during replay\n"); printf(" --switch-threshold switch tables when that many tuples are left to catchup\n"); } + +/* + * Validate a WHERE clause by running EXPLAIN SELECT 1 with the clause + * Returns true if the WHERE clause is valid, false otherwise + */ +static bool +validate_where_clause(PGconn *conn, const char *table_name, const char *where_clause, char *errbuf, size_t errsize) +{ + bool valid = false; + PGresult *res = NULL; + StringInfoData sql; + + if (!where_clause || !where_clause[0]) + return true; /* Empty clause is considered valid */ + + initStringInfo(&sql); + + /* Build EXPLAIN query to validate the WHERE clause */ + appendStringInfo(&sql, "EXPLAIN SELECT 1 FROM %s WHERE %s", + table_name, where_clause); + + res = PQexec(conn, sql.data); + + if (PQresultStatus(res) != PGRES_TUPLES_OK) + { + if (errbuf) + snprintf(errbuf, errsize, "invalid WHERE clause: %s", + PQerrorMessage(conn)); + valid = false; + } + else + { + valid = true; + } + + CLEARPGRES(res); + termStringInfo(&sql); + + return valid; +} From f50b8c57b62363b9272fcb83c9a077712ac34ddb Mon Sep 17 00:00:00 2001 From: ankit mittal Date: Sat, 1 Mar 2025 12:20:38 -0500 Subject: [PATCH 23/26] validation complete, Clean error messages that can be tested --- bin/pg_repack.c | 53 ++++++++++++++++++++++++++++--- doc/pg_repack.rst | 6 ++-- regress/expected/where-clause.out | 8 ++--- regress/sql/where-clause.sql | 3 ++ 4 files changed, 59 insertions(+), 11 deletions(-) diff --git a/bin/pg_repack.c b/bin/pg_repack.c index ffdce8ab..e8e678a6 100644 --- a/bin/pg_repack.c +++ b/bin/pg_repack.c @@ -284,7 +284,7 @@ static pgut_option options[] = { 'l', 'c', "schema", &schema_list }, { 'b', 'n', "no-order", &noorder }, { 'b', 'N', "dry-run", &dryrun }, - { 's', 'X', "where-clause", &where_clause }, + { 's', 'X', "where-clause", &where_clause }, { 's', 'o', "order-by", &orderby }, { 's', 's', "tablespace", &tablespace }, { 'b', 'S', "moveidx", &moveidx }, @@ -2465,6 +2465,7 @@ pgut_help(bool details) printf(" -Z, --no-analyze don't analyze at end\n"); printf(" -k, --no-superuser-check skip superuser checks in client\n"); printf(" -C, --exclude-extension don't repack tables which belong to specific extension\n"); + printf(" -X, --where=WHERE_CLAUSE (delete rows mode) keep only speficic rows in the table as per condition specified\n"); printf(" --no-error-on-invalid-index repack even though invalid index is found\n"); printf(" --error-on-invalid-index don't repack when invalid index is found, deprecated, as this is the default behavior now\n"); printf(" --apply-count number of tuples to apply in one transaction during replay\n"); @@ -2483,7 +2484,11 @@ validate_where_clause(PGconn *conn, const char *table_name, const char *where_cl StringInfoData sql; if (!where_clause || !where_clause[0]) - return true; /* Empty clause is considered valid */ + { + if (errbuf) + snprintf(errbuf, errsize, "empty WHERE clause is not valid"); + return false; /* Empty where clause */ + } initStringInfo(&sql); @@ -2494,10 +2499,50 @@ validate_where_clause(PGconn *conn, const char *table_name, const char *where_cl res = PQexec(conn, sql.data); if (PQresultStatus(res) != PGRES_TUPLES_OK) + /* Clean up the error message. Remove duplicate ERROR: prefix as well as remove postgres LINE: error */ { if (errbuf) - snprintf(errbuf, errsize, "invalid WHERE clause: %s", - PQerrorMessage(conn)); + { + const char *pg_error = PQerrorMessage(conn); + char cleaned_error[1024] = {0}; // Buffer for cleaned error message + + if (strstr(pg_error, "ERROR:") != NULL) + { + const char *error_start = pg_error; + const char *error_prefix = "ERROR: "; + + if (strncmp(error_start, error_prefix, strlen(error_prefix)) == 0) + error_start += strlen(error_prefix); + + /* Grab the actual error message ends (before LINE: part) */ + const char *line_part = strstr(error_start, "LINE "); + if (line_part != NULL) + { + size_t len = line_part - error_start; + strncpy(cleaned_error, error_start, len); + cleaned_error[len] = '\0'; + + /* Trim text */ + char *end = cleaned_error + len - 1; + while (end > cleaned_error && (*end == ' ' || *end == '\n' || *end == '\r')) + *end-- = '\0'; + } + else + { + /* Edge case: No LINE: message from postgres */ + strncpy(cleaned_error, error_start, sizeof(cleaned_error) - 1); + cleaned_error[sizeof(cleaned_error) - 1] = '\0'; + } + } + else + { + /* Edge case: Unparsable error message, use the original message as it is*/ + strncpy(cleaned_error, pg_error, sizeof(cleaned_error) - 1); + cleaned_error[sizeof(cleaned_error) - 1] = '\0'; + } + + snprintf(errbuf, errsize, "invalid WHERE clause: %s", cleaned_error); + } valid = false; } else diff --git a/doc/pg_repack.rst b/doc/pg_repack.rst index a3e601c6..fed52f3e 100644 --- a/doc/pg_repack.rst +++ b/doc/pg_repack.rst @@ -342,13 +342,13 @@ Move the specified index to tablespace ``tbs``:: $ pg_repack -d test --index idx --tablespace tbs Select only rows where id > 10 in table ``foo`` (note that the condition specifies -which rows will remain in the table after repacking, not which rows will be deleted):: +which rows will remain in the table after repacking, not which rows will be deleted). This would delete all rows where id <= 10:: $ pg_repack -d test --table foo --where-clause="id > 10" -Select only rows where value > 100 in table ``bar`` that has a column with spaces in its name:: +Select only rows where value > 100 in table ``bar`` in column foo. This would delete all rows where foo <= 100:: - $ pg_repack -d test --table bar --where-clause="\"column with space\" > 100" + $ pg_repack -d test --table bar --where-clause="foo > 100" Select only rows where create_date is within the last year:: diff --git a/regress/expected/where-clause.out b/regress/expected/where-clause.out index f49bc436..9eaaaac9 100644 --- a/regress/expected/where-clause.out +++ b/regress/expected/where-clause.out @@ -63,9 +63,9 @@ SELECT * FROM tbl_where_clause ORDER BY id; -- Test with non-existent column in where clause (should fail) \! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="non_existent_column = true" -INFO: repacking table "public.tbl_where_clause" -ERROR: invalid WHERE clause: ERROR: column "non_existent_column" does not exist -LINE 1: ...PLAIN SELECT 1 FROM public.tbl_where_clause WHERE non_existe... - ^ +ERROR: invalid WHERE clause: column "non_existent_column" does not exist +-- Test with special character in column name without proper quoting (should fail) +\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="column with space = 'test'" +ERROR: invalid WHERE clause: syntax error at or near "column" -- Clean up DROP TABLE tbl_where_clause; diff --git a/regress/sql/where-clause.sql b/regress/sql/where-clause.sql index cabc59b7..6cf2fcf0 100644 --- a/regress/sql/where-clause.sql +++ b/regress/sql/where-clause.sql @@ -43,5 +43,8 @@ SELECT * FROM tbl_where_clause ORDER BY id; -- Test with non-existent column in where clause (should fail) \! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="non_existent_column = true" +-- Test with special character in column name without proper quoting (should fail) +\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="column with space = 'test'" + -- Clean up DROP TABLE tbl_where_clause; \ No newline at end of file From 368b034348f77f1af7ebc67b67066a38753ea572 Mon Sep 17 00:00:00 2001 From: ankit mittal Date: Sat, 1 Mar 2025 13:07:52 -0500 Subject: [PATCH 24/26] docs updated --- bin/pg_repack.c | 9 ++++++++- doc/pg_repack.rst | 7 ++++--- regress/expected/where-clause.out | 3 +++ regress/sql/where-clause.sql | 3 +++ 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/bin/pg_repack.c b/bin/pg_repack.c index e8e678a6..72ea15a5 100644 --- a/bin/pg_repack.c +++ b/bin/pg_repack.c @@ -950,6 +950,12 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha /* Validate WHERE clause if provided */ if (where_clause && where_clause[0]) { + if (num > 1) { + ereport(ERROR, + (errcode(E_PG_COMMAND), + errmsg("where-clause can only be used when repacking a single table. Found %d tables matching criteria.", num))); + continue; + } if (!validate_where_clause(connection, table.target_name, where_clause, errbuf, errsize)) { ereport(ERROR, @@ -2465,7 +2471,8 @@ pgut_help(bool details) printf(" -Z, --no-analyze don't analyze at end\n"); printf(" -k, --no-superuser-check skip superuser checks in client\n"); printf(" -C, --exclude-extension don't repack tables which belong to specific extension\n"); - printf(" -X, --where=WHERE_CLAUSE (delete rows mode) keep only speficic rows in the table as per condition specified\n"); + printf(" -X, --where=WHERE_CLAUSE (delete rows mode) keep only specific rows in the table as per condition specified.\n"); + printf(" Note: where-clause can only be used when repacking a single table\n"); printf(" --no-error-on-invalid-index repack even though invalid index is found\n"); printf(" --error-on-invalid-index don't repack when invalid index is found, deprecated, as this is the default behavior now\n"); printf(" --apply-count number of tuples to apply in one transaction during replay\n"); diff --git a/doc/pg_repack.rst b/doc/pg_repack.rst index fed52f3e..8e4a27ef 100644 --- a/doc/pg_repack.rst +++ b/doc/pg_repack.rst @@ -130,7 +130,7 @@ Options: --error-on-invalid-index don't repack when invalid index is found, deprecated, as this is the default behavior now --apply-count number of tuples to apply in one trasaction during replay --switch-threshold switch tables when that many tuples are left to catchup - -X, --where-clause=CONDITION Deletion support. You can use this option to remove certain rows from the table. + -X CONDITION, --where-clause=CONDITION Remove certain live rows from the table. Use this option to clean up rows that are not needed anymore. Connection options: -d, --dbname=DBNAME database to connect @@ -173,12 +173,13 @@ Reorg Options ``-X CONDITION``, ``--where-clause=CONDITION`` Remove certain live rows from the table. Use this option to clean up rows that are not needed anymore. - table. The condition is a SQL WHERE clause that is applied to the table being + table. The condition is a SQL where-clause that is applied to the table being repacked. Note that the condition specifies which rows WILL remain in the table after repacking, not which rows will be deleted. --where-clause="deleted_at IS NOT NULL" will lead to the repacked table removing all rows where deleted_at is NULL at the time of repack start. - + This option can only be used when repacking a single table and will fail if multiple + tables would be repacked. ``-n``, ``--no-order`` Perform an online VACUUM FULL. Since version 1.2 this is the default for diff --git a/regress/expected/where-clause.out b/regress/expected/where-clause.out index 9eaaaac9..28dfbb28 100644 --- a/regress/expected/where-clause.out +++ b/regress/expected/where-clause.out @@ -67,5 +67,8 @@ ERROR: invalid WHERE clause: column "non_existent_column" does not exist -- Test with special character in column name without proper quoting (should fail) \! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="column with space = 'test'" ERROR: invalid WHERE clause: syntax error at or near "column" +-- Test for repackaing whole database with a where clause (should fail) +\! pg_repack --dbname=contrib_regression --where-clause="id > 3" +ERROR: where-clause can only be used when repacking a single table. Found 40 tables matching criteria. -- Clean up DROP TABLE tbl_where_clause; diff --git a/regress/sql/where-clause.sql b/regress/sql/where-clause.sql index 6cf2fcf0..39a4d99b 100644 --- a/regress/sql/where-clause.sql +++ b/regress/sql/where-clause.sql @@ -46,5 +46,8 @@ SELECT * FROM tbl_where_clause ORDER BY id; -- Test with special character in column name without proper quoting (should fail) \! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="column with space = 'test'" +-- Test for repackaing whole database with a where clause (should fail) +\! pg_repack --dbname=contrib_regression --where-clause="id > 3" + -- Clean up DROP TABLE tbl_where_clause; \ No newline at end of file From 5d8b0e7c360fa865faeeacccc5d8c672d6c8c796 Mon Sep 17 00:00:00 2001 From: ankit mittal Date: Sat, 1 Mar 2025 13:28:29 -0500 Subject: [PATCH 25/26] updated tests, tablespace failing on local runs --- bin/pg_repack.c | 2 +- regress/expected/where-clause.out | 74 ++++++++++++++++++------------- regress/sql/where-clause.sql | 25 +++++++---- 3 files changed, 62 insertions(+), 39 deletions(-) diff --git a/bin/pg_repack.c b/bin/pg_repack.c index 72ea15a5..d133dd21 100644 --- a/bin/pg_repack.c +++ b/bin/pg_repack.c @@ -953,7 +953,7 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize, const cha if (num > 1) { ereport(ERROR, (errcode(E_PG_COMMAND), - errmsg("where-clause can only be used when repacking a single table. Found %d tables matching criteria.", num))); + errmsg("where-clause can only be used when repacking a single table."))); continue; } if (!validate_where_clause(connection, table.target_name, where_clause, errbuf, errsize)) diff --git a/regress/expected/where-clause.out b/regress/expected/where-clause.out index 28dfbb28..c96a4588 100644 --- a/regress/expected/where-clause.out +++ b/regress/expected/where-clause.out @@ -4,23 +4,23 @@ CREATE TABLE tbl_where_clause ( id int PRIMARY KEY, value text, active boolean, - "column with space" text + deleted_at timestamp ); -- Insert some test data -INSERT INTO tbl_where_clause VALUES (1, 'one', true, 'one'); -INSERT INTO tbl_where_clause VALUES (2, 'two', true, 'two'); -INSERT INTO tbl_where_clause VALUES (3, 'three', false, 'three'); -INSERT INTO tbl_where_clause VALUES (4, 'four', false, 'four'); -INSERT INTO tbl_where_clause VALUES (5, 'five', true, 'five'); +INSERT INTO tbl_where_clause VALUES (1, 'one', true, NULL); +INSERT INTO tbl_where_clause VALUES (2, 'two', true, NULL); +INSERT INTO tbl_where_clause VALUES (3, 'three', false, NULL); +INSERT INTO tbl_where_clause VALUES (4, 'four', false, NULL); +INSERT INTO tbl_where_clause VALUES (5, 'five', true, NULL); -- Check initial data SELECT * FROM tbl_where_clause ORDER BY id; - id | value | active | column with space -----+-------+--------+------------------- - 1 | one | t | one - 2 | two | t | two - 3 | three | f | three - 4 | four | f | four - 5 | five | t | five + id | value | active | deleted_at +----+-------+--------+-------------------------- + 1 | one | t | + 2 | two | t | + 3 | three | f | + 4 | four | f | + 5 | five | t | (5 rows) -- Run pg_repack with where clause to only repack active rows @@ -28,26 +28,26 @@ SELECT * FROM tbl_where_clause ORDER BY id; INFO: repacking table "public.tbl_where_clause" -- Verify data after repack SELECT * FROM tbl_where_clause ORDER BY id; - id | value | active | column with space -----+-------+--------+------------------- - 1 | one | t | one - 2 | two | t | two - 5 | five | t | five + id | value | active | deleted_at +----+-------+--------+------------ + 1 | one | t | + 2 | two | t | + 5 | five | t | (3 rows) -- Insert more data to verify the where clause worked correctly -INSERT INTO tbl_where_clause VALUES (6, 'six', true, 'six'); -INSERT INTO tbl_where_clause VALUES (7, 'seven', false, 'seven'); +INSERT INTO tbl_where_clause VALUES (6, 'six', true, NULL); +INSERT INTO tbl_where_clause VALUES (7, 'seven', false, NULL); -- Run pg_repack with a different where clause \! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="id > 3" INFO: repacking table "public.tbl_where_clause" -- Verify data after second repack SELECT * FROM tbl_where_clause ORDER BY id; - id | value | active | column with space -----+-------+--------+------------------- - 5 | five | t | five - 6 | six | t | six - 7 | seven | f | seven + id | value | active | deleted_at +----+-------+--------+-------------------------- + 5 | five | t | + 6 | six | t | + 7 | seven | f | NULL (3 rows) -- Test with where clause and order-by together @@ -55,12 +55,26 @@ SELECT * FROM tbl_where_clause ORDER BY id; INFO: repacking table "public.tbl_where_clause" -- Verify data after repack with order-by SELECT * FROM tbl_where_clause ORDER BY id; - id | value | active | column with space -----+-------+--------+------------------- - 5 | five | t | five - 6 | six | t | six + id | value | active | deleted_at +----+-------+--------+------------ + 5 | five | t | + 6 | six | t | (2 rows) +INSERT INTO tbl_where_clause VALUES (8, 'eight', true, NULL); +INSERT INTO tbl_where_clause VALUES (9, 'nine', false, '2023-01-04 10:00:00'); +-- Test with deleted_at is null where clause (keep only non-deleted rows) +\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="deleted_at IS NULL" +INFO: repacking table "public.tbl_where_clause" +-- Verify data after repack with deleted_at IS NULL +SELECT * FROM tbl_where_clause ORDER BY id; + id | value | active | deleted_at +----+-------+--------+------------ + 5 | five | t | + 6 | six | t | + 8 | eight | t | +(3 rows) + -- Test with non-existent column in where clause (should fail) \! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="non_existent_column = true" ERROR: invalid WHERE clause: column "non_existent_column" does not exist @@ -69,6 +83,6 @@ ERROR: invalid WHERE clause: column "non_existent_column" does not exist ERROR: invalid WHERE clause: syntax error at or near "column" -- Test for repackaing whole database with a where clause (should fail) \! pg_repack --dbname=contrib_regression --where-clause="id > 3" -ERROR: where-clause can only be used when repacking a single table. Found 40 tables matching criteria. +ERROR: where-clause can only be used when repacking a single table. -- Clean up DROP TABLE tbl_where_clause; diff --git a/regress/sql/where-clause.sql b/regress/sql/where-clause.sql index 39a4d99b..eb2ba852 100644 --- a/regress/sql/where-clause.sql +++ b/regress/sql/where-clause.sql @@ -5,15 +5,15 @@ CREATE TABLE tbl_where_clause ( id int PRIMARY KEY, value text, active boolean, - "column with space" text + deleted_at timestamp ); -- Insert some test data -INSERT INTO tbl_where_clause VALUES (1, 'one', true, 'one'); -INSERT INTO tbl_where_clause VALUES (2, 'two', true, 'two'); -INSERT INTO tbl_where_clause VALUES (3, 'three', false, 'three'); -INSERT INTO tbl_where_clause VALUES (4, 'four', false, 'four'); -INSERT INTO tbl_where_clause VALUES (5, 'five', true, 'five'); +INSERT INTO tbl_where_clause VALUES (1, 'one', true, NULL); +INSERT INTO tbl_where_clause VALUES (2, 'two', true, NULL); +INSERT INTO tbl_where_clause VALUES (3, 'three', false, NULL); +INSERT INTO tbl_where_clause VALUES (4, 'four', false, NULL); +INSERT INTO tbl_where_clause VALUES (5, 'five', true, NULL); -- Check initial data SELECT * FROM tbl_where_clause ORDER BY id; @@ -25,8 +25,8 @@ SELECT * FROM tbl_where_clause ORDER BY id; SELECT * FROM tbl_where_clause ORDER BY id; -- Insert more data to verify the where clause worked correctly -INSERT INTO tbl_where_clause VALUES (6, 'six', true, 'six'); -INSERT INTO tbl_where_clause VALUES (7, 'seven', false, 'seven'); +INSERT INTO tbl_where_clause VALUES (6, 'six', true, NULL); +INSERT INTO tbl_where_clause VALUES (7, 'seven', false, NULL); -- Run pg_repack with a different where clause \! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="id > 3" @@ -40,6 +40,15 @@ SELECT * FROM tbl_where_clause ORDER BY id; -- Verify data after repack with order-by SELECT * FROM tbl_where_clause ORDER BY id; +INSERT INTO tbl_where_clause VALUES (8, 'eight', true, NULL); +INSERT INTO tbl_where_clause VALUES (9, 'nine', false, '2023-01-04 10:00:00'); + +-- Test with deleted_at is null where clause (keep only non-deleted rows) +\! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="deleted_at IS NULL" + +-- Verify data after repack with deleted_at IS NULL +SELECT * FROM tbl_where_clause ORDER BY id; + -- Test with non-existent column in where clause (should fail) \! pg_repack --dbname=contrib_regression --table=tbl_where_clause --where-clause="non_existent_column = true" From 14f20697e97c10c19999a6f346ad274e50423a32 Mon Sep 17 00:00:00 2001 From: ankit mittal Date: Sat, 1 Mar 2025 14:22:41 -0500 Subject: [PATCH 26/26] ISO C guidelines --- bin/pg_repack.c | 7 +++++-- regress/expected/where-clause.out | 10 +++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/bin/pg_repack.c b/bin/pg_repack.c index d133dd21..0c48a9ad 100644 --- a/bin/pg_repack.c +++ b/bin/pg_repack.c @@ -2517,20 +2517,23 @@ validate_where_clause(PGconn *conn, const char *table_name, const char *where_cl { const char *error_start = pg_error; const char *error_prefix = "ERROR: "; + const char *line_part; if (strncmp(error_start, error_prefix, strlen(error_prefix)) == 0) error_start += strlen(error_prefix); /* Grab the actual error message ends (before LINE: part) */ - const char *line_part = strstr(error_start, "LINE "); + line_part = strstr(error_start, "LINE "); if (line_part != NULL) { size_t len = line_part - error_start; + char *end; + strncpy(cleaned_error, error_start, len); cleaned_error[len] = '\0'; /* Trim text */ - char *end = cleaned_error + len - 1; + end = cleaned_error + len - 1; while (end > cleaned_error && (*end == ' ' || *end == '\n' || *end == '\r')) *end-- = '\0'; } diff --git a/regress/expected/where-clause.out b/regress/expected/where-clause.out index c96a4588..5279c67f 100644 --- a/regress/expected/where-clause.out +++ b/regress/expected/where-clause.out @@ -14,8 +14,8 @@ INSERT INTO tbl_where_clause VALUES (4, 'four', false, NULL); INSERT INTO tbl_where_clause VALUES (5, 'five', true, NULL); -- Check initial data SELECT * FROM tbl_where_clause ORDER BY id; - id | value | active | deleted_at -----+-------+--------+-------------------------- + id | value | active | deleted_at +----+-------+--------+------------ 1 | one | t | 2 | two | t | 3 | three | f | @@ -43,11 +43,11 @@ INSERT INTO tbl_where_clause VALUES (7, 'seven', false, NULL); INFO: repacking table "public.tbl_where_clause" -- Verify data after second repack SELECT * FROM tbl_where_clause ORDER BY id; - id | value | active | deleted_at -----+-------+--------+-------------------------- + id | value | active | deleted_at +----+-------+--------+------------ 5 | five | t | 6 | six | t | - 7 | seven | f | NULL + 7 | seven | f | (3 rows) -- Test with where clause and order-by together