Skip to content

Commit a682742

Browse files
committed
half open ranges
1 parent fc9d807 commit a682742

File tree

9 files changed

+126
-67
lines changed

9 files changed

+126
-67
lines changed

expected/pathman_calamity.out

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -238,22 +238,6 @@ EXPLAIN (COSTS OFF) SELECT * FROM calamity.part_ok; /* check that pathman is ena
238238
-> Seq Scan on part_ok_3
239239
(5 rows)
240240

241-
ALTER TABLE calamity.wrong_partition
242-
ADD CONSTRAINT pathman_wrong_partition_1_check
243-
CHECK (val < 10); /* wrong constraint */
244-
SELECT add_to_pathman_config('calamity.part_test', 'val', '10');
245-
ERROR: Wrong constraint format for RANGE partition "wrong_partition"
246-
EXPLAIN (COSTS OFF) SELECT * FROM calamity.part_ok; /* check that pathman is enabled */
247-
QUERY PLAN
248-
-----------------------------
249-
Append
250-
-> Seq Scan on part_ok_0
251-
-> Seq Scan on part_ok_1
252-
-> Seq Scan on part_ok_2
253-
-> Seq Scan on part_ok_3
254-
(5 rows)
255-
256-
ALTER TABLE calamity.wrong_partition DROP CONSTRAINT pathman_wrong_partition_1_check;
257241
ALTER TABLE calamity.wrong_partition
258242
ADD CONSTRAINT pathman_wrong_partition_1_check
259243
CHECK (val = 1 OR val = 2); /* wrong constraint */

sql/pathman_calamity.sql

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,6 @@ EXPLAIN (COSTS OFF) SELECT * FROM calamity.part_ok; /* check that pathman is ena
8585
SELECT add_to_pathman_config('calamity.part_test', 'val', '10');
8686
EXPLAIN (COSTS OFF) SELECT * FROM calamity.part_ok; /* check that pathman is enabled */
8787

88-
ALTER TABLE calamity.wrong_partition
89-
ADD CONSTRAINT pathman_wrong_partition_1_check
90-
CHECK (val < 10); /* wrong constraint */
91-
SELECT add_to_pathman_config('calamity.part_test', 'val', '10');
92-
EXPLAIN (COSTS OFF) SELECT * FROM calamity.part_ok; /* check that pathman is enabled */
93-
ALTER TABLE calamity.wrong_partition DROP CONSTRAINT pathman_wrong_partition_1_check;
94-
9588
ALTER TABLE calamity.wrong_partition
9689
ADD CONSTRAINT pathman_wrong_partition_1_check
9790
CHECK (val = 1 OR val = 2); /* wrong constraint */

src/init.c

Lines changed: 70 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ static int cmp_range_entries(const void *p1, const void *p2, void *arg);
7575

7676
static bool validate_range_constraint(const Expr *expr,
7777
const PartRelationInfo *prel,
78-
Datum *min,
79-
Datum *max);
78+
Datum *lower, Datum *upper,
79+
bool *lower_null, bool *upper_null);
8080

8181
static bool validate_hash_constraint(const Expr *expr,
8282
const PartRelationInfo *prel,
@@ -375,14 +375,18 @@ fill_prel_with_partitions(const Oid *partitions,
375375

376376
case PT_RANGE:
377377
{
378-
Datum range_min, range_max;
378+
Datum lower, upper;
379+
bool lower_null, upper_null;
379380

380381
if (validate_range_constraint(con_expr, prel,
381-
&range_min, &range_max))
382+
&lower, &upper,
383+
&lower_null, &upper_null))
382384
{
383-
prel->ranges[i].child_oid = partitions[i];
384-
prel->ranges[i].min = range_min;
385-
prel->ranges[i].max = range_max;
385+
prel->ranges[i].child_oid = partitions[i];
386+
prel->ranges[i].min = lower;
387+
prel->ranges[i].max = upper;
388+
prel->ranges[i].infinite_min = lower_null;
389+
prel->ranges[i].infinite_max = upper_null;
386390
}
387391
else
388392
{
@@ -864,61 +868,91 @@ cmp_range_entries(const void *p1, const void *p2, void *arg)
864868

865869
Oid cmp_proc_oid = *(Oid *) arg;
866870

871+
/* If range is half open */
872+
if (v1->infinite_min)
873+
if (v2->infinite_min)
874+
return Int32GetDatum(0);
875+
return Int32GetDatum(-1);
876+
877+
/* Else if range is closed */
867878
return OidFunctionCall2(cmp_proc_oid, v1->min, v2->min);
868879
}
869880

870881
/*
871-
* Validates range constraint. It MUST have this exact format:
882+
* Validates range constraint. It MUST have one of the following formats:
872883
*
873884
* VARIABLE >= CONST AND VARIABLE < CONST
885+
* VARIABLE >= CONST
886+
* VARIABLE < CONST
874887
*
875-
* Writes 'min' & 'max' values on success.
888+
* Writes 'lower' & 'upper' and 'lower_null' & 'upper_null' values on success.
876889
*/
877890
static bool
878891
validate_range_constraint(const Expr *expr,
879892
const PartRelationInfo *prel,
880-
Datum *min,
881-
Datum *max)
893+
Datum *lower, Datum *upper,
894+
bool *lower_null, bool *upper_null)
882895
{
883896
const TypeCacheEntry *tce;
884-
const BoolExpr *boolexpr = (const BoolExpr *) expr;
885897
const OpExpr *opexpr;
886898
int strategy;
887899

888-
if (!expr)
889-
return false;
900+
/* Validates a single expression of kind VAR >= CONST or VAR < CONST */
901+
#define validate_range_expr(expr) \
902+
{ \
903+
Datum val; \
904+
opexpr = (OpExpr *) (expr); \
905+
strategy = get_op_opfamily_strategy(opexpr->opno, tce->btree_opf); \
906+
\
907+
/* Get const value */ \
908+
if (!read_opexpr_const(opexpr, prel, &val)) \
909+
return false; \
910+
\
911+
/* Set min or max depending on operator */ \
912+
switch (strategy) \
913+
{ \
914+
case BTGreaterEqualStrategyNumber: \
915+
*lower_null = false; \
916+
*lower = val; \
917+
break; \
918+
case BTLessStrategyNumber: \
919+
*upper_null = false; \
920+
*upper = val; \
921+
break; \
922+
default: \
923+
return false; \
924+
} \
925+
}
890926

891-
/* it should be an AND operator on top */
892-
if (!and_clause((Node *) expr))
927+
if (!expr)
893928
return false;
894-
929+
*lower_null = *upper_null = false;
895930
tce = lookup_type_cache(prel->atttype, TYPECACHE_BTREE_OPFAMILY);
896931

897-
/* check that left operand is >= operator */
898-
opexpr = (OpExpr *) linitial(boolexpr->args);
899-
strategy = get_op_opfamily_strategy(opexpr->opno, tce->btree_opf);
900-
901-
if (strategy == BTGreaterEqualStrategyNumber)
932+
/* It could be either AND operator on top or just an OpExpr */
933+
if (and_clause((Node *) expr))
902934
{
903-
if (!read_opexpr_const(opexpr, prel, min))
904-
return false;
905-
}
906-
else
907-
return false;
935+
const BoolExpr *boolexpr = (const BoolExpr *) expr;
936+
ListCell *lc;
937+
938+
foreach (lc, boolexpr->args)
939+
{
940+
Node *arg = lfirst(lc);
908941

909-
/* check that right operand is < operator */
910-
opexpr = (OpExpr *) lsecond(boolexpr->args);
911-
strategy = get_op_opfamily_strategy(opexpr->opno, tce->btree_opf);
942+
if(!IsA(arg, OpExpr))
943+
return false;
912944

913-
if (strategy == BTLessStrategyNumber)
945+
validate_range_expr(arg);
946+
}
947+
return true;
948+
}
949+
else if(IsA(expr, OpExpr))
914950
{
915-
if (!read_opexpr_const(opexpr, prel, max))
916-
return false;
951+
validate_range_expr(expr);
952+
return true;
917953
}
918-
else
919-
return false;
920954

921-
return true;
955+
return false;
922956
}
923957

924958
/*

src/partition_creation.c

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -834,11 +834,29 @@ build_range_check_constraint(Oid child_relid,
834834
return range_constr;
835835
}
836836

837+
// static int16_t
838+
// cmp_boundaries(FmgrInfo cmp_func, Datum s1, Datum s2, bool s1_infinite, bool s2_infinite)
839+
// {
840+
// if (s1_infinite && s2_infinite)
841+
// elog(ERROR,
842+
// "two half open ranges are overlap");
843+
844+
// if (s1_infinite)
845+
// return -1;
846+
847+
// if (s2_infinite)
848+
// return 1;
849+
850+
// return DatumGetInt16(FunctionCall2(&cmp_func, start_value, ranges[i].max));
851+
// }
852+
837853
/* Check if range overlaps with any partitions */
838854
bool
839855
check_range_available(Oid parent_relid,
840856
Datum start_value,
841857
Datum end_value,
858+
bool infinite_start,
859+
bool infinite_end,
842860
Oid value_type,
843861
bool raise_error)
844862
{
@@ -864,8 +882,32 @@ check_range_available(Oid parent_relid,
864882
ranges = PrelGetRangesArray(prel);
865883
for (i = 0; i < PrelChildrenCount(prel); i++)
866884
{
867-
int c1 = FunctionCall2(&cmp_func, start_value, ranges[i].max),
868-
c2 = FunctionCall2(&cmp_func, end_value, ranges[i].min);
885+
// int c1 = cmp_boundaries(cmp_func, start_value, ranges[i].max, infinite_start, ranges[i].infinite_max);
886+
// int c2 = cmp_boundaries(cmp_func, end_value, ranges[i].min, infinite_end, ranges[i].infinite_min);
887+
888+
/* If both ranges are half open then they are obviously overlap */
889+
// if (infinite_start && ranges[i].infinite_max)
890+
// return false;
891+
// if (infinite_end && ranges[i].infinite_min)
892+
// return false;
893+
894+
// int c1 = FunctionCall2(&cmp_func, start_value, ranges[i].max),
895+
// c2 = FunctionCall2(&cmp_func, end_value, ranges[i].min);
896+
int c1, c2;
897+
898+
/*
899+
* If the range we're checking starts with minus infinity or current
900+
* range is ends in plus infinity then the left boundary of the first
901+
* one is on the left
902+
*/
903+
c1 = (infinite_start || ranges[i].infinite_max) ?
904+
-1 : FunctionCall2(&cmp_func, start_value, ranges[i].max);
905+
/*
906+
* Similary check the right boundary of first range is on the right
907+
* of the beginning of second one
908+
*/
909+
c2 = (infinite_end || ranges[i].infinite_min) ?
910+
-1 : FunctionCall2(&cmp_func, end_value, ranges[i].max);
869911

870912
/* There's someone! */
871913
if (c1 < 0 && c2 > 0)

src/partition_creation.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@ Node * build_raw_range_check_tree(char *attname,
3535
Datum end_value,
3636
Oid value_type);
3737

38-
bool check_range_available(Oid partition_relid,
38+
bool check_range_available(Oid parent_relid,
3939
Datum start_value,
4040
Datum end_value,
41+
bool infinite_start,
42+
bool infinite_end,
4143
Oid value_type,
4244
bool raise_error);
4345

src/pg_pathman.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -446,8 +446,10 @@ select_range_partitions(const Datum value,
446446
Assert(cmp_func);
447447

448448
/* Corner cases */
449-
cmp_min = FunctionCall2(cmp_func, value, ranges[startidx].min),
450-
cmp_max = FunctionCall2(cmp_func, value, ranges[endidx].max);
449+
cmp_min = ranges[startidx].infinite_min ?
450+
1 : DatumGetInt32(FunctionCall2(cmp_func, value, ranges[startidx].min));
451+
cmp_max = ranges[endidx].infinite_max ?
452+
-1 : DatumGetInt32(FunctionCall2(cmp_func, value, ranges[endidx].max));
451453

452454
if ((cmp_min <= 0 && strategy == BTLessStrategyNumber) ||
453455
(cmp_min < 0 && (strategy == BTLessEqualStrategyNumber ||

src/pl_range_funcs.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,15 @@ check_range_available_pl(PG_FUNCTION_ARGS)
166166

167167
Datum start_value = PG_GETARG_DATUM(1),
168168
end_value = PG_GETARG_DATUM(2);
169+
bool start_null = PG_ARGISNULL(1),
170+
end_null = PG_ARGISNULL(2);
169171
Oid value_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
170172

171173
/* Raise ERROR if range overlaps with any partition */
172174
check_range_available(parent_relid,
173175
start_value,
174176
end_value,
177+
start_null, end_null,
175178
value_type,
176179
true);
177180

src/rangeset.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ typedef struct {
2828
uint32 upper; /* lossy + upper_bound */
2929
} IndexRange;
3030

31-
3231
/* Convenience macros for make_irange(...) */
3332
#define IR_LOSSY true
3433
#define IR_COMPLETE false
@@ -43,7 +42,6 @@ typedef struct {
4342
#define irange_lower(irange) ( (uint32) (irange.lower & IRANGE_BONDARY_MASK) )
4443
#define irange_upper(irange) ( (uint32) (irange.upper & IRANGE_BONDARY_MASK) )
4544

46-
4745
#define lfirst_irange(lc) ( *(IndexRange *) lfirst(lc) )
4846
#define lappend_irange(list, irange) ( lappend((list), alloc_irange(irange)) )
4947
#define lcons_irange(irange, list) ( lcons(alloc_irange(irange), (list)) )

src/relation_info.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,10 @@ typedef enum
3333
typedef struct
3434
{
3535
Oid child_oid;
36-
3736
Datum min,
3837
max;
38+
bool infinite_min,
39+
infinite_max;
3940
} RangeEntry;
4041

4142
/*

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy