Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MODULES = pg_rational
EXTENSION = pg_rational
DATA = pg_rational--0.0.1.sql pg_rational--0.0.1--0.0.2.sql pg_rational--0.0.2.sql pg_rational--0.0.2--0.0.3.sql pg_rational--0.0.3.sql
DATA = pg_rational--0.0.1.sql pg_rational--0.0.1--0.0.2.sql pg_rational--0.0.2.sql pg_rational--0.0.2--0.0.3.sql pg_rational--0.0.3.sql pg_rational--0.0.3--0.0.4.sql pg_rational--0.0.4.sql
REGRESS = pg_rational_test
PG_CPPFLAGS = -std=c11 -Wextra -Wpedantic

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ precision in the denominator.
* Defers GCD calculation until requested or absolutely required
* Supports btree and hash indices
* Implements Stern-Brocot trees for finding intermediate points
* Coercion from integer/bigint/tuple
* Coercion from integer/float/tuple
* Custom aggregate

### Motivation
Expand Down Expand Up @@ -120,7 +120,7 @@ select * from todos order by prio asc;
*/
```

This extension uses Stern-Brocot trees to find efficient intermediate points as fractions in lowest terms. It can continue to split deeper between fractions as much as any practical application requires.
This extension uses Stern-Brocot trees to find efficient intermediate points as fractions in lowest terms. The two-argument form limits the search to one million steps; use the three-argument form to choose a different positive limit when needed.

Using floats, on the other hand, and picking the midpoints between adjacent values runs out of space rapidly (you only need 50-odd inserts at the wrong spot to start hitting problems).

Expand All @@ -141,7 +141,7 @@ create extension pg_rational;

### Caveats

The `rational_intermediate` function is super fast on typical intervals, but the narrower the range between arguments the longer it takes. We may want to add a max search depth parameter to prevent malicious values from hogging the server.
The `rational_intermediate` function is super fast on typical intervals, but the narrower the range between arguments the longer it takes. The optional third argument sets the maximum search depth and prevents unusually narrow intervals from using the server indefinitely.

### Thanks

Expand Down
65 changes: 65 additions & 0 deletions expected/pg_rational_test.out
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ select 2147483647.1::float::rational;
ERROR: value too large for rational
select 'NAN'::float::rational;
ERROR: value too large for rational
select 'Infinity'::float::rational;
ERROR: value too large for rational
select '-Infinity'::float::rational;
ERROR: value too large for rational
-- to float
select '1/2'::rational::float;
float8
Expand Down Expand Up @@ -207,6 +211,20 @@ select rational_simplify('1/-2147483648');
1/-2147483648
(1 row)

-- minimum numerator with a negative unit denominator remains representable
select rational_simplify(rational_create(-2147483648, -1));
rational_simplify
-------------------
-2147483648/-1
(1 row)

-- zero with the minimum denominator has the same hash as zero over one
select rational_hash(rational_create(0, -2147483648)) = rational_hash('0/1'::rational);
?column?
----------
t
(1 row)

-- biggest value reduces
select rational_simplify('2147483647/2147483647');
rational_simplify
Expand Down Expand Up @@ -305,6 +323,11 @@ select '2/3'::rational / '2/3';
6/6
(1 row)

-- division by zero

select '1/2'::rational / 0;
ERROR: division by zero

-- negation
-- flips sign of numerator
select -('1/2'::rational);
Expand Down Expand Up @@ -461,6 +484,26 @@ select '1/2147483647'::rational < '2/2147483647';
t
(1 row)

-- negative denominators created through the constructor compare by value

select rational_create(1, -2) = '-1/2'::rational;
?column?
----------
t
(1 row)

select rational_create(1, -2) < 0;
?column?
----------
t
(1 row)

select rational_create(1, -2) > '-1/1'::rational;
?column?
----------
t
(1 row)

-- lte
select r
from unnest(ARRAY[
Expand Down Expand Up @@ -688,3 +731,25 @@ select rational_intermediate(NULL, '15/16');
1/2
(1 row)

-- bounded intermediate search

select rational_intermediate(0, '1/2147483647', 1);
ERROR: maximum search depth exceeded

select rational_intermediate(
'1836311903/1134903170',
'1134903170/701408733',
100
);
ERROR: intermediate value overflow in rational intermediate

-- bounded intermediate search is parallel safe

select proparallel
from pg_proc
where proname = 'rational_intermediate'
and pronargs = 3;
proparallel
-------------
s
(1 row)
16 changes: 16 additions & 0 deletions pg_rational--0.0.3--0.0.4.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
CREATE FUNCTION rational_intermediate(rational, rational, integer)
RETURNS rational
AS '$libdir/pg_rational'
LANGUAGE C IMMUTABLE;

DO LANGUAGE plpgsql $$
BEGIN
IF current_setting('server_version_num')::int >= 90600
THEN
EXECUTE $alter$
ALTER FUNCTION rational_intermediate(rational, rational) PARALLEL SAFE;
ALTER FUNCTION rational_intermediate(rational, rational, integer) PARALLEL SAFE;
$alter$;
END IF;
END
$$;
Loading