Re: Question about integer out of range in function

From: Condor <condor(at)stz-bg(dot)com>
To: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
Cc: pgsql-general(at)lists(dot)postgresql(dot)org
Subject: Re: Question about integer out of range in function
Date: 2021-05-16 17:54:20
Message-ID: 1dda5d4cec444d838324c225934ddcc8@stz-bg.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-general

On 14-05-2021 17:42, Tom Lane wrote:
> Condor <condor(at)stz-bg(dot)com> writes:
>> new_time = fromtime * 1000; -- here is line 19
>
> Are you entirely certain that you counted lines correctly?
> If new_time and fromtime are both declared bigint, and
> fromtime is on the order of
>
> # select extract(epoch from now())::bigint;
> extract
> ------------
> 1621003124
> (1 row)
>
> then there's no way that that statement fails on integer overflow.
>
> What I'm wondering about is the next line:
>
>> unix_time = (new_time + rand_int)::BIGINT;
>
> You've not shown us the declaration of unix_time ...
>
> regards, tom lane

Hello Tom,

I can't figure out where the problem is. The only explanation I can
guess is that my expectations are plpgsql will sum variables like asm or
c, well .. let me explain.

CREATE FUNCTION generate_ulid(fromtime int default 0) -- Yes is INT,
that is the problem. Explain bellow.
RETURNS TEXT
AS $$
DECLARE
unix_time BIGINT;
ulid BYTEA;
rand_int INTEGER;
new_time BIGINT;

BEGIN
-- 6 timestamp bytes
IF fromtime = 0 THEN
unix_time = (EXTRACT(EPOCH FROM NOW()) * 1000)::BIGINT;
ELSE
rand_int = (random() * 1000)::INT;
new_time = fromtime * 1000; -- here is line 19
unix_time = (new_time + rand_int)::BIGINT;
END IF;

new_time is bigint, fromtime is int so I expect to be like assembly or c
:

new_time = fromtime;
new_time * 1000;

/*
mov %rip, %eax
imul $0x3e8, %eax, %eax
mov %eax, %rip
*/

but I guest it's seems plpgsql do:

fromtime = fromtime * 1000;
new_time = fromtime;

Here is the complete func:

CREATE EXTENSION IF NOT EXISTS pgcrypto;

DROP FUNCTION IF EXISTS generate_ulid(int) CASCADE;
DROP FUNCTION IF EXISTS generate_ulid(BIGINT) CASCADE;
DROP FUNCTION IF EXISTS generate_ulid() CASCADE;

CREATE FUNCTION generate_ulid(fromtime int default 0)
RETURNS TEXT
AS $$
DECLARE
-- Crockford's Base32
encoding BYTEA = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
timestamp BYTEA = E'\\000\\000\\000\\000\\000\\000';
output TEXT = '';

unix_time BIGINT;
ulid BYTEA;
rand_int INTEGER;
new_time BIGINT;

BEGIN
-- 6 timestamp bytes
IF fromtime = 0 THEN
unix_time = (EXTRACT(EPOCH FROM NOW()) * 1000)::BIGINT;
ELSE
rand_int = (random() * 1000)::INT;
new_time = fromtime * 1000; -- I think this is the broken line
unix_time = (new_time + rand_int)::BIGINT;
-- the line will look like this after debug purpose: unix_time =
((fromtime::BIGINT * 1000) + (random() * 1000)::INT)::BIGINT;
END IF;
timestamp = SET_BYTE(timestamp, 0, (unix_time >>
40)::BIT(8)::INTEGER);
timestamp = SET_BYTE(timestamp, 1, (unix_time >>
32)::BIT(8)::INTEGER);
timestamp = SET_BYTE(timestamp, 2, (unix_time >>
24)::BIT(8)::INTEGER);
timestamp = SET_BYTE(timestamp, 3, (unix_time >>
16)::BIT(8)::INTEGER);
timestamp = SET_BYTE(timestamp, 4, (unix_time >> 8)::BIT(8)::INTEGER);
timestamp = SET_BYTE(timestamp, 5, unix_time::BIT(8)::INTEGER);

-- 10 entropy bytes
ulid = timestamp || gen_random_bytes(10);

-- Encode the timestamp
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 0) & 224) >>
5));
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 0) & 31)));
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 1) & 248) >>
3));
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 1) & 7) <<
2) | ((GET_BYTE(ulid, 2) & 192) >> 6)));
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 2) & 62) >>
1));
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 2) & 1) <<
4) | ((GET_BYTE(ulid, 3) & 240) >> 4)));
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 3) & 15) <<
1) | ((GET_BYTE(ulid, 4) & 128) >> 7)));
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 4) & 124) >>
2));
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 4) & 3) <<
3) | ((GET_BYTE(ulid, 5) & 224) >> 5)));
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 5) & 31)));

-- Encode the entropy
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 6) & 248) >>
3));
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 6) & 7) <<
2) | ((GET_BYTE(ulid, 7) & 192) >> 6)));
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 7) & 62) >>
1));
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 7) & 1) <<
4) | ((GET_BYTE(ulid, 8) & 240) >> 4)));
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 8) & 15) <<
1) | ((GET_BYTE(ulid, 9) & 128) >> 7)));
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 9) & 124) >>
2));
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 9) & 3) <<
3) | ((GET_BYTE(ulid, 10) & 224) >> 5)));
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 10) & 31)));
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 11) & 248)
>> 3));
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 11) & 7) <<
2) | ((GET_BYTE(ulid, 12) & 192) >> 6)));
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 12) & 62) >>
1));
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 12) & 1) <<
4) | ((GET_BYTE(ulid, 13) & 240) >> 4)));
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 13) & 15)
<< 1) | ((GET_BYTE(ulid, 14) & 128) >> 7)));
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 14) & 124)
>> 2));
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 14) & 3) <<
3) | ((GET_BYTE(ulid, 15) & 224) >> 5)));
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 15) & 31)));

RETURN output;
END
$$
LANGUAGE plpgsql
VOLATILE;

Regards,
HS

In response to

Responses

Browse pgsql-general by date

  From Date Subject
Next Message David G. Johnston 2021-05-16 18:09:38 Question about integer out of range in function
Previous Message David Rowley 2021-05-16 12:57:32 Re: Postgres upgrade 12 - issues with OIDs