diff --git a/expected/33_no_gaps_numeric_test.out b/expected/33_no_gaps_numeric_test.out index 827dddf..fa9f926 100644 --- a/expected/33_no_gaps_numeric_test.out +++ b/expected/33_no_gaps_numeric_test.out @@ -56,6 +56,10 @@ WHERE job_id = 1; SELECT sql_saga.no_gaps(numrange(valid_from, valid_to), numrange(1.5, 12.5)) FROM numeric_shifts WHERE job_id = 1; + no_gaps +--------- + t +(1 row) -- Test 3: Range with Extra at the Beginning -- Expected: TRUE @@ -72,18 +76,30 @@ WHERE job_id = 1; SELECT sql_saga.no_gaps(numrange(valid_from, valid_to), numrange(1.5, 11.5)) FROM numeric_shifts WHERE job_id = 1; + no_gaps +--------- + t +(1 row) -- Test 5: Range with Extra on Both Sides -- Expected: TRUE SELECT sql_saga.no_gaps(numrange(valid_from, valid_to), numrange(2.5, 11.5)) FROM numeric_shifts WHERE job_id = 1; + no_gaps +--------- + t +(1 row) -- Test 6: Range that Misses Completely -- Expected: FALSE SELECT sql_saga.no_gaps(numrange(valid_from, valid_to), numrange(20.5, 25.5)) FROM numeric_shifts WHERE job_id = 1; + no_gaps +--------- + f +(1 row) -- Test 7: Range with Uncovered Time at the Beginning -- Expected: FALSE @@ -100,6 +116,10 @@ WHERE job_id = 1; SELECT sql_saga.no_gaps(numrange(valid_from, valid_to), numrange(1.5, 15.5)) FROM numeric_shifts WHERE job_id = 1; + no_gaps +--------- + f +(1 row) SELECT sql_saga.drop_unique_key('numeric_shifts', 'numeric_shifts_job_id_worker_id_valid'); drop_unique_key diff --git a/no_gaps.c b/no_gaps.c index a3d99ca..88a00ca 100644 --- a/no_gaps.c +++ b/no_gaps.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ // Declarations/Prototypes +char *DatumGetString(TypeCacheEntry *typcache, RangeBound bound); Datum DatumGet(TypeCacheEntry *typcache, RangeBound bound); Datum DatumNegativeInfinity(TypeCacheEntry *typcache); @@ -65,7 +67,7 @@ typedef struct no_gaps_state { // Implementations Datum no_gaps_transfn(PG_FUNCTION_ARGS) { - MemoryContext aggContext; + MemoryContext aggContext, oldContext; no_gaps_state *state; RangeType *current_range, *target_range; @@ -107,7 +109,9 @@ Datum no_gaps_transfn(PG_FUNCTION_ARGS) ereport(DEBUG1, (errmsg("target is [%ld, %ld)", DatumGet(typcache, state->target_start), DatumGet(typcache, state->target_end)))); // Initialize covered_to to negative infinity bound + oldContext = MemoryContextSwitchTo(aggContext); state->covered_to.val = DatumNegativeInfinity(typcache); + MemoryContextSwitchTo(oldContext); state->covered_to.infinite = true; state->covered_to.inclusive = true; state->covered_to.lower = true; @@ -181,15 +185,25 @@ Datum no_gaps_transfn(PG_FUNCTION_ARGS) // Update the covered range if the current range extends beyond it if (range_cmp_bounds(typcache, ¤t_end, &state->covered_to) > 0) { + Oid datum_oid = typcache->rngelemtype->type_id; + TypeCacheEntry *datumtypcache; + state->covered_to = current_end; - //if (!typcache->typbyval && typcache->typlen == -1) { - // Size datumSize = VARSIZE_ANY(DatumGetPointer(current_end.val)); - // MemoryContext oldContext = MemoryContextSwitchTo(aggContext); - // state->covered_to.val = PointerGetDatum(palloc(datumSize)); - // memcpy(DatumGetPointer(state->covered_to.val), DatumGetPointer(current_end.val), datumSize); - // MemoryContextSwitchTo(oldContext); - //} + datumtypcache = lookup_type_cache(datum_oid, 0); + if (!datumtypcache->typbyval && datumtypcache->typlen == -1) { + MemoryContext oldContext; + ereport(DEBUG1, (errmsg("Performing memory copy."))); + //ereport(DEBUG1, (errmsg("Before pfree(state->covered_to.val)"))); + //pfree(state->covered_to.val); + oldContext = MemoryContextSwitchTo(aggContext); + ereport(DEBUG1, (errmsg("Before datumCopy."))); + state->covered_to.val = datumCopy(current_end.val, /*pass by reference when false*/ false, /*dynamic length when -1*/ -1); + ereport(DEBUG1, (errmsg("After datumCopy."))); + MemoryContextSwitchTo(oldContext); + } else { + ereport(DEBUG1, (errmsg("Skipping memory copy."))); + } // Notice that the previous non-inclusive end is included in the next start. state->covered_to.inclusive = true; @@ -264,3 +278,46 @@ Datum DatumNegativeInfinity(TypeCacheEntry *typcache) return (Datum) 0; // This line will not be reached due to the elog(ERROR) above } } + + +char *DatumGetString(TypeCacheEntry *typcache, RangeBound bound) { + Oid elem_type_id = typcache->rngelemtype->type_id; + char *result; + + switch (elem_type_id) { + case INT4OID: + result = psprintf("%d", DatumGetInt32(bound.val)); + break; + case INT8OID: + result = psprintf("%ld", DatumGetInt64(bound.val)); + break; + case DATEOID: { + char *dateStr = DatumGetCString(DirectFunctionCall1(date_out, bound.val)); + result = psprintf("%s", dateStr); + pfree(dateStr); + break; + } + case NUMERICOID: { + char *numericStr = DatumGetCString(DirectFunctionCall1(numeric_out, bound.val)); + result = psprintf("%s", numericStr); + pfree(numericStr); + break; + } + case TIMESTAMPOID: { + char *timestampStr = DatumGetCString(DirectFunctionCall1(timestamp_out, bound.val)); + result = psprintf("%s", timestampStr); + pfree(timestampStr); + break; + } + case TIMESTAMPTZOID: { + char *timestamptzStr = DatumGetCString(DirectFunctionCall1(timestamptz_out, bound.val)); + result = psprintf("%s", timestamptzStr); + pfree(timestamptzStr); + break; + } + default: + elog(ERROR, "Unsupported element type id: %u", elem_type_id); + return NULL; // This line will not be reached due to the elog(ERROR) above + } + return result; +} diff --git a/sql/33_no_gaps_numeric_test.sql b/sql/33_no_gaps_numeric_test.sql index fca5e60..bb4dbfe 100644 --- a/sql/33_no_gaps_numeric_test.sql +++ b/sql/33_no_gaps_numeric_test.sql @@ -20,8 +20,6 @@ INSERT INTO numeric_shifts(job_id, worker_id, valid_from, valid_to) VALUES TABLE numeric_shifts; -SET client_min_messages TO DEBUG1; - -- This test checks for an exact match with one range -- Expected: TRUE SELECT sql_saga.no_gaps(numrange(valid_from, valid_to), numrange(1.5, 6.5)) @@ -70,8 +68,6 @@ SELECT sql_saga.no_gaps(numrange(valid_from, valid_to), numrange(1.5, 15.5)) FROM numeric_shifts WHERE job_id = 1; -SET client_min_messages TO NOTICE; - SELECT sql_saga.drop_unique_key('numeric_shifts', 'numeric_shifts_job_id_worker_id_valid'); SELECT sql_saga.drop_era('numeric_shifts');