[SCM] PostGIS branch master updated. 3.6.0rc2-379-ga586d720e
git at osgeo.org
git at osgeo.org
Sun Mar 8 16:25:55 PDT 2026
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "PostGIS".
The branch, master has been updated
via a586d720eb01112b22052865feca01a6d4b6eb9e (commit)
from 7af31bf2cb1968d7f9f3a275e9723702788b8615 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit a586d720eb01112b22052865feca01a6d4b6eb9e
Author: Darafei Praliaskouski <me at komzpa.net>
Date: Mon Mar 9 03:24:04 2026 +0400
Fix #6048 ST_Clip sparse band selection crash
Closes #6048
diff --git a/NEWS b/NEWS
index df835634c..4bbdc0e1d 100644
--- a/NEWS
+++ b/NEWS
@@ -39,6 +39,8 @@ This version requires GEOS 3.10 or higher
grid size exceeds their extent (Darafei Praliaskouski)
- #5959, Prevent histogram target overflow when analysing massive tables (Darafei Praliaskouski)
- #4828, geometry_columns handles NOT VALID SRID checks without errors (Darafei Praliaskouski)
+ - #6048, [raster] ST_Clip no longer crashes when clipping sparse band
+ selections (Darafei Praliaskouski)
- #5645, Docs: keep code operators ("=>") intact in translated manuals
by enforcing verbatim CSS (Darafei Praliaskouski)
- #5593, Docs: render manual images with GraphicsMagick without temporary files,
diff --git a/raster/rt_pg/rtpg_mapalgebra.c b/raster/rt_pg/rtpg_mapalgebra.c
index c1cac17c3..bb173aa29 100644
--- a/raster/rt_pg/rtpg_mapalgebra.c
+++ b/raster/rt_pg/rtpg_mapalgebra.c
@@ -11,6 +11,7 @@
* Copyright (C) 2009-2011 Mateusz Loskot <mateusz at loskot.net>
* Copyright (C) 2008-2009 Sandro Santilli <strk at kbt.io>
* Copyright (C) 2013 Nathaniel Hunter Clay <clay.nathaniel at gmail.com>
+ * Copyright (C) 2026 Darafei Praliaskouski <me at komzpa.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -2993,8 +2994,6 @@ Datum RASTER_clip(PG_FUNCTION_ARGS)
bool typbyval;
char typalign;
- int i = 0;
- int j = 0;
int k = 0;
rtpg_clip_arg arg = NULL;
LWGEOM *tmpgeom = NULL;
@@ -3004,12 +3003,6 @@ Datum RASTER_clip(PG_FUNCTION_ARGS)
double nodataval;
double offset[4] = {0.};
- int input_x = 0;
- int input_y = 0;
- int mask_x = 0;
- int mask_y = 0;
- int x = 0;
- int y = 0;
int width = 0;
int height = 0;
int mask_width = 0;
@@ -3145,6 +3138,8 @@ Datum RASTER_clip(PG_FUNCTION_ARGS)
/* nband (1) */
if (!PG_ARGISNULL(1)) {
+ uint32_t band_count = 0;
+
array = PG_GETARG_ARRAYTYPE_P(1);
etype = ARR_ELEMTYPE(array);
get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
@@ -3177,23 +3172,26 @@ Datum RASTER_clip(PG_FUNCTION_ARGS)
PG_RETURN_NULL();
}
- for (i = 0, j = 0; i < arg->numbands; i++) {
- if (nulls[i]) continue;
+ for (uint32_t i = 0; i < (uint32_t)arg->numbands; i++)
+ {
+ if (nulls[i])
+ continue;
switch (etype) {
case INT2OID:
- arg->band[j].nband = DatumGetInt16(e[i]) - 1;
+ arg->band[band_count].nband = DatumGetInt16(e[i]) - 1;
break;
case INT4OID:
- arg->band[j].nband = DatumGetInt32(e[i]) - 1;
+ arg->band[band_count].nband = DatumGetInt32(e[i]) - 1;
break;
}
- j++;
+ band_count++;
}
- if (j < arg->numbands) {
- arg->band = repalloc(arg->band, sizeof(struct rtpg_clip_band_t) * j);
+ if (band_count < (uint32_t)arg->numbands)
+ {
+ arg->band = repalloc(arg->band, sizeof(struct rtpg_clip_band_t) * band_count);
if (arg->band == NULL) {
rtpg_clip_arg_destroy(arg);
PG_FREE_IF_COPY(pgraster, 0);
@@ -3202,12 +3200,14 @@ Datum RASTER_clip(PG_FUNCTION_ARGS)
PG_RETURN_NULL();
}
- arg->numbands = j;
+ arg->numbands = (int)band_count;
}
/* validate band */
- for (i = 0; i < arg->numbands; i++) {
- if (!rt_raster_has_band(arg->raster, arg->band[i].nband)) {
+ for (uint32_t i = 0; i < (uint32_t)arg->numbands; i++)
+ {
+ if (!rt_raster_has_band(arg->raster, arg->band[i].nband))
+ {
elog(NOTICE, "Band at index %d not found in raster", arg->band[i].nband + 1);
rtpg_clip_arg_destroy(arg);
PG_FREE_IF_COPY(pgraster, 0);
@@ -3235,8 +3235,9 @@ Datum RASTER_clip(PG_FUNCTION_ARGS)
PG_RETURN_NULL();
}
- for (i = 0; i < arg->numbands; i++) {
- arg->band[i].nband = i;
+ for (uint32_t i = 0; i < (uint32_t)arg->numbands; i++)
+ {
+ arg->band[i].nband = (int)i;
arg->band[i].hasnodata = 0;
arg->band[i].nodataval = 0;
}
@@ -3269,7 +3270,8 @@ Datum RASTER_clip(PG_FUNCTION_ARGS)
);
/* it doesn't matter if there are more nodataval */
- for (i = 0, j = 0; i < arg->numbands; i++, j++) {
+ for (int32_t i = 0, j = 0; i < arg->numbands; i++, j++)
+ {
/* cap j to the last nodataval element */
if (j >= k)
j = k - 1;
@@ -3359,8 +3361,16 @@ Datum RASTER_clip(PG_FUNCTION_ARGS)
mask_band = rt_raster_get_band(arg->mask, 0);
- for (i = 0; i < arg->numbands; i++) {
+ for (uint32_t i = 0; i < (uint32_t)arg->numbands; i++)
+ {
input_band = rt_raster_get_band(arg->raster, arg->band[i].nband);
+ if (!input_band)
+ {
+ rtpg_clip_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_clip: Could not get input band at index %d", arg->band[i].nband);
+ PG_RETURN_NULL();
+ }
/* band metadata */
pixtype = rt_band_get_pixtype(input_band);
@@ -3378,7 +3388,8 @@ Datum RASTER_clip(PG_FUNCTION_ARGS)
nodataval = rt_band_get_min_value(input_band);
}
- if (rt_raster_generate_new_band(rtn, pixtype, nodataval, hasnodata, nodataval, i) < 0) {
+ if (rt_raster_generate_new_band(rtn, pixtype, nodataval, hasnodata, nodataval, (int)i) < 0)
+ {
rtpg_clip_arg_destroy(arg);
PG_FREE_IF_COPY(pgraster, 0);
elog(ERROR, "RASTER_clip: Could not add new band in output raster");
@@ -3389,16 +3400,32 @@ Datum RASTER_clip(PG_FUNCTION_ARGS)
continue;
}
- output_band = rt_raster_get_band(rtn, arg->band[i].nband);
+ /*
+ * Output bands are packed densely in request order, regardless of
+ * the source band numbers used to populate them.
+ */
+ output_band = rt_raster_get_band(rtn, (int)i);
+ if (!output_band)
+ {
+ rtpg_clip_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_clip: Could not get output band at index %u", i);
+ PG_RETURN_NULL();
+ }
- if (!mask_band) {
+ if (!mask_band)
+ {
continue;
}
- for (y = 0; y < height; y++) {
- for (x = 0; x < width; x++) {
- mask_x = x - (int)offset[2];
- mask_y = y - (int)offset[3];
+ for (int y = 0; y < height; y++)
+ {
+ for (int x = 0; x < width; x++)
+ {
+ int mask_x = x - (int)offset[2];
+ int mask_y = y - (int)offset[3];
+ int input_x = x - (int)offset[0];
+ int input_y = y - (int)offset[1];
if (!(
mask_x >= 0 &&
@@ -3420,9 +3447,6 @@ Datum RASTER_clip(PG_FUNCTION_ARGS)
continue;
}
- input_x = x - (int)offset[0];
- input_y = y - (int)offset[1];
-
if (rt_band_get_pixel(input_band, input_x, input_y, &value, &isnodata) != ES_NONE) {
rtpg_clip_arg_destroy(arg);
PG_FREE_IF_COPY(pgraster, 0);
diff --git a/raster/test/regress/rt_clip.sql b/raster/test/regress/rt_clip.sql
index 462e73b21..af358506c 100644
--- a/raster/test/regress/rt_clip.sql
+++ b/raster/test/regress/rt_clip.sql
@@ -172,4 +172,61 @@ FROM ST_Clip(
1, '8BUI', 1, 0
),
ST_GeomFromText('POLYGON((0 0.0009999, 0.0001 0.0009999, 0.0001 0.0009, 0 0.0009, 0 0.0009999))')
-) AS rast;
\ No newline at end of file
+) AS rast;
+
+-- #6048 clipping a sparse band selection should populate dense output bands
+WITH test_rast AS (
+ SELECT ST_AddBand(
+ ST_AddBand(
+ ST_AddBand(
+ ST_SetSRID(
+ ST_MakeEmptyRaster(4, 4, 0, 4, 1, -1, 0, 0),
+ 4326
+ ),
+ '8BUI'::text, 11, 1
+ ),
+ '8BUI'::text, 22, 2
+ ),
+ '8BUI'::text, 33, 3
+ ) AS rast
+), test_geom AS (
+ SELECT ST_SetSRID(ST_MakeEnvelope(1, 1, 3, 3), 4326) AS geom
+)
+SELECT '#6048.1',
+ ST_Width(rast),
+ ST_Height(rast),
+ ST_NumBands(rast),
+ ST_BandNoDataValue(rast, 1)::int,
+ ST_Value(rast, 1, 2, 2)::int
+FROM (
+ SELECT _ST_Clip(rast, ARRAY[3], geom, ARRAY[255, 255, 255], FALSE) AS rast
+ FROM test_rast, test_geom
+) AS clipped;
+
+WITH test_rast AS (
+ SELECT ST_AddBand(
+ ST_AddBand(
+ ST_AddBand(
+ ST_SetSRID(
+ ST_MakeEmptyRaster(4, 4, 0, 4, 1, -1, 0, 0),
+ 4326
+ ),
+ '8BUI'::text, 11, 1
+ ),
+ '8BUI'::text, 22, 2
+ ),
+ '8BUI'::text, 33, 3
+ ) AS rast
+), test_geom AS (
+ SELECT ST_SetSRID(ST_MakeEnvelope(1, 1, 3, 3), 4326) AS geom
+)
+SELECT '#6048.2',
+ ST_NumBands(rast),
+ ST_BandNoDataValue(rast, 1)::int,
+ ST_BandNoDataValue(rast, 2)::int,
+ ST_Value(rast, 1, 2, 2)::int,
+ ST_Value(rast, 2, 2, 2)::int
+FROM (
+ SELECT _ST_Clip(rast, ARRAY[3, 1], geom, ARRAY[253, 251], FALSE) AS rast
+ FROM test_rast, test_geom
+) AS clipped;
diff --git a/raster/test/regress/rt_clip_expected b/raster/test/regress/rt_clip_expected
index c704cb356..c2754b520 100644
--- a/raster/test/regress/rt_clip_expected
+++ b/raster/test/regress/rt_clip_expected
@@ -1065,3 +1065,5 @@
4|2|5|3|4|4|f|3|POLYGON((3 -3,4 -3,4 -4,3 -4,3 -3))|16
4|2|5|3|4|4|t|3|POLYGON((3 -3,4 -3,4 -4,3 -4,3 -3))|16
0|0.001|10|10
+#6048.1|4|4|1|255|33
+#6048.2|2|253|251|33|11
-----------------------------------------------------------------------
Summary of changes:
NEWS | 2 +
raster/rt_pg/rtpg_mapalgebra.c | 88 +++++++++++++++++++++++-------------
raster/test/regress/rt_clip.sql | 59 +++++++++++++++++++++++-
raster/test/regress/rt_clip_expected | 2 +
4 files changed, 118 insertions(+), 33 deletions(-)
hooks/post-receive
--
PostGIS
More information about the postgis-tickets
mailing list