Merge remote-tracking branch 'piro/master'

This commit is contained in:
Daniele Varrazzo 2013-05-05 00:19:22 +01:00
commit 20dea46184
20 changed files with 707 additions and 273 deletions

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
# Global excludes across all subdirectories
*.o
*.so
bin/regression.diffs
bin/regression.out

View File

@ -2,7 +2,7 @@
"name": "pg_repack",
"abstract": "PostgreSQL module for data reorganization",
"description": "Reorganize tables in PostgreSQL databases with minimal locks",
"version": "1.2dev0",
"version": "1.2dev1",
"maintainer": [
"Josh Kupershmidt <schmiddy@gmail.com>",
"Daniele Varrazzo <daniele.varrazzo@gmail.com>"
@ -13,7 +13,7 @@
"provides": {
"pg_repack": {
"file": "lib/pg_repack.sql",
"version": "1.2dev0",
"version": "1.2dev1",
"abstract": "Reorganize tables in PostgreSQL databases with minimal locks"
}
},

View File

@ -6,22 +6,25 @@
# Portions Copyright (c) 2012, The Reorg Development Team
#
USE_PGXS = 1
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
SUBDIRS = bin lib
PG_CONFIG ?= pg_config
# Pull out the version number from pg_config
VERSION = $(shell $(PG_CONFIG) --version | awk '{print $$2}')
VERSION := $(shell $(PG_CONFIG) --version | awk '{print $$2}')
ifeq ("$(VERSION)","")
$(error pg_config not found)
endif
# version as a number, e.g. 9.1.4 -> 901
INTVERSION := $(shell echo $$(($$(echo $(VERSION) | sed -E 's/([0-9]+)\.([0-9]+).*/\1*100+\2/'))))
# We support PostgreSQL 8.3 and later.
ifneq ($(shell echo $(VERSION) | grep -E "^7\.|^8\.[012]"),)
ifeq ($(shell echo $$(($(INTVERSION) < 803))),1)
$(error pg_repack requires PostgreSQL 8.3 or later. This is $(VERSION))
endif
SUBDIRS = bin lib
all install installdirs uninstall distprep clean distclean maintainer-clean debug:
@for dir in $(SUBDIRS); do \
$(MAKE) -C $$dir $@ || exit; \

View File

@ -5,12 +5,40 @@
# Portions Copyright (c) 2011, Itagaki Takahiro
# Portions Copyright (c) 2012, The Reorg Development Team
#
PG_CONFIG ?= pg_config
# version as a number, e.g. 9.1.4 -> 901
VERSION := $(shell $(PG_CONFIG) --version | awk '{print $$2}')
INTVERSION := $(shell echo $$(($$(echo $(VERSION) | sed -E 's/([0-9]+)\.([0-9]+).*/\1*100+\2/'))))
SRCS = pg_repack.c pgut/pgut.c pgut/pgut-fe.c
OBJS = $(SRCS:.c=.o)
PROGRAM = pg_repack
REGRESS = init repack
EXTRA_CLEAN = sql/init-$(MAJORVERSION).sql sql/init.sql
#
# Test suite
#
ifeq ($(shell echo $$(($(INTVERSION) >= 901))),1)
REGRESS := init-extension
else
REGRESS := init-legacy
endif
# plpgsql not available by default on pg < 9.0
ifeq ($(shell echo $$(($(INTVERSION) < 900))),1)
REGRESS += plpgsql
endif
REGRESS += repack tablespace
# This test depends on collate, not supported before 9.1
ifeq ($(shell echo $$(($(INTVERSION) >= 901))),1)
REGRESS += issue3
endif
# The version number of the program. It should be the same of the library.
REPACK_VERSION = $(shell grep '"version":' ../META.json | head -1 \
@ -33,25 +61,3 @@ include $(PGXS)
LIBS := $(filter-out -lxml2, $(LIBS))
LIBS := $(filter-out -lxslt, $(LIBS))
ifndef MAJORVERSION
MAJORVERSION := $(basename $(VERSION))
endif
sql/init.sql: sql/init-$(MAJORVERSION).sql
cp sql/init-$(MAJORVERSION).sql sql/init.sql
expected/init.out: expected/init-$(MAJORVERSION).out
cp expected/init-$(MAJORVERSION).out expected/init.out
sql/init-8.3.sql:
cp sql/init-legacy.sql sql/init-8.3.sql
sql/init-8.4.sql:
cp sql/init-legacy.sql sql/init-8.4.sql
sql/init-9.0.sql:
cp sql/init-legacy.sql sql/init-9.0.sql
sql/init-9.1.sql:
cp sql/init-extension.sql sql/init-9.1.sql
sql/init-9.2.sql:
cp sql/init-extension.sql sql/init-9.2.sql
sql/init-9.3.sql:
cp sql/init-extension.sql sql/init-9.3.sql
installcheck: sql/init.sql

View File

@ -0,0 +1,3 @@
SET client_min_messages = warning;
CREATE EXTENSION pg_repack;
RESET client_min_messages;

53
bin/expected/issue3.out Normal file
View File

@ -0,0 +1,53 @@
--
-- pg_repack issue #3
--
CREATE TABLE issue3_1 (col1 int NOT NULL, col2 text NOT NULL);
CREATE UNIQUE INDEX issue3_1_idx ON issue3_1 (col1, col2 DESC);
SELECT repack.get_order_by('issue3_1_idx'::regclass::oid, 'issue3_1'::regclass::oid);
get_order_by
-----------------
col1, col2 DESC
(1 row)
\! pg_repack --dbname=contrib_regression --table=issue3_1
INFO: repacking table "issue3_1"
CREATE TABLE issue3_2 (col1 int NOT NULL, col2 text NOT NULL);
CREATE UNIQUE INDEX issue3_2_idx ON issue3_2 (col1 DESC, col2 text_pattern_ops);
SELECT repack.get_order_by('issue3_2_idx'::regclass::oid, 'issue3_2'::regclass::oid);
get_order_by
---------------------------
col1 DESC, col2 USING ~<~
(1 row)
\! pg_repack --dbname=contrib_regression --table=issue3_2
INFO: repacking table "issue3_2"
CREATE TABLE issue3_3 (col1 int NOT NULL, col2 text NOT NULL);
CREATE UNIQUE INDEX issue3_3_idx ON issue3_3 (col1 DESC, col2 DESC);
SELECT repack.get_order_by('issue3_3_idx'::regclass::oid, 'issue3_3'::regclass::oid);
get_order_by
----------------------
col1 DESC, col2 DESC
(1 row)
\! pg_repack --dbname=contrib_regression --table=issue3_3
INFO: repacking table "issue3_3"
CREATE TABLE issue3_4 (col1 int NOT NULL, col2 text NOT NULL);
CREATE UNIQUE INDEX issue3_4_idx ON issue3_4 (col1 NULLS FIRST, col2 text_pattern_ops DESC NULLS LAST);
SELECT repack.get_order_by('issue3_4_idx'::regclass::oid, 'issue3_4'::regclass::oid);
get_order_by
--------------------------------------------------
col1 NULLS FIRST, col2 DESC USING ~<~ NULLS LAST
(1 row)
\! pg_repack --dbname=contrib_regression --table=issue3_4
INFO: repacking table "issue3_4"
CREATE TABLE issue3_5 (col1 int NOT NULL, col2 text NOT NULL);
CREATE UNIQUE INDEX issue3_5_idx ON issue3_5 (col1 DESC NULLS FIRST, col2 COLLATE "POSIX" DESC);
SELECT repack.get_order_by('issue3_5_idx'::regclass::oid, 'issue3_5'::regclass::oid);
get_order_by
--------------------------------------
col1 DESC, col2 COLLATE "POSIX" DESC
(1 row)
\! pg_repack --dbname=contrib_regression --table=issue3_5
INFO: repacking table "issue3_5"

1
bin/expected/plpgsql.out Normal file
View File

@ -0,0 +1 @@
CREATE LANGUAGE plpgsql;

View File

@ -114,10 +114,20 @@ SELECT * FROM tbl_with_dropped_toast;
--
-- do repack
--
\! pg_repack --dbname=contrib_regression --no-order
\! pg_repack --dbname=contrib_regression --table=tbl_cluster
INFO: repacking table "tbl_cluster"
\! pg_repack --dbname=contrib_regression --table=tbl_badindex
INFO: repacking table "tbl_badindex"
WARNING: skipping invalid index: CREATE UNIQUE INDEX idx_badindex_n ON tbl_badindex USING btree (n)
\! pg_repack --dbname=contrib_regression
\! pg_repack --dbname=contrib_regression --table=tbl_cluster
INFO: repacking table "tbl_cluster"
INFO: repacking table "tbl_only_pkey"
INFO: repacking table "tbl_gistkey"
INFO: repacking table "tbl_with_dropped_column"
INFO: repacking table "tbl_with_dropped_toast"
INFO: repacking table "tbl_badindex"
WARNING: skipping invalid index: CREATE UNIQUE INDEX idx_badindex_n ON tbl_badindex USING btree (n)
INFO: repacking table "tbl_idxopts"
--
-- after
--
@ -301,64 +311,44 @@ CREATE TABLE tbl_nn_uk (col1 int NOT NULL, col2 int NOT NULL, UNIQUE(col1, col2)
CREATE TABLE tbl_pk_uk (col1 int NOT NULL, col2 int NOT NULL, PRIMARY KEY(col1, col2), UNIQUE(col2, col1));
CREATE TABLE tbl_nn_puk (col1 int NOT NULL, col2 int NOT NULL);
CREATE UNIQUE INDEX tbl_nn_puk_pcol1_idx ON tbl_nn_puk(col1) WHERE col1 < 10;
\! pg_repack --dbname=contrib_regression --no-order --table=tbl_nn
\! pg_repack --dbname=contrib_regression --table=tbl_nn
WARNING: relation "tbl_nn" must have a primary key or not-null unique keys
-- => WARNING
\! pg_repack --dbname=contrib_regression --no-order --table=tbl_uk
\! pg_repack --dbname=contrib_regression --table=tbl_uk
WARNING: relation "tbl_uk" must have a primary key or not-null unique keys
-- => WARNING
\! pg_repack --dbname=contrib_regression --no-order --table=tbl_nn_uk
\! pg_repack --dbname=contrib_regression --table=tbl_nn_uk
INFO: repacking table "tbl_nn_uk"
-- => OK
\! pg_repack --dbname=contrib_regression --no-order --table=tbl_pk_uk
\! pg_repack --dbname=contrib_regression --table=tbl_pk_uk
INFO: repacking table "tbl_pk_uk"
-- => OK
\! pg_repack --dbname=contrib_regression --no-order --table=tbl_nn_puk
\! pg_repack --dbname=contrib_regression --table=tbl_nn_puk
WARNING: relation "tbl_nn_puk" must have a primary key or not-null unique keys
-- => WARNING
--
-- pg_repack issue #3
-- Triggers handling
--
CREATE TABLE issue3_1 (col1 int NOT NULL, col2 text NOT NULL);
CREATE UNIQUE INDEX issue3_1_idx ON issue3_1 (col1, col2 DESC);
SELECT repack.get_order_by('issue3_1_idx'::regclass::oid, 'issue3_1'::regclass::oid);
get_order_by
-----------------
col1, col2 DESC
(1 row)
\! pg_repack --dbname=contrib_regression --no-order --table=issue3_1
CREATE TABLE issue3_2 (col1 int NOT NULL, col2 text NOT NULL);
CREATE UNIQUE INDEX issue3_2_idx ON issue3_2 (col1 DESC, col2 text_pattern_ops);
SELECT repack.get_order_by('issue3_2_idx'::regclass::oid, 'issue3_2'::regclass::oid);
get_order_by
---------------------------
col1 DESC, col2 USING ~<~
(1 row)
\! pg_repack --dbname=contrib_regression --no-order --table=issue3_2
CREATE TABLE issue3_3 (col1 int NOT NULL, col2 text NOT NULL);
CREATE UNIQUE INDEX issue3_3_idx ON issue3_3 (col1 DESC, col2 DESC);
SELECT repack.get_order_by('issue3_3_idx'::regclass::oid, 'issue3_3'::regclass::oid);
get_order_by
----------------------
col1 DESC, col2 DESC
(1 row)
\! pg_repack --dbname=contrib_regression --no-order --table=issue3_3
CREATE TABLE issue3_4 (col1 int NOT NULL, col2 text NOT NULL);
CREATE UNIQUE INDEX issue3_4_idx ON issue3_4 (col1 NULLS FIRST, col2 text_pattern_ops DESC NULLS LAST);
SELECT repack.get_order_by('issue3_4_idx'::regclass::oid, 'issue3_4'::regclass::oid);
get_order_by
--------------------------------------------------
col1 NULLS FIRST, col2 DESC USING ~<~ NULLS LAST
(1 row)
\! pg_repack --dbname=contrib_regression --no-order --table=issue3_4
CREATE TABLE issue3_5 (col1 int NOT NULL, col2 text NOT NULL);
CREATE UNIQUE INDEX issue3_5_idx ON issue3_5 (col1 DESC NULLS FIRST, col2 COLLATE "POSIX" DESC);
SELECT repack.get_order_by('issue3_5_idx'::regclass::oid, 'issue3_5'::regclass::oid);
get_order_by
--------------------------------------
col1 DESC, col2 COLLATE "POSIX" DESC
(1 row)
\! pg_repack --dbname=contrib_regression --no-order --table=issue3_5
CREATE FUNCTION trgtest() RETURNS trigger AS
$$BEGIN RETURN NEW; END$$
LANGUAGE plpgsql;
CREATE TABLE trg1 (id integer PRIMARY KEY);
CREATE TRIGGER z_repack_triggeq BEFORE UPDATE ON trg1 FOR EACH ROW EXECUTE PROCEDURE trgtest();
\! pg_repack --dbname=contrib_regression --table=trg1
INFO: repacking table "trg1"
CREATE TABLE trg2 (id integer PRIMARY KEY);
CREATE TRIGGER z_repack_trigger BEFORE UPDATE ON trg2 FOR EACH ROW EXECUTE PROCEDURE trgtest();
\! pg_repack --dbname=contrib_regression --table=trg2
INFO: repacking table "trg2"
WARNING: the table "trg2" has already a trigger called "z_repack_trigger"
DETAIL: The trigger was probably installed during a previous 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 to remove all the temporary objects left over.
CREATE TABLE trg3 (id integer PRIMARY KEY);
CREATE TRIGGER z_repack_trigges BEFORE UPDATE ON trg3 FOR EACH ROW EXECUTE PROCEDURE trgtest();
\! pg_repack --dbname=contrib_regression --table=trg3
INFO: repacking table "trg3"
WARNING: trigger "z_repack_trigges" conflicting on table "trg3"
DETAIL: The trigger "z_repack_trigger" must be the last of the BEFORE triggers to fire on the table (triggers fire in alphabetical order). Please rename the trigger so that it sorts before "z_repack_trigger": you can use "ALTER TRIGGER z_repack_trigges ON trg3 RENAME TO newname".
CREATE TABLE trg4 (id integer PRIMARY KEY);
CREATE TRIGGER zzzzzz AFTER UPDATE ON trg4 FOR EACH ROW EXECUTE PROCEDURE trgtest();
\! pg_repack --dbname=contrib_regression --table=trg4
INFO: repacking table "trg4"

110
bin/expected/tablespace.out Normal file
View File

@ -0,0 +1,110 @@
SET client_min_messages = warning;
--
-- Tablespace features tests
--
-- Note: in order to pass this test you must create a tablespace called 'testts'
--
SELECT spcname FROM pg_tablespace WHERE spcname = 'testts';
spcname
---------
testts
(1 row)
-- If the query above failed you must create the 'testts' tablespace;
CREATE TABLE testts1 (id serial primary key, data text);
CREATE INDEX testts1_partial_idx on testts1 (id) where (id > 0);
CREATE INDEX testts1_with_idx on testts1 (id) with (fillfactor=80);
INSERT INTO testts1 (data) values ('a');
INSERT INTO testts1 (data) values ('b');
INSERT INTO testts1 (data) values ('c');
-- check the indexes definitions
SELECT regexp_replace(
repack.repack_indexdef(indexrelid, 'testts1'::regclass, NULL),
'_[0-9]+', '_OID', 'g')
FROM pg_index i join pg_class c ON c.oid = indexrelid
WHERE indrelid = 'testts1'::regclass ORDER BY relname;
regexp_replace
----------------------------------------------------------------------------------
CREATE INDEX index_OID ON repack.table_OID USING btree (id) WHERE (id > 0)
CREATE UNIQUE INDEX index_OID ON repack.table_OID USING btree (id)
CREATE INDEX index_OID ON repack.table_OID USING btree (id) WITH (fillfactor=80)
(3 rows)
SELECT regexp_replace(
repack.repack_indexdef(indexrelid, 'testts1'::regclass, 'foo'),
'_[0-9]+', '_OID', 'g')
FROM pg_index i join pg_class c ON c.oid = indexrelid
WHERE indrelid = 'testts1'::regclass ORDER BY relname;
regexp_replace
-------------------------------------------------------------------------------------------------
CREATE INDEX index_OID ON repack.table_OID USING btree (id) TABLESPACE foo WHERE (id > 0)
CREATE UNIQUE INDEX index_OID ON repack.table_OID USING btree (id) TABLESPACE foo
CREATE INDEX index_OID ON repack.table_OID USING btree (id) WITH (fillfactor=80) TABLESPACE foo
(3 rows)
-- can move the tablespace from default
\! pg_repack --dbname=contrib_regression --no-order --table=testts1 --tablespace testts
INFO: repacking table "testts1"
SELECT relname, spcname
FROM pg_class JOIN pg_tablespace ts ON ts.oid = reltablespace
WHERE relname ~ '^testts1'
ORDER BY relname;
relname | spcname
---------+---------
testts1 | testts
(1 row)
SELECT * from testts1 order by id;
id | data
----+------
1 | a
2 | b
3 | c
(3 rows)
-- tablespace stays where it is
\! pg_repack --dbname=contrib_regression --no-order --table=testts1
INFO: repacking table "testts1"
SELECT relname, spcname
FROM pg_class JOIN pg_tablespace ts ON ts.oid = reltablespace
WHERE relname ~ '^testts1'
ORDER BY relname;
relname | spcname
---------+---------
testts1 | testts
(1 row)
-- can move the ts back to default
\! pg_repack --dbname=contrib_regression --no-order --table=testts1 -s pg_default
INFO: repacking table "testts1"
SELECT relname, spcname
FROM pg_class JOIN pg_tablespace ts ON ts.oid = reltablespace
WHERE relname ~ '^testts1'
ORDER BY relname;
relname | spcname
---------+---------
(0 rows)
-- can move the table together with the indexes
\! pg_repack --dbname=contrib_regression --no-order --table=testts1 --tablespace testts --moveidx
INFO: repacking table "testts1"
SELECT relname, spcname
FROM pg_class JOIN pg_tablespace ts ON ts.oid = reltablespace
WHERE relname ~ '^testts1'
ORDER BY relname;
relname | spcname
---------------------+---------
testts1 | testts
testts1_partial_idx | testts
testts1_pkey | testts
testts1_with_idx | testts
(4 rows)
-- can't specify --moveidx without --tablespace
\! pg_repack --dbname=contrib_regression --no-order --table=testts1 --moveidx
ERROR: cannot specify --moveidx (-S) without --tablespace (-s)
\! pg_repack --dbname=contrib_regression --no-order --table=testts1 -S
ERROR: cannot specify --moveidx (-S) without --tablespace (-s)
-- not broken with order
\! pg_repack --dbname=contrib_regression -o id --table=testts1 --tablespace pg_default --moveidx
INFO: repacking table "testts1"

View File

@ -93,7 +93,7 @@ const char *PROGRAM_VERSION = "unknown";
"SELECT repack.array_accum(virtualtransaction) FROM pg_locks" \
" WHERE locktype = 'virtualxid' AND pid NOT IN (pg_backend_pid(), $1)" \
" AND (virtualxid, virtualtransaction) <> ('1/1', '-1/0') " \
" AND ($2 IS NOT NULL)"
" AND ($2::text IS NOT NULL)"
#define SQL_XID_SNAPSHOT \
(PQserverVersion(connection) >= 90200 ? SQL_XID_SNAPSHOT_90200 : \
@ -172,6 +172,7 @@ typedef struct repack_index
} repack_index;
static bool is_superuser(void);
static void check_tablespace(void);
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(const repack_table *table, const char *order_by);
@ -185,6 +186,7 @@ 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_QUERY_CANCELED "57014"
static bool sqlstate_equals(PGresult *res, const char *state)
@ -197,6 +199,8 @@ static bool alldb = false;
static bool noorder = false;
static SimpleStringList table_list = {NULL, NULL};
static char *orderby = NULL;
static char *tablespace = NULL;
static bool moveidx = false;
static int wait_timeout = 60; /* in seconds */
static int jobs = 0; /* number of concurrent worker conns. */
@ -214,6 +218,8 @@ static pgut_option options[] =
{ 'l', 't', "table", &table_list },
{ 'b', 'n', "no-order", &noorder },
{ 's', 'o', "order-by", &orderby },
{ 's', 's', "tablespace", &tablespace },
{ 'b', 'S', "moveidx", &moveidx },
{ 'i', 'T', "wait-timeout", &wait_timeout },
{ 'B', 'Z', "no-analyze", &analyze },
{ 'i', 'j', "jobs", &jobs },
@ -234,6 +240,8 @@ main(int argc, char *argv[])
(errcode(EINVAL),
errmsg("too many arguments")));
check_tablespace();
if (noorder)
orderby = "";
@ -281,6 +289,56 @@ is_superuser(void)
return false;
}
/*
* Check if the tablespace requested exists.
*
* Raise an exception on error.
*/
void
check_tablespace()
{
PGresult *res = NULL;
const char *params[1];
if (tablespace == NULL)
{
/* nothing to check, but let's see the options */
if (moveidx)
{
ereport(ERROR,
(errcode(EINVAL),
errmsg("cannot specify --moveidx (-S) without --tablespace (-s)")));
}
return;
}
/* check if the tablespace exists */
reconnect(ERROR);
params[0] = tablespace;
res = execute_elevel(
"select spcname from pg_tablespace where spcname = $1",
1, params, DEBUG2);
if (PQresultStatus(res) == PGRES_TUPLES_OK)
{
if (PQntuples(res) == 0)
{
ereport(ERROR,
(errcode(EINVAL),
errmsg("the tablespace \"%s\" doesn't exist", tablespace)));
}
}
else
{
ereport(ERROR,
(errcode(EINVAL),
errmsg("error checking the namespace: %s",
PQerrorMessage(connection))));
}
CLEARPGRES(res);
}
/*
* Call repack_one_database for each database.
@ -307,22 +365,10 @@ repack_all_databases(const char *orderby)
dbname = PQgetvalue(result, i, 0);
if (pgut_log_level >= INFO)
{
printf("%s: repack database \"%s\"\n", PROGRAM_NAME, dbname);
fflush(stdout);
}
elog(INFO, "repacking database \"%s\"", dbname);
ret = repack_one_database(orderby, errbuf, sizeof(errbuf));
if (pgut_log_level >= INFO)
{
if (ret)
printf("\n");
else
printf(" ... skipped: %s\n", errbuf);
fflush(stdout);
}
if (!ret)
elog(INFO, "database \"%s\" skipped: %s", dbname, errbuf);
}
CLEARPGRES(result);
@ -360,9 +406,14 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize)
StringInfoData sql;
SimpleStringListCell *cell;
const char **params = NULL;
size_t num_params = simple_string_list_size(table_list);
int iparam = 0;
size_t num_tables;
size_t num_params;
if (num_params)
num_tables = simple_string_list_size(table_list);
/* 1st param is the user-specified tablespace */
num_params = num_tables + 1;
params = pgut_malloc(num_params * sizeof(char *));
initStringInfo(&sql);
@ -415,12 +466,16 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize)
}
else
{
if (sqlstate_equals(res, SQLSTATE_INVALID_SCHEMA_NAME))
if (sqlstate_equals(res, SQLSTATE_INVALID_SCHEMA_NAME)
|| sqlstate_equals(res, SQLSTATE_UNDEFINED_FUNCTION))
{
/* Schema repack does not exist. Skip the database. */
/* Schema repack does not exist, or version too old (version
* functions not found). Skip the database.
*/
if (errbuf)
snprintf(errbuf, errsize,
"%s is not installed in the database", PROGRAM_NAME);
"%s %s is not installed in the database",
PROGRAM_NAME, PROGRAM_VERSION);
}
else
{
@ -442,45 +497,50 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize)
command("SET client_min_messages = warning", 0, NULL);
/* acquire target tables */
appendStringInfoString(&sql, "SELECT * FROM repack.tables WHERE ");
if (num_params)
appendStringInfoString(&sql,
"SELECT t.*,"
" coalesce(v.tablespace, t.tablespace_orig) as tablespace_dest"
" FROM repack.tables t, "
" (VALUES (quote_ident($1::text))) as v (tablespace)"
" WHERE ");
params[iparam++] = tablespace;
if (num_tables)
{
appendStringInfoString(&sql, "(");
for (i = 0, cell = table_list.head; cell; cell = cell->next, i++)
for (cell = table_list.head; cell; cell = cell->next)
{
/* Construct table name placeholders to be used by PQexecParams */
appendStringInfo(&sql, "relid = $%d::regclass", i + 1);
params[i] = cell->val;
appendStringInfo(&sql, "relid = $%d::regclass", iparam + 1);
params[iparam++] = cell->val;
if (cell->next)
appendStringInfoString(&sql, " OR ");
}
appendStringInfoString(&sql, ")");
res = execute_elevel(sql.data, (int) num_params, params, DEBUG2);
}
else
{
appendStringInfoString(&sql, "pkid IS NOT NULL");
if (!orderby)
appendStringInfoString(&sql, " AND ckid IS NOT NULL");
res = execute_elevel(sql.data, 0, NULL, DEBUG2);
}
/* double check the parameters array is sane */
if (iparam != num_params)
{
if (errbuf)
snprintf(errbuf, errsize,
"internal error: bad parameters count: %i instead of %zi",
iparam, num_params);
goto cleanup;
}
res = execute_elevel(sql.data, (int) num_params, params, DEBUG2);
/* on error skip the database */
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
if (sqlstate_equals(res, SQLSTATE_INVALID_SCHEMA_NAME))
{
/* Schema repack does not exist. Skip the database. */
if (errbuf)
snprintf(errbuf, errsize,
"%s is not installed in the database", PROGRAM_NAME);
}
else
{
/* Return the error message otherwise */
if (errbuf)
snprintf(errbuf, errsize, "%s", PQerrorMessage(connection));
}
goto cleanup;
}
@ -489,7 +549,9 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize)
for (i = 0; i < num; i++)
{
repack_table table;
const char *create_table;
const char *create_table_1;
const char *create_table_2;
const char *tablespace;
const char *ckey;
int c = 0;
@ -512,43 +574,51 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize)
table.create_trigger = getstr(res, i, c++);
table.enable_trigger = getstr(res, i, c++);
create_table = getstr(res, i, c++);
create_table_1 = getstr(res, i, c++);
tablespace = getstr(res, i, c++); /* to be clobbered */
create_table_2 = getstr(res, i, c++);
table.drop_columns = getstr(res, i, c++);
table.delete_log = getstr(res, i, c++);
table.lock_table = getstr(res, i, c++);
ckey = getstr(res, i, c++);
resetStringInfo(&sql);
if (!orderby)
{
/* CLUSTER mode */
if (ckey == NULL)
{
ereport(WARNING,
(errcode(E_PG_COMMAND),
errmsg("relation \"%s\" has no cluster key", table.target_name)));
continue;
}
appendStringInfo(&sql, "%s ORDER BY %s", create_table, ckey);
table.create_table = sql.data;
}
else if (!orderby[0])
{
/* VACUUM FULL mode */
table.create_table = create_table;
}
else
{
/* User specified ORDER BY */
appendStringInfo(&sql, "%s ORDER BY %s", create_table, orderby);
table.create_table = sql.data;
}
table.sql_peek = getstr(res, i, c++);
table.sql_insert = getstr(res, i, c++);
table.sql_delete = getstr(res, i, c++);
table.sql_update = getstr(res, i, c++);
table.sql_pop = getstr(res, i, c++);
tablespace = getstr(res, i, c++);
resetStringInfo(&sql);
appendStringInfoString(&sql, create_table_1);
appendStringInfoString(&sql, tablespace);
appendStringInfoString(&sql, create_table_2);
if (!orderby)
{
if (ckey != NULL)
{
/* CLUSTER mode */
appendStringInfoString(&sql, " ORDER BY ");
appendStringInfoString(&sql, ckey);
table.create_table = sql.data;
}
else
{
/* VACUUM FULL mode (non-clustered tables) */
table.create_table = sql.data;
}
}
else if (!orderby[0])
{
/* VACUUM FULL mode (for clustered tables too) */
table.create_table = sql.data;
}
else
{
/* User specified ORDER BY */
appendStringInfoString(&sql, " ORDER BY ");
appendStringInfoString(&sql, orderby);
table.create_table = sql.data;
}
repack_one_table(&table, orderby);
}
@ -593,7 +663,7 @@ static bool
rebuild_indexes(const repack_table *table)
{
PGresult *res;
const char *params[1];
const char *params[2];
int num_indexes;
int i;
int num_active_workers;
@ -605,6 +675,7 @@ rebuild_indexes(const repack_table *table)
elog(DEBUG2, "---- create indexes ----");
params[0] = utoa(table->target_oid, buffer);
params[1] = moveidx ? tablespace : NULL;
/* First, just display a warning message for any invalid indexes
* which may be on the table (mostly to match the behavior of 1.1.8).
@ -620,8 +691,9 @@ rebuild_indexes(const repack_table *table)
}
res = execute("SELECT indexrelid,"
" repack.repack_indexdef(indexrelid, indrelid) "
" FROM pg_index WHERE indrelid = $1 AND indisvalid", 1, params);
" repack.repack_indexdef(indexrelid, indrelid, $2) "
" FROM pg_index WHERE indrelid = $1 AND indisvalid",
2, params);
num_indexes = PQntuples(res);
@ -845,6 +917,8 @@ repack_one_table(const repack_table *table, const char *orderby)
initStringInfo(&sql);
elog(INFO, "repacking table \"%s\"", table->target_name);
elog(DEBUG2, "---- repack_one_table ----");
elog(DEBUG2, "target_name : %s", table->target_name);
elog(DEBUG2, "target_oid : %u", table->target_oid);
@ -885,11 +959,36 @@ repack_one_table(const repack_table *table, const char *orderby)
res = execute("SELECT repack.conflicted_triggers($1)", 1, params);
if (PQntuples(res) > 0)
{
if (0 == strcmp("z_repack_trigger", PQgetvalue(res, 0, 0)))
{
ereport(WARNING,
(errcode(E_PG_COMMAND),
errmsg("trigger %s conflicted for %s",
errmsg("the table \"%s\" has already a trigger called \"%s\"",
table->target_name, PQgetvalue(res, 0, 0)),
errdetail(
"The trigger was probably installed during a previous"
" 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"
" to remove all the temporary objects left over.")));
}
else
{
ereport(WARNING,
(errcode(E_PG_COMMAND),
errmsg("trigger \"%s\" conflicting on table \"%s\"",
PQgetvalue(res, 0, 0), table->target_name),
errdetail(
"The trigger \"z_repack_trigger\" must be the last of the"
" BEFORE triggers to fire on the table (triggers fire in"
" alphabetical order). Please rename the trigger so that"
" it sorts before \"z_repack_trigger\": you can use"
" \"ALTER TRIGGER %s ON %s RENAME TO newname\".",
PQgetvalue(res, 0, 0), table->target_name)));
}
have_error = true;
goto cleanup;
}
@ -914,12 +1013,6 @@ repack_one_table(const repack_table *table, const char *orderby)
* pg_locks momentarily.
*/
res = pgut_execute(conn2, "SELECT pg_backend_pid()", 0, NULL);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
printf("%s", PQerrorMessage(conn2));
have_error = true;
goto cleanup;
}
buffer[0] = '\0';
strncat(buffer, PQgetvalue(res, 0, 0), sizeof(buffer) - 1);
CLEARPGRES(res);
@ -1161,6 +1254,10 @@ cleanup:
if (vxid)
free(vxid);
/* Rollback current transactions */
pgut_rollback(connection);
pgut_rollback(conn2);
/* XXX: distinguish between fatal and non-fatal errors via the first
* arg to repack_cleanup().
*/
@ -1399,10 +1496,6 @@ repack_cleanup(bool fatal, const repack_table *table)
char buffer[12];
const char *params[1];
/* Rollback current transactions */
pgut_rollback(connection);
pgut_rollback(conn2);
/* Try reconnection if not available. */
if (PQstatus(connection) != CONNECTION_OK ||
PQstatus(conn2) != CONNECTION_OK)
@ -1426,10 +1519,12 @@ pgut_help(bool details)
printf("Options:\n");
printf(" -a, --all repack all databases\n");
printf(" -j --jobs Use this many parallel jobs for each table\n");
printf(" -n, --no-order do vacuum full instead of cluster\n");
printf(" -o, --order-by=COLUMNS order by columns instead of cluster keys\n");
printf(" -t, --table=TABLE repack specific table only\n");
printf(" -s, --tablespace=TBLSPC move repacked tables to a new tablespace\n");
printf(" -S, --moveidx move repacked indexes to TBLSPC too\n");
printf(" -o, --order-by=COLUMNS order by columns instead of cluster keys\n");
printf(" -n, --no-order do vacuum full instead of cluster\n");
printf(" -j --jobs Use this many parallel jobs for each table\n");
printf(" -T, --wait-timeout=SECS timeout to cancel other backends on conflict\n");
printf(" -Z, --no-analyze don't analyze at end\n");
}

View File

@ -1,5 +1,3 @@
SET client_min_messages = warning;
\set ECHO none
CREATE EXTENSION pg_repack;
\set ECHO all
RESET client_min_messages;

27
bin/sql/issue3.sql Normal file
View File

@ -0,0 +1,27 @@
--
-- pg_repack issue #3
--
CREATE TABLE issue3_1 (col1 int NOT NULL, col2 text NOT NULL);
CREATE UNIQUE INDEX issue3_1_idx ON issue3_1 (col1, col2 DESC);
SELECT repack.get_order_by('issue3_1_idx'::regclass::oid, 'issue3_1'::regclass::oid);
\! pg_repack --dbname=contrib_regression --table=issue3_1
CREATE TABLE issue3_2 (col1 int NOT NULL, col2 text NOT NULL);
CREATE UNIQUE INDEX issue3_2_idx ON issue3_2 (col1 DESC, col2 text_pattern_ops);
SELECT repack.get_order_by('issue3_2_idx'::regclass::oid, 'issue3_2'::regclass::oid);
\! pg_repack --dbname=contrib_regression --table=issue3_2
CREATE TABLE issue3_3 (col1 int NOT NULL, col2 text NOT NULL);
CREATE UNIQUE INDEX issue3_3_idx ON issue3_3 (col1 DESC, col2 DESC);
SELECT repack.get_order_by('issue3_3_idx'::regclass::oid, 'issue3_3'::regclass::oid);
\! pg_repack --dbname=contrib_regression --table=issue3_3
CREATE TABLE issue3_4 (col1 int NOT NULL, col2 text NOT NULL);
CREATE UNIQUE INDEX issue3_4_idx ON issue3_4 (col1 NULLS FIRST, col2 text_pattern_ops DESC NULLS LAST);
SELECT repack.get_order_by('issue3_4_idx'::regclass::oid, 'issue3_4'::regclass::oid);
\! pg_repack --dbname=contrib_regression --table=issue3_4
CREATE TABLE issue3_5 (col1 int NOT NULL, col2 text NOT NULL);
CREATE UNIQUE INDEX issue3_5_idx ON issue3_5 (col1 DESC NULLS FIRST, col2 COLLATE "POSIX" DESC);
SELECT repack.get_order_by('issue3_5_idx'::regclass::oid, 'issue3_5'::regclass::oid);
\! pg_repack --dbname=contrib_regression --table=issue3_5

1
bin/sql/plpgsql.sql Normal file
View File

@ -0,0 +1 @@
CREATE LANGUAGE plpgsql;

View File

@ -120,9 +120,9 @@ SELECT * FROM tbl_with_dropped_toast;
-- do repack
--
\! pg_repack --dbname=contrib_regression --no-order
\! pg_repack --dbname=contrib_regression
\! pg_repack --dbname=contrib_regression --table=tbl_cluster
\! pg_repack --dbname=contrib_regression --table=tbl_badindex
\! pg_repack --dbname=contrib_regression
--
-- after
@ -177,41 +177,32 @@ CREATE TABLE tbl_nn_uk (col1 int NOT NULL, col2 int NOT NULL, UNIQUE(col1, col2)
CREATE TABLE tbl_pk_uk (col1 int NOT NULL, col2 int NOT NULL, PRIMARY KEY(col1, col2), UNIQUE(col2, col1));
CREATE TABLE tbl_nn_puk (col1 int NOT NULL, col2 int NOT NULL);
CREATE UNIQUE INDEX tbl_nn_puk_pcol1_idx ON tbl_nn_puk(col1) WHERE col1 < 10;
\! pg_repack --dbname=contrib_regression --no-order --table=tbl_nn
\! pg_repack --dbname=contrib_regression --table=tbl_nn
-- => WARNING
\! pg_repack --dbname=contrib_regression --no-order --table=tbl_uk
\! pg_repack --dbname=contrib_regression --table=tbl_uk
-- => WARNING
\! pg_repack --dbname=contrib_regression --no-order --table=tbl_nn_uk
\! pg_repack --dbname=contrib_regression --table=tbl_nn_uk
-- => OK
\! pg_repack --dbname=contrib_regression --no-order --table=tbl_pk_uk
\! pg_repack --dbname=contrib_regression --table=tbl_pk_uk
-- => OK
\! pg_repack --dbname=contrib_regression --no-order --table=tbl_nn_puk
\! pg_repack --dbname=contrib_regression --table=tbl_nn_puk
-- => WARNING
--
-- pg_repack issue #3
-- Triggers handling
--
CREATE TABLE issue3_1 (col1 int NOT NULL, col2 text NOT NULL);
CREATE UNIQUE INDEX issue3_1_idx ON issue3_1 (col1, col2 DESC);
SELECT repack.get_order_by('issue3_1_idx'::regclass::oid, 'issue3_1'::regclass::oid);
\! pg_repack --dbname=contrib_regression --no-order --table=issue3_1
CREATE TABLE issue3_2 (col1 int NOT NULL, col2 text NOT NULL);
CREATE UNIQUE INDEX issue3_2_idx ON issue3_2 (col1 DESC, col2 text_pattern_ops);
SELECT repack.get_order_by('issue3_2_idx'::regclass::oid, 'issue3_2'::regclass::oid);
\! pg_repack --dbname=contrib_regression --no-order --table=issue3_2
CREATE TABLE issue3_3 (col1 int NOT NULL, col2 text NOT NULL);
CREATE UNIQUE INDEX issue3_3_idx ON issue3_3 (col1 DESC, col2 DESC);
SELECT repack.get_order_by('issue3_3_idx'::regclass::oid, 'issue3_3'::regclass::oid);
\! pg_repack --dbname=contrib_regression --no-order --table=issue3_3
CREATE TABLE issue3_4 (col1 int NOT NULL, col2 text NOT NULL);
CREATE UNIQUE INDEX issue3_4_idx ON issue3_4 (col1 NULLS FIRST, col2 text_pattern_ops DESC NULLS LAST);
SELECT repack.get_order_by('issue3_4_idx'::regclass::oid, 'issue3_4'::regclass::oid);
\! pg_repack --dbname=contrib_regression --no-order --table=issue3_4
CREATE TABLE issue3_5 (col1 int NOT NULL, col2 text NOT NULL);
CREATE UNIQUE INDEX issue3_5_idx ON issue3_5 (col1 DESC NULLS FIRST, col2 COLLATE "POSIX" DESC);
SELECT repack.get_order_by('issue3_5_idx'::regclass::oid, 'issue3_5'::regclass::oid);
\! pg_repack --dbname=contrib_regression --no-order --table=issue3_5
CREATE FUNCTION trgtest() RETURNS trigger AS
$$BEGIN RETURN NEW; END$$
LANGUAGE plpgsql;
CREATE TABLE trg1 (id integer PRIMARY KEY);
CREATE TRIGGER z_repack_triggeq BEFORE UPDATE ON trg1 FOR EACH ROW EXECUTE PROCEDURE trgtest();
\! pg_repack --dbname=contrib_regression --table=trg1
CREATE TABLE trg2 (id integer PRIMARY KEY);
CREATE TRIGGER z_repack_trigger BEFORE UPDATE ON trg2 FOR EACH ROW EXECUTE PROCEDURE trgtest();
\! pg_repack --dbname=contrib_regression --table=trg2
CREATE TABLE trg3 (id integer PRIMARY KEY);
CREATE TRIGGER z_repack_trigges BEFORE UPDATE ON trg3 FOR EACH ROW EXECUTE PROCEDURE trgtest();
\! pg_repack --dbname=contrib_regression --table=trg3
CREATE TABLE trg4 (id integer PRIMARY KEY);
CREATE TRIGGER zzzzzz AFTER UPDATE ON trg4 FOR EACH ROW EXECUTE PROCEDURE trgtest();
\! pg_repack --dbname=contrib_regression --table=trg4

71
bin/sql/tablespace.sql Normal file
View File

@ -0,0 +1,71 @@
SET client_min_messages = warning;
--
-- Tablespace features tests
--
-- Note: in order to pass this test you must create a tablespace called 'testts'
--
SELECT spcname FROM pg_tablespace WHERE spcname = 'testts';
-- If the query above failed you must create the 'testts' tablespace;
CREATE TABLE testts1 (id serial primary key, data text);
CREATE INDEX testts1_partial_idx on testts1 (id) where (id > 0);
CREATE INDEX testts1_with_idx on testts1 (id) with (fillfactor=80);
INSERT INTO testts1 (data) values ('a');
INSERT INTO testts1 (data) values ('b');
INSERT INTO testts1 (data) values ('c');
-- check the indexes definitions
SELECT regexp_replace(
repack.repack_indexdef(indexrelid, 'testts1'::regclass, NULL),
'_[0-9]+', '_OID', 'g')
FROM pg_index i join pg_class c ON c.oid = indexrelid
WHERE indrelid = 'testts1'::regclass ORDER BY relname;
SELECT regexp_replace(
repack.repack_indexdef(indexrelid, 'testts1'::regclass, 'foo'),
'_[0-9]+', '_OID', 'g')
FROM pg_index i join pg_class c ON c.oid = indexrelid
WHERE indrelid = 'testts1'::regclass ORDER BY relname;
-- can move the tablespace from default
\! pg_repack --dbname=contrib_regression --no-order --table=testts1 --tablespace testts
SELECT relname, spcname
FROM pg_class JOIN pg_tablespace ts ON ts.oid = reltablespace
WHERE relname ~ '^testts1'
ORDER BY relname;
SELECT * from testts1 order by id;
-- tablespace stays where it is
\! pg_repack --dbname=contrib_regression --no-order --table=testts1
SELECT relname, spcname
FROM pg_class JOIN pg_tablespace ts ON ts.oid = reltablespace
WHERE relname ~ '^testts1'
ORDER BY relname;
-- can move the ts back to default
\! pg_repack --dbname=contrib_regression --no-order --table=testts1 -s pg_default
SELECT relname, spcname
FROM pg_class JOIN pg_tablespace ts ON ts.oid = reltablespace
WHERE relname ~ '^testts1'
ORDER BY relname;
-- can move the table together with the indexes
\! pg_repack --dbname=contrib_regression --no-order --table=testts1 --tablespace testts --moveidx
SELECT relname, spcname
FROM pg_class JOIN pg_tablespace ts ON ts.oid = reltablespace
WHERE relname ~ '^testts1'
ORDER BY relname;
-- can't specify --moveidx without --tablespace
\! pg_repack --dbname=contrib_regression --no-order --table=testts1 --moveidx
\! pg_repack --dbname=contrib_regression --no-order --table=testts1 -S
-- not broken with order
\! pg_repack --dbname=contrib_regression -o id --table=testts1 --tablespace pg_default --moveidx

View File

@ -117,10 +117,12 @@ The following options can be specified in ``OPTIONS``.
Options:
-a, --all repack all databases
-j, --jobs Use this many parallel jobs for each table
-n, --no-order do vacuum full instead of cluster
-o, --order-by=COLUMNS order by columns instead of cluster keys
-t, --table=TABLE repack specific table only
-s, --tablespace=TBLSPC move repacked tables to a new tablespace
-S, --moveidx move repacked indexes to *TBLSPC* too
-o, --order-by=COLUMNS order by columns instead of cluster keys
-n, --no-order do vacuum full instead of cluster
-j, --jobs Use this many parallel jobs for each table
-T, --wait-timeout=SECS timeout to cancel other backends on conflict
-Z, --no-analyze don't analyze at end
@ -142,9 +144,20 @@ Generic options:
Reorg Options
^^^^^^^^^^^^^
Options to order rows. If not specified, pg_repack performs an online CLUSTER
using cluster indexes. Only one option can be specified. You may also specify
target tables or databases.
``-a``, ``--all``
Attempt repack all the databases of the cluster. Databases where the
``pg_repack`` extension is not installed will be skipped.
``-t TABLE``, ``--table=TABLE``
Reorganize the specified table only. By default, all eligible tables in
the target databases are reorganized.
``-o COLUMNS [,...]``, ``--order-by=COLUMNS [,...]``
Perform an online CLUSTER ordered by the specified columns.
``-n``, ``--no-order``
Perform an online VACUUM FULL. Since version 1.2 this is the default for
non-clustered tables.
``-j``, ``--jobs``
Create the specified number of extra connections to PostgreSQL, and
@ -152,15 +165,14 @@ target tables or databases.
on each table. If your PostgreSQL server has extra cores and disk
I/O available, this can be a useful way to speed up pg_repack.
``-n``, ``--no-order``
Perform an online VACUUM FULL.
``-s TBLSPC``, ``--tablespace=TBLSPC``
Move the repacked tables to the specified tablespace: essentially an
online version of ``ALTER TABLE ... SET TABLESPACE``. The tables indexes
are left on the original tablespace unless ``--moveidx`` is specified too.
``-o COLUMNS [,...]``, ``--order-by=COLUMNS [,...]``
Perform an online CLUSTER ordered by the specified columns.
``-t TABLE``, ``--table=TABLE``
Reorganize the specified table only. By default, all eligible tables in
the target databases are reorganized.
``-S``, ``--moveidx``
Move the indexes too of the repacked tables to the tablespace specified
by the option ``--tablespace``.
``-T SECS``, ``--wait-timeout=SECS``
pg_repack needs to take an exclusive lock at the end of the
@ -175,6 +187,7 @@ target tables or databases.
Disable ANALYZE after the reorganization. If not specified, run ANALYZE
after the reorganization.
Connection Options
^^^^^^^^^^^^^^^^^^
@ -253,13 +266,13 @@ Environment
Examples
--------
Execute the following command to perform an online CLUSTER of all tables in
database ``test``::
Perform an online CLUSTER of all the clustered tables in the database
``test``, and perform an online VACUUM FULL of all the non-clustered tables::
$ pg_repack test
Execute the following command to perform an online VACUUM FULL of table
``foo`` in database ``test``::
Perform an online VACUUM FULL on the table ``foo`` in the database ``test``
(an eventual cluster index is ignored)::
$ pg_repack --no-order --table foo -d test
@ -280,13 +293,13 @@ database where the error occured and then load
.. class:: diag
pg_repack: repack database "template1" ... skipped: pg_repack is not installed in the database
pg_repack is not installed in the database when ``--all`` option is
INFO: database "db" skipped: pg_repack VER is not installed in the database
pg_repack is not installed in the database when the ``--all`` option is
specified.
Create the pg_repack extension in the database.
ERROR: pg_repack is not installed
ERROR: pg_repack VER is not installed in the database
pg_repack is not installed in the database specified by ``--dbname``.
Create the pg_repack extension in the database.
@ -313,37 +326,28 @@ ERROR: relation "table" must have a primary key or not-null unique keys
Define a PRIMARY KEY or a UNIQUE constraint on the table.
ERROR: relation "table" has no cluster key
The target table doesn't have CLUSTER KEY.
Define a CLUSTER KEY on the table, via ALTER TABLE CLUSTER ON, or use
one of the --no-order or --order-by modes.
pg_repack: query failed: ERROR: column "col" does not exist
ERROR: query failed: ERROR: column "col" does not exist
The target table doesn't have columns specified by ``--order-by`` option.
Specify existing columns.
ERROR: permission denied for schema repack
Permission error.
pg_repack must be executed by a superuser.
pg_repack: query failed: ERROR: trigger "z_repack_trigger" for relation "tbl" already exists
The target table has already a trigger named ``z_repack_trigger``. This
is probably caused by a previous failed attempt to run pg_repack on the
table, which for some reason failed to clean up the temporary object.
WARNING: the table "tbl" has already a trigger called z_repack_trigger
The trigger was probably installed during a previous attempt to run
pg_repack on the table which was interrupted and for some reason failed
to clean up the temporary objects.
You can remove all the temporary objects by dropping and re-creating the
extension: see the installation_ section for the details.
pg_repack: trigger conflicted for tbl
WARNING: trigger "trg" conflicting on table "tbl"
The target table has a trigger whose name follows ``z_repack_trigger``
in alphabetical order.
The ``z_repack_trigger`` should be the last BEFORE trigger to fire.
Please rename your trigger to that it sorts alphabetically before
pg_repack's one.
pg_repack's one; you can use::
ALTER TRIGGER zzz_my_trigger ON sometable RENAME TO yyy_my_trigger;
Restrictions
@ -399,6 +403,17 @@ and the original one.
Releases
--------
* pg_repack 1.2
* Added ``--tablespace`` and ``--moveidx`` options to perform online
SET TABLESPACE.
* Added ``--jobs`` option for parallel operation.
* Don't require ``--no-order`` to perform a VACUUM FULL on non-clustered
tables (pg_repack issue #6).
* Bugfix: correctly handle key indexes with options such as DESC, NULL
FIRST/LAST, COLLATE (pg_repack issue #3).
* More helpful program output and error messages.
* pg_repack 1.1.8
* Added support for PostgreSQL 9.2.

View File

@ -6,7 +6,11 @@
# Portions Copyright (c) 2012, The Reorg Development Team
#
PG_CONFIG = pg_config
PG_CONFIG ?= pg_config
# version as a number, e.g. 9.1.4 -> 901
VERSION := $(shell $(PG_CONFIG) --version | awk '{print $$2}')
INTVERSION := $(shell echo $$(($$(echo $(VERSION) | sed -E 's/([0-9]+)\.([0-9]+).*/\1*100+\2/'))))
EXTENSION = pg_repack
MODULE_big = $(EXTENSION)
@ -20,10 +24,7 @@ REPACK_VERSION = $(shell grep '"version":' ../META.json | head -1 \
PG_CPPFLAGS = -DREPACK_VERSION=$(REPACK_VERSION)
# Support CREATE EXTENSION for PG >= 9.1 and a simple sql script for PG < 9.1
HAVE_EXTENSION = $(shell $(PG_CONFIG) --version \
| grep -qE " 8\.| 9\.0" && echo no || echo yes)
ifeq ($(HAVE_EXTENSION),yes)
ifeq ($(shell echo $$(($(INTVERSION) >= 901))),1)
DATA_built = pg_repack--$(REPACK_VERSION).sql pg_repack.control
else
DATA_built = pg_repack.sql

View File

@ -179,7 +179,9 @@ CREATE VIEW repack.tables AS
'CREATE TABLE repack.log_' || R.oid || ' (id bigserial PRIMARY KEY, pk repack.pk_' || R.oid || ', row ' || repack.oid2text(R.oid) || ')' AS create_log,
repack.get_create_trigger(R.oid, PK.indexrelid) AS create_trigger,
repack.get_enable_trigger(R.oid) as enable_trigger,
'CREATE TABLE repack.table_' || R.oid || ' WITH (' || array_to_string(array_append(R.reloptions, 'oids=' || CASE WHEN R.relhasoids THEN 'true' ELSE 'false' END), ',') || ') TABLESPACE ' || coalesce(quote_ident(S.spcname), 'pg_default') || ' AS SELECT ' || repack.get_columns_for_create_as(R.oid) || ' FROM ONLY ' || repack.oid2text(R.oid) AS create_table,
'CREATE TABLE repack.table_' || R.oid || ' WITH (' || array_to_string(array_append(R.reloptions, 'oids=' || CASE WHEN R.relhasoids THEN 'true' ELSE 'false' END), ',') || ') TABLESPACE ' AS create_table_1,
coalesce(quote_ident(S.spcname), 'pg_default') as tablespace_orig,
' AS SELECT ' || repack.get_columns_for_create_as(R.oid) || ' FROM ONLY ' || repack.oid2text(R.oid) AS create_table_2,
repack.get_drop_columns(R.oid, 'repack.table_' || R.oid) AS drop_columns,
'DELETE FROM repack.log_' || R.oid AS delete_log,
'LOCK TABLE ' || repack.oid2text(R.oid) || ' IN ACCESS EXCLUSIVE MODE' AS lock_table,
@ -205,9 +207,9 @@ CREATE VIEW repack.tables AS
AND N.nspname NOT IN ('pg_catalog', 'information_schema')
AND N.nspname NOT LIKE E'pg\\_temp\\_%';
CREATE FUNCTION repack.repack_indexdef(oid, oid) RETURNS text AS
CREATE FUNCTION repack.repack_indexdef(oid, oid, name) RETURNS text AS
'MODULE_PATHNAME', 'repack_indexdef'
LANGUAGE C STABLE STRICT;
LANGUAGE C STABLE;
CREATE FUNCTION repack.repack_trigger() RETURNS trigger AS
'MODULE_PATHNAME', 'repack_trigger'
@ -217,6 +219,8 @@ CREATE FUNCTION repack.conflicted_triggers(oid) RETURNS SETOF name AS
$$
SELECT tgname FROM pg_trigger
WHERE tgrelid = $1 AND tgname >= 'z_repack_trigger'
AND (tgtype & 2) = 2 -- BEFORE trigger
ORDER BY tgname;
$$
LANGUAGE sql STABLE STRICT;

View File

@ -307,7 +307,9 @@ typedef struct IndexDef
char *table; /* table name including schema */
char *type; /* btree, hash, gist or gin */
char *columns; /* column definition */
char *options; /* options after columns. WITH, TABLESPACE and WHERE */
char *options; /* options after columns, before TABLESPACE (e.g. COLLATE) */
char *tablespace; /* tablespace if specified */
char *where; /* WHERE content if specified */
} IndexDef;
static char *
@ -348,6 +350,24 @@ skip_const(Oid index, char *sql, const char *arg1, const char *arg2)
return parse_error(index);
}
static char *
skip_until_const(Oid index, char *sql, const char *what)
{
char *pos;
if ((pos = strstr(sql, what)))
{
size_t len;
len = strlen(what);
pos[-1] = '\0';
return pos + len + 1;
}
/* error */
return parse_error(index);
}
static char *
skip_ident(Oid index, char *sql)
{
@ -446,6 +466,7 @@ parse_indexdef(IndexDef *stmt, Oid index, Oid table)
char *sql = pg_get_indexdef_string(index);
const char *idxname = get_quoted_relname(index);
const char *tblname = get_relation_name(table);
const char *limit = strchr(sql, '\0');
/* CREATE [UNIQUE] INDEX */
stmt->create = sql;
@ -470,8 +491,36 @@ parse_indexdef(IndexDef *stmt, Oid index, Oid table)
stmt->columns = sql;
if ((sql = skip_until(index, sql, ')')) == NULL)
parse_error(index);
/* options */
stmt->options = sql;
stmt->tablespace = NULL;
stmt->where = NULL;
/* Is there a tablespace? Note that apparently there is never, but
* if there was one it would appear here. */
if (sql < limit && strstr(sql, "TABLESPACE"))
{
sql = skip_until_const(index, sql, "TABLESPACE");
stmt->tablespace = sql;
sql = skip_ident(index, sql);
}
/* Note: assuming WHERE is the only clause allowed after TABLESPACE */
if (sql < limit && strstr(sql, "WHERE"))
{
sql = skip_until_const(index, sql, "WHERE");
stmt->where = sql;
}
elog(DEBUG2, "indexdef.create = %s", stmt->create);
elog(DEBUG2, "indexdef.index = %s", stmt->index);
elog(DEBUG2, "indexdef.table = %s", stmt->table);
elog(DEBUG2, "indexdef.type = %s", stmt->type);
elog(DEBUG2, "indexdef.columns = %s", stmt->columns);
elog(DEBUG2, "indexdef.options = %s", stmt->options);
elog(DEBUG2, "indexdef.tspace = %s", stmt->tablespace);
elog(DEBUG2, "indexdef.where = %s", stmt->where);
}
/*
@ -530,12 +579,6 @@ repack_get_order_by(PG_FUNCTION_ARGS)
int nattr;
parse_indexdef(&stmt, index, table);
elog(DEBUG2, "indexdef.create = %s", stmt.create);
elog(DEBUG2, "indexdef.index = %s", stmt.index);
elog(DEBUG2, "indexdef.table = %s", stmt.table);
elog(DEBUG2, "indexdef.type = %s", stmt.type);
elog(DEBUG2, "indexdef.columns = %s", stmt.columns);
elog(DEBUG2, "indexdef.options = %s", stmt.options);
/*
* FIXME: this is very unreliable implementation but I don't want to
@ -620,21 +663,41 @@ repack_get_order_by(PG_FUNCTION_ARGS)
*
* @param index Oid of target index.
* @param table Oid of table of the index.
* @param tablespace Namespace for the index. If NULL keep the original.
* @retval Create index DDL for temp table.
*/
Datum
repack_indexdef(PG_FUNCTION_ARGS)
{
Oid index = PG_GETARG_OID(0);
Oid table = PG_GETARG_OID(1);
Oid index;
Oid table;
Name tablespace = NULL;
IndexDef stmt;
StringInfoData str;
if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
PG_RETURN_NULL();
index = PG_GETARG_OID(0);
table = PG_GETARG_OID(1);
if (!PG_ARGISNULL(2))
tablespace = PG_GETARG_NAME(2);
parse_indexdef(&stmt, index, table);
initStringInfo(&str);
appendStringInfo(&str, "%s index_%u ON repack.table_%u USING %s (%s)%s",
stmt.create, index, table, stmt.type, stmt.columns, stmt.options);
/* specify the new tablespace or the original one if any */
if (tablespace || stmt.tablespace)
appendStringInfo(&str, " TABLESPACE %s",
(tablespace ? NameStr(*tablespace) : stmt.tablespace));
if (stmt.where)
appendStringInfo(&str, " WHERE %s", stmt.where);
PG_RETURN_TEXT_P(cstring_to_text(str.data));
}