default_partition_v11.patch
application/x-download
Filename: default_partition_v11.patch
Type: application/x-download
Part: 0
Patch
Format: unified
Series: patch v11
| File | + | − |
|---|---|---|
| src/backend/catalog/partition.c | 328 | 26 |
| src/backend/parser/gram.y | 18 | 0 |
| src/backend/parser/parse_utilcmd.c | 52 | 39 |
| src/backend/utils/adt/ruleutils.c | 24 | 9 |
| src/bin/psql/tab-complete.c | 2 | 2 |
| src/include/catalog/partition.h | 3 | 0 |
| src/include/nodes/parsenodes.h | 1 | 0 |
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 8641ae1..1f1ed9f 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -34,6 +34,7 @@
#include "nodes/nodeFuncs.h"
#include "nodes/parsenodes.h"
#include "optimizer/clauses.h"
+#include "optimizer/prep.h"
#include "optimizer/planmain.h"
#include "optimizer/var.h"
#include "rewrite/rewriteManip.h"
@@ -90,6 +91,10 @@ typedef struct PartitionBoundInfoData
* for range partitioned tables */
int null_index; /* Index of the null-accepting partition; -1
* for range partitioned tables */
+ bool has_default; /* Is there a default partition? Currently false
+ * for a range partitioned table */
+ int default_index; /* Index of the default list partition. -1 for
+ * range partitioned tables */
} PartitionBoundInfoData;
/*
@@ -118,7 +123,8 @@ static int32 qsort_partition_list_value_cmp(const void *a, const void *b,
static int32 qsort_partition_rbound_cmp(const void *a, const void *b,
void *arg);
-static List *get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec);
+static List *get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec,
+ bool is_def, List *boundspecs);
static List *get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec);
static Oid get_partition_operator(PartitionKey key, int col,
StrategyNumber strategy, bool *need_relabel);
@@ -166,6 +172,8 @@ RelationBuildPartitionDesc(Relation rel)
/* List partitioning specific */
PartitionListValue **all_values = NULL;
bool found_null = false;
+ bool found_def = false;
+ int def_index = -1;
int null_index = -1;
/* Range partitioning specific */
@@ -249,9 +257,16 @@ RelationBuildPartitionDesc(Relation rel)
foreach(c, spec->listdatums)
{
+ Node *value = lfirst(c);
Const *val = lfirst(c);
PartitionListValue *list_value = NULL;
+ if (isDefaultPartitionBound(value))
+ {
+ found_def = true;
+ def_index = i;
+ continue;
+ }
if (!val->constisnull)
{
list_value = (PartitionListValue *)
@@ -459,6 +474,7 @@ RelationBuildPartitionDesc(Relation rel)
case PARTITION_STRATEGY_LIST:
{
boundinfo->has_null = found_null;
+ boundinfo->has_default = found_def;
boundinfo->indexes = (int *) palloc(ndatums * sizeof(int));
/*
@@ -496,6 +512,8 @@ RelationBuildPartitionDesc(Relation rel)
if (mapping[null_index] == -1)
mapping[null_index] = next_index++;
}
+ if (found_def && mapping[def_index] == -1)
+ mapping[def_index] = next_index++;
/* All partition must now have a valid mapping */
Assert(next_index == nparts);
@@ -504,6 +522,11 @@ RelationBuildPartitionDesc(Relation rel)
boundinfo->null_index = mapping[null_index];
else
boundinfo->null_index = -1;
+
+ if (found_def)
+ boundinfo->default_index = mapping[def_index];
+ else
+ boundinfo->default_index = -1;
break;
}
@@ -513,6 +536,7 @@ RelationBuildPartitionDesc(Relation rel)
sizeof(RangeDatumContent *));
boundinfo->indexes = (int *) palloc((ndatums + 1) *
sizeof(int));
+ boundinfo->has_default = found_def;
for (i = 0; i < ndatums; i++)
{
@@ -671,6 +695,7 @@ check_new_partition_bound(char *relname, Relation parent, Node *bound)
PartitionBoundSpec *spec = (PartitionBoundSpec *) bound;
PartitionKey key = RelationGetPartitionKey(parent);
PartitionDesc partdesc = RelationGetPartitionDesc(parent);
+ PartitionBoundInfo boundinfo = partdesc->boundinfo;
ParseState *pstate = make_parsestate(NULL);
int with = -1;
bool overlap = false;
@@ -683,18 +708,19 @@ check_new_partition_bound(char *relname, Relation parent, Node *bound)
if (partdesc->nparts > 0)
{
- PartitionBoundInfo boundinfo = partdesc->boundinfo;
ListCell *cell;
Assert(boundinfo &&
boundinfo->strategy == PARTITION_STRATEGY_LIST &&
- (boundinfo->ndatums > 0 || boundinfo->has_null));
+ (boundinfo->ndatums > 0 || boundinfo->has_null
+ || boundinfo->has_default));
foreach(cell, spec->listdatums)
{
- Const *val = lfirst(cell);
+ Node *value = lfirst(cell);
+ Const *val = (Const *) value;
- if (!val->constisnull)
+ if (!val->constisnull && !(isDefaultPartitionBound(value)))
{
int offset;
bool equal;
@@ -709,7 +735,14 @@ check_new_partition_bound(char *relname, Relation parent, Node *bound)
break;
}
}
- else if (boundinfo->has_null)
+ else if (isDefaultPartitionBound(value) &&
+ boundinfo->has_default)
+ {
+ overlap = true;
+ with = boundinfo->default_index;
+ break;
+ }
+ else if (val->constisnull && boundinfo->has_null)
{
overlap = true;
with = boundinfo->null_index;
@@ -836,6 +869,116 @@ check_new_partition_bound(char *relname, Relation parent, Node *bound)
relname, get_rel_name(partdesc->oids[with])),
parser_errposition(pstate, spec->location)));
}
+
+ /*
+ * When adding a list partition after default partition, scan the
+ * default partition for rows satisfying the new partition
+ * constraint. If found don't allow addition of a new partition.
+ * Otherwise continue with the creation of new partition.
+ */
+ if (spec->strategy == PARTITION_STRATEGY_LIST && partdesc->nparts > 0
+ && boundinfo->has_default)
+ {
+ List *partConstraint = NIL;
+ ExprContext *econtext;
+ EState *estate;
+ Relation defrel;
+ HeapScanDesc scan;
+ HeapTuple tuple;
+ ExprState *partqualstate = NULL;
+ Snapshot snapshot;
+ Oid defid;
+ MemoryContext oldCxt;
+ TupleTableSlot *tupslot;
+ TupleDesc tupdesc;
+ List *all_parts;
+ ListCell *lc;
+
+
+ partConstraint = generate_qual_for_defaultpart(parent, bound, &defid);
+ partConstraint = (List *) eval_const_expressions(NULL,
+ (Node *) partConstraint);
+ partConstraint = (List *) canonicalize_qual((Expr *) partConstraint);
+ partConstraint = list_make1(make_ands_explicit(partConstraint));
+
+ /*
+ * Generate the constraint and default execution states
+ */
+ defrel = heap_open(defid, AccessExclusiveLock);
+
+ if (defrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ all_parts = find_all_inheritors(RelationGetRelid(defrel),
+ AccessExclusiveLock, NULL);
+ else
+ all_parts = list_make1_oid(RelationGetRelid(defrel));
+
+ foreach(lc, all_parts)
+ {
+ Oid part_relid = lfirst_oid(lc);
+ Relation part_rel;
+ Expr *constr;
+ Expr *partition_constraint;
+
+ if (part_relid != RelationGetRelid(defrel))
+ part_rel = heap_open(part_relid, NoLock);
+ else if (part_relid == RELKIND_PARTITIONED_TABLE)
+ continue;
+ else
+ part_rel = defrel;
+
+ /*
+ * Skip if it's a partitioned table. Only RELKIND_RELATION
+ * relations (ie, leaf partitions) need to be scanned.
+ */
+ if (part_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ {
+ heap_close(part_rel, NoLock);
+ continue;
+ }
+ tupdesc = CreateTupleDescCopy(RelationGetDescr(part_rel));
+ constr = linitial(partConstraint);
+ partition_constraint = (Expr *)
+ map_partition_varattnos((List *) constr, 1,
+ part_rel, parent);
+ estate = CreateExecutorState();
+
+ /* Build expression execution states for partition check quals */
+ partqualstate = ExecPrepareExpr(partition_constraint,
+ estate);
+
+ econtext = GetPerTupleExprContext(estate);
+ snapshot = RegisterSnapshot(GetLatestSnapshot());
+ scan = heap_beginscan(part_rel, snapshot, 0, NULL);
+ tupslot = MakeSingleTupleTableSlot(tupdesc);
+
+ /*
+ * Switch to per-tuple memory context and reset it for each tuple
+ * produced, so we don't leak memory.
+ */
+ oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
+
+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ {
+ ExecStoreTuple(tuple, tupslot, InvalidBuffer, false);
+ econtext->ecxt_scantuple = tupslot;
+ if (partqualstate && !ExecCheck(partqualstate, econtext))
+ ereport(ERROR,
+ (errcode(ERRCODE_CHECK_VIOLATION),
+ errmsg("new default partition constraint is violated by some row")));
+ ResetExprContext(econtext);
+ CHECK_FOR_INTERRUPTS();
+ }
+ CacheInvalidateRelcache(part_rel);
+ MemoryContextSwitchTo(oldCxt);
+ heap_endscan(scan);
+ UnregisterSnapshot(snapshot);
+ ExecDropSingleTupleTableSlot(tupslot);
+ FreeExecutorState(estate);
+ heap_close(part_rel, NoLock);
+ }
+ CacheInvalidateRelcache(defrel);
+ heap_close(defrel, AccessExclusiveLock);
+ }
}
/*
@@ -884,6 +1027,117 @@ get_partition_parent(Oid relid)
}
/*
+ * Returns true if the partition bound is default
+ */
+bool
+isDefaultPartitionBound(Node *value)
+{
+ if (IsA(value, DefElem))
+ {
+ DefElem *defvalue = (DefElem *) value;
+ if(!strcmp(defvalue->defname, "DEFAULT"))
+ return true;
+ return false;
+ }
+ return false;
+}
+
+/*
+ * Return the bound spec list to be used
+ * in partition constraint of default partition
+ */
+List *
+get_qual_for_default(Relation parent, Oid *defid)
+{
+ List *inhoids;
+ ListCell *cell;
+ List *boundspecs = NIL;
+
+ inhoids = find_inheritance_children(RelationGetRelid(parent), AccessExclusiveLock);
+ foreach(cell, inhoids)
+ {
+ Oid inhrelid = lfirst_oid(cell);
+ HeapTuple tuple;
+ Datum datum;
+ bool isnull;
+ bool def_elem = false;
+ PartitionBoundSpec *bspec;
+ ListCell *cell1;
+
+ tuple = SearchSysCache1(RELOID, inhrelid);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for relation %u", inhrelid);
+
+ /*
+ * It is possible that the pg_class tuple of a partition has not been
+ * updated yet to set its relpartbound field. The only case where
+ * this happens is when we open the parent relation to check using its
+ * partition descriptor that a new partition's bound does not overlap
+ * some existing partition.
+ */
+ if (!((Form_pg_class) GETSTRUCT(tuple))->relispartition)
+ {
+ ReleaseSysCache(tuple);
+ continue;
+ }
+
+ datum = SysCacheGetAttr(RELOID, tuple,
+ Anum_pg_class_relpartbound,
+ &isnull);
+ Assert(!isnull);
+ bspec = (PartitionBoundSpec *) stringToNode(TextDatumGetCString(datum));
+ foreach(cell1, bspec->listdatums)
+ {
+ Node *value = lfirst(cell1);
+ if (isDefaultPartitionBound(value))
+ {
+ def_elem = true;
+ *defid = inhrelid;
+ break;
+ }
+ }
+ if (!def_elem)
+ {
+ foreach(cell1, bspec->listdatums)
+ {
+ Node *value = lfirst(cell1);
+ boundspecs = lappend(boundspecs, value);
+ }
+ }
+ ReleaseSysCache(tuple);
+ }
+ return boundspecs;
+}
+
+/*
+ * Return a list of executable expressions as new partition constraint
+ * for default partition while adding a new partition after default
+ */
+List *
+generate_qual_for_defaultpart(Relation parent, Node *bound, Oid * defid)
+{
+ PartitionKey key = RelationGetPartitionKey(parent);
+ PartitionBoundSpec *spec;
+ List *partConstraint = NIL;
+ ListCell *cell;
+ List *boundspecs = NIL;
+ List *bound_datums;
+
+ spec = (PartitionBoundSpec *) bound;
+ bound_datums = list_copy(spec->listdatums);
+
+ boundspecs = get_qual_for_default(parent, defid);
+
+ foreach(cell, bound_datums)
+ {
+ Node *value = lfirst(cell);
+ boundspecs = lappend(boundspecs, value);
+ }
+ partConstraint = get_qual_for_list(key, spec, true, boundspecs);
+ return partConstraint;
+}
+
+/*
* get_qual_from_partbound
* Given a parser node for partition bound, return the list of executable
* expressions as partition constraint
@@ -894,6 +1148,10 @@ get_qual_from_partbound(Relation rel, Relation parent, Node *bound)
PartitionBoundSpec *spec = (PartitionBoundSpec *) bound;
PartitionKey key = RelationGetPartitionKey(parent);
List *my_qual = NIL;
+ bool is_def = false;
+ Oid defid;
+ ListCell *cell;
+ List *boundspecs = NIL;
Assert(key != NULL);
@@ -901,9 +1159,16 @@ get_qual_from_partbound(Relation rel, Relation parent, Node *bound)
{
case PARTITION_STRATEGY_LIST:
Assert(spec->strategy == PARTITION_STRATEGY_LIST);
- my_qual = get_qual_for_list(key, spec);
+ foreach(cell, spec->listdatums)
+ {
+ Node *value = lfirst(cell);
+ if (isDefaultPartitionBound(value))
+ is_def = true;
+ }
+ if (is_def)
+ boundspecs = get_qual_for_default(parent, &defid);
+ my_qual = get_qual_for_list(key, spec, is_def, boundspecs);
break;
-
case PARTITION_STRATEGY_RANGE:
Assert(spec->strategy == PARTITION_STRATEGY_RANGE);
my_qual = get_qual_for_range(key, spec);
@@ -1151,7 +1416,8 @@ RelationGetPartitionDispatchInfo(Relation rel, int lockmode,
* Returns a list of expressions to use as a list partition's constraint.
*/
static List *
-get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec)
+get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec, bool is_def,
+ List *boundspecs)
{
List *result;
ArrayExpr *arr;
@@ -1177,12 +1443,14 @@ get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec)
else
keyCol = (Expr *) copyObject(linitial(key->partexprs));
+ if (!is_def)
+ boundspecs = spec->listdatums;
/*
* We must remove any NULL value in the list; we handle it separately
* below.
*/
prev = NULL;
- for (cell = list_head(spec->listdatums); cell; cell = next)
+ for (cell = list_head(boundspecs); cell; cell = next)
{
Const *val = (Const *) lfirst(cell);
@@ -1191,14 +1459,14 @@ get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec)
if (val->constisnull)
{
list_has_null = true;
- spec->listdatums = list_delete_cell(spec->listdatums,
+ boundspecs = list_delete_cell(boundspecs,
cell, prev);
}
else
prev = cell;
}
- if (!list_has_null)
+ if ((is_def && list_has_null) || (!is_def && !list_has_null))
{
/*
* Gin up a col IS NOT NULL test that will be AND'd with other
@@ -1210,7 +1478,7 @@ get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec)
nulltest1->argisrow = false;
nulltest1->location = -1;
}
- else
+ else if(!is_def && list_has_null)
{
/*
* Gin up a col IS NULL test that will be OR'd with other expressions
@@ -1229,7 +1497,7 @@ get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec)
: key->parttypid[0];
arr->array_collid = key->parttypcoll[0];
arr->element_typeid = key->parttypid[0];
- arr->elements = spec->listdatums;
+ arr->elements = boundspecs;
arr->multidims = false;
arr->location = -1;
@@ -1243,15 +1511,34 @@ get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec)
key->partcollation[0],
COERCE_EXPLICIT_CAST);
+ /* Build leftop <> ALL(rightop). This is for default partition */
+ if (is_def)
+ {
+ /* Find the negator for the partition operator above */
+ if(get_negator(operoid) == InvalidOid)
+ ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION),
+ errmsg("DEFAULT partition cannot be used without negator of operator %s",
+ get_opname(operoid))));
+ operoid = get_negator(operoid);
+ opexpr = makeNode(ScalarArrayOpExpr);
+ opexpr->opno = operoid;
+ opexpr->opfuncid = get_opcode(operoid);
+ opexpr->useOr = false;
+ opexpr->inputcollid = key->partcollation[0];
+ opexpr->args = list_make2(keyCol, arr);
+ opexpr->location = -1;
+ }
/* Build leftop = ANY (rightop) */
- opexpr = makeNode(ScalarArrayOpExpr);
- opexpr->opno = operoid;
- opexpr->opfuncid = get_opcode(operoid);
- opexpr->useOr = true;
- opexpr->inputcollid = key->partcollation[0];
- opexpr->args = list_make2(keyCol, arr);
- opexpr->location = -1;
-
+ else
+ {
+ opexpr = makeNode(ScalarArrayOpExpr);
+ opexpr->opno = operoid;
+ opexpr->opfuncid = get_opcode(operoid);
+ opexpr->useOr = true;
+ opexpr->inputcollid = key->partcollation[0];
+ opexpr->args = list_make2(keyCol, arr);
+ opexpr->location = -1;
+ }
if (nulltest1)
result = list_make2(nulltest1, opexpr);
else if (nulltest2)
@@ -1775,10 +2062,25 @@ get_partition_for_tuple(PartitionDispatch *pd,
*/
if (cur_index < 0)
{
- result = -1;
- *failed_at = parent;
- *failed_slot = slot;
- break;
+ /*
+ * If partitioned table has a default partition, return
+ * its sequence number
+ */
+ if (partdesc->boundinfo->has_default)
+ {
+ result = parent->indexes[partdesc->boundinfo->default_index];
+ if (result >= 0)
+ break;
+ else
+ parent = pd[-parent->indexes[partdesc->boundinfo->default_index]];
+ }
+ else
+ {
+ result = -1;
+ *failed_at = parent;
+ *failed_slot = slot;
+ break;
+ }
}
else if (parent->indexes[cur_index] >= 0)
{
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 65c004c..f2590e2 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -579,6 +579,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <node> ForValues
%type <node> partbound_datum
%type <list> partbound_datum_list
+%type <list> default_partition
%type <partrange_datum> PartitionRangeDatum
%type <list> range_datum_list
@@ -2677,6 +2678,17 @@ ForValues:
$$ = (Node *) n;
}
+ | default_partition
+ {
+ PartitionBoundSpec *n = makeNode(PartitionBoundSpec);
+
+ n->strategy = PARTITION_DEFAULT;
+ n->listdatums = $1;
+ n->location = @1;
+
+ $$ = (Node *) n;
+ }
+
;
partbound_datum:
@@ -2691,6 +2703,12 @@ partbound_datum_list:
{ $$ = lappend($1, $3); }
;
+default_partition:
+ DEFAULT {
+ Node *def = (Node *)makeDefElem("DEFAULT", NULL, @1);
+ $$ = list_make1(def);
+ }
+
range_datum_list:
PartitionRangeDatum { $$ = list_make1($1); }
| range_datum_list ',' PartitionRangeDatum
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 882955b..27c8460 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -33,6 +33,7 @@
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
+#include "catalog/partition.h"
#include "catalog/pg_am.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
@@ -3298,55 +3299,67 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound)
false, false);
if (spec->strategy != PARTITION_STRATEGY_LIST)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
- errmsg("invalid bound specification for a list partition"),
+ {
+ /*
+ * If the partition is the default partition switch
+ * back to PARTITION_STRATEGY_LIST
+ */
+ if (spec->strategy == PARTITION_DEFAULT)
+ result_spec->strategy = PARTITION_STRATEGY_LIST;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("invalid bound specification for a list partition"),
parser_errposition(pstate, exprLocation(bound))));
+ }
result_spec->listdatums = NIL;
foreach(cell, spec->listdatums)
{
- A_Const *con = (A_Const *) lfirst(cell);
- Node *value;
- ListCell *cell2;
- bool duplicate;
-
- value = (Node *) make_const(pstate, &con->val, con->location);
- value = coerce_to_target_type(pstate,
- value, exprType(value),
- get_partition_col_typid(key, 0),
- get_partition_col_typmod(key, 0),
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
-
- if (value == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("specified value cannot be cast to type \"%s\" of column \"%s\"",
- format_type_be(get_partition_col_typid(key, 0)),
- colname),
- parser_errposition(pstate,
- exprLocation((Node *) con))));
-
- /* Simplify the expression */
- value = (Node *) expression_planner((Expr *) value);
-
- /* Don't add to the result if the value is a duplicate */
- duplicate = false;
- foreach(cell2, result_spec->listdatums)
+ Node *value = lfirst(cell);
+ /* Perform the transformation only for non default partition */
+ if (!(isDefaultPartitionBound(value)))
{
- Const *value2 = (Const *) lfirst(cell2);
+ A_Const *con = (A_Const *) lfirst(cell);
+ ListCell *cell2;
+ bool duplicate;
+
+ value = (Node *) make_const(pstate, &con->val, con->location);
+ value = coerce_to_target_type(pstate,
+ value, exprType(value),
+ get_partition_col_typid(key, 0),
+ get_partition_col_typmod(key, 0),
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
- if (equal(value, value2))
+ if (value == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("specified value cannot be cast to type \"%s\" of column \"%s\"",
+ format_type_be(get_partition_col_typid(key, 0)),
+ colname),
+ parser_errposition(pstate,
+ exprLocation((Node *) con))));
+
+ /* Simplify the expression */
+ value = (Node *) expression_planner((Expr *) value);
+
+ /* Don't add to the result if the value is a duplicate */
+ duplicate = false;
+ foreach(cell2, result_spec->listdatums)
{
- duplicate = true;
- break;
+ Const *value2 = (Const *) lfirst(cell2);
+
+ if (equal(value, value2))
+ {
+ duplicate = true;
+ break;
+ }
}
+ if (duplicate)
+ continue;
}
- if (duplicate)
- continue;
-
result_spec->listdatums = lappend(result_spec->listdatums,
value);
}
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index cbde1ff..c3c9e70 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -24,6 +24,7 @@
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
+#include "catalog/partition.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_am.h"
#include "catalog/pg_authid.h"
@@ -8616,25 +8617,39 @@ get_rule_expr(Node *node, deparse_context *context,
PartitionBoundSpec *spec = (PartitionBoundSpec *) node;
ListCell *cell;
char *sep;
+ bool is_def = false;
switch (spec->strategy)
{
case PARTITION_STRATEGY_LIST:
Assert(spec->listdatums != NIL);
-
- appendStringInfoString(buf, "FOR VALUES");
- appendStringInfoString(buf, " IN (");
- sep = "";
foreach(cell, spec->listdatums)
{
- Const *val = lfirst(cell);
+ Node *value = lfirst(cell);
- appendStringInfoString(buf, sep);
- get_const_expr(val, context, -1);
- sep = ", ";
+ if (isDefaultPartitionBound(value))
+ {
+ appendStringInfoString(buf, "DEFAULT");
+ is_def = true;
+ break;
+ }
}
+ if(!is_def)
+ {
+ appendStringInfoString(buf, "FOR VALUES");
+ appendStringInfoString(buf, " IN (");
+ sep = "";
+ foreach(cell, spec->listdatums)
+ {
+ Const *val = lfirst(cell);
- appendStringInfoString(buf, ")");
+ appendStringInfoString(buf, sep);
+ get_const_expr(val, context, -1);
+ sep = ", ";
+ }
+
+ appendStringInfoString(buf, ")");
+ }
break;
case PARTITION_STRATEGY_RANGE:
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 3bd5277..cc0af61 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -2001,7 +2001,7 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
/* Limited completion support for partition bound specification */
else if (TailMatches3("ATTACH", "PARTITION", MatchAny))
- COMPLETE_WITH_CONST("FOR VALUES");
+ COMPLETE_WITH_LIST2("FOR VALUES", "DEFAULT");
else if (TailMatches2("FOR", "VALUES"))
COMPLETE_WITH_LIST2("FROM (", "IN (");
/*
@@ -2398,7 +2398,7 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_tables, "");
/* Limited completion support for partition bound specification */
else if (TailMatches3("PARTITION", "OF", MatchAny))
- COMPLETE_WITH_CONST("FOR VALUES");
+ COMPLETE_WITH_LIST2("FOR VALUES", "DEFAULT");
/* CREATE TABLESPACE */
else if (Matches3("CREATE", "TABLESPACE", MatchAny))
diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h
index 421644c..7cb4f0a 100644
--- a/src/include/catalog/partition.h
+++ b/src/include/catalog/partition.h
@@ -74,9 +74,12 @@ extern void RelationBuildPartitionDesc(Relation relation);
extern bool partition_bounds_equal(PartitionKey key,
PartitionBoundInfo p1, PartitionBoundInfo p2);
+extern bool isDefaultPartitionBound(Node *value);
extern void check_new_partition_bound(char *relname, Relation parent, Node *bound);
extern Oid get_partition_parent(Oid relid);
extern List *get_qual_from_partbound(Relation rel, Relation parent, Node *bound);
+extern List *generate_qual_for_defaultpart(Relation parent, Node *bound, Oid *defid);
+extern List *get_qual_for_default(Relation parent, Oid *defid);
extern List *map_partition_varattnos(List *expr, int target_varno,
Relation partrel, Relation parent);
extern List *RelationGetPartitionQual(Relation rel);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 46c23c2..8236af3 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -780,6 +780,7 @@ typedef struct PartitionSpec
#define PARTITION_STRATEGY_LIST 'l'
#define PARTITION_STRATEGY_RANGE 'r'
+#define PARTITION_DEFAULT 'd'
/*
* PartitionBoundSpec - a partition bound specification