|
31 | 31 | #include "postgres.h" |
32 | 32 |
|
33 | 33 | #include "common/hashfn.h" |
| 34 | +#include "funcapi.h" |
34 | 35 | #include "libpq/pqformat.h" |
35 | 36 | #include "miscadmin.h" |
36 | 37 | #include "nodes/makefuncs.h" |
|
39 | 40 | #include "optimizer/clauses.h" |
40 | 41 | #include "optimizer/cost.h" |
41 | 42 | #include "optimizer/optimizer.h" |
| 43 | +#include "utils/array.h" |
42 | 44 | #include "utils/builtins.h" |
43 | 45 | #include "utils/date.h" |
44 | 46 | #include "utils/lsyscache.h" |
@@ -1214,6 +1216,170 @@ range_split_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeT |
1214 | 1216 | return false; |
1215 | 1217 | } |
1216 | 1218 |
|
| 1219 | +/* |
| 1220 | + * range_minus_multi - like range_minus but as a SRF to accommodate splits, |
| 1221 | + * with no result rows if the result would be empty. |
| 1222 | + */ |
| 1223 | +Datum |
| 1224 | +range_minus_multi(PG_FUNCTION_ARGS) |
| 1225 | +{ |
| 1226 | + typedef struct |
| 1227 | + { |
| 1228 | + RangeType *rs[2]; |
| 1229 | + int n; |
| 1230 | + } range_minus_multi_fctx; |
| 1231 | + |
| 1232 | + FuncCallContext *funcctx; |
| 1233 | + range_minus_multi_fctx *fctx; |
| 1234 | + MemoryContext oldcontext; |
| 1235 | + |
| 1236 | + /* stuff done only on the first call of the function */ |
| 1237 | + if (SRF_IS_FIRSTCALL()) |
| 1238 | + { |
| 1239 | + RangeType *r1; |
| 1240 | + RangeType *r2; |
| 1241 | + Oid rngtypid; |
| 1242 | + TypeCacheEntry *typcache; |
| 1243 | + |
| 1244 | + /* create a function context for cross-call persistence */ |
| 1245 | + funcctx = SRF_FIRSTCALL_INIT(); |
| 1246 | + |
| 1247 | + /* |
| 1248 | + * switch to memory context appropriate for multiple function calls |
| 1249 | + */ |
| 1250 | + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); |
| 1251 | + |
| 1252 | + r1 = PG_GETARG_RANGE_P(0); |
| 1253 | + r2 = PG_GETARG_RANGE_P(1); |
| 1254 | + |
| 1255 | + /* Different types should be prevented by ANYRANGE matching rules */ |
| 1256 | + if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2)) |
| 1257 | + elog(ERROR, "range types do not match"); |
| 1258 | + |
| 1259 | + /* allocate memory for user context */ |
| 1260 | + fctx = (range_minus_multi_fctx *) palloc(sizeof(range_minus_multi_fctx)); |
| 1261 | + |
| 1262 | + /* |
| 1263 | + * Initialize state. We can't store the range typcache in fn_extra |
| 1264 | + * because the caller uses that for the SRF state. |
| 1265 | + */ |
| 1266 | + rngtypid = RangeTypeGetOid(r1); |
| 1267 | + typcache = lookup_type_cache(rngtypid, TYPECACHE_RANGE_INFO); |
| 1268 | + if (typcache->rngelemtype == NULL) |
| 1269 | + elog(ERROR, "type %u is not a range type", rngtypid); |
| 1270 | + range_minus_multi_internal(typcache, r1, r2, fctx->rs, &fctx->n); |
| 1271 | + |
| 1272 | + funcctx->user_fctx = fctx; |
| 1273 | + MemoryContextSwitchTo(oldcontext); |
| 1274 | + } |
| 1275 | + |
| 1276 | + /* stuff done on every call of the function */ |
| 1277 | + funcctx = SRF_PERCALL_SETUP(); |
| 1278 | + fctx = funcctx->user_fctx; |
| 1279 | + |
| 1280 | + if (funcctx->call_cntr < fctx->n) |
| 1281 | + { |
| 1282 | + /* |
| 1283 | + * We must keep these on separate lines because SRF_RETURN_NEXT does |
| 1284 | + * call_cntr++: |
| 1285 | + */ |
| 1286 | + RangeType *ret = fctx->rs[funcctx->call_cntr]; |
| 1287 | + |
| 1288 | + SRF_RETURN_NEXT(funcctx, RangeTypePGetDatum(ret)); |
| 1289 | + } |
| 1290 | + else |
| 1291 | + /* do when there is no more left */ |
| 1292 | + SRF_RETURN_DONE(funcctx); |
| 1293 | +} |
| 1294 | + |
| 1295 | +/* |
| 1296 | + * range_minus_multi_internal - Sets outputs and outputn to the ranges |
| 1297 | + * remaining and their count (respectively) after subtracting r2 from r1. |
| 1298 | + * The array should never contain empty ranges. |
| 1299 | + * The outputs will be ordered. We expect that outputs is an array of |
| 1300 | + * RangeType pointers, already allocated with two elements. |
| 1301 | + */ |
| 1302 | +void |
| 1303 | +range_minus_multi_internal(TypeCacheEntry *typcache, RangeType *r1, |
| 1304 | + RangeType *r2, RangeType **outputs, int *outputn) |
| 1305 | +{ |
| 1306 | + int cmp_l1l2, |
| 1307 | + cmp_l1u2, |
| 1308 | + cmp_u1l2, |
| 1309 | + cmp_u1u2; |
| 1310 | + RangeBound lower1, |
| 1311 | + lower2; |
| 1312 | + RangeBound upper1, |
| 1313 | + upper2; |
| 1314 | + bool empty1, |
| 1315 | + empty2; |
| 1316 | + |
| 1317 | + range_deserialize(typcache, r1, &lower1, &upper1, &empty1); |
| 1318 | + range_deserialize(typcache, r2, &lower2, &upper2, &empty2); |
| 1319 | + |
| 1320 | + if (empty1) |
| 1321 | + { |
| 1322 | + /* if r1 is empty then r1 - r2 is empty, so return zero results */ |
| 1323 | + *outputn = 0; |
| 1324 | + return; |
| 1325 | + } |
| 1326 | + else if (empty2) |
| 1327 | + { |
| 1328 | + /* r2 is empty so the result is just r1 (which we know is not empty) */ |
| 1329 | + outputs[0] = r1; |
| 1330 | + *outputn = 1; |
| 1331 | + return; |
| 1332 | + } |
| 1333 | + |
| 1334 | + /* |
| 1335 | + * Use the same logic as range_minus_internal, but support the split case |
| 1336 | + */ |
| 1337 | + cmp_l1l2 = range_cmp_bounds(typcache, &lower1, &lower2); |
| 1338 | + cmp_l1u2 = range_cmp_bounds(typcache, &lower1, &upper2); |
| 1339 | + cmp_u1l2 = range_cmp_bounds(typcache, &upper1, &lower2); |
| 1340 | + cmp_u1u2 = range_cmp_bounds(typcache, &upper1, &upper2); |
| 1341 | + |
| 1342 | + if (cmp_l1l2 < 0 && cmp_u1u2 > 0) |
| 1343 | + { |
| 1344 | + lower2.inclusive = !lower2.inclusive; |
| 1345 | + lower2.lower = false; /* it will become the upper bound */ |
| 1346 | + outputs[0] = make_range(typcache, &lower1, &lower2, false, NULL); |
| 1347 | + |
| 1348 | + upper2.inclusive = !upper2.inclusive; |
| 1349 | + upper2.lower = true; /* it will become the lower bound */ |
| 1350 | + outputs[1] = make_range(typcache, &upper2, &upper1, false, NULL); |
| 1351 | + |
| 1352 | + *outputn = 2; |
| 1353 | + } |
| 1354 | + else if (cmp_l1u2 > 0 || cmp_u1l2 < 0) |
| 1355 | + { |
| 1356 | + outputs[0] = r1; |
| 1357 | + *outputn = 1; |
| 1358 | + } |
| 1359 | + else if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0) |
| 1360 | + { |
| 1361 | + *outputn = 0; |
| 1362 | + } |
| 1363 | + else if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0) |
| 1364 | + { |
| 1365 | + lower2.inclusive = !lower2.inclusive; |
| 1366 | + lower2.lower = false; /* it will become the upper bound */ |
| 1367 | + outputs[0] = make_range(typcache, &lower1, &lower2, false, NULL); |
| 1368 | + *outputn = 1; |
| 1369 | + } |
| 1370 | + else if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0) |
| 1371 | + { |
| 1372 | + upper2.inclusive = !upper2.inclusive; |
| 1373 | + upper2.lower = true; /* it will become the lower bound */ |
| 1374 | + outputs[0] = make_range(typcache, &upper2, &upper1, false, NULL); |
| 1375 | + *outputn = 1; |
| 1376 | + } |
| 1377 | + else |
| 1378 | + { |
| 1379 | + elog(ERROR, "unexpected case in range_minus_multi"); |
| 1380 | + } |
| 1381 | +} |
| 1382 | + |
1217 | 1383 | /* range -> range aggregate functions */ |
1218 | 1384 |
|
1219 | 1385 | Datum |
|
0 commit comments