I have this function
how i use it without a trigger ?
There are a few possible alternatives doing so without a trigger, it depends on when you want to call this function.
using cron:
*/15 * * * * psql -d mydatabase -c "interv()"
using LISTEN: https://www.postgresql.org/docs/current/sql-listen.html
Functions are used throughout your database, they can be called from within stored procedures as well. Functions should return something. So if you have a date in one format and want to convert it into a different format, you can create a function that does just that, and 'call' that function within a stored procedure, for example.
Related
If we have only Out parameter in our PLSQL procedure.Then can we use function instead of procedure as function is also able to return the value.
And if we still using procedure then we use this instead of function.
I hope I am able to convey the right question which I want to ask?
Some important difference between both are as following:
Function:
It can be called from the SQL statement (SELECT, UPDATE, DELETE)
Can return only one value
DML operations are not allowed in it
Best for selecting the value for some common complex logic.
Procedure:
It cannot be called from the SQL statement. You must need the PL/SQL block to call it.
Can return multiple values (OUT parameters)
All DML operations are allowed within procedures.
Best for doing some complex logic and updating the table data accordingly.
It depends on what the procedure does.
For example, if it (along with returning some value) uses DML operations (e.g. inserts rows into some table), then function can't do that - you'll have to use a procedure.
Procedure's drawback is that you can't use it in a SELECT statement, such as
select proc_name(out_param) from dual;
You'll have to use a function in such cases.
Also, OUT parameter has to be stored somewhere, and that's usually a locally declared variable, but that means that you need another PL/SQL block to call the procedure and see its result.
If everything your current procedure does is to find & return some value, then yes - a function might be a better choice.
When creating a function, Oracle allows the following syntax:
CREATE OR REPLACE FUNCTION
Is something similar also possible in HSQL? I.e. creating a function, avoiding an error if it already exists and replacing it instead?
CREATE OR REPLACE is supported in ORA compatibility mode but it works only when the function does not exist.
The ALTER SPECIFIC ROUTINE syntax is supported when the function does exist and allows you to change the body of the function without changing its parameter.
You can use SELECT * FROM INFORMATION_SCHEMA.ROUTINES to check if a function exists.
I have to execute a loop in database. This is only a one time requirement.
After executing the function, I am dropping the function now.
Is there any good approach for creating temporary / disposable functions?
I needed to know how to do a many time use in a script I was writing. Turns out you can create a temporary function using the pg_temp schema. This is a schema that is created on demand for your connection and is where temporary tables are stored. When your connection is closed or expires this schema is dropped. Turns out if you create a function on this schema, the schema will be created automatically. Therefore,
create function pg_temp.testfunc() returns text as
$$ select 'hello'::text $$ language sql;
will be a function that will stick around as long as your connection sticks around. No need to call a drop command.
A couple of additional notes to the smart trick in #crowmagnumb's answer:
The function must be schema-qualified at all times, even if pg_temp is in the search_path (like it is by default), according to Tom Lane to prevent Trojan horses:
CREATE FUNCTION pg_temp.f_inc(int)
RETURNS int AS 'SELECT $1 + 1' LANGUAGE sql IMMUTABLE;
SELECT pg_temp.f_inc(42);
f_inc
-----
43
A function created in the temporary schema is only visible inside the same session (just like temp tables). It's invisible to all other sessions (even for the same role). You could access the function as a different role in the same session after SET ROLE.
You could even create a functional index based on this "temp" function:
CREATE INDEX foo_idx ON tbl (pg_temp.f_inc(id));
Thereby creating a plain index using a temporary function on a non-temp table. Such an index would be visible to all sessions but still only valid for the creating session. The query planner will not use a functional index, where the expression is not repeated in the query. Still a bit of a dirty trick. It will be dropped automatically when the session is closed - as a depending object. Feels like this should not be allowed at all ...
If you just need to execute a function repeatedly and all you need is SQL, consider a prepared statement instead. It acts much like a temporary SQL function that dies at the end of the session. Not the same thing, though, and can only be used by itself with EXECUTE, not nested inside another query. Example:
PREPARE upd_tbl AS
UPDATE tbl t SET set_name = $2 WHERE tbl_id = $1;
Call:
EXECUTE upd_tbl(123, 'foo_name');
Details:
Split given string and prepare case statement
If you are using version 9.0, you can do this with the new DO statement:
http://www.postgresql.org/docs/current/static/sql-do.html
With previous versions, you'll need to create the function, call it, and drop it again.
For ad hock procedures, cursors aren't too bad. They are too inefficient for productino use however.
They will let you easily loop on sql results in the db.
I write PSQL script and using variables (for psql --variable key=value commandline syntax).
This works perfectly for top level scope like select * from :key, but I create functions with the script and need variable value inside them.
So, the syntax like
create function foo() returns void as
$$
declare
begin
grant select on my_table to group :user;
end;
$$
language plpgsql;
fails at :user.
As far as I understand psql variables is a plain macro substitution feature, but it doesn't process function bodies.
Are there any escaping syntax for such cases? Surrounding :user with $$ works regarding substitution, but psql fails at $$.
Are there any other way to do this besides standalone macro processing (sed, awk, etc)?
PSQL SET variables aren't interpolated inside dollar-quoted strings. I don't know this for certain, but I think there's no escape or other trickery to turn on SET variable interpolation in there.
One might think you could wedge an unquoted :user between two dollar-quoted stretches of PL/pgSQL to get the desired effect. But this doesn't seem to work... I think the syntax requires a single string and not an expression concatenating strings. Might be mistaken on that.
Anyway, that doesn't matter. There's another approach (as Pasco noted): write the stored procedure to accept a PL/pgSQL argument. Here's what that would look like.
CREATE OR REPLACE FUNCTION foo("user" TEXT) RETURNS void AS
$$
BEGIN
EXECUTE 'GRANT SELECT ON my_table TO GROUP ' || quote_ident(user);
END;
$$ LANGUAGE plpgsql;
Notes on this function:
EXECUTE generates an appropriate GRANT on each invocation using on our procedure argument. The PG manual section called "Executing Dynamic Commands" explains EXECUTE in detail.
The declaration of procedure argument user must be double quoted. Double quotes force it to be interpreted as an identifier.
Once you define the function like this, you can call it using interpolated PSQL variables. Here's an outline.
Run psql --variable user="'whoever'" --file=myscript.sql. Single quotes are required around the username!
In myscript.sql, define function like above.
In myscript.sql, put select foo(:user);. This is where we rely on those single quotes we put in the value of user.
Although this seems to work, it strikes me as rather squirrely. I thought SET variables were intended for runtime configuration. Carrying data around in SET seems odd.
Edit: here's a concrete reason to not use SET variables. From the manpage: "These assignments are done during a very early stage of startup, so variables reserved for internal purposes might get overwritten later." If Postgres decided to use a variable named user (or whatever you pick), it could overwrite your script argument with something you never intended. In fact, psql already takes USER for itself -- this only works because SET is case sensitive. This very nearly broke things from the start!
You could use the method Dan LaRocque describes to make it kind of hack'ishly work (not a knock at Dan) - but psql is not really your friend for doing this kind of work (assuming this kind of work is scripting and not one-off things). If you've got a function, rewrite it to take a parameter like so:
create function foo(v_user text) returns void as
$$
begin
execute 'grant select on my_table to group '||quote_ident(v_user);
end;
$$
language plpgsql;
(quote_ident() makes it so you don't have to assure the double-quotes, it handles all that.)
But then use a real scripting language like Perl, Python, Ruby, etc that has a Postgres driver to invoke your function with a parameter.
Psql has its place, I'm just not sure that this is it.
Actually, there's a way in more modern versions of psql.
Since the function body has to be a string constant and psql does not perform variable interpolation within string constants we have to resort to assembling the function body in a separate first step:
select format($gexec$
create function foo() returns void as
$$
begin
grant select on my_table to group %I;
end;
$$
language plpgsql;
$gexec$, :'user') \gexec
The trailing \gexec command
executes the "outer" query using the format function to produce the desired query sting and then
executes the produced query string as a query.
I have several stored procedures that all use the same set of parameters. Is there a way to define and save the parameter list as a reusable block of code? Something like this:
CREATE PROCEDURE test
Using StoredParameterList
AS
BEGIN
SQL Statement
END
Is this possible? It would make code maintenance easier if a parameter needed to be changed.
Well, sort of. I've never used them, but Sql Server supports something called User Defined Types. I suspect you can create a user-defined type that represents your parameter list and then just have one parameter on each procedure with UDT.