[SCM] PostGIS branch master updated. 3.6.0rc2-633-g6ce331f4e

git at osgeo.org git at osgeo.org
Sat Jun 20 11:16:43 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  6ce331f4e2bfd2054f56ba09c43907621259bffc (commit)
       via  4ec0a1dd0a311af57c593656637f5c98ac7a1799 (commit)
      from  177963ca6c91740aea1ab0000637017f9ad5dda5 (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 6ce331f4e2bfd2054f56ba09c43907621259bffc
Author: Darafei Praliaskouski <me at komzpa.net>
Date:   Sat Jun 20 21:47:07 2026 +0400

    Add raster2pgsql --if-not-exists modifier
    
    Add a loader modifier that emits CREATE TABLE IF NOT EXISTS in create and prepare modes, allowing -c --if-not-exists to create a missing target table or continue loading into an existing compatible table.
    
    Represent the short operation modes as explicit internal actions for dropping tables, creating tables, loading data, and creating indexes. When -I is used with --if-not-exists, emit CREATE INDEX IF NOT EXISTS with stable loader index names; plain -I keeps its existing create-index behavior.
    
    Closes #4659
    
    Closes https://github.com/postgis/postgis/pull/1000

diff --git a/NEWS b/NEWS
index 52fafcd05..e668c7a62 100644
--- a/NEWS
+++ b/NEWS
@@ -63,6 +63,8 @@ To take advantage of all postgis_sfcgal extension features SFCGAL 2.3+ is needed
           (Sandro Santilli)
  - #3743, [raster] Document and test raster2pgsql -s FROM_SRID:SRID
           reprojection support (Darafei Praliaskouski)
+ - #4659, [raster] Add raster2pgsql --if-not-exists creation modifier
+          (Darafei Praliaskouski)
  - #4749, Use point-in-polygon predicate fast paths for point-only
           GeometryCollections (Darafei Praliaskouski)
  - #5532, Validate the manual against DocBook XMLSchema in check-xml
diff --git a/doc/man/raster2pgsql.1 b/doc/man/raster2pgsql.1
index f33b9c504..10adc8d9e 100644
--- a/doc/man/raster2pgsql.1
+++ b/doc/man/raster2pgsql.1
@@ -54,6 +54,11 @@ Create a new table and populate it. This is the default mode.
 \fB\-p\fR
 Prepare mode. Only emit SQL to create the table.
 .TP
+\fB\-\-if\-not\-exists\fR
+Use IF NOT EXISTS for table creation in \-c and \-p modes. When \-I is also
+specified, use IF NOT EXISTS for index creation too. This option cannot be used
+with \-a or \-d.
+.TP
 \fB\-f\fR <\fIcolumn\fR>
 Specify the name of the raster column.
 .TP
diff --git a/doc/using_raster_dataman.xml b/doc/using_raster_dataman.xml
index 2f8773895..64973efc0 100644
--- a/doc/using_raster_dataman.xml
+++ b/doc/using_raster_dataman.xml
@@ -137,6 +137,20 @@ Available GDAL raster formats:
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term><option>--if-not-exists</option></term>
+          <listitem>
+            <para>
+              Use <literal>IF NOT EXISTS</literal> for table creation in
+              <option>-c</option> and <option>-p</option> modes. When
+              <option>-I</option> is also specified, use
+              <literal>IF NOT EXISTS</literal> for index creation too. This
+              option cannot be used with <option>-a</option> or
+              <option>-d</option>.
+            </para>
+          </listitem>
+        </varlistentry>
+
 		<varlistentry>
 			<term>Raster processing: Applying constraints for proper registering in raster catalogs</term>
 			<listitem>
diff --git a/raster/loader/raster2pgsql.c b/raster/loader/raster2pgsql.c
index e8ddcc478..85508bb67 100644
--- a/raster/loader/raster2pgsql.c
+++ b/raster/loader/raster2pgsql.c
@@ -371,16 +371,18 @@ usage() {
 		"  -R  Register the raster as an out-of-db (filesystem) raster. Provided\n"
 		"      raster should have absolute path to the file\n"
 	));
-	printf(_(
-		" (-d|a|c|p) These are mutually exclusive options:\n"
-		"     -d  Drops the table, then recreates it and populates\n"
-		"         it with current raster data.\n"
-		"     -a  Appends raster into current table, must be\n"
-		"         exactly the same table schema.\n"
-		"     -c  Creates a new table and populates it, this is the\n"
-		"         default if you do not specify any options.\n"
-		"     -p  Prepare mode, only creates the table.\n"
-	));
+	printf(
+	    _(" (-d|a|c|p) These are mutually exclusive options:\n"
+	      "     -d  Drops the table, then recreates it and populates\n"
+	      "         it with current raster data.\n"
+	      "     -a  Appends raster into current table, must be\n"
+	      "         exactly the same table schema.\n"
+	      "     -c  Creates a new table and populates it, this is the\n"
+	      "         default if you do not specify any options.\n"
+	      "     -p  Prepare mode, only creates the table.\n"));
+	printf(
+	    _("  --if-not-exists  Use IF NOT EXISTS for table creation in -c and -p\n"
+	      "     modes. With -I, also use IF NOT EXISTS for index creation.\n"));
 	printf(_(
 		"  -f <column> Specify the name of the raster column\n"
 	));
@@ -690,7 +692,11 @@ init_config(RTLOADERCFG *config) {
 	config->pad_tile = 0;
 	config->outdb = 0;
 	config->opt = 'c';
-	config->idx = 0;
+	config->if_not_exists = 0;
+	config->drop_table = 0;
+	config->create_table = CREATE_TABLE_ALWAYS;
+	config->load_data = 1;
+	config->create_index = CREATE_INDEX_NONE;
 	config->maintenance = 0;
 	config->constraints = 0;
 	config->max_extent = 1;
@@ -707,6 +713,52 @@ init_config(RTLOADERCFG *config) {
 	config->max_tiles_per_copy = 50;
 }
 
+static int
+apply_action_presets(RTLOADERCFG *config)
+{
+	config->drop_table = 0;
+	config->create_table = CREATE_TABLE_NONE;
+	config->load_data = 0;
+
+	switch (config->opt)
+	{
+	case 'd':
+		config->drop_table = 1;
+		config->create_table = CREATE_TABLE_ALWAYS;
+		config->load_data = 1;
+		break;
+	case 'a':
+		config->load_data = 1;
+		break;
+	case 'c':
+		config->create_table = CREATE_TABLE_ALWAYS;
+		config->load_data = 1;
+		break;
+	case 'p':
+		config->create_table = CREATE_TABLE_ALWAYS;
+		break;
+	default:
+		rterror(_("Unknown loader operation: -%c"), config->opt);
+		return 0;
+	}
+
+	if (config->if_not_exists)
+	{
+		if (config->opt == 'd' || config->opt == 'a')
+		{
+			rterror(_("--if-not-exists can only modify create and prepare modes"));
+			return 0;
+		}
+
+		if (config->create_table == CREATE_TABLE_ALWAYS)
+			config->create_table = CREATE_TABLE_IF_NOT_EXISTS;
+		if (config->create_index == CREATE_INDEX_ALWAYS)
+			config->create_index = CREATE_INDEX_IF_NOT_EXISTS;
+	}
+
+	return 1;
+}
+
 static void
 rtdealloc_config(RTLOADERCFG *config) {
 	int i = 0;
@@ -1000,19 +1052,23 @@ drop_table(const char *schema, const char *table, STRINGBUFFER *buffer) {
 }
 
 static int
-create_table(
-	const char *schema, const char *table, const char *column,
-	const int file_column, const char *file_column_name,
-	const char *tablespace, const char *idx_tablespace,
-	STRINGBUFFER *buffer
-) {
+create_table(const char *schema,
+	     const char *table,
+	     const char *column,
+	     const int file_column,
+	     const char *file_column_name,
+	     const char *tablespace,
+	     const char *idx_tablespace,
+	     int if_not_exists,
+	     STRINGBUFFER *buffer)
+{
 	char *sql = NULL;
 	uint32_t len = 0;
 
 	assert(table != NULL);
 	assert(column != NULL);
 
-	len = strlen("CREATE TABLE  (\"rid\" serial PRIMARY KEY, raster);") + 1;
+	len = strlen("CREATE TABLE IF NOT EXISTS  (\"rid\" serial PRIMARY KEY, raster);") + 1;
 	if (schema != NULL)
 		len += strlen(schema);
 	len += strlen(table);
@@ -1029,7 +1085,9 @@ create_table(
 		rterror(_("create_table: Could not allocate memory for CREATE TABLE statement"));
 		return 0;
 	}
-	sprintf(sql, "CREATE TABLE %s%s (\"rid\" serial PRIMARY KEY%s%s,%s raster%s%s%s)%s%s;",
+	sprintf(sql,
+		"CREATE TABLE %s%s%s (\"rid\" serial PRIMARY KEY%s%s,%s raster%s%s%s)%s%s;",
+		(if_not_exists ? "IF NOT EXISTS " : ""),
 		(schema != NULL ? schema : ""),
 		table,
 		(idx_tablespace != NULL ? " USING INDEX TABLESPACE " : ""),
@@ -1039,8 +1097,7 @@ create_table(
 		(file_column ? file_column_name : ""),
 		(file_column ? " text" : ""),
 		(tablespace != NULL ? " TABLESPACE " : ""),
-		(tablespace != NULL ? tablespace : "")
-	);
+		(tablespace != NULL ? tablespace : ""));
 
 	append_sql_to_buffer(buffer, sql);
 
@@ -1048,11 +1105,13 @@ create_table(
 }
 
 static int
-create_index(
-	const char *schema, const char *table, const char *column,
-	const char *tablespace,
-	STRINGBUFFER *buffer
-) {
+create_index(const char *schema,
+	     const char *table,
+	     const char *column,
+	     const char *tablespace,
+	     int if_not_exists,
+	     STRINGBUFFER *buffer)
+{
 	char *sql = NULL;
 	uint32_t len = 0;
 	char *_table = NULL;
@@ -1065,7 +1124,7 @@ create_index(
 	_column = chartrim(column, "\"");
 
 	/* create index */
-	len = strlen("CREATE INDEX \"__gist\" ON  USING gist (st_convexhull());") + 1;
+	len = strlen("CREATE INDEX IF NOT EXISTS \"__gist\" ON  USING gist (st_convexhull());") + 1;
 	if (schema != NULL)
 		len += strlen(schema);
 	len += strlen(_table);
@@ -1082,13 +1141,28 @@ create_index(
 		rtdealloc(_column);
 		return 0;
 	}
-	sprintf(sql, "CREATE INDEX ON %s%s USING gist (st_convexhull(%s))%s%s;",
-		(schema != NULL ? schema : ""),
-		table,
-		column,
-		(tablespace != NULL ? " TABLESPACE " : ""),
-		(tablespace != NULL ? tablespace : "")
-	);
+	if (if_not_exists)
+	{
+		sprintf(sql,
+			"CREATE INDEX IF NOT EXISTS \"%s_%s_gist\" ON %s%s USING gist (st_convexhull(%s))%s%s;",
+			_table,
+			_column,
+			(schema != NULL ? schema : ""),
+			table,
+			column,
+			(tablespace != NULL ? " TABLESPACE " : ""),
+			(tablespace != NULL ? tablespace : ""));
+	}
+	else
+	{
+		sprintf(sql,
+			"CREATE INDEX ON %s%s USING gist (st_convexhull(%s))%s%s;",
+			(schema != NULL ? schema : ""),
+			table,
+			column,
+			(tablespace != NULL ? " TABLESPACE " : ""),
+			(tablespace != NULL ? tablespace : ""));
+	}
 	rtdealloc(_table);
 	rtdealloc(_column);
 
@@ -2006,7 +2080,8 @@ process_rasters(RTLOADERCFG *config, STRINGBUFFER *buffer) {
 	}
 
 	/* drop table */
-	if (config->opt == 'd') {
+	if (config->drop_table)
+	{
 		if (!drop_table(config->schema, config->table, buffer)) {
 			rterror(_("process_rasters: Could not add DROP TABLE statement to string buffer"));
 			return 0;
@@ -2023,25 +2098,34 @@ process_rasters(RTLOADERCFG *config, STRINGBUFFER *buffer) {
 	}
 
 	/* create table */
-	if (config->opt != 'a') {
-		if (!create_table(
-			config->schema, config->table, config->raster_column,
-			config->file_column, config->file_column_name,
-			config->tablespace, config->idx_tablespace,
-			buffer
-		)) {
+	if (config->create_table != CREATE_TABLE_NONE)
+	{
+		if (!create_table(config->schema,
+				  config->table,
+				  config->raster_column,
+				  config->file_column,
+				  config->file_column_name,
+				  config->tablespace,
+				  config->idx_tablespace,
+				  config->create_table == CREATE_TABLE_IF_NOT_EXISTS,
+				  buffer))
+		{
 			rterror(_("process_rasters: Could not add CREATE TABLE statement to string buffer"));
 			return 0;
 		}
 
 		if (config->overview_count) {
 			for (i = 0; i < config->overview_count; i++) {
-				if (!create_table(
-					config->schema, config->overview_table[i], config->raster_column,
-					config->file_column, config->file_column_name,
-					config->tablespace, config->idx_tablespace,
-					buffer
-				)) {
+				if (!create_table(config->schema,
+						  config->overview_table[i],
+						  config->raster_column,
+						  config->file_column,
+						  config->file_column_name,
+						  config->tablespace,
+						  config->idx_tablespace,
+						  config->create_table == CREATE_TABLE_IF_NOT_EXISTS,
+						  buffer))
+				{
 					rterror(_("process_rasters: Could not add an overview's CREATE TABLE statement to string buffer"));
 					return 0;
 				}
@@ -2049,8 +2133,9 @@ process_rasters(RTLOADERCFG *config, STRINGBUFFER *buffer) {
 		}
 	}
 
-	/* no need to run if opt is 'p' */
-	if (config->opt != 'p') {
+	/* no need to load data in prepare mode */
+	if (config->load_data)
+	{
 		RASTERINFO refinfo;
 		init_rastinfo(&refinfo);
 
@@ -2138,19 +2223,23 @@ process_rasters(RTLOADERCFG *config, STRINGBUFFER *buffer) {
 	}
 
 	/* index */
-	if (config->idx) {
+	if (config->create_index != CREATE_INDEX_NONE)
+	{
 		/* create index */
-		if (!create_index(
-			config->schema, config->table, config->raster_column,
-			config->idx_tablespace,
-			buffer
-		)) {
+		if (!create_index(config->schema,
+				  config->table,
+				  config->raster_column,
+				  config->idx_tablespace,
+				  config->create_index == CREATE_INDEX_IF_NOT_EXISTS,
+				  buffer))
+		{
 			rterror(_("process_rasters: Could not add CREATE INDEX statement to string buffer"));
 			return 0;
 		}
 
 		/* analyze */
-		if (config->opt != 'p') {
+		if (config->load_data)
+		{
 			if (!analyze_table(
 				config->schema, config->table,
 				buffer
@@ -2163,17 +2252,20 @@ process_rasters(RTLOADERCFG *config, STRINGBUFFER *buffer) {
 		if (config->overview_count) {
 			for (i = 0; i < config->overview_count; i++) {
 				/* create index */
-				if (!create_index(
-					config->schema, config->overview_table[i], config->raster_column,
-					config->idx_tablespace,
-					buffer
-				)) {
+				if (!create_index(config->schema,
+						  config->overview_table[i],
+						  config->raster_column,
+						  config->idx_tablespace,
+						  config->create_index == CREATE_INDEX_IF_NOT_EXISTS,
+						  buffer))
+				{
 					rterror(_("process_rasters: Could not add an overview's CREATE INDEX statement to string buffer"));
 					return 0;
 				}
 
 				/* analyze */
-				if (config->opt != 'p') {
+				if (config->load_data)
+				{
 					if (!analyze_table(
 						config->schema, config->overview_table[i],
 						buffer
@@ -2234,7 +2326,8 @@ process_rasters(RTLOADERCFG *config, STRINGBUFFER *buffer) {
 	}
 
 	/* maintenance */
-	if (config->opt != 'p' && config->maintenance) {
+	if (config->load_data && config->maintenance)
+	{
 		if (!vacuum_table(
 			config->schema, config->table,
 			buffer
@@ -2254,7 +2347,6 @@ process_rasters(RTLOADERCFG *config, STRINGBUFFER *buffer) {
 				}
 			}
 		}
-
 	}
 
 	return 1;
@@ -2464,6 +2556,11 @@ main(int argc, char **argv) {
 		else if (CSEQUAL(argv[argit], "-p")) {
 			config->opt = 'p';
 		}
+		/* make creation statements idempotent */
+		else if (CSEQUAL(argv[argit], "--if-not-exists"))
+		{
+			config->if_not_exists = 1;
+		}
 		/* raster column name */
 		else if (CSEQUAL(argv[argit], "-f") && argit < argc - 1) {
 			const size_t len = (strlen(argv[++argit]) + 1);
@@ -2531,7 +2628,7 @@ main(int argc, char **argv) {
 		}
 		/* create index */
 		else if (CSEQUAL(argv[argit], "-I")) {
-			config->idx = 1;
+			config->create_index = CREATE_INDEX_ALWAYS;
 		}
 		/* maintenance */
 		else if (CSEQUAL(argv[argit], "-M")) {
@@ -2664,6 +2761,12 @@ main(int argc, char **argv) {
 		}
 	}
 
+	if (!apply_action_presets(config))
+	{
+		rtdealloc_config(config);
+		exit(1);
+	}
+
 	/* register GDAL drivers */
 	GDALAllRegister();
 
diff --git a/raster/loader/raster2pgsql.h b/raster/loader/raster2pgsql.h
index 8a5cbb195..96f674191 100644
--- a/raster/loader/raster2pgsql.h
+++ b/raster/loader/raster2pgsql.h
@@ -69,6 +69,20 @@
 
 #define RCSID "$Id$"
 
+typedef enum raster_loader_create_table_action
+{
+	CREATE_TABLE_NONE = 0,
+	CREATE_TABLE_ALWAYS,
+	CREATE_TABLE_IF_NOT_EXISTS
+} CREATE_TABLE_ACTION;
+
+typedef enum raster_loader_create_index_action
+{
+	CREATE_INDEX_NONE = 0,
+	CREATE_INDEX_ALWAYS,
+	CREATE_INDEX_IF_NOT_EXISTS
+} CREATE_INDEX_ACTION;
+
 typedef struct raster_loader_config {
 	/* raster filename */
 	uint32_t rt_file_count;
@@ -118,8 +132,14 @@ typedef struct raster_loader_config {
 	/* type of operation, (d|a|c|p) */
 	char opt;
 
-	/* create index, 1 = yes, 0 = no (default) */
-	int idx;
+	/* make creation actions idempotent */
+	int if_not_exists;
+
+	/* actions derived from operation presets */
+	int drop_table;
+	CREATE_TABLE_ACTION create_table;
+	int load_data;
+	CREATE_INDEX_ACTION create_index;
 
 	/* maintenance statements, 1 = yes, 0 = no (default) */
 	int maintenance;
diff --git a/raster/test/regress/loader/IfNotExists-pre.sql b/raster/test/regress/loader/IfNotExists-pre.sql
new file mode 100644
index 000000000..76dd4370d
--- /dev/null
+++ b/raster/test/regress/loader/IfNotExists-pre.sql
@@ -0,0 +1,3 @@
+DROP TABLE IF EXISTS loadedrast;
+CREATE TABLE loadedrast ("rid" serial PRIMARY KEY, rast raster);
+CREATE INDEX loadedrast_rast_gist ON loadedrast USING gist (st_convexhull(rast));
diff --git a/raster/test/regress/loader/IfNotExists.opts b/raster/test/regress/loader/IfNotExists.opts
new file mode 100644
index 000000000..5189c54d2
--- /dev/null
+++ b/raster/test/regress/loader/IfNotExists.opts
@@ -0,0 +1 @@
+-c --if-not-exists -I
diff --git a/raster/test/regress/loader/IfNotExists.select.expected b/raster/test/regress/loader/IfNotExists.select.expected
new file mode 100644
index 000000000..47e5ca813
--- /dev/null
+++ b/raster/test/regress/loader/IfNotExists.select.expected
@@ -0,0 +1,4 @@
+1
+90|50|3
+255
+1
diff --git a/raster/test/regress/loader/IfNotExists.select.sql b/raster/test/regress/loader/IfNotExists.select.sql
new file mode 100644
index 000000000..0ffca6a0a
--- /dev/null
+++ b/raster/test/regress/loader/IfNotExists.select.sql
@@ -0,0 +1,8 @@
+SELECT count(*) FROM loadedrast;
+SELECT ST_Width(rast), ST_Height(rast), ST_NumBands(rast) FROM loadedrast WHERE rid = 1;
+SELECT ST_Value(rast, 1, 1, 1) FROM loadedrast WHERE rid = 1;
+SELECT COUNT(*)
+FROM pg_indexes
+WHERE schemaname = 'public'
+  AND tablename = 'loadedrast'
+  AND indexdef LIKE 'CREATE INDEX loadedrast_rast_gist ON public.loadedrast USING gist (st_convexhull(rast))';
diff --git a/raster/test/regress/loader/IfNotExists.tif.ref b/raster/test/regress/loader/IfNotExists.tif.ref
new file mode 100644
index 000000000..1e1cc0fc8
--- /dev/null
+++ b/raster/test/regress/loader/IfNotExists.tif.ref
@@ -0,0 +1 @@
+testraster.tif
diff --git a/raster/test/regress/tests.mk.in b/raster/test/regress/tests.mk.in
index 996f26583..8fa0cf6d2 100644
--- a/raster/test/regress/tests.mk.in
+++ b/raster/test/regress/tests.mk.in
@@ -131,6 +131,7 @@ RASTER_TEST_LOADER = \
 	$(top_srcdir)/raster/test/regress/loader/Basic \
 	$(top_srcdir)/raster/test/regress/loader/Projected \
 	$(top_srcdir)/raster/test/regress/loader/OverviewNoPadding \
+	$(top_srcdir)/raster/test/regress/loader/IfNotExists \
 	$(top_srcdir)/raster/test/regress/loader/BasicCopy \
 	$(top_srcdir)/raster/test/regress/loader/BasicFilename \
 	$(top_srcdir)/raster/test/regress/loader/BasicOutDB \

commit 4ec0a1dd0a311af57c593656637f5c98ac7a1799
Author: Darafei Praliaskouski <me at komzpa.net>
Date:   Sat Jun 20 17:16:59 2026 +0400

    Improve raster MapAlgebra callback arity handling
    
    Allow n-raster ST_MapAlgebra callbacks to omit the variadic userargs parameter, matching the existing callback arity flexibility in adjacent MapAlgebra variants. Size raster MapAlgebra FunctionCallInfo storage to the callback arities used instead of allocating FUNC_MAX_ARGS slots.
    
    Track the owned empty userargs array used for strict three-argument callbacks so it can be released with the MapAlgebra argument state.
    
    Closes #2804
    
    Closes #4315
    
    Closes https://github.com/postgis/postgis/pull/1035

diff --git a/NEWS b/NEWS
index 42f7dbaa8..52fafcd05 100644
--- a/NEWS
+++ b/NEWS
@@ -54,6 +54,8 @@ To take advantage of all postgis_sfcgal extension features SFCGAL 2.3+ is needed
 
 * Enhancements *
 
+ - #2804, #4315, [raster] Support two-argument ST_MapAlgebra callbacks
+          and pass callback call data as actual arguments (Darafei Praliaskouski)
  - #2898, Document SQL function cost tiers for contributors
           (Darafei Praliaskouski)
  - #6062, [topology] Stop using recursive snapping, for improved robustness (Sandro Santilli)
diff --git a/doc/reference_raster.xml b/doc/reference_raster.xml
index 3ddd3e373..04d059ffc 100644
--- a/doc/reference_raster.xml
+++ b/doc/reference_raster.xml
@@ -10276,7 +10276,7 @@ CREATE OR REPLACE FUNCTION sample_callbackfunc(value double precision[][][], pos
                                     </programlisting>
 
                                 <para>
-                                    The <varname>callbackfunc</varname> must have three arguments: a 3-dimension double precision array, a 2-dimension integer array and a variadic 1-dimension text array. The first argument <varname>value</varname> is the set of values (as double precision) from all input rasters. The three dimensions (where indexes are 1-based) are: raster #, row y, column x. The second argument <varname>position</varname> is the set of pixel positions from the output raster and input rasters. The outer dimension (where indexes are 0-based) is the raster #.  The position at outer dimension index 0 is the output raster's pixel position.  For each outer dimension, there are two elements in the inner dimension for X and Y.  The third argument <varname>userargs</varname> is for passing through any user-specified arguments.
+                                    The <varname>callbackfunc</varname> must have two or three arguments: a 3-dimension double precision array, a 2-dimension integer array and, optionally, a variadic 1-dimension text array. The first argument <varname>value</varname> is the set of values (as double precision) from all input rasters. The three dimensions (where indexes are 1-based) are: raster #, row y, column x. The second argument <varname>position</varname> is the set of pixel positions from the output raster and input rasters. The outer dimension (where indexes are 0-based) is the raster #.  The position at outer dimension index 0 is the output raster's pixel position.  For each outer dimension, there are two elements in the inner dimension for X and Y.  The optional third argument <varname>userargs</varname> is for passing through any user-specified arguments.
                                 </para>
 
                                 <para>
@@ -10364,7 +10364,7 @@ CREATE OR REPLACE FUNCTION sample_callbackfunc(value double precision[][][], pos
                             <term><varname>userargs</varname></term>
                             <listitem>
                                 <para>
-                                    The third argument to the <varname>callbackfunc</varname> is a <type>variadic text</type> array. All trailing text arguments are passed through to the specified <varname>callbackfunc</varname>, and are contained in the <varname>userargs</varname> argument.
+                                    If the <varname>callbackfunc</varname> declares a third argument, that argument is a <type>variadic text</type> array. All trailing text arguments are passed through to the specified <varname>callbackfunc</varname>, and are contained in the <varname>userargs</varname> argument.
                                 </para>
                             </listitem>
                         </varlistentry>
@@ -10376,12 +10376,6 @@ CREATE OR REPLACE FUNCTION sample_callbackfunc(value double precision[][][], pos
                         </para>
                     </note>
 
-                    <note>
-                        <para>
-                            The <type>text[]</type> argument to the <varname>callbackfunc</varname> is required, regardless of whether you choose to pass any arguments to the callback function for processing or not.
-                        </para>
-                    </note>
-
                     <para>
                         Variant 1 accepts an array of <varname>rastbandarg</varname> allowing the use of a map algebra operation on many rasters and/or many bands. See example Variant 1.
                     </para>
diff --git a/raster/rt_pg/rtpg_mapalgebra.c b/raster/rt_pg/rtpg_mapalgebra.c
index ba34e6323..f2109a30d 100644
--- a/raster/rt_pg/rtpg_mapalgebra.c
+++ b/raster/rt_pg/rtpg_mapalgebra.c
@@ -91,9 +91,10 @@ typedef struct {
 	/* copied from LOCAL_FCINFO in fmgr.h */
 	union {
 		FunctionCallInfoBaseData fcinfo;
-		char fcinfo_data[SizeForFunctionCallInfo(FUNC_MAX_ARGS)]; /* Could be optimized */
+		char fcinfo_data[SizeForFunctionCallInfo(3)];
 	} ufc_info_data;
 	FunctionCallInfo ufc_info;
+	ArrayType *empty_userargs;
 } rtpg_nmapalgebra_callback_arg;
 
 #if defined(__clang__)
@@ -157,6 +158,7 @@ static rtpg_nmapalgebra_arg rtpg_nmapalgebra_arg_init(void) {
 
 	arg->callback.ufc_noid = InvalidOid;
 	arg->callback.ufc_rettype = InvalidOid;
+	arg->callback.empty_userargs = NULL;
 
 	return arg;
 }
@@ -183,6 +185,8 @@ static void rtpg_nmapalgebra_arg_destroy(rtpg_nmapalgebra_arg arg) {
 		rt_raster_destroy(arg->cextent);
 	if( arg->mask != NULL )
 	  pfree(arg->mask);
+	if (arg->callback.empty_userargs != NULL)
+		pfree(arg->callback.empty_userargs);
 
 	pfree(arg);
 }
@@ -803,7 +807,8 @@ Datum RASTER_nMapAlgebra(PG_FUNCTION_ARGS)
 			noerr = 1;
 		}
 		/* function should have correct # of args */
-		else if (arg->callback.ufl_info.fn_nargs != 3) {
+		else if (arg->callback.ufl_info.fn_nargs < 2 || arg->callback.ufl_info.fn_nargs > 3)
+		{
 			noerr = 2;
 		}
 
@@ -842,7 +847,9 @@ Datum RASTER_nMapAlgebra(PG_FUNCTION_ARGS)
 					elog(ERROR, "RASTER_nMapAlgebra: Function provided must return scalar (double precision, float, int, smallint)");
 					break;
 				case 2:
-					elog(ERROR, "RASTER_nMapAlgebra: Function provided must have three input parameters");
+					elog(
+					    ERROR,
+					    "RASTER_nMapAlgebra: Function provided must have two or three input parameters");
 					break;
 				case 1:
 					elog(ERROR, "RASTER_nMapAlgebra: Function provided must return double precision, not resultset");
@@ -864,20 +871,26 @@ Datum RASTER_nMapAlgebra(PG_FUNCTION_ARGS)
 
 		arg->callback.ufc_info->args[0].isnull = FALSE;
 		arg->callback.ufc_info->args[1].isnull = FALSE;
-		arg->callback.ufc_info->args[2].isnull = FALSE;
-		/* userargs (7) */
-		if (!PG_ARGISNULL(9))
-			arg->callback.ufc_info->args[2].value = PG_GETARG_DATUM(9);
-		else {
-      if (arg->callback.ufl_info.fn_strict) {
-				/* build and assign an empty TEXT array */
-				/* TODO: manually free the empty array? */
-				arg->callback.ufc_info->args[2].value = PointerGetDatum(construct_empty_array(TEXTOID));
-				arg->callback.ufc_info->args[2].isnull = FALSE;
-      }
-			else {
-				arg->callback.ufc_info->args[2].value = (Datum)NULL;
-				arg->callback.ufc_info->args[2].isnull = TRUE;
+		if (arg->callback.ufl_info.fn_nargs == 3)
+		{
+			arg->callback.ufc_info->args[2].isnull = FALSE;
+			/* userargs (7) */
+			if (!PG_ARGISNULL(9))
+				arg->callback.ufc_info->args[2].value = PG_GETARG_DATUM(9);
+			else
+			{
+				if (arg->callback.ufl_info.fn_strict)
+				{
+					arg->callback.empty_userargs = construct_empty_array(TEXTOID);
+					arg->callback.ufc_info->args[2].value =
+					    PointerGetDatum(arg->callback.empty_userargs);
+					arg->callback.ufc_info->args[2].isnull = FALSE;
+				}
+				else
+				{
+					arg->callback.ufc_info->args[2].value = (Datum)NULL;
+					arg->callback.ufc_info->args[2].isnull = TRUE;
+				}
 			}
 		}
 	}
@@ -5280,7 +5293,7 @@ Datum RASTER_mapAlgebraFct(PG_FUNCTION_ARGS)
     int ret = -1;
     Oid oid;
     FmgrInfo cbinfo;
-    LOCAL_FCINFO(cbdata, FUNC_MAX_ARGS); /* Could be optimized */
+    LOCAL_FCINFO(cbdata, 3);
 
     Datum tmpnewval;
     char * strFromText = NULL;
@@ -5521,11 +5534,12 @@ Datum RASTER_mapAlgebraFct(PG_FUNCTION_ARGS)
     }
 
     /* prep function call data */
-    InitFunctionCallInfoData(*cbdata, &cbinfo, 2, InvalidOid, NULL, NULL);
+    InitFunctionCallInfoData(*cbdata, &cbinfo, cbinfo.fn_nargs, InvalidOid, NULL, NULL);
 
     cbdata->args[0].isnull = FALSE;
     cbdata->args[1].isnull = FALSE;
-    cbdata->args[2].isnull = FALSE;
+    if (cbinfo.fn_nargs == 3)
+	    cbdata->args[2].isnull = FALSE;
 
     /* check that the function isn't strict if the args are null. */
     if (PG_ARGISNULL(4)) {
@@ -5706,7 +5720,7 @@ Datum RASTER_mapAlgebraFctNgb(PG_FUNCTION_ARGS)
     int ret = -1;
     Oid oid;
     FmgrInfo cbinfo;
-    LOCAL_FCINFO(cbdata, FUNC_MAX_ARGS); /* Could be optimized */
+    LOCAL_FCINFO(cbdata, 3);
     Datum tmpnewval;
     ArrayType * neighborDatum;
     char * strFromText = NULL;
@@ -5953,7 +5967,7 @@ Datum RASTER_mapAlgebraFctNgb(PG_FUNCTION_ARGS)
     }
 
     /* prep function call data */
-    InitFunctionCallInfoData(*cbdata, &cbinfo, 3, InvalidOid, NULL, NULL);
+    InitFunctionCallInfoData(*cbdata, &cbinfo, cbinfo.fn_nargs, InvalidOid, NULL, NULL);
     cbdata->args[0].isnull = FALSE;
     cbdata->args[1].isnull = FALSE;
     cbdata->args[2].isnull = FALSE;
@@ -6332,7 +6346,7 @@ Datum RASTER_mapAlgebra2(PG_FUNCTION_ARGS)
 
 	Oid ufc_noid = InvalidOid;
 	FmgrInfo ufl_info;
-	LOCAL_FCINFO(ufc_info, FUNC_MAX_ARGS); /* Could be optimized */
+	LOCAL_FCINFO(ufc_info, 4);
 
 	int ufc_nullcount = 0;
 
diff --git a/raster/test/regress/rt_mapalgebra.sql b/raster/test/regress/rt_mapalgebra.sql
index a2810ae06..a3ad1e527 100644
--- a/raster/test/regress/rt_mapalgebra.sql
+++ b/raster/test/regress/rt_mapalgebra.sql
@@ -34,6 +34,15 @@ CREATE OR REPLACE FUNCTION raster_nmapalgebra_test(
 	END;
 	$$ LANGUAGE 'plpgsql' IMMUTABLE;
 
+CREATE OR REPLACE FUNCTION raster_nmapalgebra_test_no_userargs(
+	value double precision[][][],
+	pos int[][]
+)
+	RETURNS double precision
+	AS $$
+		SELECT $1[1][1][1] + $2[0][1] + $2[0][2];
+	$$ LANGUAGE 'sql' IMMUTABLE STRICT;
+
 SET client_min_messages TO notice;
 
 SELECT
@@ -60,6 +69,18 @@ SELECT
 FROM raster_nmapalgebra_in
 WHERE rid IN (2,3,4);
 
+SELECT
+	rid,
+	ST_Value(
+		ST_MapAlgebra(
+			ARRAY[ROW(rast, 1)]::rastbandarg[],
+			'raster_nmapalgebra_test_no_userargs(double precision[], int[])'::regprocedure
+		),
+		1, 1, 1
+	) = 3
+FROM raster_nmapalgebra_in
+WHERE rid = 2;
+
 SELECT
 	rid,
 	round(ST_Value(
@@ -594,5 +615,6 @@ FROM raster_nmapalgebra_in
 WHERE rid IN (2);
 
 DROP FUNCTION IF EXISTS raster_nmapalgebra_test(double precision[], int[], text[]);
+DROP FUNCTION IF EXISTS raster_nmapalgebra_test_no_userargs(double precision[], int[]);
 DROP FUNCTION IF EXISTS raster_nmapalgebra_test_bad_return(double precision[], int[], text[]);
 DROP TABLE IF EXISTS raster_nmapalgebra_in;
diff --git a/raster/test/regress/rt_mapalgebra_expected b/raster/test/regress/rt_mapalgebra_expected
index a7753a70e..407d368be 100644
--- a/raster/test/regress/rt_mapalgebra_expected
+++ b/raster/test/regress/rt_mapalgebra_expected
@@ -43,6 +43,7 @@ NOTICE:  userargs = <NULL>
 2|t
 3|t
 4|t
+2|t
 NOTICE:  value = {{{20}}}
 NOTICE:  pos = [0:1][1:2]={{1,1},{1,1}}
 NOTICE:  userargs = {3.14}

-----------------------------------------------------------------------

Summary of changes:
 NEWS                                               |   4 +
 doc/man/raster2pgsql.1                             |   5 +
 doc/reference_raster.xml                           |  10 +-
 doc/using_raster_dataman.xml                       |  14 ++
 raster/loader/raster2pgsql.c                       | 235 +++++++++++++++------
 raster/loader/raster2pgsql.h                       |  24 ++-
 raster/rt_pg/rtpg_mapalgebra.c                     |  60 ++++--
 raster/test/regress/loader/IfNotExists-pre.sql     |   3 +
 raster/test/regress/loader/IfNotExists.opts        |   1 +
 .../regress/loader/IfNotExists.select.expected     |   4 +
 raster/test/regress/loader/IfNotExists.select.sql  |   8 +
 .../loader/{Basic.tif.ref => IfNotExists.tif.ref}  |   0
 raster/test/regress/rt_mapalgebra.sql              |  22 ++
 raster/test/regress/rt_mapalgebra_expected         |   1 +
 raster/test/regress/tests.mk.in                    |   1 +
 15 files changed, 293 insertions(+), 99 deletions(-)
 create mode 100644 raster/test/regress/loader/IfNotExists-pre.sql
 create mode 100644 raster/test/regress/loader/IfNotExists.opts
 create mode 100644 raster/test/regress/loader/IfNotExists.select.expected
 create mode 100644 raster/test/regress/loader/IfNotExists.select.sql
 copy raster/test/regress/loader/{Basic.tif.ref => IfNotExists.tif.ref} (100%)


hooks/post-receive
-- 
PostGIS


More information about the postgis-tickets mailing list