workaround for toast tables. The bug is reported by Kenny Gorman.
This commit is contained in:
parent
29469f40f9
commit
24a03c01a5
3
Makefile
3
Makefile
@ -24,3 +24,6 @@ debug:
|
|||||||
uninstall:
|
uninstall:
|
||||||
make -C bin uninstall
|
make -C bin uninstall
|
||||||
make -C lib uninstall
|
make -C lib uninstall
|
||||||
|
|
||||||
|
installcheck:
|
||||||
|
make -C bin installcheck
|
||||||
|
@ -7,6 +7,7 @@ SRCS = \
|
|||||||
pg_reorg.c
|
pg_reorg.c
|
||||||
OBJS = $(SRCS:.c=.o) $(top_builddir)/src/bin/scripts/common.o
|
OBJS = $(SRCS:.c=.o) $(top_builddir)/src/bin/scripts/common.o
|
||||||
PROGRAM = pg_reorg
|
PROGRAM = pg_reorg
|
||||||
|
REGRESS = init reorg
|
||||||
|
|
||||||
ifdef DEBUG_REORG
|
ifdef DEBUG_REORG
|
||||||
PG_CPPFLAGS = -I$(libpq_srcdir) -I$(top_builddir)/src/bin/scripts -DDEBUG_REORG
|
PG_CPPFLAGS = -I$(libpq_srcdir) -I$(top_builddir)/src/bin/scripts -DDEBUG_REORG
|
||||||
|
3
bin/expected/init.out
Executable file
3
bin/expected/init.out
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
SET client_min_messages = warning;
|
||||||
|
\set ECHO none
|
||||||
|
RESET client_min_messages;
|
122
bin/expected/reorg.out
Executable file
122
bin/expected/reorg.out
Executable file
@ -0,0 +1,122 @@
|
|||||||
|
SET client_min_messages = warning;
|
||||||
|
--
|
||||||
|
-- create table.
|
||||||
|
--
|
||||||
|
CREATE TABLE tbl_cluster (
|
||||||
|
col1 int,
|
||||||
|
col2 timestamp,
|
||||||
|
":-)" text,
|
||||||
|
primary key(":-)", col1)
|
||||||
|
) WITH (fillfactor = 70);
|
||||||
|
CREATE INDEX cidx_cluster ON tbl_cluster (col2, length(":-)"));
|
||||||
|
ALTER TABLE tbl_cluster CLUSTER ON cidx_cluster;
|
||||||
|
CREATE TABLE tbl_only_pkey (
|
||||||
|
col1 int PRIMARY KEY,
|
||||||
|
":-)" text
|
||||||
|
);
|
||||||
|
CREATE TABLE tbl_only_ckey (
|
||||||
|
col1 int,
|
||||||
|
col2 timestamp,
|
||||||
|
":-)" text
|
||||||
|
) WITH (fillfactor = 70);
|
||||||
|
CREATE INDEX cidx_only_ckey ON tbl_only_ckey (col2, ":-)");
|
||||||
|
ALTER TABLE tbl_only_ckey CLUSTER ON cidx_only_ckey;
|
||||||
|
CREATE TABLE tbl_gistkey (
|
||||||
|
id integer PRIMARY KEY,
|
||||||
|
c circle
|
||||||
|
);
|
||||||
|
CREATE INDEX cidx_circle ON tbl_gistkey USING gist (c);
|
||||||
|
ALTER TABLE tbl_gistkey CLUSTER ON cidx_circle;
|
||||||
|
INSERT INTO tbl_cluster VALUES(1, '2008-12-31 10:00:00', 'admin');
|
||||||
|
INSERT INTO tbl_cluster VALUES(2, '2008-01-01 00:00:00', 'king');
|
||||||
|
INSERT INTO tbl_cluster VALUES(3, '2008-03-04 12:00:00', 'joker');
|
||||||
|
INSERT INTO tbl_cluster VALUES(4, '2008-03-05 15:00:00', 'queen');
|
||||||
|
INSERT INTO tbl_cluster VALUES(5, '2008-01-01 00:30:00', sqrt(2::numeric(1000,999))::text || sqrt(3::numeric(1000,999))::text);
|
||||||
|
--
|
||||||
|
-- do reorg
|
||||||
|
--
|
||||||
|
\! pg_reorg --dbname=contrib_regression --table=tbl_cluster
|
||||||
|
--
|
||||||
|
-- results
|
||||||
|
--
|
||||||
|
\d+ tbl_cluster
|
||||||
|
Table "public.tbl_cluster"
|
||||||
|
Column | Type | Modifiers | Storage | Description
|
||||||
|
--------+-----------------------------+-----------+----------+-------------
|
||||||
|
col1 | integer | not null | plain |
|
||||||
|
col2 | timestamp without time zone | | plain |
|
||||||
|
:-) | text | not null | extended |
|
||||||
|
Indexes:
|
||||||
|
"tbl_cluster_pkey" PRIMARY KEY, btree (":-)", col1)
|
||||||
|
"cidx_cluster" btree (col2, length(":-)")) CLUSTER
|
||||||
|
Has OIDs: no
|
||||||
|
Options: fillfactor=70
|
||||||
|
|
||||||
|
\d+ tbl_gistkey
|
||||||
|
Table "public.tbl_gistkey"
|
||||||
|
Column | Type | Modifiers | Storage | Description
|
||||||
|
--------+---------+-----------+---------+-------------
|
||||||
|
id | integer | not null | plain |
|
||||||
|
c | circle | | plain |
|
||||||
|
Indexes:
|
||||||
|
"tbl_gistkey_pkey" PRIMARY KEY, btree (id)
|
||||||
|
"cidx_circle" gist (c) CLUSTER
|
||||||
|
Has OIDs: no
|
||||||
|
|
||||||
|
\d+ tbl_only_ckey
|
||||||
|
Table "public.tbl_only_ckey"
|
||||||
|
Column | Type | Modifiers | Storage | Description
|
||||||
|
--------+-----------------------------+-----------+----------+-------------
|
||||||
|
col1 | integer | | plain |
|
||||||
|
col2 | timestamp without time zone | | plain |
|
||||||
|
:-) | text | | extended |
|
||||||
|
Indexes:
|
||||||
|
"cidx_only_ckey" btree (col2, ":-)") CLUSTER
|
||||||
|
Has OIDs: no
|
||||||
|
Options: fillfactor=70
|
||||||
|
|
||||||
|
\d+ tbl_only_pkey
|
||||||
|
Table "public.tbl_only_pkey"
|
||||||
|
Column | Type | Modifiers | Storage | Description
|
||||||
|
--------+---------+-----------+----------+-------------
|
||||||
|
col1 | integer | not null | plain |
|
||||||
|
:-) | text | | extended |
|
||||||
|
Indexes:
|
||||||
|
"tbl_only_pkey_pkey" PRIMARY KEY, btree (col1)
|
||||||
|
Has OIDs: no
|
||||||
|
|
||||||
|
SET synchronize_seqscans = off;
|
||||||
|
SELECT col1, to_char(col2, 'YYYY-MM-DD HH24:MI:SS'), ":-)" FROM tbl_cluster;
|
||||||
|
col1 | to_char | :-)
|
||||||
|
------+---------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
2 | 2008-01-01 00:00:00 | king
|
||||||
|
5 | 2008-01-01 00:30:00 | 1.4142135623730950488016887242096980785696718753769480731766797379907324784621070388503875343276415727350138462309122970249248360558507372126441214970999358314132226659275055927557999505011527820605714701095599716059702745345968620147285174186408891986095523292304843087143214508397626036279952514079896872533965463318088296406206152583523950547457502877599617298355752203375318570113543746034084988471603868999706990048150305440277903164542478230684929369186215805784631115966687130130156185689872372352885092648612494977154218334204285686060146824720771435854874155657069677653720226485447015858801620758474922657226002085584466521458398893944370926591800311388246468157082630100594858704003186480342194897278290641045072636881313739855256117322040245091227700226941127573627280495738108967504018369868368450725799364729060762996941380475654823728997180326802474420629269124859052181004459842150591120249441341728531478105803603371077309182869314710171111683916581726889419758716582152128229518488471.732050807568877293527446341505872366942805253810380628055806979451933016908800037081146186757248575675626141415406703029969945094998952478811655512094373648528093231902305582067974820101084674923265015312343266903322886650672254668921837971227047131660367861588019049986537379859389467650347506576050756618348129606100947602187190325083145829523959832997789824508288714463832917347224163984587855397667958063818353666110843173780894378316102088305524901670023520711144288695990956365797087168498072899493296484283020786408603988738697537582317317831395992983007838702877053913369563312103707264019249106768231199288375641141422016742752102372994270831059898459475987664288897796147837958390228854852903576033852808064381972344661059689722872865264153822664698420021195484155278441181286534507035191650016689294415480846071277143999762926834629577438361895110127148638746976545982451788550975379013880664961911962222957110555242923723192197738262561631468842032853716682938649611917049738836395495938
|
||||||
|
3 | 2008-03-04 12:00:00 | joker
|
||||||
|
4 | 2008-03-05 15:00:00 | queen
|
||||||
|
1 | 2008-12-31 10:00:00 | admin
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
|
SELECT * FROM tbl_gistkey;
|
||||||
|
id | c
|
||||||
|
----+---
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM tbl_only_ckey;
|
||||||
|
col1 | col2 | :-)
|
||||||
|
------+------+-----
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM tbl_only_pkey;
|
||||||
|
col1 | :-)
|
||||||
|
------+-----
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
RESET synchronize_seqscans;
|
||||||
|
--
|
||||||
|
-- clean up
|
||||||
|
--
|
||||||
|
DROP TABLE tbl_cluster;
|
||||||
|
DROP TABLE tbl_gistkey;
|
||||||
|
DROP TABLE tbl_only_pkey;
|
||||||
|
DROP TABLE tbl_only_ckey;
|
||||||
|
RESET client_min_messages;
|
@ -368,7 +368,7 @@ reorg_one_database(const char *orderby, const char *table)
|
|||||||
reconnect();
|
reconnect();
|
||||||
|
|
||||||
/* Restrict search_path to system catalog. */
|
/* Restrict search_path to system catalog. */
|
||||||
command("SET search_path = pg_catalog, pg_temp", 0, NULL);
|
command("SET search_path = pg_catalog, pg_temp, public", 0, NULL);
|
||||||
|
|
||||||
/* To avoid annoying "create implicit ..." messages. */
|
/* To avoid annoying "create implicit ..." messages. */
|
||||||
command("SET client_min_messages = warning", 0, NULL);
|
command("SET client_min_messages = warning", 0, NULL);
|
||||||
|
5
bin/sql/init.sql
Executable file
5
bin/sql/init.sql
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
SET client_min_messages = warning;
|
||||||
|
\set ECHO none
|
||||||
|
\i ../lib/pg_reorg.sql
|
||||||
|
\set ECHO all
|
||||||
|
RESET client_min_messages;
|
73
bin/sql/reorg.sql
Executable file
73
bin/sql/reorg.sql
Executable file
@ -0,0 +1,73 @@
|
|||||||
|
SET client_min_messages = warning;
|
||||||
|
--
|
||||||
|
-- create table.
|
||||||
|
--
|
||||||
|
CREATE TABLE tbl_cluster (
|
||||||
|
col1 int,
|
||||||
|
col2 timestamp,
|
||||||
|
":-)" text,
|
||||||
|
primary key(":-)", col1)
|
||||||
|
) WITH (fillfactor = 70);
|
||||||
|
|
||||||
|
CREATE INDEX cidx_cluster ON tbl_cluster (col2, length(":-)"));
|
||||||
|
ALTER TABLE tbl_cluster CLUSTER ON cidx_cluster;
|
||||||
|
|
||||||
|
CREATE TABLE tbl_only_pkey (
|
||||||
|
col1 int PRIMARY KEY,
|
||||||
|
":-)" text
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE tbl_only_ckey (
|
||||||
|
col1 int,
|
||||||
|
col2 timestamp,
|
||||||
|
":-)" text
|
||||||
|
) WITH (fillfactor = 70);
|
||||||
|
|
||||||
|
CREATE INDEX cidx_only_ckey ON tbl_only_ckey (col2, ":-)");
|
||||||
|
ALTER TABLE tbl_only_ckey CLUSTER ON cidx_only_ckey;
|
||||||
|
|
||||||
|
CREATE TABLE tbl_gistkey (
|
||||||
|
id integer PRIMARY KEY,
|
||||||
|
c circle
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX cidx_circle ON tbl_gistkey USING gist (c);
|
||||||
|
ALTER TABLE tbl_gistkey CLUSTER ON cidx_circle;
|
||||||
|
|
||||||
|
INSERT INTO tbl_cluster VALUES(1, '2008-12-31 10:00:00', 'admin');
|
||||||
|
INSERT INTO tbl_cluster VALUES(2, '2008-01-01 00:00:00', 'king');
|
||||||
|
INSERT INTO tbl_cluster VALUES(3, '2008-03-04 12:00:00', 'joker');
|
||||||
|
INSERT INTO tbl_cluster VALUES(4, '2008-03-05 15:00:00', 'queen');
|
||||||
|
INSERT INTO tbl_cluster VALUES(5, '2008-01-01 00:30:00', sqrt(2::numeric(1000,999))::text || sqrt(3::numeric(1000,999))::text);
|
||||||
|
|
||||||
|
--
|
||||||
|
-- do reorg
|
||||||
|
--
|
||||||
|
|
||||||
|
\! pg_reorg --dbname=contrib_regression --table=tbl_cluster
|
||||||
|
|
||||||
|
--
|
||||||
|
-- results
|
||||||
|
--
|
||||||
|
|
||||||
|
\d+ tbl_cluster
|
||||||
|
\d+ tbl_gistkey
|
||||||
|
\d+ tbl_only_ckey
|
||||||
|
\d+ tbl_only_pkey
|
||||||
|
|
||||||
|
SET synchronize_seqscans = off;
|
||||||
|
SELECT col1, to_char(col2, 'YYYY-MM-DD HH24:MI:SS'), ":-)" FROM tbl_cluster;
|
||||||
|
SELECT * FROM tbl_gistkey;
|
||||||
|
SELECT * FROM tbl_only_ckey;
|
||||||
|
SELECT * FROM tbl_only_pkey;
|
||||||
|
RESET synchronize_seqscans;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- clean up
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE tbl_cluster;
|
||||||
|
DROP TABLE tbl_gistkey;
|
||||||
|
DROP TABLE tbl_only_pkey;
|
||||||
|
DROP TABLE tbl_only_ckey;
|
||||||
|
RESET client_min_messages;
|
453
lib/reorg.c
453
lib/reorg.c
@ -9,13 +9,21 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
#include "access/transam.h"
|
||||||
|
#include "access/xact.h"
|
||||||
|
#include "catalog/dependency.h"
|
||||||
|
#include "catalog/indexing.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
|
#include "catalog/pg_namespace.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
|
#include "commands/tablecmds.h"
|
||||||
#include "commands/trigger.h"
|
#include "commands/trigger.h"
|
||||||
#include "executor/spi.h"
|
#include "executor/spi.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
|
#include "utils/relcache.h"
|
||||||
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
#ifdef PG_MODULE_MAGIC
|
#ifdef PG_MODULE_MAGIC
|
||||||
PG_MODULE_MAGIC;
|
PG_MODULE_MAGIC;
|
||||||
@ -48,11 +56,13 @@ static SPIPlanPtr reorg_prepare(const char *src, int nargs, Oid *argtypes);
|
|||||||
static void reorg_execp(SPIPlanPtr plan, Datum *values, const char *nulls, int expected);
|
static void reorg_execp(SPIPlanPtr plan, Datum *values, const char *nulls, int expected);
|
||||||
static void reorg_execf(int expexted, const char *format, ...)
|
static void reorg_execf(int expexted, const char *format, ...)
|
||||||
__attribute__((format(printf, 2, 3)));
|
__attribute__((format(printf, 2, 3)));
|
||||||
|
static void reorg_execd(const char *src, int nargs, Oid *argtypes, Datum *values, const char *nulls, int expected);
|
||||||
static const char *get_quoted_relname(Oid oid);
|
static const char *get_quoted_relname(Oid oid);
|
||||||
static const char *get_quoted_nspname(Oid oid);
|
static const char *get_quoted_nspname(Oid oid);
|
||||||
|
static void swap_heap_or_index_files(Oid r1, Oid r2);
|
||||||
|
|
||||||
#define copy_tuple(tuple, tupdesc) \
|
#define copy_tuple(tuple, desc) \
|
||||||
PointerGetDatum(SPI_returntuple((tuple), (tupdesc)))
|
PointerGetDatum(SPI_returntuple((tuple), (desc)))
|
||||||
|
|
||||||
/* check access authority */
|
/* check access authority */
|
||||||
static void
|
static void
|
||||||
@ -63,34 +73,11 @@ must_be_superuser(const char *func)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if PG_VERSION_NUM < 80400
|
#if PG_VERSION_NUM < 80400
|
||||||
static int
|
static int SPI_execute_with_args(const char *src, int nargs, Oid *argtypes,
|
||||||
SPI_execute_with_args(const char *src,
|
|
||||||
int nargs, Oid *argtypes,
|
|
||||||
Datum *values, const char *nulls,
|
Datum *values, const char *nulls,
|
||||||
bool read_only, long tcount)
|
bool read_only, long tcount);
|
||||||
{
|
static text *cstring_to_text(const char * s);
|
||||||
SPIPlanPtr plan;
|
static void RenameRelationInternal(Oid myrelid, const char *newrelname, Oid namespaceId);
|
||||||
int ret;
|
|
||||||
|
|
||||||
plan = SPI_prepare(src, nargs, argtypes);
|
|
||||||
if (plan == NULL)
|
|
||||||
return SPI_result;
|
|
||||||
ret = SPI_execute_plan(plan, values, nulls, read_only, tcount);
|
|
||||||
SPI_freeplan(plan);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static text *
|
|
||||||
cstring_to_text(const char * s)
|
|
||||||
{
|
|
||||||
int len = strlen(s);
|
|
||||||
text *result = palloc(len + VARHDRSZ);
|
|
||||||
|
|
||||||
SET_VARSIZE(result, len + VARHDRSZ);
|
|
||||||
memcpy(VARDATA(result), s, len);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -105,13 +92,12 @@ Datum
|
|||||||
reorg_trigger(PG_FUNCTION_ARGS)
|
reorg_trigger(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
TriggerData *trigdata = (TriggerData *) fcinfo->context;
|
TriggerData *trigdata = (TriggerData *) fcinfo->context;
|
||||||
TupleDesc tupdesc;
|
TupleDesc desc;
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
Datum values[2];
|
Datum values[2];
|
||||||
char nulls[2] = { ' ', ' ' };
|
char nulls[2] = { ' ', ' ' };
|
||||||
Oid argtypes[2];
|
Oid argtypes[2];
|
||||||
const char *sql;
|
const char *sql;
|
||||||
int ret;
|
|
||||||
|
|
||||||
/* authority check */
|
/* authority check */
|
||||||
must_be_superuser("reorg_trigger");
|
must_be_superuser("reorg_trigger");
|
||||||
@ -125,7 +111,7 @@ reorg_trigger(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
/* retrieve parameters */
|
/* retrieve parameters */
|
||||||
sql = trigdata->tg_trigger->tgargs[0];
|
sql = trigdata->tg_trigger->tgargs[0];
|
||||||
tupdesc = RelationGetDescr(trigdata->tg_relation);
|
desc = RelationGetDescr(trigdata->tg_relation);
|
||||||
argtypes[0] = argtypes[1] = trigdata->tg_relation->rd_rel->reltype;
|
argtypes[0] = argtypes[1] = trigdata->tg_relation->rd_rel->reltype;
|
||||||
|
|
||||||
/* connect to SPI manager */
|
/* connect to SPI manager */
|
||||||
@ -136,27 +122,25 @@ reorg_trigger(PG_FUNCTION_ARGS)
|
|||||||
/* INSERT: (NULL, newtup) */
|
/* INSERT: (NULL, newtup) */
|
||||||
tuple = trigdata->tg_trigtuple;
|
tuple = trigdata->tg_trigtuple;
|
||||||
nulls[0] = 'n';
|
nulls[0] = 'n';
|
||||||
values[1] = copy_tuple(tuple, tupdesc);
|
values[1] = copy_tuple(tuple, desc);
|
||||||
}
|
}
|
||||||
else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
|
else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
|
||||||
{
|
{
|
||||||
/* DELETE: (oldtup, NULL) */
|
/* DELETE: (oldtup, NULL) */
|
||||||
tuple = trigdata->tg_trigtuple;
|
tuple = trigdata->tg_trigtuple;
|
||||||
values[0] = copy_tuple(tuple, tupdesc);
|
values[0] = copy_tuple(tuple, desc);
|
||||||
nulls[1] = 'n';
|
nulls[1] = 'n';
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* UPDATE: (oldtup, newtup) */
|
/* UPDATE: (oldtup, newtup) */
|
||||||
tuple = trigdata->tg_newtuple;
|
tuple = trigdata->tg_newtuple;
|
||||||
values[0] = copy_tuple(trigdata->tg_trigtuple, tupdesc);
|
values[0] = copy_tuple(trigdata->tg_trigtuple, desc);
|
||||||
values[1] = copy_tuple(tuple, tupdesc);
|
values[1] = copy_tuple(tuple, desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* INSERT INTO reorg.log VALUES ($1, $2) */
|
/* INSERT INTO reorg.log VALUES ($1, $2) */
|
||||||
ret = SPI_execute_with_args(sql, 2, argtypes, values, nulls, false, 1);
|
reorg_execd(sql, 2, argtypes, values, nulls, SPI_OK_INSERT);
|
||||||
if (ret < 0)
|
|
||||||
elog(ERROR, "reorg_trigger: SPI_execp returned %d", ret);
|
|
||||||
|
|
||||||
SPI_finish();
|
SPI_finish();
|
||||||
|
|
||||||
@ -195,7 +179,7 @@ reorg_apply(PG_FUNCTION_ARGS)
|
|||||||
SPIPlanPtr plan_update = NULL;
|
SPIPlanPtr plan_update = NULL;
|
||||||
SPIPlanPtr plan_pop = NULL;
|
SPIPlanPtr plan_pop = NULL;
|
||||||
uint32 n, i;
|
uint32 n, i;
|
||||||
TupleDesc tupdesc;
|
TupleDesc desc;
|
||||||
Oid argtypes[3]; /* id, pk, row */
|
Oid argtypes[3]; /* id, pk, row */
|
||||||
Datum values[3]; /* id, pk, row */
|
Datum values[3]; /* id, pk, row */
|
||||||
char nulls[3]; /* id, pk, row */
|
char nulls[3]; /* id, pk, row */
|
||||||
@ -230,10 +214,10 @@ reorg_apply(PG_FUNCTION_ARGS)
|
|||||||
/* copy tuptable because we will call other sqls. */
|
/* copy tuptable because we will call other sqls. */
|
||||||
ntuples = SPI_processed;
|
ntuples = SPI_processed;
|
||||||
tuptable = SPI_tuptable;
|
tuptable = SPI_tuptable;
|
||||||
tupdesc = tuptable->tupdesc;
|
desc = tuptable->tupdesc;
|
||||||
argtypes[0] = SPI_gettypeid(tupdesc, 1); /* id */
|
argtypes[0] = SPI_gettypeid(desc, 1); /* id */
|
||||||
argtypes[1] = SPI_gettypeid(tupdesc, 2); /* pk */
|
argtypes[1] = SPI_gettypeid(desc, 2); /* pk */
|
||||||
argtypes[2] = SPI_gettypeid(tupdesc, 3); /* row */
|
argtypes[2] = SPI_gettypeid(desc, 3); /* row */
|
||||||
|
|
||||||
for (i = 0; i < ntuples; i++, n++)
|
for (i = 0; i < ntuples; i++, n++)
|
||||||
{
|
{
|
||||||
@ -241,11 +225,11 @@ reorg_apply(PG_FUNCTION_ARGS)
|
|||||||
bool isnull;
|
bool isnull;
|
||||||
|
|
||||||
tuple = tuptable->vals[i];
|
tuple = tuptable->vals[i];
|
||||||
values[0] = SPI_getbinval(tuple, tupdesc, 1, &isnull);
|
values[0] = SPI_getbinval(tuple, desc, 1, &isnull);
|
||||||
nulls[0] = ' ';
|
nulls[0] = ' ';
|
||||||
values[1] = SPI_getbinval(tuple, tupdesc, 2, &isnull);
|
values[1] = SPI_getbinval(tuple, desc, 2, &isnull);
|
||||||
nulls[1] = (isnull ? 'n' : ' ');
|
nulls[1] = (isnull ? 'n' : ' ');
|
||||||
values[2] = SPI_getbinval(tuple, tupdesc, 3, &isnull);
|
values[2] = SPI_getbinval(tuple, desc, 3, &isnull);
|
||||||
nulls[2] = (isnull ? 'n' : ' ');
|
nulls[2] = (isnull ? 'n' : ' ');
|
||||||
|
|
||||||
if (nulls[1] == 'n')
|
if (nulls[1] == 'n')
|
||||||
@ -479,40 +463,18 @@ reorg_indexdef(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_TEXT_P(cstring_to_text(str.data));
|
PG_RETURN_TEXT_P(cstring_to_text(str.data));
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SQL_GET_SWAPINFO "\
|
static Oid
|
||||||
SELECT X.oid, X.relfilenode, X.relfrozenxid, Y.oid, Y.relfilenode, Y.relfrozenxid \
|
getoid(HeapTuple tuple, TupleDesc desc, int column)
|
||||||
FROM pg_class X, pg_class Y \
|
{
|
||||||
WHERE X.oid = $1\
|
bool isnull;
|
||||||
AND Y.oid = ('reorg.table_' || X.oid)::regclass \
|
Datum datum = SPI_getbinval(tuple, desc, column, &isnull);
|
||||||
UNION ALL \
|
return isnull ? InvalidOid : DatumGetObjectId(datum);
|
||||||
SELECT T.oid, T.relfilenode, T.relfrozenxid, U.oid, U.relfilenode, U.relfrozenxid \
|
}
|
||||||
FROM pg_class X, pg_class T, pg_class Y, pg_class U \
|
|
||||||
WHERE X.oid = $1\
|
|
||||||
AND T.oid = X.reltoastrelid\
|
|
||||||
AND Y.oid = ('reorg.table_' || X.oid)::regclass\
|
|
||||||
AND U.oid = Y.reltoastrelid \
|
|
||||||
UNION ALL \
|
|
||||||
SELECT I.oid, I.relfilenode, I.relfrozenxid, J.oid, J.relfilenode, J.relfrozenxid \
|
|
||||||
FROM pg_class X, pg_class T, pg_class I, pg_class Y, pg_class U, pg_class J \
|
|
||||||
WHERE X.oid = $1\
|
|
||||||
AND T.oid = X.reltoastrelid\
|
|
||||||
AND I.oid = T.reltoastidxid\
|
|
||||||
AND Y.oid = ('reorg.table_' || X.oid)::regclass\
|
|
||||||
AND U.oid = Y.reltoastrelid\
|
|
||||||
AND J.oid = U.reltoastidxid \
|
|
||||||
UNION ALL \
|
|
||||||
SELECT X.oid, X.relfilenode, X.relfrozenxid, Y.oid, Y.relfilenode, Y.relfrozenxid \
|
|
||||||
FROM pg_index I, pg_class X, pg_class Y \
|
|
||||||
WHERE I.indrelid = $1\
|
|
||||||
AND I.indexrelid = X.oid\
|
|
||||||
AND Y.oid = ('reorg.index_' || X.oid)::regclass\
|
|
||||||
ORDER BY 1\
|
|
||||||
"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @fn Datum reorg_swap(PG_FUNCTION_ARGS)
|
* @fn Datum reorg_swap(PG_FUNCTION_ARGS)
|
||||||
* @brief Swapping relfilenode of table, toast, toast index
|
* @brief Swapping relfilenode of tables and relation ids of toast tables
|
||||||
* and table indexes on target table and temp table mutually.
|
* and toast indexes.
|
||||||
*
|
*
|
||||||
* reorg_swap(oid, relname)
|
* reorg_swap(oid, relname)
|
||||||
*
|
*
|
||||||
@ -525,13 +487,20 @@ reorg_swap(PG_FUNCTION_ARGS)
|
|||||||
Oid oid = PG_GETARG_OID(0);
|
Oid oid = PG_GETARG_OID(0);
|
||||||
const char *relname = get_quoted_relname(oid);
|
const char *relname = get_quoted_relname(oid);
|
||||||
const char *nspname = get_quoted_nspname(oid);
|
const char *nspname = get_quoted_nspname(oid);
|
||||||
|
Oid argtypes[1] = { OIDOID };
|
||||||
|
char nulls[1] = { ' ' };
|
||||||
|
Datum values[1];
|
||||||
|
SPITupleTable *tuptable;
|
||||||
|
TupleDesc desc;
|
||||||
|
HeapTuple tuple;
|
||||||
|
uint32 records;
|
||||||
|
uint32 i;
|
||||||
|
|
||||||
SPIPlanPtr plan_swapinfo;
|
Oid reltoastrelid1;
|
||||||
SPIPlanPtr plan_swap;
|
Oid reltoastidxid1;
|
||||||
Oid argtypes[3] = { OIDOID, OIDOID, XIDOID };
|
Oid oid2;
|
||||||
char nulls[3] = { ' ', ' ', ' ' };
|
Oid reltoastrelid2;
|
||||||
Datum values[3];
|
Oid reltoastidxid2;
|
||||||
int record;
|
|
||||||
|
|
||||||
/* authority check */
|
/* authority check */
|
||||||
must_be_superuser("reorg_swap");
|
must_be_superuser("reorg_swap");
|
||||||
@ -539,46 +508,85 @@ reorg_swap(PG_FUNCTION_ARGS)
|
|||||||
/* connect to SPI manager */
|
/* connect to SPI manager */
|
||||||
reorg_init();
|
reorg_init();
|
||||||
|
|
||||||
/* parepare */
|
/* swap relfilenode and dependencies for tables. */
|
||||||
plan_swapinfo = reorg_prepare(
|
|
||||||
SQL_GET_SWAPINFO,
|
|
||||||
1, argtypes);
|
|
||||||
plan_swap = reorg_prepare(
|
|
||||||
"UPDATE pg_class SET relfilenode = $2, relfrozenxid = $3 WHERE oid = $1",
|
|
||||||
3, argtypes);
|
|
||||||
|
|
||||||
/* swap relfilenode */
|
|
||||||
values[0] = ObjectIdGetDatum(oid);
|
values[0] = ObjectIdGetDatum(oid);
|
||||||
reorg_execp(plan_swapinfo, values, nulls, SPI_OK_SELECT);
|
reorg_execd(
|
||||||
|
"SELECT X.oid, X.reltoastrelid, TX.reltoastidxid,"
|
||||||
record = SPI_processed;
|
" Y.oid, Y.reltoastrelid, TY.reltoastidxid"
|
||||||
|
" FROM pg_class X, pg_class Y, pg_class TX, pg_class TY"
|
||||||
if (record > 0)
|
" WHERE X.oid = $1"
|
||||||
{
|
" AND X.reltoastrelid = TX.oid"
|
||||||
SPITupleTable *tuptable;
|
" AND Y.oid = ('reorg.table_' || X.oid)::regclass"
|
||||||
TupleDesc tupdesc;
|
" AND Y.reltoastrelid = TY.oid",
|
||||||
HeapTuple tuple;
|
1, argtypes, values, nulls, SPI_OK_SELECT);
|
||||||
char isnull;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
tuptable = SPI_tuptable;
|
tuptable = SPI_tuptable;
|
||||||
tupdesc = tuptable->tupdesc;
|
desc = tuptable->tupdesc;
|
||||||
|
records = SPI_processed;
|
||||||
|
|
||||||
for (i = 0; i < record; i++)
|
if (records == 0)
|
||||||
|
elog(ERROR, "reorg_swap : unexpected");
|
||||||
|
|
||||||
|
tuple = tuptable->vals[0];
|
||||||
|
|
||||||
|
reltoastrelid1 = getoid(tuple, desc, 2);
|
||||||
|
reltoastidxid1 = getoid(tuple, desc, 3);
|
||||||
|
oid2 = getoid(tuple, desc, 4);
|
||||||
|
reltoastrelid2 = getoid(tuple, desc, 5);
|
||||||
|
reltoastidxid2 = getoid(tuple, desc, 6);
|
||||||
|
|
||||||
|
swap_heap_or_index_files(oid, oid2);
|
||||||
|
CommandCounterIncrement();
|
||||||
|
|
||||||
|
/* swap relfilenode and dependencies for indxes. */
|
||||||
|
values[0] = ObjectIdGetDatum(oid);
|
||||||
|
reorg_execd(
|
||||||
|
"SELECT X.oid, Y.oid"
|
||||||
|
" FROM pg_index I, pg_class X, pg_class Y"
|
||||||
|
" WHERE I.indrelid = $1"
|
||||||
|
" AND I.indexrelid = X.oid"
|
||||||
|
" AND Y.oid = ('reorg.index_' || X.oid)::regclass",
|
||||||
|
1, argtypes, values, nulls, SPI_OK_SELECT);
|
||||||
|
|
||||||
|
tuptable = SPI_tuptable;
|
||||||
|
desc = tuptable->tupdesc;
|
||||||
|
records = SPI_processed;
|
||||||
|
|
||||||
|
for (i = 0; i < records; i++)
|
||||||
{
|
{
|
||||||
tuple = tuptable->vals[i];
|
tuple = tuptable->vals[i];
|
||||||
|
swap_heap_or_index_files(
|
||||||
/* target -> temp */
|
getoid(tuple, desc, 1),
|
||||||
values[0] = SPI_getbinval(tuple, tupdesc, 4, &isnull);
|
getoid(tuple, desc, 2));
|
||||||
values[1] = SPI_getbinval(tuple, tupdesc, 2, &isnull);
|
CommandCounterIncrement();
|
||||||
values[2] = SPI_getbinval(tuple, tupdesc, 3, &isnull);
|
|
||||||
reorg_execp(plan_swap, values, nulls, SPI_OK_UPDATE);
|
|
||||||
/* temp -> target */
|
|
||||||
values[0] = SPI_getbinval(tuple, tupdesc, 1, &isnull);
|
|
||||||
values[1] = SPI_getbinval(tuple, tupdesc, 5, &isnull);
|
|
||||||
values[2] = SPI_getbinval(tuple, tupdesc, 6, &isnull);
|
|
||||||
reorg_execp(plan_swap, values, nulls, SPI_OK_UPDATE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* swap names for toast tables and toast indexes */
|
||||||
|
if (reltoastrelid1 != InvalidOid)
|
||||||
|
{
|
||||||
|
char name[NAMEDATALEN];
|
||||||
|
int pid = getpid();
|
||||||
|
|
||||||
|
/* rename X to TEMP */
|
||||||
|
snprintf(name, NAMEDATALEN, "pg_toast_pid%d", pid);
|
||||||
|
RenameRelationInternal(reltoastrelid1, name, PG_TOAST_NAMESPACE);
|
||||||
|
snprintf(name, NAMEDATALEN, "pg_toast_pid%d_index", pid);
|
||||||
|
RenameRelationInternal(reltoastidxid1, name, PG_TOAST_NAMESPACE);
|
||||||
|
CommandCounterIncrement();
|
||||||
|
|
||||||
|
/* rename Y to X */
|
||||||
|
snprintf(name, NAMEDATALEN, "pg_toast_%u", oid);
|
||||||
|
RenameRelationInternal(reltoastrelid2, name, PG_TOAST_NAMESPACE);
|
||||||
|
snprintf(name, NAMEDATALEN, "pg_toast_%u_index", oid);
|
||||||
|
RenameRelationInternal(reltoastidxid2, name, PG_TOAST_NAMESPACE);
|
||||||
|
CommandCounterIncrement();
|
||||||
|
|
||||||
|
/* rename TEMP to Y */
|
||||||
|
snprintf(name, NAMEDATALEN, "pg_toast_%u", oid2);
|
||||||
|
RenameRelationInternal(reltoastrelid1, name, PG_TOAST_NAMESPACE);
|
||||||
|
snprintf(name, NAMEDATALEN, "pg_toast_%u_index", oid2);
|
||||||
|
RenameRelationInternal(reltoastidxid1, name, PG_TOAST_NAMESPACE);
|
||||||
|
CommandCounterIncrement();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* drop reorg trigger */
|
/* drop reorg trigger */
|
||||||
@ -691,6 +699,15 @@ reorg_execf(int expected, const char *format, ...)
|
|||||||
elog(ERROR, "pg_reorg: reorg_execf failed (sql=%s, code=%d, expected=%d)", sql.data, ret, expected);
|
elog(ERROR, "pg_reorg: reorg_execf failed (sql=%s, code=%d, expected=%d)", sql.data, ret, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* execute a query */
|
||||||
|
static void
|
||||||
|
reorg_execd(const char *src, int nargs, Oid *argtypes, Datum *values, const char *nulls, int expected)
|
||||||
|
{
|
||||||
|
int ret = SPI_execute_with_args(src, nargs, argtypes, values, nulls, expected == SPI_OK_SELECT, 0);
|
||||||
|
if (ret != expected)
|
||||||
|
elog(ERROR, "pg_reorg: reorg_execd failed (sql=%s, code=%d, expected=%d)", src, ret, expected);
|
||||||
|
}
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
get_quoted_relname(Oid oid)
|
get_quoted_relname(Oid oid)
|
||||||
{
|
{
|
||||||
@ -702,3 +719,205 @@ get_quoted_nspname(Oid oid)
|
|||||||
{
|
{
|
||||||
return quote_identifier(get_namespace_name(get_rel_namespace(oid)));
|
return quote_identifier(get_namespace_name(get_rel_namespace(oid)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is a copy of swap_relation_files in cluster.c, but it also swaps
|
||||||
|
* relfrozenxid.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
swap_heap_or_index_files(Oid r1, Oid r2)
|
||||||
|
{
|
||||||
|
Relation relRelation;
|
||||||
|
HeapTuple reltup1,
|
||||||
|
reltup2;
|
||||||
|
Form_pg_class relform1,
|
||||||
|
relform2;
|
||||||
|
Oid swaptemp;
|
||||||
|
CatalogIndexState indstate;
|
||||||
|
|
||||||
|
/* We need writable copies of both pg_class tuples. */
|
||||||
|
relRelation = heap_open(RelationRelationId, RowExclusiveLock);
|
||||||
|
|
||||||
|
reltup1 = SearchSysCacheCopy(RELOID,
|
||||||
|
ObjectIdGetDatum(r1),
|
||||||
|
0, 0, 0);
|
||||||
|
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);
|
||||||
|
if (!HeapTupleIsValid(reltup2))
|
||||||
|
elog(ERROR, "cache lookup failed for relation %u", r2);
|
||||||
|
relform2 = (Form_pg_class) GETSTRUCT(reltup2);
|
||||||
|
|
||||||
|
Assert(relform1->relkind == relform2->relkind);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Actually swap the fields in the two tuples
|
||||||
|
*/
|
||||||
|
swaptemp = relform1->relfilenode;
|
||||||
|
relform1->relfilenode = relform2->relfilenode;
|
||||||
|
relform2->relfilenode = swaptemp;
|
||||||
|
|
||||||
|
swaptemp = relform1->reltablespace;
|
||||||
|
relform1->reltablespace = relform2->reltablespace;
|
||||||
|
relform2->reltablespace = swaptemp;
|
||||||
|
|
||||||
|
swaptemp = relform1->reltoastrelid;
|
||||||
|
relform1->reltoastrelid = relform2->reltoastrelid;
|
||||||
|
relform2->reltoastrelid = swaptemp;
|
||||||
|
|
||||||
|
/* set rel1's frozen Xid to larger one */
|
||||||
|
if (TransactionIdIsNormal(relform1->relfrozenxid))
|
||||||
|
{
|
||||||
|
if (TransactionIdFollows(relform1->relfrozenxid,
|
||||||
|
relform2->relfrozenxid))
|
||||||
|
relform1->relfrozenxid = relform2->relfrozenxid;
|
||||||
|
else
|
||||||
|
relform2->relfrozenxid = relform1->relfrozenxid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* swap size statistics too, since new rel has freshly-updated stats */
|
||||||
|
{
|
||||||
|
int4 swap_pages;
|
||||||
|
float4 swap_tuples;
|
||||||
|
|
||||||
|
swap_pages = relform1->relpages;
|
||||||
|
relform1->relpages = relform2->relpages;
|
||||||
|
relform2->relpages = swap_pages;
|
||||||
|
|
||||||
|
swap_tuples = relform1->reltuples;
|
||||||
|
relform1->reltuples = relform2->reltuples;
|
||||||
|
relform2->reltuples = swap_tuples;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update the tuples in pg_class */
|
||||||
|
simple_heap_update(relRelation, &reltup1->t_self, reltup1);
|
||||||
|
simple_heap_update(relRelation, &reltup2->t_self, reltup2);
|
||||||
|
|
||||||
|
/* Keep system catalogs current */
|
||||||
|
indstate = CatalogOpenIndexes(relRelation);
|
||||||
|
CatalogIndexInsert(indstate, reltup1);
|
||||||
|
CatalogIndexInsert(indstate, reltup2);
|
||||||
|
CatalogCloseIndexes(indstate);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we have toast tables associated with the relations being swapped,
|
||||||
|
* change their dependency links to re-associate them with their new
|
||||||
|
* owning relations. Otherwise the wrong one will get dropped ...
|
||||||
|
*
|
||||||
|
* NOTE: it is possible that only one table has a toast table; this can
|
||||||
|
* happen in CLUSTER if there were dropped columns in the old table, and
|
||||||
|
* in ALTER TABLE when adding or changing type of columns.
|
||||||
|
*
|
||||||
|
* NOTE: at present, a TOAST table's only dependency is the one on its
|
||||||
|
* owning table. If more are ever created, we'd need to use something
|
||||||
|
* more selective than deleteDependencyRecordsFor() to get rid of only the
|
||||||
|
* link we want.
|
||||||
|
*/
|
||||||
|
if (relform1->reltoastrelid || relform2->reltoastrelid)
|
||||||
|
{
|
||||||
|
ObjectAddress baseobject,
|
||||||
|
toastobject;
|
||||||
|
long count;
|
||||||
|
|
||||||
|
/* Delete old dependencies */
|
||||||
|
if (relform1->reltoastrelid)
|
||||||
|
{
|
||||||
|
count = deleteDependencyRecordsFor(RelationRelationId,
|
||||||
|
relform1->reltoastrelid);
|
||||||
|
if (count != 1)
|
||||||
|
elog(ERROR, "expected one dependency record for TOAST table, found %ld",
|
||||||
|
count);
|
||||||
|
}
|
||||||
|
if (relform2->reltoastrelid)
|
||||||
|
{
|
||||||
|
count = deleteDependencyRecordsFor(RelationRelationId,
|
||||||
|
relform2->reltoastrelid);
|
||||||
|
if (count != 1)
|
||||||
|
elog(ERROR, "expected one dependency record for TOAST table, found %ld",
|
||||||
|
count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Register new dependencies */
|
||||||
|
baseobject.classId = RelationRelationId;
|
||||||
|
baseobject.objectSubId = 0;
|
||||||
|
toastobject.classId = RelationRelationId;
|
||||||
|
toastobject.objectSubId = 0;
|
||||||
|
|
||||||
|
if (relform1->reltoastrelid)
|
||||||
|
{
|
||||||
|
baseobject.objectId = r1;
|
||||||
|
toastobject.objectId = relform1->reltoastrelid;
|
||||||
|
recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relform2->reltoastrelid)
|
||||||
|
{
|
||||||
|
baseobject.objectId = r2;
|
||||||
|
toastobject.objectId = relform2->reltoastrelid;
|
||||||
|
recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Blow away the old relcache entries now. We need this kluge because
|
||||||
|
* relcache.c keeps a link to the smgr relation for the physical file, and
|
||||||
|
* that will be out of date as soon as we do CommandCounterIncrement.
|
||||||
|
* Whichever of the rels is the second to be cleared during cache
|
||||||
|
* invalidation will have a dangling reference to an already-deleted smgr
|
||||||
|
* relation. Rather than trying to avoid this by ordering operations just
|
||||||
|
* so, it's easiest to not have the relcache entries there at all.
|
||||||
|
* (Fortunately, since one of the entries is local in our transaction,
|
||||||
|
* it's sufficient to clear out our own relcache this way; the problem
|
||||||
|
* cannot arise for other backends when they see our update on the
|
||||||
|
* non-local relation.)
|
||||||
|
*/
|
||||||
|
RelationForgetRelation(r1);
|
||||||
|
RelationForgetRelation(r2);
|
||||||
|
|
||||||
|
/* Clean up. */
|
||||||
|
heap_freetuple(reltup1);
|
||||||
|
heap_freetuple(reltup2);
|
||||||
|
|
||||||
|
heap_close(relRelation, RowExclusiveLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM < 80400
|
||||||
|
static int
|
||||||
|
SPI_execute_with_args(const char *src,
|
||||||
|
int nargs, Oid *argtypes,
|
||||||
|
Datum *values, const char *nulls,
|
||||||
|
bool read_only, long tcount)
|
||||||
|
{
|
||||||
|
SPIPlanPtr plan;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
plan = SPI_prepare(src, nargs, argtypes);
|
||||||
|
if (plan == NULL)
|
||||||
|
return SPI_result;
|
||||||
|
ret = SPI_execute_plan(plan, values, nulls, read_only, tcount);
|
||||||
|
SPI_freeplan(plan);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static text *
|
||||||
|
cstring_to_text(const char * s)
|
||||||
|
{
|
||||||
|
int len = strlen(s);
|
||||||
|
text *result = palloc(len + VARHDRSZ);
|
||||||
|
|
||||||
|
SET_VARSIZE(result, len + VARHDRSZ);
|
||||||
|
memcpy(VARDATA(result), s, len);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
RenameRelationInternal(Oid myrelid, const char *newrelname, Oid namespaceId)
|
||||||
|
{
|
||||||
|
renamerel(myrelid, newrelname, OBJECT_TABLE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user