Skip to content

Commit 2e6d2f8

Browse files
committed
try reloading cached 'prel' entry when about to spawn a new partition for INSERT (issue #57), extract function perform_type_cast(), select_partition_for_insert() & find_partitions_for_value() now take 'value_type', improve handle_const(), xact_lock_partitioned_rel() and xact_lock_rel_exclusive() return LockAcquireResult
1 parent 681aa43 commit 2e6d2f8

12 files changed

+238
-134
lines changed

src/copy_stmt_hooking.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,7 @@ PathmanCopyFrom(CopyState cstate, Relation parent_rel,
508508
/* Search for a matching partition */
509509
rri_holder_child = select_partition_for_insert(prel, &parts_storage,
510510
values[prel->attnum - 1],
511+
prel->atttype,
511512
estate, true);
512513
child_result_rel = rri_holder_child->result_rel_info;
513514
estate->es_result_relation_info = child_result_rel;

src/init.c

Lines changed: 11 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
#include "catalog/pg_type.h"
2828
#include "miscadmin.h"
2929
#include "optimizer/clauses.h"
30-
#include "parser/parse_coerce.h"
3130
#include "utils/datum.h"
3231
#include "utils/inval.h"
3332
#include "utils/builtins.h"
@@ -925,6 +924,7 @@ read_opexpr_const(const OpExpr *opexpr,
925924
const Node *right;
926925
const Var *part_attr; /* partitioned column */
927926
const Const *constant;
927+
bool cast_success;
928928

929929
if (list_length(opexpr->args) != 2)
930930
return false;
@@ -960,52 +960,18 @@ read_opexpr_const(const OpExpr *opexpr,
960960

961961
constant = (Const *) right;
962962

963-
/* Check that types are binary coercible */
964-
if (IsBinaryCoercible(constant->consttype, prel->atttype))
965-
{
966-
*val = constant->constvalue;
967-
}
968-
/* If not, try to perfrom a type cast */
969-
else
970-
{
971-
CoercionPathType ret;
972-
Oid castfunc = InvalidOid;
973-
974-
ret = find_coercion_pathway(prel->atttype, constant->consttype,
975-
COERCION_EXPLICIT, &castfunc);
976-
977-
switch (ret)
978-
{
979-
/* There's a function */
980-
case COERCION_PATH_FUNC:
981-
{
982-
/* Perform conversion */
983-
Assert(castfunc != InvalidOid);
984-
*val = OidFunctionCall1(castfunc, constant->constvalue);
985-
}
986-
break;
963+
/* Cast Const to a proper type if needed */
964+
*val = perform_type_cast(constant->constvalue,
965+
getBaseType(constant->consttype),
966+
getBaseType(prel->atttype),
967+
&cast_success);
987968

988-
/* Types are binary compatible (no implicit cast) */
989-
case COERCION_PATH_RELABELTYPE:
990-
{
991-
/* We don't perform any checks here */
992-
*val = constant->constvalue;
993-
}
994-
break;
995-
996-
/* TODO: implement these if needed */
997-
case COERCION_PATH_ARRAYCOERCE:
998-
case COERCION_PATH_COERCEVIAIO:
969+
if (!cast_success)
970+
{
971+
elog(WARNING, "Constant type in some check constraint "
972+
"does not match the partitioned column's type");
999973

1000-
/* There's no cast available */
1001-
case COERCION_PATH_NONE:
1002-
default:
1003-
{
1004-
elog(WARNING, "Constant type in some check constraint "
1005-
"does not match the partitioned column's type");
1006-
return false;
1007-
}
1008-
}
974+
return false;
1009975
}
1010976

1011977
return true;

src/partition_filter.c

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,9 @@ scan_result_parts_storage(Oid partid, ResultPartsStorage *parts_storage)
282282
* Find matching partitions for 'value' using PartRelationInfo.
283283
*/
284284
Oid *
285-
find_partitions_for_value(Datum value, const PartRelationInfo *prel,
286-
ExprContext *econtext, int *nparts)
285+
find_partitions_for_value(Datum value, Oid value_type,
286+
const PartRelationInfo *prel,
287+
int *nparts)
287288
{
288289
#define CopyToTempConst(const_field, attr_field) \
289290
( temp_const.const_field = prel->attr_field )
@@ -308,7 +309,7 @@ find_partitions_for_value(Datum value, const PartRelationInfo *prel,
308309
CopyToTempConst(constbyval, attbyval);
309310

310311
/* We use 0 since varno doesn't matter for Const */
311-
InitWalkerContext(&wcxt, 0, prel, econtext, true);
312+
InitWalkerContext(&wcxt, 0, prel, NULL, true);
312313
ranges = walk_expr_tree((Expr *) &temp_const, &wcxt)->rangeset;
313314
return get_partition_oids(ranges, nparts, prel, false);
314315
}
@@ -429,9 +430,9 @@ partition_filter_exec(CustomScanState *node)
429430
old_cxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
430431

431432
/* Search for a matching partition */
432-
rri_holder = select_partition_for_insert(prel,
433-
&state->result_parts,
434-
value, estate, true);
433+
rri_holder = select_partition_for_insert(prel, &state->result_parts,
434+
value, prel->atttype,
435+
estate, true);
435436
estate->es_result_relation_info = rri_holder->result_rel_info;
436437

437438
/* Switch back and clean up per-tuple context */
@@ -475,20 +476,18 @@ partition_filter_explain(CustomScanState *node, List *ancestors, ExplainState *e
475476
ResultRelInfoHolder *
476477
select_partition_for_insert(const PartRelationInfo *prel,
477478
ResultPartsStorage *parts_storage,
478-
Datum value, EState *estate,
479+
Datum value, Oid value_type,
480+
EState *estate,
479481
bool spawn_partitions)
480482
{
481483
MemoryContext old_cxt;
482-
ExprContext *econtext;
483484
ResultRelInfoHolder *rri_holder;
484485
Oid selected_partid = InvalidOid;
485486
Oid *parts;
486487
int nparts;
487488

488-
econtext = GetPerTupleExprContext(estate);
489-
490489
/* Search for matching partitions */
491-
parts = find_partitions_for_value(value, prel, econtext, &nparts);
490+
parts = find_partitions_for_value(value, value_type, prel, &nparts);
492491

493492
if (nparts > 1)
494493
elog(ERROR, ERR_PART_ATTR_MULTIPLE);

src/partition_filter.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,9 @@ ResultRelInfoHolder * scan_result_parts_storage(Oid partid,
106106
ResultPartsStorage *storage);
107107

108108
/* Find suitable partition using 'value' */
109-
Oid *find_partitions_for_value(Datum value, const PartRelationInfo *prel,
110-
ExprContext *econtext, int *nparts);
109+
Oid * find_partitions_for_value(Datum value, Oid value_type,
110+
const PartRelationInfo *prel,
111+
int *nparts);
111112

112113
Plan * make_partition_filter(Plan *subplan,
113114
Oid partitioned_table,
@@ -131,7 +132,8 @@ void partition_filter_explain(CustomScanState *node,
131132

132133
ResultRelInfoHolder * select_partition_for_insert(const PartRelationInfo *prel,
133134
ResultPartsStorage *parts_storage,
134-
Datum value, EState *estate,
135+
Datum value, Oid value_type,
136+
EState *estate,
135137
bool spawn_partitions);
136138

137139
#endif

src/pathman_workers.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ partition_table_concurrently(PG_FUNCTION_ARGS)
610610
/* Check if relation is a partitioned table */
611611
shout_if_prel_is_invalid(relid,
612612
/* We also lock the parent relation */
613-
get_pathman_relation_info_after_lock(relid, true),
613+
get_pathman_relation_info_after_lock(relid, true, NULL),
614614
/* Partitioning type does not matter here */
615615
PT_INDIFFERENT);
616616
/*

src/pg_pathman.c

Lines changed: 100 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,7 @@ create_partitions_internal(Oid relid, Datum value, Oid value_type)
704704
PG_TRY();
705705
{
706706
const PartRelationInfo *prel;
707+
LockAcquireResult lock_result; /* could we lock the parent? */
707708
Datum values[Natts_pathman_config];
708709
bool isnull[Natts_pathman_config];
709710

@@ -713,57 +714,91 @@ create_partitions_internal(Oid relid, Datum value, Oid value_type)
713714
Oid base_atttype; /* base type of prel->atttype */
714715
Oid base_value_type; /* base type of value_type */
715716

716-
Datum min_rvalue, /* absolute MIN */
717-
max_rvalue; /* absolute MAX */
718-
719-
Oid interval_type = InvalidOid;
720-
Datum interval_binary, /* assigned 'width' of a single partition */
721-
interval_text;
722-
723-
FmgrInfo interval_type_cmp;
724-
725717
/* Fetch PartRelationInfo by 'relid' */
726-
prel = get_pathman_relation_info(relid);
718+
prel = get_pathman_relation_info_after_lock(relid, true, &lock_result);
727719
shout_if_prel_is_invalid(relid, prel, PT_RANGE);
728720

729721
/* Fetch base types of prel->atttype & value_type */
730722
base_atttype = getBaseType(prel->atttype);
731723
base_value_type = getBaseType(value_type);
732724

733-
/* Read max & min range values from PartRelationInfo */
734-
min_rvalue = PrelGetRangesArray(prel)[0].min;
735-
max_rvalue = PrelGetRangesArray(prel)[PrelLastChild(prel)].max;
736-
737-
/* Copy datums on order to protect them from cache invalidation */
738-
min_rvalue = datumCopy(min_rvalue, prel->attbyval, prel->attlen);
739-
max_rvalue = datumCopy(max_rvalue, prel->attbyval, prel->attlen);
725+
/* Search for a suitable partition if we didn't hold it */
726+
Assert(lock_result != LOCKACQUIRE_NOT_AVAIL);
727+
if (lock_result == LOCKACQUIRE_OK)
728+
{
729+
Oid *parts;
730+
int nparts;
740731

741-
/* Retrieve interval as TEXT from tuple */
742-
interval_text = values[Anum_pathman_config_range_interval - 1];
732+
/* Search for matching partitions */
733+
parts = find_partitions_for_value(value, value_type, prel, &nparts);
743734

744-
/* Convert interval to binary representation */
745-
interval_binary = extract_binary_interval_from_text(interval_text,
746-
base_atttype,
747-
&interval_type);
735+
/* Shout if there's more than one */
736+
if (nparts > 1)
737+
elog(ERROR, ERR_PART_ATTR_MULTIPLE);
748738

749-
/* Fill the FmgrInfo struct with a cmp(value, part_attribute) function */
750-
fill_type_cmp_fmgr_info(&interval_type_cmp, base_value_type, base_atttype);
739+
/* It seems that we got a partition! */
740+
else if (nparts == 1)
741+
{
742+
/* Unlock the parent (we're not going to spawn) */
743+
xact_unlock_partitioned_rel(relid);
751744

752-
if (SPI_connect() != SPI_OK_CONNECT)
753-
elog(ERROR, "could not connect using SPI");
745+
/* Simply return the suitable partition */
746+
partid = parts[0];
747+
}
754748

755-
/* while (value >= MAX) ... */
756-
spawn_partitions(PrelParentRelid(prel), value, max_rvalue,
757-
base_atttype, &interval_type_cmp, interval_binary,
758-
interval_type, true, &partid);
749+
/* Don't forget to free */
750+
pfree(parts);
751+
}
759752

760-
/* while (value < MIN) ... */
753+
/* Else spawn a new one (we hold a lock on the parent) */
761754
if (partid == InvalidOid)
762-
spawn_partitions(PrelParentRelid(prel), value, min_rvalue,
763-
base_atttype, &interval_type_cmp, interval_binary,
764-
interval_type, false, &partid);
755+
{
756+
Datum min_rvalue, /* absolute MIN */
757+
max_rvalue; /* absolute MAX */
758+
759+
Oid interval_type = InvalidOid;
760+
Datum interval_binary, /* assigned 'width' of one partition */
761+
interval_text;
762+
763+
FmgrInfo interval_type_cmp;
764+
765+
/* Read max & min range values from PartRelationInfo */
766+
min_rvalue = PrelGetRangesArray(prel)[0].min;
767+
max_rvalue = PrelGetRangesArray(prel)[PrelLastChild(prel)].max;
768+
769+
/* Copy datums on order to protect them from cache invalidation */
770+
min_rvalue = datumCopy(min_rvalue, prel->attbyval, prel->attlen);
771+
max_rvalue = datumCopy(max_rvalue, prel->attbyval, prel->attlen);
772+
773+
/* Retrieve interval as TEXT from tuple */
774+
interval_text = values[Anum_pathman_config_range_interval - 1];
765775

766-
SPI_finish(); /* close SPI connection */
776+
/* Convert interval to binary representation */
777+
interval_binary = extract_binary_interval_from_text(interval_text,
778+
base_atttype,
779+
&interval_type);
780+
781+
/* Fill the FmgrInfo struct with a cmp(value, part_attribute) */
782+
fill_type_cmp_fmgr_info(&interval_type_cmp,
783+
base_value_type,
784+
base_atttype);
785+
786+
if (SPI_connect() != SPI_OK_CONNECT)
787+
elog(ERROR, "could not connect using SPI");
788+
789+
/* while (value >= MAX) ... */
790+
spawn_partitions(PrelParentRelid(prel), value, max_rvalue,
791+
base_atttype, &interval_type_cmp,
792+
interval_binary, interval_type, true, &partid);
793+
794+
/* while (value < MIN) ... */
795+
if (partid == InvalidOid)
796+
spawn_partitions(PrelParentRelid(prel), value, min_rvalue,
797+
base_atttype, &interval_type_cmp,
798+
interval_binary, interval_type, false, &partid);
799+
800+
SPI_finish(); /* close SPI connection */
801+
}
767802
}
768803
else
769804
elog(ERROR, "pg_pathman's config does not contain relation \"%s\"",
@@ -1082,7 +1117,7 @@ handle_binary_opexpr(WalkerContext *context, WrapperNode *result,
10821117
PrelGetRangesArray(context->prel),
10831118
PrelChildrenCount(context->prel),
10841119
strategy,
1085-
result);
1120+
result); /* output */
10861121

10871122
result->paramsel = estimate_paramsel_using_prel(prel, strategy);
10881123

@@ -1169,7 +1204,7 @@ search_range_partition_eq(const Datum value,
11691204
ranges,
11701205
nranges,
11711206
BTEqualStrategyNumber,
1172-
&result);
1207+
&result); /* output */
11731208

11741209
if (result.found_gap)
11751210
{
@@ -1220,7 +1255,7 @@ handle_const(const Const *c, WalkerContext *context)
12201255

12211256
/*
12221257
* Had to add this check for queries like:
1223-
* select * from test.hash_rel where txt = NULL;
1258+
* select * from test.hash_rel where txt = NULL;
12241259
*/
12251260
if (!context->for_insert || c->constisnull)
12261261
{
@@ -1234,9 +1269,30 @@ handle_const(const Const *c, WalkerContext *context)
12341269
{
12351270
case PT_HASH:
12361271
{
1237-
Datum value = OidFunctionCall1(prel->hash_proc, c->constvalue);
1238-
uint32 idx = hash_to_part_index(DatumGetInt32(value),
1239-
PrelChildrenCount(prel));
1272+
Datum value, /* value to be hashed */
1273+
hash; /* 32-bit hash */
1274+
uint32 idx; /* index of partition */
1275+
bool cast_success;
1276+
1277+
/* Peform type cast if types mismatch */
1278+
if (prel->atttype != c->consttype)
1279+
{
1280+
value = perform_type_cast(c->constvalue,
1281+
getBaseType(c->consttype),
1282+
getBaseType(prel->atttype),
1283+
&cast_success);
1284+
1285+
if (!cast_success)
1286+
elog(ERROR, "Cannot select partition: "
1287+
"unable to perform type cast");
1288+
}
1289+
/* Else use the Const's value */
1290+
else value = c->constvalue;
1291+
1292+
/* Calculate 32-bit hash of 'value' and corresponding index */
1293+
hash = OidFunctionCall1(prel->hash_proc, value);
1294+
idx = hash_to_part_index(DatumGetInt32(hash),
1295+
PrelChildrenCount(prel));
12401296

12411297
result->paramsel = estimate_paramsel_using_prel(prel, strategy);
12421298
result->rangeset = list_make1_irange(make_irange(idx, idx, IR_LOSSY));
@@ -1245,7 +1301,7 @@ handle_const(const Const *c, WalkerContext *context)
12451301

12461302
case PT_RANGE:
12471303
{
1248-
FmgrInfo cmp_finfo;
1304+
FmgrInfo cmp_finfo;
12491305

12501306
fill_type_cmp_fmgr_info(&cmp_finfo,
12511307
getBaseType(c->consttype),
@@ -1256,7 +1312,7 @@ handle_const(const Const *c, WalkerContext *context)
12561312
PrelGetRangesArray(context->prel),
12571313
PrelChildrenCount(context->prel),
12581314
strategy,
1259-
result);
1315+
result); /* output */
12601316

12611317
result->paramsel = estimate_paramsel_using_prel(prel, strategy);
12621318
}

0 commit comments

Comments
 (0)