Skip to content

Commit

Permalink
Implement ALTER TABLE|INDEX ... SET|RESET ().
Browse files Browse the repository at this point in the history
PostgreSQL implements support for several relation kinds in a single
statement, such as in the AlterTableStmt case, which supports both tables
and indexes and more (see ATExecSetRelOptions in PostgreSQL source code file
src/backend/commands/tablecmds.c for an example of that).

As a consequence, this patch implements support for setting and resetting
storage parameters on both relation kinds.
  • Loading branch information
DimCitus committed Jan 17, 2018
1 parent 17266e3 commit 952da72
Show file tree
Hide file tree
Showing 5 changed files with 446 additions and 24 deletions.
109 changes: 98 additions & 11 deletions src/backend/distributed/executor/multi_utility.c
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ static void ErrorIfUnstableCreateOrAlterExtensionStmt(Node *parsetree);
static void ErrorIfUnsupportedIndexStmt(IndexStmt *createIndexStatement);
static void ErrorIfUnsupportedDropIndexStmt(DropStmt *dropIndexStatement);
static void ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement);
static void ErrorIfUnsupportedAlterIndexStmt(AlterTableStmt *alterTableStatement);
static void ErrorIfAlterDropsPartitionColumn(AlterTableStmt *alterTableStatement);
static void ErrorIfUnsupportedSeqStmt(CreateSeqStmt *createSeqStmt);
static void ErrorIfDistributedAlterSeqOwnedBy(AlterSeqStmt *alterSeqStmt);
Expand Down Expand Up @@ -366,7 +367,8 @@ multi_ProcessUtility(PlannedStmt *pstmt,
if (IsA(parsetree, AlterTableStmt))
{
AlterTableStmt *alterTableStmt = (AlterTableStmt *) parsetree;
if (alterTableStmt->relkind == OBJECT_TABLE)
if (alterTableStmt->relkind == OBJECT_TABLE ||
alterTableStmt->relkind == OBJECT_INDEX)
{
ddlJobs = PlanAlterTableStmt(alterTableStmt, queryString);
}
Expand Down Expand Up @@ -1238,6 +1240,7 @@ PlanAlterTableStmt(AlterTableStmt *alterTableStatement, const char *alterTableCo
LOCKMODE lockmode = 0;
Oid leftRelationId = InvalidOid;
Oid rightRelationId = InvalidOid;
char leftRelationKind;
bool isDistributedRelation = false;
List *commandList = NIL;
ListCell *commandCell = NULL;
Expand All @@ -1255,13 +1258,38 @@ PlanAlterTableStmt(AlterTableStmt *alterTableStatement, const char *alterTableCo
return NIL;
}

/*
* AlterTableStmt applies also to INDEX relations, and we have support for
* SET/SET storage parameters in Citus, so we might have to check for
* another relation here.
*/
leftRelationKind = get_rel_relkind(leftRelationId);
if (leftRelationKind == RELKIND_INDEX)
{
leftRelationId = IndexGetRelation(leftRelationId, false);
}

isDistributedRelation = IsDistributedTable(leftRelationId);
if (!isDistributedRelation)
{
return NIL;
}

ErrorIfUnsupportedAlterTableStmt(alterTableStatement);
/*
* The PostgreSQL parser dispatches several commands into the node type
* AlterTableStmt, from ALTER INDEX to ALTER SEQUENCE or ALTER VIEW. Here
* we have a special implementation for ALTER INDEX, and a specific error
* message in case of unsupported sub-command.
*/
if (leftRelationKind == RELKIND_INDEX)
{
ErrorIfUnsupportedAlterIndexStmt(alterTableStatement);
}
else
{
/* this function also accepts more than just RELKIND_RELATION... */
ErrorIfUnsupportedAlterTableStmt(alterTableStatement);
}

/*
* We check if there is a ADD FOREIGN CONSTRAINT command in sub commands list.
Expand Down Expand Up @@ -2011,16 +2039,18 @@ ErrorIfUnsupportedDropIndexStmt(DropStmt *dropIndexStatement)


/*
* ErrorIfUnsupportedAlterTableStmt checks if the corresponding alter table statement
* is supported for distributed tables and errors out if it is not. Currently,
* only the following commands are supported.
* ErrorIfUnsupportedAlterTableStmt checks if the corresponding alter table
* statement is supported for distributed tables and errors out if it is not.
* Currently, only the following commands are supported.
*
* ALTER TABLE ADD|DROP COLUMN
* ALTER TABLE ALTER COLUMN SET DATA TYPE
* ALTER TABLE SET|DROP NOT NULL
* ALTER TABLE SET|DROP DEFAULT
* ALTER TABLE ADD|DROP CONSTRAINT
* ALTER TABLE REPLICA IDENTITY
* ALTER TABLE SET ()
* ALTER TABLE RESET ()
*/
static void
ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement)
Expand Down Expand Up @@ -2170,14 +2200,71 @@ ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement)
break;
}

case AT_SetRelOptions: /* SET (...) */
case AT_ResetRelOptions: /* RESET (...) */
case AT_ReplaceRelOptions: /* replace entire option list */
{
/* this command is supported by Citus */
break;
}

default:
{
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("alter table command is currently unsupported"),
errdetail("Only ADD|DROP COLUMN, SET|DROP NOT NULL, "
"SET|DROP DEFAULT, ADD|DROP CONSTRAINT, "
"ATTACH|DETACH PARTITION and TYPE subcommands "
"are supported.")));
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("alter table command is currently unsupported"),
errdetail("Only ADD|DROP COLUMN, SET|DROP NOT NULL, "
"SET|DROP DEFAULT, ADD|DROP CONSTRAINT, "
"SET (), RESET (), "
"ATTACH|DETACH PARTITION and TYPE subcommands "
"are supported.")));
}
}
}
}


/*
* ErrorIfUnsupportedAlterIndexStmt checks if the corresponding alter index
* statement is supported for distributed tables and errors out if it is not.
* Currently, only the following commands are supported.
*
* ALTER INDEX SET ()
* ALTER INDEX RESET ()
*/
static void
ErrorIfUnsupportedAlterIndexStmt(AlterTableStmt *alterTableStatement)
{
List *commandList = alterTableStatement->cmds;
ListCell *commandCell = NULL;

/* error out if any of the subcommands are unsupported */
foreach(commandCell, commandList)
{
AlterTableCmd *command = (AlterTableCmd *) lfirst(commandCell);
AlterTableType alterTableType = command->subtype;

switch (alterTableType)
{
case AT_SetRelOptions: /* SET (...) */
case AT_ResetRelOptions: /* RESET (...) */
case AT_ReplaceRelOptions: /* replace entire option list */
{
/* this command is supported by Citus */
break;
}

/* unsupported create index statements */
case AT_SetTableSpace:
default:
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("alter index ... set tablespace ... "
"is currently unsupported"),
errdetail("Only RENAME TO, SET (), and RESET () "
"are supported.")));
return; /* keep compiler happy */
}
}
}
Expand Down
143 changes: 142 additions & 1 deletion src/backend/distributed/utils/citus_ruleutils.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "catalog/pg_extension.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_index.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "commands/extension.h"
#include "distributed/citus_ruleutils.h"
Expand All @@ -45,6 +46,7 @@
#include "nodes/parsenodes.h"
#include "nodes/pg_list.h"
#include "parser/parse_utilcmd.h"
#include "parser/parser.h"
#include "storage/lock.h"
#include "utils/acl.h"
#include "utils/array.h"
Expand All @@ -62,7 +64,8 @@

static void AppendOptionListToString(StringInfo stringData, List *options);
static const char * convert_aclright_to_string(int aclright);

static void simple_quote_literal(StringInfo buf, const char *val);
static char * flatten_reloptions(Oid relid);

/*
* pg_get_extensiondef_string finds the foreign data wrapper that corresponds to
Expand Down Expand Up @@ -474,6 +477,20 @@ pg_get_tableschemadef_string(Oid tableRelationId, bool includeSequenceDefaults)
appendStringInfo(&buffer, " PARTITION BY %s ", partitioningInformation);
}
#endif

/*
* Add any reloptions (storage parameters) defined on the table in a WITH
* clause.
*/
{
char *reloptions = flatten_reloptions(tableRelationId);
if (reloptions)
{
appendStringInfo(&buffer, " WITH (%s)", reloptions);
pfree(reloptions);
}
}

relation_close(relation, AccessShareLock);

return (buffer.data);
Expand Down Expand Up @@ -1112,3 +1129,127 @@ pg_get_replica_identity_command(Oid tableRelationId)

return (buf->len > 0) ? buf->data : NULL;
}


/*
* Generate a C string representing a relation's reloptions, or NULL if none.
*
* This function comes from PostgreSQL source code in
* src/backend/utils/adt/ruleutils.c
*/
static char *
flatten_reloptions(Oid relid)
{
char *result = NULL;
HeapTuple tuple;
Datum reloptions;
bool isnull;

tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (!HeapTupleIsValid(tuple))
{
elog(ERROR, "cache lookup failed for relation %u", relid);
}

reloptions = SysCacheGetAttr(RELOID, tuple,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
{
StringInfoData buf;
Datum *options;
int noptions;
int i;

initStringInfo(&buf);

deconstruct_array(DatumGetArrayTypeP(reloptions),
TEXTOID, -1, false, 'i',
&options, NULL, &noptions);

for (i = 0; i < noptions; i++)
{
char *option = TextDatumGetCString(options[i]);
char *name;
char *separator;
char *value;

/*
* Each array element should have the form name=value. If the "="
* is missing for some reason, treat it like an empty value.
*/
name = option;
separator = strchr(option, '=');
if (separator)
{
*separator = '\0';
value = separator + 1;
}
else
{
value = "";
}

if (i > 0)
{
appendStringInfoString(&buf, ", ");
}
appendStringInfo(&buf, "%s=", quote_identifier(name));

/*
* In general we need to quote the value; but to avoid unnecessary
* clutter, do not quote if it is an identifier that would not
* need quoting. (We could also allow numbers, but that is a bit
* trickier than it looks --- for example, are leading zeroes
* significant? We don't want to assume very much here about what
* custom reloptions might mean.)
*/
if (quote_identifier(value) == value)
{
appendStringInfoString(&buf, value);
}
else
{
simple_quote_literal(&buf, value);
}

pfree(option);
}

result = buf.data;
}

ReleaseSysCache(tuple);

return result;
}


/*
* simple_quote_literal - Format a string as a SQL literal, append to buf
*
* This function comes from PostgreSQL source code in
* src/backend/utils/adt/ruleutils.c
*/
static void
simple_quote_literal(StringInfo buf, const char *val)
{
const char *valptr;

/*
* We form the string literal according to the prevailing setting of
* standard_conforming_strings; we never use E''. User is responsible for
* making sure result is used correctly.
*/
appendStringInfoChar(buf, '\'');
for (valptr = val; *valptr; valptr++)
{
char ch = *valptr;

if (SQL_STR_DOUBLE(ch, !standard_conforming_strings))
{
appendStringInfoChar(buf, ch);
}
appendStringInfoChar(buf, ch);
}
appendStringInfoChar(buf, '\'');
}
2 changes: 1 addition & 1 deletion src/test/regress/expected/multi_reference_table.out
Original file line number Diff line number Diff line change
Expand Up @@ -1385,7 +1385,7 @@ SELECT "Column", "Type", "Modifiers" FROM table_desc WHERE relid='reference_sche
-- as we expect, setting WITH OIDS does not work for reference tables
ALTER TABLE reference_schema.reference_table_ddl SET WITH OIDS;
ERROR: alter table command is currently unsupported
DETAIL: Only ADD|DROP COLUMN, SET|DROP NOT NULL, SET|DROP DEFAULT, ADD|DROP CONSTRAINT, ATTACH|DETACH PARTITION and TYPE subcommands are supported.
DETAIL: Only ADD|DROP COLUMN, SET|DROP NOT NULL, SET|DROP DEFAULT, ADD|DROP CONSTRAINT, SET (), RESET (), ATTACH|DETACH PARTITION and TYPE subcommands are supported.
-- now test the renaming of the table, and back to the expected name
ALTER TABLE reference_schema.reference_table_ddl RENAME TO reference_table_ddl_test;
ALTER TABLE reference_schema.reference_table_ddl_test RENAME TO reference_table_ddl;
Expand Down
Loading

0 comments on commit 952da72

Please sign in to comment.