diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index 832049c..8477195 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; /* @@ -121,14 +126,15 @@ static int32 qsort_partition_rbound_cmp(const void *a, const void *b, static Oid get_partition_operator(PartitionKey key, int col, StrategyNumber strategy, bool *need_relabel); static Expr *make_partition_op_expr(PartitionKey key, int keynum, - uint16 strategy, Expr *arg1, Expr *arg2); + uint16 strategy, Expr *arg1, Expr *arg2, bool is_def); static void get_range_key_properties(PartitionKey key, int keynum, PartitionRangeDatum *ldatum, PartitionRangeDatum *udatum, ListCell **partexprs_item, Expr **keyCol, Const **lower_val, Const **upper_val); -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 List *generate_partition_qual(Relation rel); @@ -174,6 +180,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 */ @@ -257,9 +265,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 *) @@ -467,6 +482,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)); /* @@ -504,6 +520,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); @@ -512,6 +530,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; } @@ -521,6 +544,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++) { @@ -679,6 +703,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; @@ -691,18 +716,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; @@ -717,7 +743,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; @@ -844,6 +877,112 @@ 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 + 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); + } + } } /* @@ -892,6 +1031,116 @@ 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 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 @@ -902,6 +1151,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); @@ -909,9 +1162,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); @@ -1239,7 +1499,8 @@ get_partition_operator(PartitionKey key, int col, StrategyNumber strategy, */ static Expr * make_partition_op_expr(PartitionKey key, int keynum, - uint16 strategy, Expr *arg1, Expr *arg2) + uint16 strategy, Expr *arg1, Expr *arg2, + bool is_def) { Oid operoid; bool need_relabel = false; @@ -1269,11 +1530,17 @@ make_partition_op_expr(PartitionKey key, int keynum, { ScalarArrayOpExpr *saopexpr; + if (is_def && ((operoid = get_negator(operoid)) == InvalidOid)) + ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), + errmsg("DEFAULT partition cannot be used" + " without negator of operator %s", + get_opname(operoid)))); + /* Build leftop = ANY (rightop) */ saopexpr = makeNode(ScalarArrayOpExpr); saopexpr->opno = operoid; saopexpr->opfuncid = get_opcode(operoid); - saopexpr->useOr = true; + saopexpr->useOr = !is_def; saopexpr->inputcollid = key->partcollation[0]; saopexpr->args = list_make2(arg1, arg2); saopexpr->location = -1; @@ -1305,7 +1572,8 @@ make_partition_op_expr(PartitionKey key, int keynum, * 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; @@ -1329,12 +1597,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); @@ -1343,14 +1613,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 @@ -1362,7 +1632,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 @@ -1381,13 +1651,13 @@ 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; /* Generate the main expression, i.e., keyCol = ANY (arr) */ opexpr = make_partition_op_expr(key, 0, BTEqualStrategyNumber, - keyCol, (Expr *) arr); + keyCol, (Expr *) arr, is_def); if (nulltest1) result = list_make2(nulltest1, opexpr); @@ -1596,7 +1866,7 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec) oldcxt = MemoryContextSwitchTo(estate->es_query_cxt); test_expr = make_partition_op_expr(key, i, BTEqualStrategyNumber, (Expr *) lower_val, - (Expr *) upper_val); + (Expr *) upper_val, false); fix_opfuncids((Node *) test_expr); test_exprstate = ExecInitExpr(test_expr, NULL); test_result = ExecEvalExprSwitchContext(test_exprstate, @@ -1620,7 +1890,8 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec) /* Equal, so generate keyCol = lower_val expression */ result = lappend(result, make_partition_op_expr(key, i, BTEqualStrategyNumber, - keyCol, (Expr *) lower_val)); + keyCol, (Expr *) lower_val, + false)); i++; } @@ -1678,7 +1949,8 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec) make_partition_op_expr(key, j, strategy, keyCol, - (Expr *) lower_val)); + (Expr *) lower_val, + false)); } if (need_next_upper_arm && upper_val) @@ -1700,7 +1972,8 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec) make_partition_op_expr(key, j, strategy, keyCol, - (Expr *) upper_val)); + (Expr *) upper_val, + false)); } @@ -2026,10 +2299,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 2822331..3af286f 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -578,6 +578,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type ForValues %type partbound_datum %type partbound_datum_list +%type default_partition %type PartitionRangeDatum %type range_datum_list @@ -2676,6 +2677,17 @@ ForValues: $$ = (Node *) n; } + | default_partition + { + PartitionBoundSpec *n = makeNode(PartitionBoundSpec); + + n->strategy = PARTITION_DEFAULT; + n->listdatums = $1; + n->location = @1; + + $$ = (Node *) n; + } + ; partbound_datum: @@ -2690,6 +2702,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 43b1475..3fcbe72 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -8646,12 +8646,28 @@ 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); + foreach(cell, spec->listdatums) + { + Node *value = lfirst(cell); + + if (isDefaultPartitionBound(value)) + { + appendStringInfoString(buf, "DEFAULT"); + is_def = true; + break; + } + } + + if (is_def) + break; + appendStringInfoString(buf, "FOR VALUES"); appendStringInfoString(buf, " IN ("); sep = ""; diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index b9e3491..0e93926 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -2020,7 +2020,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 ("); /* @@ -2429,7 +2429,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 25fb0a0..68f46fe 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 d396be3..24e6ac1 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