Skip to content

Commit

Permalink
Merge pull request #277 from Melkij/support_for_postgresql_14
Browse files Browse the repository at this point in the history
Support for postgresql 14
  • Loading branch information
Melkij authored Sep 28, 2021
2 parents 76a38e2 + d263fc3 commit 2493809
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 40 deletions.
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
# Travis CI configuration file for psycopg2

dist: xenial
dist: focal
sudo: required

env:
- PGVER=14
- PGVER=13
- PGVER=12
- PGVER=11
- PGVER=10
# Disabled because packages broken at least on xenial
# https://www.postgresql.org/message-id/CA%2Bmi_8a1oEnCzkt0CvqysgY4MQ6jEefjmS%3Dq_K-AvOx%3DF7m2%2BQ%40mail.gmail.com
# - PGVER=9.6
- PGVER=9.6
- PGVER=9.5
- PGVER=9.4

Expand Down
4 changes: 2 additions & 2 deletions META.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "pg_repack",
"abstract": "PostgreSQL module for data reorganization",
"description": "Reorganize tables in PostgreSQL databases with minimal locks",
"version": "1.4.6",
"version": "1.4.7",
"maintainer": [
"Beena Emerson <[email protected]>",
"Josh Kupershmidt <[email protected]>",
Expand All @@ -15,7 +15,7 @@
"provides": {
"pg_repack": {
"file": "lib/pg_repack.sql",
"version": "1.4.6",
"version": "1.4.7",
"abstract": "Reorganize tables in PostgreSQL databases with minimal locks"
}
},
Expand Down
120 changes: 117 additions & 3 deletions bin/pg_repack.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ const char *PROGRAM_VERSION = "unknown";
* pg_regress.
*/
#define SQL_XID_SNAPSHOT_90200 \
"SELECT repack.array_accum(l.virtualtransaction) " \
"SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \
" FROM pg_locks AS l " \
" LEFT JOIN pg_stat_activity AS a " \
" ON l.pid = a.pid " \
Expand All @@ -90,7 +90,7 @@ const char *PROGRAM_VERSION = "unknown";
" AND ((d.datname IS NULL OR d.datname = current_database()) OR l.database = 0)"

#define SQL_XID_SNAPSHOT_90000 \
"SELECT repack.array_accum(l.virtualtransaction) " \
"SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \
" FROM pg_locks AS l " \
" LEFT JOIN pg_stat_activity AS a " \
" ON l.pid = a.procpid " \
Expand All @@ -108,7 +108,7 @@ const char *PROGRAM_VERSION = "unknown";
* the WHERE clause is just to eat the $2 parameter (application name).
*/
#define SQL_XID_SNAPSHOT_80300 \
"SELECT repack.array_accum(l.virtualtransaction) " \
"SELECT coalesce(array_agg(l.virtualtransaction), '{}') " \
" FROM pg_locks AS l" \
" LEFT JOIN pg_stat_activity AS a " \
" ON l.pid = a.procpid " \
Expand Down Expand Up @@ -212,6 +212,7 @@ typedef struct 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);
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);
Expand Down Expand Up @@ -558,6 +559,113 @@ preliminary_checks(char *errbuf, size_t errsize){
return ret;
}

/*
* Check the presence of tables specified by --parent-table and --table
* 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;

num_relations = simple_string_list_size(parent_table_list) +
simple_string_list_size(table_list);

/* nothing was implicitly requested, so nothing to do here */
if (num_relations == 0)
return true;

/* has no suitable to_regclass(text) */
if (PQserverVersion(connection)<90600)
return true;

params = pgut_malloc(num_relations * sizeof(char *));
initStringInfo(&sql);
appendStringInfoString(&sql, "SELECT r FROM (VALUES ");

for (cell = table_list.head; cell; cell = cell->next)
{
appendStringInfo(&sql, "($%d)", iparam + 1);
params[iparam++] = cell->val;
if (iparam < num_relations)
appendStringInfoChar(&sql, ',');
}
for (cell = parent_table_list.head; cell; cell = cell->next)
{
appendStringInfo(&sql, "($%d)", iparam + 1);
params[iparam++] = cell->val;
if (iparam < num_relations)
appendStringInfoChar(&sql, ',');
}
appendStringInfoString(&sql,
") AS given_t(r)"
" WHERE NOT EXISTS("
" SELECT FROM repack.tables WHERE relid=to_regclass(given_t.r) )"
);

/* 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);
goto cleanup;
}

res = execute_elevel(sql.data, iparam, params, DEBUG2);
if (PQresultStatus(res) == PGRES_TUPLES_OK)
{
int num;

num = PQntuples(res);

if (num != 0)
{
int i;
StringInfoData rel_names;
initStringInfo(&rel_names);

for (i = 0; i < num; i++)
{
appendStringInfo(&rel_names, "\"%s\"", getstr(res, i, 0));
if ((i + 1) != num)
appendStringInfoString(&rel_names, ", ");
}

if (errbuf)
{
if (num > 1)
snprintf(errbuf, errsize,
"relations do not exist: %s", rel_names.data);
else
snprintf(errbuf, errsize,
"ERROR: relation %s does not exist", rel_names.data);
}
termStringInfo(&rel_names);
}
else
ret = true;
}
else
{
if (errbuf)
snprintf(errbuf, errsize, "%s", PQerrorMessage(connection));
}
CLEARPGRES(res);

cleanup:
CLEARPGRES(res);
termStringInfo(&sql);
free(params);
return ret;
}

/*
* Call repack_one_database for each database.
*/
Expand Down Expand Up @@ -657,6 +765,9 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize)
if (!preliminary_checks(errbuf, errsize))
goto cleanup;

if (!is_requested_relation_exists(errbuf, errsize))
goto cleanup;

/* acquire target tables */
appendStringInfoString(&sql,
"SELECT t.*,"
Expand Down Expand Up @@ -2115,6 +2226,9 @@ repack_all_indexes(char *errbuf, size_t errsize)
if (!preliminary_checks(errbuf, errsize))
goto cleanup;

if (!is_requested_relation_exists(errbuf, errsize))
goto cleanup;

if (r_index.head)
{
appendStringInfoString(&sql,
Expand Down
16 changes: 15 additions & 1 deletion bin/pgut/pgut.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
#include "postgres_fe.h"
#include "libpq/pqsignal.h"

#if PG_VERSION_NUM >= 140000
#include "common/string.h" /* for simple_prompt */
#endif

#include <limits.h>
#include <sys/stat.h>
#include <time.h>
Expand Down Expand Up @@ -450,7 +454,7 @@ prompt_for_password(void)
passwdbuf = pgut_malloc(BUFSIZE);
memcpy(passwdbuf, buf, sizeof(char)*BUFSIZE);
}
#else
#elif PG_VERSION_NUM < 140000
buf = pgut_malloc(BUFSIZE);
if (have_passwd) {
memcpy(buf, passwdbuf, sizeof(char)*BUFSIZE);
Expand All @@ -461,6 +465,16 @@ prompt_for_password(void)
passwdbuf = pgut_malloc(BUFSIZE);
memcpy(passwdbuf, buf, sizeof(char)*BUFSIZE);
}
#else
if (have_passwd) {
buf = pgut_malloc(BUFSIZE);
memcpy(buf, passwdbuf, sizeof(char)*BUFSIZE);
} else {
buf = simple_prompt("Password: ", false);
have_passwd = true;
passwdbuf = pgut_malloc(BUFSIZE);
memcpy(passwdbuf, buf, sizeof(char)*BUFSIZE);
}
#endif

if (buf == NULL)
Expand Down
6 changes: 5 additions & 1 deletion doc/pg_repack.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Requirements
------------

PostgreSQL versions
PostgreSQL 9.4, 9.5, 9.6, 10, 11, 12, 13
PostgreSQL 9.4, 9.5, 9.6, 10, 11, 12, 13, 14

Disks
Performing a full-table repack requires free disk space about twice as
Expand Down Expand Up @@ -466,6 +466,10 @@ Creating indexes concurrently comes with a few caveats, please see `the document
Releases
--------

* pg_repack 1.4.7

* Added support for PostgreSQL 14

* pg_repack 1.4.6

* Added support for PostgreSQL 13
Expand Down
31 changes: 12 additions & 19 deletions lib/pg_repack.sql.in
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,6 @@ CREATE FUNCTION repack.version_sql() RETURNS text AS
$$SELECT 'pg_repack REPACK_VERSION'::text$$
LANGUAGE SQL IMMUTABLE STRICT;

CREATE AGGREGATE repack.array_accum (
sfunc = array_append,
basetype = anyelement,
stype = anyarray,
initcond = '{}'
);

-- Always specify search_path to 'pg_catalog' so that we
-- always can get schema-qualified relation name
CREATE FUNCTION repack.oid2text(oid) RETURNS text AS
Expand All @@ -33,7 +26,7 @@ LANGUAGE sql STABLE STRICT SET search_path to 'pg_catalog';

CREATE FUNCTION repack.get_index_columns(oid, text) RETURNS text AS
$$
SELECT array_to_string(repack.array_accum(quote_ident(attname)), $2)
SELECT coalesce(string_agg(quote_ident(attname), $2), '')
FROM pg_attribute,
(SELECT indrelid,
indkey,
Expand All @@ -53,8 +46,8 @@ LANGUAGE C STABLE STRICT;
CREATE FUNCTION repack.get_create_index_type(oid, name) RETURNS text AS
$$
SELECT 'CREATE TYPE ' || $2 || ' AS (' ||
array_to_string(repack.array_accum(quote_ident(attname) || ' ' ||
pg_catalog.format_type(atttypid, atttypmod)), ', ') || ')'
coalesce(string_agg(quote_ident(attname) || ' ' ||
pg_catalog.format_type(atttypid, atttypmod), ', '), '') || ')'
FROM pg_attribute,
(SELECT indrelid,
indkey,
Expand Down Expand Up @@ -90,9 +83,9 @@ LANGUAGE sql STABLE STRICT;

CREATE FUNCTION repack.get_assign(oid, text) RETURNS text AS
$$
SELECT '(' || array_to_string(repack.array_accum(quote_ident(attname)), ', ') ||
SELECT '(' || coalesce(string_agg(quote_ident(attname), ', '), '') ||
') = (' || $2 || '.' ||
array_to_string(repack.array_accum(quote_ident(attname)), ', ' || $2 || '.') || ')'
coalesce(string_agg(quote_ident(attname), ', ' || $2 || '.'), '') || ')'
FROM (SELECT attname FROM pg_attribute
WHERE attrelid = $1 AND attnum > 0 AND NOT attisdropped
ORDER BY attnum) tmp;
Expand All @@ -102,9 +95,9 @@ LANGUAGE sql STABLE STRICT;
CREATE FUNCTION repack.get_compare_pkey(oid, text)
RETURNS text AS
$$
SELECT '(' || array_to_string(repack.array_accum(quote_ident(attname)), ', ') ||
SELECT '(' || coalesce(string_agg(quote_ident(attname), ', '), '') ||
') = (' || $2 || '.' ||
array_to_string(repack.array_accum(quote_ident(attname)), ', ' || $2 || '.') || ')'
coalesce(string_agg(quote_ident(attname), ', ' || $2 || '.'), '') || ')'
FROM pg_attribute,
(SELECT indrelid,
indkey,
Expand All @@ -122,7 +115,7 @@ LANGUAGE sql STABLE STRICT;
CREATE FUNCTION repack.get_columns_for_create_as(oid)
RETURNS text AS
$$
SELECT array_to_string(repack.array_accum(c), ',') FROM (SELECT
SELECT coalesce(string_agg(c, ','), '') FROM (SELECT
CASE WHEN attisdropped
THEN 'NULL::integer AS ' || quote_ident(attname)
ELSE quote_ident(attname)
Expand All @@ -142,7 +135,7 @@ SELECT
'ALTER TABLE ' || $2 || ' ' || array_to_string(dropped_columns, ', ')
FROM (
SELECT
repack.array_accum('DROP COLUMN ' || quote_ident(attname)) AS dropped_columns
array_agg('DROP COLUMN ' || quote_ident(attname)) AS dropped_columns
FROM (
SELECT * FROM pg_attribute
WHERE attrelid = $1 AND attnum > 0 AND attisdropped
Expand All @@ -161,7 +154,7 @@ LANGUAGE sql STABLE STRICT;
CREATE FUNCTION repack.get_storage_param(oid)
RETURNS TEXT AS
$$
SELECT array_to_string(array_agg(param), ', ')
SELECT string_agg(param, ', ')
FROM (
-- table storage parameter
SELECT unnest(reloptions) as param
Expand Down Expand Up @@ -196,7 +189,7 @@ $$
SELECT 'ALTER TABLE repack.table_' || $1 || array_to_string(column_storage, ',')
FROM (
SELECT
repack.array_accum(' ALTER ' || quote_ident(attname) ||
array_agg(' ALTER ' || quote_ident(attname) ||
CASE attstorage
WHEN 'p' THEN ' SET STORAGE PLAIN'
WHEN 'm' THEN ' SET STORAGE MAIN'
Expand All @@ -222,7 +215,7 @@ LANGUAGE sql STABLE STRICT;

-- includes not only PRIMARY KEYS but also UNIQUE NOT NULL keys
CREATE VIEW repack.primary_keys AS
SELECT indrelid, (repack.array_accum(indexrelid))[1] AS indexrelid
SELECT indrelid, min(indexrelid) AS indexrelid
FROM (SELECT indrelid, indexrelid FROM pg_index
WHERE indisunique
AND indisvalid
Expand Down
8 changes: 2 additions & 6 deletions lib/repack.c
Original file line number Diff line number Diff line change
Expand Up @@ -1167,16 +1167,12 @@ swap_heap_or_index_files(Oid r1, Oid r2)
relRelation = heap_open(RelationRelationId, RowExclusiveLock);
#endif

reltup1 = SearchSysCacheCopy(RELOID,
ObjectIdGetDatum(r1),
0, 0, 0);
reltup1 = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(r1));
if (!HeapTupleIsValid(reltup1))
elog(ERROR, "cache lookup failed for relation %u", r1);
relform1 = (Form_pg_class) GETSTRUCT(reltup1);

reltup2 = SearchSysCacheCopy(RELOID,
ObjectIdGetDatum(r2),
0, 0, 0);
reltup2 = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(r2));
if (!HeapTupleIsValid(reltup2))
elog(ERROR, "cache lookup failed for relation %u", r2);
relform2 = (Form_pg_class) GETSTRUCT(reltup2);
Expand Down
Loading

0 comments on commit 2493809

Please sign in to comment.