Fixed database corruption when target tables have dropped columns, and
there are views or functions depending on columns after dropped ones. The issue was reported by depesz, and original patch by Denish Patel. Improved documentation how to build binaries from source. COPYRIGHT updated.
This commit is contained in:
@ -1,7 +1,8 @@
|
||||
#
|
||||
# pg_reorg: lib/Makefile
|
||||
#
|
||||
# Copyright (c) 2008-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
||||
# Portions Copyright (c) 2008-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
||||
# Portions Copyright (c) 2011, Itagaki Takahiro
|
||||
#
|
||||
SRCS = reorg.c pgut/pgut-be.c pgut/pgut-spi.c
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
|
@ -1,7 +1,8 @@
|
||||
/*
|
||||
* pg_reorg: lib/pg_reorg.sql.in
|
||||
*
|
||||
* Copyright (c) 2008-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
||||
* Portions Copyright (c) 2008-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
||||
* Portions Copyright (c) 2011, Itagaki Takahiro
|
||||
*/
|
||||
|
||||
-- Adjust this setting to control where the objects get created.
|
||||
@ -106,6 +107,43 @@ $$
|
||||
$$
|
||||
LANGUAGE sql STABLE STRICT;
|
||||
|
||||
-- Get a column list for SELECT all columns including dropped ones.
|
||||
-- We use NULLs of integer types for dropped columns (types are not important).
|
||||
CREATE FUNCTION reorg.get_columns_for_create_as(oid)
|
||||
RETURNS text AS
|
||||
$$
|
||||
SELECT array_to_string(reorg.array_accum(c), ',') FROM (SELECT
|
||||
CASE WHEN attisdropped
|
||||
THEN 'NULL::integer AS ' || quote_ident(attname)
|
||||
ELSE quote_ident(attname)
|
||||
END AS c
|
||||
FROM pg_attribute
|
||||
WHERE attrelid = $1 AND attnum > 0 ORDER BY attnum
|
||||
) AS COL
|
||||
$$
|
||||
LANGUAGE sql STABLE STRICT;
|
||||
|
||||
-- Get a SQL text to DROP dropped columns for the table,
|
||||
-- or NULL if it has no dropped columns.
|
||||
CREATE FUNCTION reorg.get_drop_columns(oid, text)
|
||||
RETURNS text AS
|
||||
$$
|
||||
SELECT
|
||||
'ALTER TABLE ' || $2 || ' ' || array_to_string(dropped_columns, ', ')
|
||||
FROM (
|
||||
SELECT
|
||||
reorg.array_accum('DROP COLUMN ' || quote_ident(attname)) AS dropped_columns
|
||||
FROM (
|
||||
SELECT * FROM pg_attribute
|
||||
WHERE attrelid = $1 AND attnum > 0 AND attisdropped
|
||||
ORDER BY attnum
|
||||
) T
|
||||
) T
|
||||
WHERE
|
||||
array_upper(dropped_columns, 1) > 0
|
||||
$$
|
||||
LANGUAGE sql STABLE STRICT;
|
||||
|
||||
-- includes not only PRIMARY KEYS but also UNIQUE NOT NULL keys
|
||||
CREATE VIEW reorg.primary_keys AS
|
||||
SELECT indrelid, (reorg.array_accum(indexrelid))[1] AS indexrelid
|
||||
@ -130,7 +168,8 @@ CREATE VIEW reorg.tables AS
|
||||
reorg.get_create_index_type(PK.indexrelid, 'reorg.pk_' || R.oid) AS create_pktype,
|
||||
'CREATE TABLE reorg.log_' || R.oid || ' (id bigserial PRIMARY KEY, pk reorg.pk_' || R.oid || ', row ' || reorg.oid2text(R.oid) || ')' AS create_log,
|
||||
reorg.get_create_trigger(R.oid, PK.indexrelid) AS create_trigger,
|
||||
'CREATE TABLE reorg.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 * FROM ONLY ' || reorg.oid2text(R.oid) AS create_table,
|
||||
'CREATE TABLE reorg.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 ' || reorg.get_columns_for_create_as(R.oid) || ' FROM ONLY ' || reorg.oid2text(R.oid) AS create_table,
|
||||
reorg.get_drop_columns(R.oid, 'reorg.table_' || R.oid) AS drop_columns,
|
||||
'DELETE FROM reorg.log_' || R.oid AS delete_log,
|
||||
'LOCK TABLE ' || reorg.oid2text(R.oid) || ' IN ACCESS EXCLUSIVE MODE' AS lock_table,
|
||||
reorg.get_index_keys(CK.indexrelid, R.oid) AS ckey,
|
||||
|
@ -1,9 +1,8 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* pgut-be.c
|
||||
*
|
||||
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
||||
*
|
||||
* Portions Copyright (c) 2008-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
||||
* Portions Copyright (c) 2011, Itagaki Takahiro
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* pgut-be.h
|
||||
*
|
||||
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
||||
*
|
||||
* Portions Copyright (c) 2008-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
||||
* Portions Copyright (c) 2011, Itagaki Takahiro
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* pgut-spi.c
|
||||
*
|
||||
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
||||
*
|
||||
* Portions Copyright (c) 2008-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
||||
* Portions Copyright (c) 2011, Itagaki Takahiro
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* pgut-spi.h
|
||||
*
|
||||
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
||||
*
|
||||
* Portions Copyright (c) 2008-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
||||
* Portions Copyright (c) 2011, Itagaki Takahiro
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
125
lib/reorg.c
125
lib/reorg.c
@ -1,7 +1,8 @@
|
||||
/*
|
||||
* pg_reorg: lib/reorg.c
|
||||
*
|
||||
* Copyright (c) 2008-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
||||
* Portions Copyright (c) 2008-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
||||
* Portions Copyright (c) 2011, Itagaki Takahiro
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
@ -75,7 +76,7 @@ static void RenameRelationInternal(Oid myrelid, const char *newrelname, Oid name
|
||||
Datum
|
||||
reorg_version(PG_FUNCTION_ARGS)
|
||||
{
|
||||
return CStringGetTextDatum("pg_reorg 1.1.5");
|
||||
return CStringGetTextDatum("pg_reorg 1.1.6");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -592,101 +593,6 @@ getint16(HeapTuple tuple, TupleDesc desc, int column)
|
||||
return isnull ? 0 : DatumGetInt16(datum);
|
||||
}
|
||||
|
||||
static void
|
||||
remove_dropped_columns_and_adjust_attnum(Oid oid, int16 natts1, int16 natts2)
|
||||
{
|
||||
/* delete dropped columns */
|
||||
execute_with_format(SPI_OK_DELETE,
|
||||
"DELETE FROM pg_catalog.pg_attribute"
|
||||
" WHERE attrelid = %u AND attisdropped",
|
||||
oid);
|
||||
if (SPI_processed != natts1 - natts2)
|
||||
elog(ERROR, "cannot remove %d dropped columns (%u columns removed)",
|
||||
natts2 - natts1, SPI_processed);
|
||||
|
||||
/* renumber attnum */
|
||||
#if PG_VERSION_NUM >= 90000
|
||||
execute_with_format(SPI_OK_UPDATE,
|
||||
"UPDATE pg_catalog.pg_attribute m"
|
||||
" SET attnum = (SELECT count(*) FROM pg_attribute a"
|
||||
" WHERE m.attrelid = a.attrelid"
|
||||
" AND m.attnum >= a.attnum"
|
||||
" AND a.attnum > 0 AND NOT a.attisdropped)"
|
||||
" WHERE attrelid = %u AND attnum > 0 AND NOT attisdropped",
|
||||
oid);
|
||||
if (SPI_processed != natts2)
|
||||
elog(ERROR, "cannot update %d columns (%u columns updated)",
|
||||
natts2, SPI_processed);
|
||||
#elif PG_VERSION_NUM >= 80300
|
||||
execute_with_format(SPI_OK_UPDATE,
|
||||
"UPDATE pg_catalog.pg_attribute"
|
||||
" SET attnum = (SELECT count(*) FROM pg_attribute a"
|
||||
" WHERE pg_catalog.pg_attribute.attrelid = a.attrelid"
|
||||
" AND pg_catalog.pg_attribute.attnum >= a.attnum"
|
||||
" AND a.attnum > 0 AND NOT a.attisdropped)"
|
||||
" WHERE attrelid = %u AND attnum > 0 AND NOT attisdropped",
|
||||
oid);
|
||||
if (SPI_processed != natts2)
|
||||
elog(ERROR, "cannot update %d columns (%u columns updated)",
|
||||
natts2, SPI_processed);
|
||||
#else
|
||||
/*
|
||||
* Use count(*) in subquery because 8.2 doesn't support aggregates
|
||||
* in UPDATE SET.
|
||||
*/
|
||||
do
|
||||
{
|
||||
uint32 i;
|
||||
uint32 ntuples;
|
||||
SPITupleTable *tuptable;
|
||||
TupleDesc desc;
|
||||
|
||||
execute_with_format(SPI_OK_SELECT,
|
||||
"SELECT attnum FROM pg_catalog.pg_attribute"
|
||||
" WHERE attrelid = %u AND attnum > 0 AND NOT attisdropped"
|
||||
" ORDER BY attnum",
|
||||
oid);
|
||||
if (SPI_processed != natts2)
|
||||
elog(ERROR, "number of columns should be %d (%d returned)",
|
||||
natts2, SPI_processed);
|
||||
|
||||
ntuples = SPI_processed;
|
||||
tuptable = SPI_tuptable;
|
||||
desc = tuptable->tupdesc;
|
||||
|
||||
for (i = 0; i < ntuples; i++)
|
||||
{
|
||||
int attnum;
|
||||
int count;
|
||||
|
||||
attnum = getint16(tuptable->vals[i], desc, 1);
|
||||
|
||||
execute_with_format(SPI_OK_SELECT,
|
||||
"SELECT count(*)::smallint FROM pg_catalog.pg_attribute"
|
||||
" WHERE attrelid = %u AND attnum > 0 AND attnum <= %d",
|
||||
oid, attnum);
|
||||
if (SPI_processed != 1)
|
||||
elog(ERROR, "cannot adjust column %d", attnum);
|
||||
|
||||
count = getint16(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1);
|
||||
|
||||
execute_with_format(SPI_OK_UPDATE,
|
||||
"UPDATE pg_catalog.pg_attribute"
|
||||
" SET attnum = %d"
|
||||
" WHERE attrelid = %u AND attnum = %d",
|
||||
count, oid, attnum);
|
||||
if (SPI_processed != 1)
|
||||
elog(ERROR, "cannot update column %d", attnum);
|
||||
}
|
||||
} while(0);
|
||||
#endif
|
||||
|
||||
/* adjust attribute number of the table */
|
||||
execute_with_format(SPI_OK_UPDATE,
|
||||
"UPDATE pg_catalog.pg_class SET relnatts = %d WHERE oid = %u",
|
||||
natts2, oid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @fn Datum reorg_swap(PG_FUNCTION_ARGS)
|
||||
* @brief Swapping relfilenode of tables and relation ids of toast tables
|
||||
@ -799,27 +705,6 @@ reorg_swap(PG_FUNCTION_ARGS)
|
||||
idx2 = getoid(tuple, desc, 2);
|
||||
swap_heap_or_index_files(idx1, idx2);
|
||||
|
||||
/* adjust key attnum if the target table has dropped columns */
|
||||
if (natts1 != natts2)
|
||||
{
|
||||
#if PG_VERSION_NUM >= 90000
|
||||
execute_with_format(SPI_OK_UPDATE,
|
||||
"UPDATE pg_catalog.pg_index m SET indkey = n.indkey"
|
||||
" FROM pg_catalog.pg_index n"
|
||||
" WHERE m.indexrelid = %u"
|
||||
" AND n.indexrelid = 'reorg.index_%u'::regclass",
|
||||
idx1, idx1);
|
||||
#else
|
||||
execute_with_format(SPI_OK_UPDATE,
|
||||
"UPDATE pg_catalog.pg_index SET indkey = n.indkey"
|
||||
" FROM pg_catalog.pg_index n"
|
||||
" WHERE pg_catalog.pg_index.indexrelid = %u"
|
||||
" AND n.indexrelid = 'reorg.index_%u'::regclass",
|
||||
idx1, idx1);
|
||||
#endif
|
||||
if (SPI_processed != 1)
|
||||
elog(ERROR, "failed to update pg_index.indkey (%u rows updated)", SPI_processed);
|
||||
}
|
||||
CommandCounterIncrement();
|
||||
}
|
||||
|
||||
@ -876,10 +761,6 @@ reorg_swap(PG_FUNCTION_ARGS)
|
||||
CommandCounterIncrement();
|
||||
}
|
||||
|
||||
/* adjust attribute numbers if the target table has dropped columns */
|
||||
if (natts1 != natts2)
|
||||
remove_dropped_columns_and_adjust_attnum(oid, natts1, natts2);
|
||||
|
||||
/* drop reorg trigger */
|
||||
execute_with_format(
|
||||
SPI_OK_UTILITY,
|
||||
|
@ -1,7 +1,8 @@
|
||||
/*
|
||||
* pg_reorg: lib/uninstall_reorg.sql
|
||||
*
|
||||
* Copyright (c) 2008-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
||||
* Portions Copyright (c) 2008-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
||||
* Portions Copyright (c) 2011, Itagaki Takahiro
|
||||
*/
|
||||
|
||||
DROP SCHEMA IF EXISTS reorg CASCADE;
|
||||
|
Reference in New Issue
Block a user