diff --git a/lib/pgut/pgut-be.c b/lib/pgut/pgut-be.c index 96534b1..d7ea853 100755 --- a/lib/pgut/pgut-be.c +++ b/lib/pgut/pgut-be.c @@ -8,6 +8,7 @@ */ #include "postgres.h" +#include "fmgr.h" #include "pgut-be.h" #if PG_VERSION_NUM < 80400 diff --git a/lib/pgut/pgut-be.h b/lib/pgut/pgut-be.h index 9467b69..2ecf1a5 100755 --- a/lib/pgut/pgut-be.h +++ b/lib/pgut/pgut-be.h @@ -38,8 +38,6 @@ #define PageAddItem(page, item, size, offnum, overwrite, is_heap) \ PageAddItem((page), (item), (size), (offnum), LP_USED) -typedef void *SPIPlanPtr; - #endif #if PG_VERSION_NUM < 80400 diff --git a/lib/pgut/pgut-spi.c b/lib/pgut/pgut-spi.c index 54aace1..eb3ab09 100755 --- a/lib/pgut/pgut-spi.c +++ b/lib/pgut/pgut-spi.c @@ -10,6 +10,9 @@ #include "postgres.h" #include "pgut-spi.h" +#define EXEC_FAILED(ret, expected) \ + (((expected) > 0 && (ret) != (expected)) || (ret) < 0) + static void termStringInfo(StringInfo str) { @@ -17,12 +20,23 @@ termStringInfo(StringInfo str) pfree(str->data); } +/* appendStringInfoVA + automatic buffer extension */ +static void +appendStringInfoVA_s(StringInfo str, const char *fmt, va_list args) +{ + while (!appendStringInfoVA(str, fmt, args)) + { + /* Double the buffer size and try again. */ + enlargeStringInfo(str, str->maxlen); + } +} + /* simple execute */ void execute(int expected, const char *sql) { int ret = SPI_execute(sql, false, 0); - if ((expected > 0 && ret != expected) || ret < 0) + if EXEC_FAILED(ret, expected) elog(ERROR, "query failed: (sql=%s, code=%d, expected=%d)", sql, ret, expected); } @@ -31,7 +45,7 @@ void execute_plan(int expected, SPIPlanPtr plan, Datum *values, const char *nulls) { int ret = SPI_execute_plan(plan, values, nulls, false, 0); - if ((expected > 0 && ret != expected) || ret < 0) + if EXEC_FAILED(ret, expected) elog(ERROR, "query failed: (code=%d, expected=%d)", ret, expected); } @@ -45,11 +59,13 @@ execute_with_format(int expected, const char *format, ...) initStringInfo(&sql); va_start(ap, format); - appendStringInfoVA(&sql, format, ap); + appendStringInfoVA_s(&sql, format, ap); va_end(ap); + if (strlen(sql.data) == 0) + elog(WARNING, "execute_with_format(%s)", format); ret = SPI_exec(sql.data, 0); - if ((expected > 0 && ret != expected) || ret < 0) + if EXEC_FAILED(ret, expected) elog(ERROR, "query failed: (sql=%s, code=%d, expected=%d)", sql.data, ret, expected); termStringInfo(&sql); @@ -66,7 +82,7 @@ execute_with_args(int expected, const char *src, int nargs, Oid argtypes[], Datu c_nulls[i] = (nulls[i] ? 'n' : ' '); ret = SPI_execute_with_args(src, nargs, argtypes, values, c_nulls, false, 0); - if ((expected > 0 && ret != expected) || ret < 0) + if EXEC_FAILED(ret, expected) elog(ERROR, "query failed: (sql=%s, code=%d, expected=%d)", src, ret, expected); } @@ -78,7 +94,7 @@ execute_with_format_args(int expected, const char *format, int nargs, Oid argtyp initStringInfo(&sql); va_start(ap, nulls); - appendStringInfoVA(&sql, format, ap); + appendStringInfoVA_s(&sql, format, ap); va_end(ap); execute_with_args(expected, sql.data, nargs, argtypes, values, nulls); diff --git a/lib/pgut/pgut-spi.h b/lib/pgut/pgut-spi.h index dd83820..0f92db9 100755 --- a/lib/pgut/pgut-spi.h +++ b/lib/pgut/pgut-spi.h @@ -12,13 +12,11 @@ #include "executor/spi.h" -extern void execute(int expected, const char *sql); -extern void execute_plan(int expected, SPIPlanPtr plan, Datum *values, const char *nulls); -extern void execute_with_format(int expected, const char *format, ...) -__attribute__((format(printf, 2, 3))); -extern void execute_with_args(int expected, const char *src, int nargs, Oid argtypes[], Datum values[], const bool nulls[]); -extern void execute_with_format_args(int expected, const char *format, int nargs, Oid argtypes[], Datum values[], const bool nulls[], ...) -__attribute__((format(printf, 2, 7))); +#if PG_VERSION_NUM < 80300 + +typedef void *SPIPlanPtr; + +#endif #if PG_VERSION_NUM < 80400 @@ -27,4 +25,12 @@ extern int SPI_execute_with_args(const char *src, int nargs, Oid *argtypes, #endif +extern void execute(int expected, const char *sql); +extern void execute_plan(int expected, SPIPlanPtr plan, Datum *values, const char *nulls); +extern void execute_with_format(int expected, const char *format, ...) +__attribute__((format(printf, 2, 3))); +extern void execute_with_args(int expected, const char *src, int nargs, Oid argtypes[], Datum values[], const bool nulls[]); +extern void execute_with_format_args(int expected, const char *format, int nargs, Oid argtypes[], Datum values[], const bool nulls[], ...) +__attribute__((format(printf, 2, 7))); + #endif /* PGUT_SPI_H */ diff --git a/lib/reorg.c b/lib/reorg.c index eea5011..cd754e7 100755 --- a/lib/reorg.c +++ b/lib/reorg.c @@ -23,8 +23,8 @@ #include "utils/relcache.h" #include "utils/syscache.h" -#include "pgut/pgut-be.h" #include "pgut/pgut-spi.h" +#include "pgut/pgut-be.h" PG_MODULE_MAGIC; @@ -468,6 +468,89 @@ 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 >= 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 @@ -635,26 +718,7 @@ reorg_swap(PG_FUNCTION_ARGS) /* adjust attribute numbers if the target table has dropped columns */ if (natts1 != natts2) - { - /* delete dropped columns */ - execute_with_format(SPI_OK_DELETE, - "DELETE FROM pg_catalog.pg_attribute" - " WHERE attrelid = %u AND attisdropped", - oid); - /* renumber attnum */ - 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); - /* 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); - } + remove_dropped_columns_and_adjust_attnum(oid, natts1, natts2); /* drop reorg trigger */ execute_with_format(