Skip to content

Commit f5b4a0b

Browse files
committed
BRIN: be more strict about required support procs
With improperly defined operator classes, it's possible to get a Postgres crash because we'd try to invoke a procedure that doesn't exist. This is because the code is being a bit too trusting that the opclass is correctly defined. Add some ereport(ERROR)s for cases where mandatory support procedures are not defined, transforming the crashes into errors. The particular case that was reported is an incomplete opclass in PostGIS. Backpatch all the way down to 13. Reported-by: Tobias Wendorff <tobias.wendorff@tu-dortmund.de> Diagnosed-by: David Rowley <dgrowleyml@gmail.com> Reviewed-by: Tomas Vondra <tomas@vondra.me> Discussion: https://postgr.es/m/fb6d9a35-6c8e-4869-af80-0a4944a793a4@tu-dortmund.de
1 parent 6c1e795 commit f5b4a0b

File tree

1 file changed

+18
-13
lines changed

1 file changed

+18
-13
lines changed

src/backend/access/brin/brin_inclusion.c

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ typedef struct InclusionOpaque
8282
} InclusionOpaque;
8383

8484
static FmgrInfo *inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno,
85-
uint16 procnum);
85+
uint16 procnum, bool missing_ok);
8686
static FmgrInfo *inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno,
8787
Oid subtype, uint16 strategynum);
8888

@@ -189,7 +189,7 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS)
189189
* new value for emptiness; if it returns true, we need to set the
190190
* "contains empty" flag in the element (unless already set).
191191
*/
192-
finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_EMPTY);
192+
finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_EMPTY, true);
193193
if (finfo != NULL && DatumGetBool(FunctionCall1Coll(finfo, colloid, newval)))
194194
{
195195
if (!DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]))
@@ -205,7 +205,7 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS)
205205
PG_RETURN_BOOL(true);
206206

207207
/* Check if the new value is already contained. */
208-
finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_CONTAINS);
208+
finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_CONTAINS, true);
209209
if (finfo != NULL &&
210210
DatumGetBool(FunctionCall2Coll(finfo, colloid,
211211
column->bv_values[INCLUSION_UNION],
@@ -220,7 +220,7 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS)
220220
* it's not going to be used any longer. However, the BRIN framework
221221
* doesn't allow for the value not being present. Improve someday.
222222
*/
223-
finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE);
223+
finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE, true);
224224
if (finfo != NULL &&
225225
!DatumGetBool(FunctionCall2Coll(finfo, colloid,
226226
column->bv_values[INCLUSION_UNION],
@@ -231,8 +231,7 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS)
231231
}
232232

233233
/* Finally, merge the new value to the existing union. */
234-
finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE);
235-
Assert(finfo != NULL);
234+
finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE, false);
236235
result = FunctionCall2Coll(finfo, colloid,
237236
column->bv_values[INCLUSION_UNION], newval);
238237
if (!attr->attbyval &&
@@ -572,7 +571,7 @@ brin_inclusion_union(PG_FUNCTION_ARGS)
572571
}
573572

574573
/* Check if A and B are mergeable; if not, mark A unmergeable. */
575-
finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE);
574+
finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE, true);
576575
if (finfo != NULL &&
577576
!DatumGetBool(FunctionCall2Coll(finfo, colloid,
578577
col_a->bv_values[INCLUSION_UNION],
@@ -583,8 +582,7 @@ brin_inclusion_union(PG_FUNCTION_ARGS)
583582
}
584583

585584
/* Finally, merge B to A. */
586-
finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE);
587-
Assert(finfo != NULL);
585+
finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE, false);
588586
result = FunctionCall2Coll(finfo, colloid,
589587
col_a->bv_values[INCLUSION_UNION],
590588
col_b->bv_values[INCLUSION_UNION]);
@@ -605,10 +603,12 @@ brin_inclusion_union(PG_FUNCTION_ARGS)
605603
* Cache and return inclusion opclass support procedure
606604
*
607605
* Return the procedure corresponding to the given function support number
608-
* or null if it is not exists.
606+
* or null if it is not exists. If missing_ok is true and the procedure
607+
* isn't set up for this opclass, return NULL instead of raising an error.
609608
*/
610609
static FmgrInfo *
611-
inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum)
610+
inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum,
611+
bool missing_ok)
612612
{
613613
InclusionOpaque *opaque;
614614
uint16 basenum = procnum - PROCNUM_BASE;
@@ -630,13 +630,18 @@ inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum)
630630
{
631631
if (RegProcedureIsValid(index_getprocid(bdesc->bd_index, attno,
632632
procnum)))
633-
{
634633
fmgr_info_copy(&opaque->extra_procinfos[basenum],
635634
index_getprocinfo(bdesc->bd_index, attno, procnum),
636635
bdesc->bd_context);
637-
}
638636
else
639637
{
638+
if (!missing_ok)
639+
ereport(ERROR,
640+
errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
641+
errmsg_internal("invalid opclass definition"),
642+
errdetail_internal("The operator class is missing support function %d for column %d.",
643+
procnum, attno));
644+
640645
opaque->extra_proc_missing[basenum] = true;
641646
return NULL;
642647
}

0 commit comments

Comments
 (0)