Project: YugabyteDB (App Definition and Development / Database)
File: overlaps.md

title: Implementations that model the overlaps operator [YSQL] headerTitle: Implementations that model the overlaps operator linkTitle: Implementations that model the overlaps operator description: Implementations that model the overlaps operator. [YSQL] menu: v2.18: identifier: overlaps parent: miscellaneous weight: 30 type: docs This function models the overlaps semantics using two distinct approaches: Using the `&&` operator with two range values. From first principles using ordinary if-the-else logic. It considers just this invocation syntax: ```output overlaps_result (left-duration-bound-1, left-duration-bound-2) overlaps (right-duration-bound-1, right-duration-bound-2) ``` The other overloads: that define one duration using a pair of moments and one using a moment and a duration size that define both durations using a moment and a duration size can be trivially expressed as wrappers around the overload that defines both durations using a pair of moments. The model addresses just the overload that specifies each duration by a pair of plain timestamp bounds. The corresponding overloads for plain time and timestamptz can be trivially derived from this. This page's parent page, in the section , explained that the overlaps operator result is insensitive, for each of the two durations on which it acts, to the ordering of the moment arguments that define the duration's boundsand that it is also insensitive to which duration is given as the left duration and which is given as the right duration. However, the implementation needs to distinguish between the start and the finish moments and needs to know how the two durations are mutually ordered. In other words, it models this invocation syntax: ```output overlaps_result (earlier-duration-start-moment, earlier-duration-finish-moment) overlaps (later-duration-start-moment, later-duration-finish-moment) ``` The implementation therefore starts by deriving these values: ```output earlierstart, earlierfinish, laterstart, laterfinish ``` from these input values: ```output left1, left2, right1, right2 ``` Create the function modeledoverlaps()_ thus: ```plpgsql drop function if exists modeled_overlaps(timestamp, timestamp, timestamp, timestamp) cascade; create function modeledoverlaps(left1 in timestamp, left2 in timestamp, right1 in timestamp, right_2 in timestamp) returns boolean language plpgsql as $body$ declare leftstart constant timestamp not null := least (left1, left_2); leftfinish constant timestamp not null := greatest(left1, left_2); rightstart constant timestamp not null := least (right1, right_2); rightfinish constant timestamp not null := greatest(right1, right_2); leftisearlier constant boolean not null := case when leftstart < rightstart then true when leftstart > rightstart then false else -- Here when leftstart and rightstart coincide case when leftfinish < rightfinish then true else false end end; earlier_start timestamp not null := '-infinity'; -- convenient initial value; earlierfinish timestamp not null := earlierstart; laterstart timestamp not null := earlierstart; laterfinish timestamp not null := earlierstart; begin if leftisearlier then earlierstart := leftstart; earlierfinish := leftfinish; laterstart := rightstart; laterfinish := rightfinish; else earlierstart := rightstart; earlierfinish := rightfinish; laterstart := leftstart; laterfinish := leftfinish; end if; declare -- Modeled using the && operator with two range

-- Accommodate the fact that if the bounds of a '[)' range, r, coincide, -- then isempty(r) evaluates to TRUE by using a '[]' range instead. r1 constant tsrange not null := case when earlierstart = earlierfinish then tsrange(earlierstart, earlierfinish, '[]') else tsrange(earlierstart, earlierfinish, '[)') end; r2 constant tsrange not null := case when laterstart = laterfinish then tsrange(laterstart, laterfinish, '[]') else tsrange(laterstart, laterfinish, '[)') end; modeledusingranges boolean not null := r1 && r2; -- Modeled from first principles using ordinary if-the-else logic. modeledfromfirst_principles boolean not null := case -- Special case: both are instants when (earlierfinish = earlierstart) and (laterfinish = laterstart) then case when earlierstart = laterstart then true else false end -- Special case: earlier is instant when (earlierfinish = earlierstart) and (laterfinish > laterstart) then case when earlierstart = laterstart then true else false end -- Later is instant doesn't need special case treatment. -- Neither is instant else (earlierstart <= laterstart) and (earlierfinish > laterstart) end; begin assert (modeledfromfirstprinciples = modeledusing_ranges), 'Assert failed'; return modeledfromfirst_principles; end; end; $body$; ``` This function returns a user-defined type value that represents the return value from the native overlaps operator together with the return value from modeledoverlaps(), for the same parameterization of the two durations, as a pair of boolean_ values. Create the type modeledoverlapsvsoverlapsresult and the function modeledoverlapsvsoverlaps)_ thus: ```plpgsql drop type if exists modeledoverlapsvsoverlapsresult cascade; create type modeledoverlapsvsoverlapsresult as (o boolean, m boolean); drop function if exists modeledoverlapsvs_overlaps(timestamp, timestamp, timestamp, timestamp) cascade; create function modeledoverlapsvsoverlaps(left1 in timestamp, left2 in timestamp, right1 in timestamp, right_2 in timestamp) returns modeledoverlapsvsoverlapsresult language plpgsql as $body$ declare o constant boolean not null := (left1, left2) overlaps (right1, right2); m constant boolean not null := modeledoverlaps(left1, left2, right1, right_2); begin return (o, m)::modeledoverlapsvsoverlapsresult; end; $body$; ``` This function creates a text value from the parameterization of the two durations that encodes: the left1, left2, right1, and right2 plain timestamp values the result, true or false from the native overlaps operator only if the result from modeledoverlaps() is different from the result from the native overlaps operator, then it appends DISAGREE_. It also adds a significant test of its own. For each supplied parameterization, (left1, left2, right1, right2), it invokes modeledoverlapsvsoverlaps()_ with each of the eight parameter orderings that supposedly express the same semantics: (left1, left2, right1, right2) (left2, left1, right1, right2) (left1, left2, right2, right1) (left2, left1, right2, right1) (right1, right2, left1, left2) (right2, right1, left1, left2) (right1, right2, left2, left1) (right2, right1, left2, left1) and it tests that all of these invocations produce the same result. First create the helper procedure assertr1equalsr(int, modeledoverlapsvsoverlapsresult, modeledoverlapsvsoverlapsresult)_ to test if the result from the invocation that uses one of the second through the eighth orderings is identical to the result from the invocation that uses the first

```plpgsql drop procedure if exists assertr1equalsr(int, modeledoverlapsvsoverlapsresult, modeledoverlapsvsoverlaps_result) cascade; create procedure assertr1equalsr(testnr in int, r1 in modeledoverlapsvsoverlapsresult, r in modeledoverlapsvsoverlapsresult) language plpgsql as $body$ declare o1 constant boolean not null := r1.o; m1 constant boolean not null := r1.m; o constant boolean not null := r.o; m constant boolean not null := r.m; msg constant text not null := 'Test number '||test_nr::text||' assert failed'; begin assert ((o1 = o) and (m1 = m)), msg; end; $body$; ``` Now create the function modeledoverlapsvsoverlapsdisplay(): ```plpgsql drop function if exists modeledoverlapsvsoverlapsdisplay(timestamp, timestamp, timestamp, timestamp) cascade; create function modeledoverlapsvsoverlapsdisplay(left1 in timestamp, left2 in timestamp, right1 in timestamp, right2 in timestamp) returns text language plpgsql as $body$ declare input constant text not null := rpad(left_1||', ' ::text, 29)|| rpad(left_2 ::text, 27)||' | ' || rpad(right_1||', ' ::text, 29)|| rpad(right_2 ::text, 27)||' '; o1 boolean not null := false; m1 boolean not null := false; o boolean not null := false; m boolean not null := false; r modeledoverlapsvsoverlapsresult; r1 modeledoverlapsvsoverlapsresult; begin for j in 1..8 loop case j when 1 then r1 := modeledoverlapsvsoverlaps(left1, left2, right1, right_2); o1 := r1.o; m1 := r1.m; when 2 then r := modeledoverlapsvsoverlaps(left2, left1, right1, right_2); call assertr1equals_r(1, r1, r); when 3 then r := modeledoverlapsvsoverlaps(left1, left2, right2, right_1); call assertr1equals_r(2, r1, r); when 4 then r := modeledoverlapsvsoverlaps(left2, left1, right2, right_1); call assertr1equals_r(3, r1, r); when 5 then r := modeledoverlapsvsoverlaps(right1, right2, left1, left_2); call assertr1equals_r(4, r1, r); when 6 then r := modeledoverlapsvsoverlaps(right2, right1, left1, left_2); call assertr1equals_r(5, r1, r); when 7 then r := modeledoverlapsvsoverlaps(right1, right2, left2, left_1); call assertr1equals_r(6, r1, r); when 8 then r := modeledoverlapsvsoverlaps(right2, right1, left2, left_1); call assertr1equals_r(7, r1, r); end case; return case (m1 = o1) when true then input||o1::text else input||rpad(o1::text, 6)||'DISAGREE' end; end loop; end; $body$; ``` This table function simply invokes modeledoverlapsvsoverlapsdisplay() for all of the parameterizations that correspond to the interesting cases that are shown in the picture in this page's parent page in the section . It also adds a few more tests. Create it thus: ```plpgsql drop function if exists modeledoverlapsvsoverlapsreport() cascade; create function modeledoverlapsvsoverlapsreport() returns table(z text) language plpgsql as $body$ begin z := 'TWO FINITE DURATIONS'; return next; z := '--'; return next; z := ''; return next; z := ' 1. Durations do not overlap '|| modeledoverlapsvsoverlapsdisplay ('2000-01-15', '2000-05-15', '2000-08-15', '2000-12-15'); return next; z := ' 2. Right start = left end '|| modeledoverlapsvsoverlapsdisplay ('2000-01-15', '2000-05-15', '2000-05-15', '2000-12-15'); return next; z := ' 3. Durations overlap '|| modeledoverlapsvsoverlapsdisplay ('2000-01-15', '2000-08-15', '2000-05-15', '2000-12-15'); return next; z := ' 3. Durations overlap by 1 microsec '|| modeledoverlapsvsoverlapsdisplay ('2000-01-15', '2000-06-15 00:00:00.000001', '2000-06-15', '2000-12-15'); return next; z := ' 3. Durations overlap by 1 microsec '|| modeledoverlapsvsoverlapsdisplay ('2000-06-15', '2000-12-15', '2000-01-15', '2000-06-15 00:00:00.000001'); return next; z := '

Contained '|| modeledoverlapsvsoverlapsdisplay ('2000-01-15', '2000-12-15', '2000-05-15', '2000-08-15'); return next; z := ' 4. Contained, co-inciding at left '|| modeledoverlapsvsoverlapsdisplay ('2000-01-15', '2000-06-15', '2000-01-15', '2000-08-15'); return next; z := ' 4. Contained, co-inciding at right '|| modeledoverlapsvsoverlapsdisplay ('2000-01-15', '2000-06-15', '2000-02-15', '2000-06-15'); return next; z := ' 4. Durations coincide '|| modeledoverlapsvsoverlapsdisplay ('2000-01-15', '2000-06-15', '2000-01-15', '2000-06-15'); return next; z := ''; return next; z := 'ONE INSTANT, ONE FINITE DURATION'; return next; z := '--'; return next; z := ''; return next; z := ' 5. Instant before duration '|| modeledoverlapsvsoverlapsdisplay ('2000-02-15', '2000-02-15', '2000-03-15', '2000-04-15'); return next; z := ' 6. Instant coincides with duration start '|| modeledoverlapsvsoverlapsdisplay ('2000-02-15', '2000-02-15', '2000-02-15', '2000-03-15'); return next; z := ' 7. Instant within duration '|| modeledoverlapsvsoverlapsdisplay ('2000-02-15', '2000-02-15', '2000-01-15', '2000-03-15'); return next; z := ' 8. Instant coincides with duration end '|| modeledoverlapsvsoverlapsdisplay ('2000-02-15', '2000-02-15', '2000-01-15', '2000-02-15'); return next; z := ' 9. Instant after duration '|| modeledoverlapsvsoverlapsdisplay ('2000-05-15', '2000-05-15', '2000-03-15', '2000-04-15'); return next; z := ''; return next; z := 'TWO INSTANTS'; return next; z := ''; return next; z := ''; return next; z := '10. Instants differ '|| modeledoverlapsvsoverlapsdisplay ('2000-01-15', '2000-01-15', '2000-06-15', '2000-06-15'); return next; z := '11. Instants coincide '|| modeledoverlapsvsoverlapsdisplay ('2000-01-15', '2000-01-15', '2000-01-15', '2000-01-15'); return next; end; $body$; ``` Invoke it thus: ```plpgsql select z from modeledoverlapsvsoverlapsreport(); ``` This is the result: ```output TWO FINITE DURATIONS -- Durations do not overlap 2000-01-15 00:00:00, 2000-05-15 00:00:00 | 2000-08-15 00:00:00, 2000-12-15 00:00:00 false Right start = left end 2000-01-15 00:00:00, 2000-05-15 00:00:00 | 2000-05-15 00:00:00, 2000-12-15 00:00:00 false Durations overlap 2000-01-15 00:00:00, 2000-08-15 00:00:00 | 2000-05-15 00:00:00, 2000-12-15 00:00:00 true Durations overlap by 1 microsec 2000-01-15 00:00:00, 2000-06-15 00:00:00.000001 | 2000-06-15 00:00:00, 2000-12-15 00:00:00 true Durations overlap by 1 microsec 2000-06-15 00:00:00, 2000-12-15 00:00:00 | 2000-01-15 00:00:00, 2000-06-15 00:00:00.000001 true Contained 2000-01-15 00:00:00, 2000-12-15 00:00:00 | 2000-05-15 00:00:00, 2000-08-15 00:00:00 true Contained, co-inciding at left 2000-01-15 00:00:00, 2000-06-15 00:00:00 | 2000-01-15 00:00:00, 2000-08-15 00:00:00 true Contained, co-inciding at right 2000-01-15 00:00:00, 2000-06-15 00:00:00 | 2000-02-15 00:00:00, 2000-06-15 00:00:00 true Durations coincide 2000-01-15 00:00:00, 2000-06-15 00:00:00 | 2000-01-15 00:00:00, 2000-06-15 00:00:00 true ONE INSTANT, ONE FINITE DURATION -- Instant before duration 2000-02-15 00:00:00, 2000-02-15 00:00:00 | 2000-03-15 00:00:00, 2000-04-15 00:00:00 false Instant coincides with duration start 2000-02-15 00:00:00, 2000-02-15 00:00:00 | 2000-02-15 00:00:00, 2000-03-15 00:00:00 true Instant within duration 2000-02-15 00:00:00, 2000-02-15 00:00:00 | 2000-01-15 00:00:00, 2000-03-15 00:00:00 true Instant coincides with duration end 2000-02-15 00:00:00, 2000-02-15 00:00:00 | 2000-01-15 00:00:00, 2000-02-15 00:00:00 false Instant after duration 2000-05-15 00:00:00, 2000-05-15 00:00:00 | 2000-03-15 00:00:00, 2000-04-15 00:00:00 false TWO INSTANTS Instants differ 2000-01-15 00:00:00, 2000-01-15 00:00:00 | 2000-06-15 00:00:00, 2000-06-15 00:00:00 false Instants coincide 2000-01-15 00:00:00, 2000-01-15 00:00:00 | 2000-01-15 00:00:00, 2000-01-15 00:00:00 true ``` This is the same output that was shown on this page's parent page in the section .