@@ -124,6 +124,11 @@ static ArrayType *create_array_envelope(int ndims, int *dimv, int *lbv, int nbyt
124
124
static ArrayType * array_fill_internal (ArrayType * dims , ArrayType * lbs ,
125
125
Datum value , bool isnull , Oid elmtype ,
126
126
FunctionCallInfo fcinfo );
127
+ static ArrayType * array_replace_internal (ArrayType * array ,
128
+ Datum search , bool search_isnull ,
129
+ Datum replace , bool replace_isnull ,
130
+ bool remove , Oid collation ,
131
+ FunctionCallInfo fcinfo );
127
132
128
133
129
134
/*
@@ -5174,3 +5179,304 @@ array_unnest(PG_FUNCTION_ARGS)
5174
5179
SRF_RETURN_DONE (funcctx );
5175
5180
}
5176
5181
}
5182
+
5183
+
5184
+ /*
5185
+ * array_replace/array_remove support
5186
+ *
5187
+ * Find all array entries matching (not distinct from) search/search_isnull,
5188
+ * and delete them if remove is true, else replace them with
5189
+ * replace/replace_isnull. Comparisons are done using the specified
5190
+ * collation. fcinfo is passed only for caching purposes.
5191
+ */
5192
+ static ArrayType *
5193
+ array_replace_internal (ArrayType * array ,
5194
+ Datum search , bool search_isnull ,
5195
+ Datum replace , bool replace_isnull ,
5196
+ bool remove , Oid collation ,
5197
+ FunctionCallInfo fcinfo )
5198
+ {
5199
+ ArrayType * result ;
5200
+ Oid element_type ;
5201
+ Datum * values ;
5202
+ bool * nulls ;
5203
+ int * dim ;
5204
+ int ndim ;
5205
+ int nitems ,
5206
+ nresult ;
5207
+ int i ;
5208
+ int32 nbytes = 0 ;
5209
+ int32 dataoffset ;
5210
+ bool hasnulls ;
5211
+ int typlen ;
5212
+ bool typbyval ;
5213
+ char typalign ;
5214
+ char * arraydataptr ;
5215
+ bits8 * bitmap ;
5216
+ int bitmask ;
5217
+ bool changed = false;
5218
+ TypeCacheEntry * typentry ;
5219
+ FunctionCallInfoData locfcinfo ;
5220
+
5221
+ element_type = ARR_ELEMTYPE (array );
5222
+ ndim = ARR_NDIM (array );
5223
+ dim = ARR_DIMS (array );
5224
+ nitems = ArrayGetNItems (ndim , dim );
5225
+
5226
+ /* Return input array unmodified if it is empty */
5227
+ if (nitems <= 0 )
5228
+ return array ;
5229
+
5230
+ /*
5231
+ * We can't remove elements from multi-dimensional arrays, since the
5232
+ * result might not be rectangular.
5233
+ */
5234
+ if (remove && ndim > 1 )
5235
+ ereport (ERROR ,
5236
+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
5237
+ errmsg ("removing elements from multidimensional arrays is not supported" )));
5238
+
5239
+ /*
5240
+ * We arrange to look up the equality function only once per series of
5241
+ * calls, assuming the element type doesn't change underneath us.
5242
+ */
5243
+ typentry = (TypeCacheEntry * ) fcinfo -> flinfo -> fn_extra ;
5244
+ if (typentry == NULL ||
5245
+ typentry -> type_id != element_type )
5246
+ {
5247
+ typentry = lookup_type_cache (element_type ,
5248
+ TYPECACHE_EQ_OPR_FINFO );
5249
+ if (!OidIsValid (typentry -> eq_opr_finfo .fn_oid ))
5250
+ ereport (ERROR ,
5251
+ (errcode (ERRCODE_UNDEFINED_FUNCTION ),
5252
+ errmsg ("could not identify an equality operator for type %s" ,
5253
+ format_type_be (element_type ))));
5254
+ fcinfo -> flinfo -> fn_extra = (void * ) typentry ;
5255
+ }
5256
+ typlen = typentry -> typlen ;
5257
+ typbyval = typentry -> typbyval ;
5258
+ typalign = typentry -> typalign ;
5259
+
5260
+ /*
5261
+ * Detoast values if they are toasted. The replacement value must be
5262
+ * detoasted for insertion into the result array, while detoasting the
5263
+ * search value only once saves cycles.
5264
+ */
5265
+ if (typlen == -1 )
5266
+ {
5267
+ if (!search_isnull )
5268
+ search = PointerGetDatum (PG_DETOAST_DATUM (search ));
5269
+ if (!replace_isnull )
5270
+ replace = PointerGetDatum (PG_DETOAST_DATUM (replace ));
5271
+ }
5272
+
5273
+ /* Prepare to apply the comparison operator */
5274
+ InitFunctionCallInfoData (locfcinfo , & typentry -> eq_opr_finfo , 2 ,
5275
+ collation , NULL , NULL );
5276
+
5277
+ /* Allocate temporary arrays for new values */
5278
+ values = (Datum * ) palloc (nitems * sizeof (Datum ));
5279
+ nulls = (bool * ) palloc (nitems * sizeof (bool ));
5280
+
5281
+ /* Loop over source data */
5282
+ arraydataptr = ARR_DATA_PTR (array );
5283
+ bitmap = ARR_NULLBITMAP (array );
5284
+ bitmask = 1 ;
5285
+ hasnulls = false;
5286
+ nresult = 0 ;
5287
+
5288
+ for (i = 0 ; i < nitems ; i ++ )
5289
+ {
5290
+ Datum elt ;
5291
+ bool isNull ;
5292
+ bool oprresult ;
5293
+ bool skip = false;
5294
+
5295
+ /* Get source element, checking for NULL */
5296
+ if (bitmap && (* bitmap & bitmask ) == 0 )
5297
+ {
5298
+ isNull = true;
5299
+ /* If searching for NULL, we have a match */
5300
+ if (search_isnull )
5301
+ {
5302
+ if (remove )
5303
+ {
5304
+ skip = true;
5305
+ changed = true;
5306
+ }
5307
+ else if (!replace_isnull )
5308
+ {
5309
+ values [nresult ] = replace ;
5310
+ isNull = false;
5311
+ changed = true;
5312
+ }
5313
+ }
5314
+ }
5315
+ else
5316
+ {
5317
+ isNull = false;
5318
+ elt = fetch_att (arraydataptr , typbyval , typlen );
5319
+ arraydataptr = att_addlength_datum (arraydataptr , typlen , elt );
5320
+ arraydataptr = (char * ) att_align_nominal (arraydataptr , typalign );
5321
+
5322
+ if (search_isnull )
5323
+ {
5324
+ /* no match possible, keep element */
5325
+ values [nresult ] = elt ;
5326
+ }
5327
+ else
5328
+ {
5329
+ /*
5330
+ * Apply the operator to the element pair
5331
+ */
5332
+ locfcinfo .arg [0 ] = elt ;
5333
+ locfcinfo .arg [1 ] = search ;
5334
+ locfcinfo .argnull [0 ] = false;
5335
+ locfcinfo .argnull [1 ] = false;
5336
+ locfcinfo .isnull = false;
5337
+ oprresult = DatumGetBool (FunctionCallInvoke (& locfcinfo ));
5338
+ if (!oprresult )
5339
+ {
5340
+ /* no match, keep element */
5341
+ values [nresult ] = elt ;
5342
+ }
5343
+ else
5344
+ {
5345
+ /* match, so replace or delete */
5346
+ changed = true;
5347
+ if (remove )
5348
+ skip = true;
5349
+ else
5350
+ {
5351
+ values [nresult ] = replace ;
5352
+ isNull = replace_isnull ;
5353
+ }
5354
+ }
5355
+ }
5356
+ }
5357
+
5358
+ if (!skip )
5359
+ {
5360
+ nulls [nresult ] = isNull ;
5361
+ if (isNull )
5362
+ hasnulls = true;
5363
+ else
5364
+ {
5365
+ /* Update total result size */
5366
+ nbytes = att_addlength_datum (nbytes , typlen , values [nresult ]);
5367
+ nbytes = att_align_nominal (nbytes , typalign );
5368
+ /* check for overflow of total request */
5369
+ if (!AllocSizeIsValid (nbytes ))
5370
+ ereport (ERROR ,
5371
+ (errcode (ERRCODE_PROGRAM_LIMIT_EXCEEDED ),
5372
+ errmsg ("array size exceeds the maximum allowed (%d)" ,
5373
+ (int ) MaxAllocSize )));
5374
+ }
5375
+ nresult ++ ;
5376
+ }
5377
+
5378
+ /* advance bitmap pointer if any */
5379
+ if (bitmap )
5380
+ {
5381
+ bitmask <<= 1 ;
5382
+ if (bitmask == 0x100 )
5383
+ {
5384
+ bitmap ++ ;
5385
+ bitmask = 1 ;
5386
+ }
5387
+ }
5388
+ }
5389
+
5390
+ /*
5391
+ * If not changed just return the original array
5392
+ */
5393
+ if (!changed )
5394
+ {
5395
+ pfree (values );
5396
+ pfree (nulls );
5397
+ return array ;
5398
+ }
5399
+
5400
+ /* Allocate and initialize the result array */
5401
+ if (hasnulls )
5402
+ {
5403
+ dataoffset = ARR_OVERHEAD_WITHNULLS (ndim , nresult );
5404
+ nbytes += dataoffset ;
5405
+ }
5406
+ else
5407
+ {
5408
+ dataoffset = 0 ; /* marker for no null bitmap */
5409
+ nbytes += ARR_OVERHEAD_NONULLS (ndim );
5410
+ }
5411
+ result = (ArrayType * ) palloc0 (nbytes );
5412
+ SET_VARSIZE (result , nbytes );
5413
+ result -> ndim = ndim ;
5414
+ result -> dataoffset = dataoffset ;
5415
+ result -> elemtype = element_type ;
5416
+ memcpy (ARR_DIMS (result ), ARR_DIMS (array ), 2 * ndim * sizeof (int ));
5417
+
5418
+ if (remove )
5419
+ {
5420
+ /* Adjust the result length */
5421
+ ARR_DIMS (result )[0 ] = nresult ;
5422
+ }
5423
+
5424
+ /* Insert data into result array */
5425
+ CopyArrayEls (result ,
5426
+ values , nulls , nresult ,
5427
+ typlen , typbyval , typalign ,
5428
+ false);
5429
+
5430
+ pfree (values );
5431
+ pfree (nulls );
5432
+
5433
+ return result ;
5434
+ }
5435
+
5436
+ /*
5437
+ * Remove any occurrences of an element from an array
5438
+ *
5439
+ * If used on a multi-dimensional array this will raise an error.
5440
+ */
5441
+ Datum
5442
+ array_remove (PG_FUNCTION_ARGS )
5443
+ {
5444
+ ArrayType * array ;
5445
+ Datum search = PG_GETARG_DATUM (1 );
5446
+ bool search_isnull = PG_ARGISNULL (1 );
5447
+
5448
+ if (PG_ARGISNULL (0 ))
5449
+ PG_RETURN_NULL ();
5450
+ array = PG_GETARG_ARRAYTYPE_P (0 );
5451
+
5452
+ array = array_replace_internal (array ,
5453
+ search , search_isnull ,
5454
+ (Datum ) 0 , true,
5455
+ true, PG_GET_COLLATION (),
5456
+ fcinfo );
5457
+ PG_RETURN_ARRAYTYPE_P (array );
5458
+ }
5459
+
5460
+ /*
5461
+ * Replace any occurrences of an element in an array
5462
+ */
5463
+ Datum
5464
+ array_replace (PG_FUNCTION_ARGS )
5465
+ {
5466
+ ArrayType * array ;
5467
+ Datum search = PG_GETARG_DATUM (1 );
5468
+ bool search_isnull = PG_ARGISNULL (1 );
5469
+ Datum replace = PG_GETARG_DATUM (2 );
5470
+ bool replace_isnull = PG_ARGISNULL (2 );
5471
+
5472
+ if (PG_ARGISNULL (0 ))
5473
+ PG_RETURN_NULL ();
5474
+ array = PG_GETARG_ARRAYTYPE_P (0 );
5475
+
5476
+ array = array_replace_internal (array ,
5477
+ search , search_isnull ,
5478
+ replace , replace_isnull ,
5479
+ false, PG_GET_COLLATION (),
5480
+ fcinfo );
5481
+ PG_RETURN_ARRAYTYPE_P (array );
5482
+ }
0 commit comments