firebird
firebird copied to clipboard
Consider the return value of deterministic functions to be invariant if all its arguments are invariant.
Currently, the return result of deterministic functions is considered invariant only if the function contains no arguments. The implementation with a results cache storing <argument_values, result_value> pairs is quite complex. However, we can consider a simpler option: if the arguments of a deterministic function are invariants, such as literals or input parameters, then the return value of that function is also an invariant.
Simple example:
CREATE EXCEPTION E_INVALID_RACETIME_FORMAT '~Incorrect friskiness format~';
CREATE TABLE BIG_TABLE (
ID BIGINT NOT NULL,
TIME_PASSED NUMERIC(18, 3),
CONSTRAINT PK_BIG_TABLE PRIMARY KEY(ID)
);
SET TERM ^;
CREATE OR ALTER FUNCTION SF_RACETIME_TO_SEC (
RACETIME VARCHAR(9))
RETURNS NUMERIC(18,3)
DETERMINISTIC
AS
DECLARE VARIABLE S VARCHAR(9);
DECLARE VARIABLE XCOMMAPOS SMALLINT;
DECLARE VARIABLE XDOTPOS SMALLINT;
DECLARE VARIABLE XMINUTE SMALLINT;
DECLARE VARIABLE XSECOND SMALLINT;
DECLARE VARIABLE XMS VARCHAR(9);
DECLARE VARIABLE XMILLISECOND SMALLINT;
DECLARE VARIABLE RTIME NUMERIC(18, 3);
BEGIN
S = TRIM(RACETIME);
S = TRIM('_' FROM S);
RTIME = NULL;
IF (TRIM(REPLACE(REPLACE(S, '.', ''), ',', '')) = '') THEN
S = NULL;
IF (S IS NOT NULL) THEN
BEGIN
IF (S NOT SIMILAR TO '[0-5]?[0-9].[0-5][0-9],[0-9]{1,3}') THEN
EXCEPTION E_INVALID_RACETIME_FORMAT '~Incorrect time format "' || RACETIME || '"~';
XDOTPOS = POSITION('.', S);
XCOMMAPOS = POSITION(',', S);
XMINUTE = CAST(SUBSTRING(S FROM 1 FOR XDOTPOS) AS SMALLINT);
XSECOND = CAST(SUBSTRING(S FROM XDOTPOS + 1 FOR XCOMMAPOS - XDOTPOS - 1) AS SMALLINT);
XMS = TRIM(SUBSTRING(S FROM XCOMMAPOS + 1));
XMILLISECOND = CAST(RPAD(XMS, 3, '0') AS SMALLINT);
RTIME = 60 * XMINUTE + XSECOND + 0.001 * XMILLISECOND;
END
RETURN RTIME;
END^
EXECUTE BLOCK
AS
DECLARE ID BIGINT = 1;
BEGIN
WHILE (ID <= 1000000) DO
BEGIN
INSERT INTO BIG_TABLE (ID, TIME_PASSED)
VALUES (:ID, ROUND(150 - 50 * RAND(), 3));
ID = ID + 1;
END
END^
SET TERM ;^
COMMIT;
SELECT
COUNT(*)
FROM BIG_TABLE
WHERE BIG_TABLE.TIME_PASSED = SF_RACETIME_TO_SEC('02.01,6');
COUNT
=====================
31
Current memory = 561798064
Delta memory = 358192
Max memory = 561813248
Elapsed time = 38.442 sec
Buffers = 32768
Reads = 0
Writes = 0
Fetches = 1015835
SQL> SELECT SF_RACETIME_TO_SEC('02.01,6') FROM RDB$DATABASE;
SF_RACETIME_TO_SEC
=====================
121.600
Current memory = 561794608
Delta memory = 10880
Max memory = 561884032
Elapsed time = 0.006 sec
Buffers = 32768
Reads = 0
Writes = 0
Fetches = 21
SELECT
COUNT(*)
FROM BIG_TABLE
WHERE BIG_TABLE.TIME_PASSED = 121.600;
COUNT
=====================
31
Current memory = 561810448
Delta memory = 2784
Max memory = 561884032
Elapsed time = 0.393 sec
Buffers = 32768
Reads = 0
Writes = 0
Fetches = 1015569
If there is no index on the TIME_PASSED column, then SF_RACETIME_TO_SEC will be called for each record in the BIG_TABLE table.