How postgresql 'remember' result of function? - sql

I faced with strange behavior of postgresql, please, could you clarify it for me?
I created function, which return constants from constants table.
CREATE TABLE constants ( key varchar PRIMARY KEY , value varchar );
CREATE OR REPLACE FUNCTION get_constant(_key varchar) RETURNS varchar
AS $$ SELECT value FROM constants WHERE key = _key; $$ LANGUAGE sql
IMMUTABLE;
Then I added a constant to the table.
insert into constants(key, value)
values('const', '1')
;
Then if I change the value of the constant and call the function:
select get_constant('const');
Then result is CORRECT.
BUT!
If I call function in other procedure, for example:
create or REPLACE PROCEDURE etl.test()
LANGUAGE plpgsql
AS $$
declare
begin
raise notice '%', etl.get_constant('const');
END $$;
Then it rememer first result of calling, and don't change result of raise notice, even if I change constant in table.
But if I recompile procedure - then new const-value printing correct.
I tried to find documentation about it, tried google: 'cache results of postgre SQL procedure', and ect., but found nothing.
Could you clarify it and attach link to documentation this issue?

The documentation for CREATE TABLE says this about the IMMUTABLE keyword:
IMMUTABLE indicates that the function cannot modify the database and always returns the same result when given the same argument values; that is, it does not do database lookups or otherwise use information not directly present in its argument list. If this option is given, any call of the function with all-constant arguments can be immediately replaced with the function value.
So by declaring etl.get_constant with that keyword, you're telling Postgres "the output of this function will always be the same for a given input, forever".
The call etl.get_constant('const') has "all-constant arguments" - the value 'const' won't ever change. Since you've told Postgres that etl.get_constant will always return the same output for the same input, it immediately replaces the function call with the result.
So when you call etl.test() it doesn't run etl.get_constant at all, it just returns the value it got earlier, which you told it would be valid forever.
Compare that with the next paragraph on the same page (emphasis mine):
STABLE indicates that the function cannot modify the database, and that within a single table scan it will consistently return the same result for the same argument values, but that its result could change across SQL statements. This is the appropriate selection for functions whose results depend on database lookups, parameter variables (such as the current time zone), etc.
So if your "constant" is subject to change, but not within the scope of a particular query, you should mark it STABLE, not IMMUTABLE.

Related

Is it possible to change the name of a parameter in a PostgresQL function?

I have a Postgres function defined as follows:
CREATE OR REPLACE FUNCTION my_test_function(query_since timestamp) RETURNS TABLE () ...
Can I update the name of the parameter, query_since, to query_from without dropping the function?
The documentation for CREATE OR REPLACE FUNCTION makes it clear that I can not change the argument types using this SQL command. While it does not specifically mention argument names, I suspect the same restriction applies.
Checking the manual: it does, in fact, mention the same restriction for argument names in the Description section:
To replace the current definition of an existing function, use CREATE OR REPLACE FUNCTION. It is not possible to change the name or
argument types of a function this way (if you tried, you would
actually be creating a new, distinct function).
Bold emphasis mine.
(But, like you commented, seems to refer to the function name rather than argument names.)
Either way, since you can refer to parameter (argument) names inside the function body in PL/pgSQL or SQL functions, simply renaming is not an option.

Oracle DETERMINISTIC HINT Overhead

DETERMINISTIC HIT (as Oracle says) is used to cache the result of a function if it could be deterministic, but what is the overhead of that benefit?
I'll try to explain this better:
CREATE OR REPLACE FUNCTION betwnstr (
string_in IN VARCHAR2
, start_in IN INTEGER
, end_in IN INTEGER
)
RETURN VARCHAR2 DETERMINISTIC
IS
BEGIN
RETURN (SUBSTR (string_in, start_in, end_in - start_in + 1));
END;
/
This simple function extract the characters from BEGIN and END index from a given string.
Now i'll start to use this Function in different tables as SELECT result(other functions, procedure, package etc) and Oracle will start caching all the result from the same input.
Sure this is a wonderful result just adding a simple world on function declaration, but what is the side effect of an intensive use of this? For example, if this function is called million of times with different input ?
I could have many other functions as DETERMINISTICT for example:
AN DETERMINISTIC function to calculate the difference (in DAYS) from two given date
ecc
The documentation says:
DETERMINISTIC
Tells the optimizer that the function returns the same value whenever it is invoked with the same parameter values (if this is not true, then specifying DETERMINISTIC causes unpredictable results). If the function was invoked previously with the same parameter values, the optimizer can use the previous result instead of invoking the function again.
The optimizer can use the previous result, but doesn't have too; this is just asserting that if it needed to call it multiple times for the same parameter values - generally within a single query - it can choose to only make the call once, since you're promised it that it would always get the same result. That doesn't necessarily imply that function results could be cached somewhere between queries, though they may be cached by other mechanisms (I think).
When Oracle does cache things it manages the cache size to stay within available memory, and to optimise the memory available for various functionality. Basically you don't need to worry about side-effects from making a function deterministic, assuming you're using it properly.
There's more documentation here, including how this relates to function-based indexes etc.

PLSQL assignment and casting

Right, a bit of background first....I have a oracle package that has the following:
g_variable constant varchar(6):= pkg_sample.get_config_num('test');
The function above "get_config_num" retrieves the value as a integer value. This seems to work and return the correct value. However the value for 'test' retrieved is actually is a varchar, so it should not work. Running "pkg_sample.get_config_num('test')" against dual I get (null) but in the application it seems to display the correct value.
Am I correct to assume that because we have "constant varchar(6)" we are assigning its type (casting it) as a varchar so it is able to find it?
Evidently there are two implicit data type casts occurring -- one in the function and one in the assignment of the function's value to the constant.
I would be uncomfortable with this, and would prefer that a get_config_string procedure be used. A get_config_date would also be handy.

Infinite optional parameters

In essence, I'd like the ability to create a scalar function which accepts a variable number of parameters and concatenates them together to return a single VARCHAR. In other words, I want the ability to create a fold over an uncertain number of variables and return the result of the fold as a VARCHAR, similar to .Aggregate in C# or Concatenate in Common Lisp.
My (procedural) pseudo code for such a function is as follows:
define a VARCHAR variable
foreach non-null parameter convert it to a VARCHAR and add it to the VARCHAR variable
return the VARCHAR variable as the result of the function
Is there an idiomatic way to do something like this in MS-SQL? Does MS-SQL Server have anything similar to the C# params/Common Lisp &rest keyword?
-- EDIT --
Is it possible to do something similar to this without using table-valued parameters, so that a call to the function could look like:
MY_SCALAR_FUNC('A', NULL, 'C', 1)
instead of having to go through the rigmarole of setting up and inserting into a new temporary table each time the function is called?
For a set of items, you could consider passing a table of values to your function?
Pass table as parameter into sql server UDF
See also http://technet.microsoft.com/en-us/library/ms191165(v=sql.105).aspx
To answer your question directly, no, there is no equivalent to the params keyword. The approach I'd use is the one above - Create a user-defined table type, populate that one row per value, and pass that to your scalar function to operate on.
EDIT: If you want to avoid table parameters, and are on SQL 2012, look at the CONCAT function:
http://technet.microsoft.com/en-us/library/hh231515.aspx
CONCAT ( string_value1, string_value2 [, string_valueN ] )
This is only for the built-in CONCAT function, you couldn't roll-your-own function with "params" style declaration.

How to prevent null values for table-valued function parameters?

I have a TSQL Table-Valued Function and it is complex. I want to ensure that one of the parameters cannot be null. Yet, when I specify NOT NULL after my parameter declaration I am presented with SQL errors.
Is it possible to prevent a parameter of a Table-Valued Function to be assigned null by the calling SQL?
In my opinion, it'd be better to check for NULL values at the beginning of your function and use RAISERROR (no, that's not a typo) to raise an exception. EDIT: Unfortunately, this doesn't work for UDFs, so you'll have to go with option 2.
You also have the option of specifying "RETURNS NULL ON NULL INPUT" when you create your function. If this flag is specified, the function will return NULL if any of its inputs are null...kind of paradoxical, but it may be what you want.
From the MSDN CREATE FUNCTION documentation (quoted because they don't have an anchor on their page, bleh):
RETURNS NULL ON NULL INPUT | CALLED ON NULL INPUT
Specifies the OnNULLCall attribute of a scalar-valued function. If not
specified, CALLED ON NULL INPUT is
implied by default. This means that
the function body executes even if
NULL is passed as an argument.
If RETURNS NULL ON NULL INPUT is specified in a CLR function, it
indicates that SQL Server can return
NULL when any of the arguments it
receives is NULL, without actually
invoking the body of the function. If
the method of a CLR function specified
in already has a
custom attribute that indicates
RETURNS NULL ON NULL INPUT, but the
CREATE FUNCTION statement indicates
CALLED ON NULL INPUT, the CREATE
FUNCTION statement takes precedence.
The OnNULLCall attribute cannot be
specified for CLR table-valued
functions.
Hope it helps somewhat, and I do agree that it's needlessly confusing.