Assigning variable in SQL editor context in Firebird - variable-assignment

I would like to know if it is possible in Firebird to assign and reference a variable in SQL Editor context.
To be clear, I present a code sample in MySQL that do what I want.
SET #var = 10;
SELECT #var;

Firebird only supports variables in PSQL (Procedural SQL), including anonymous procedures (EXECUTE BLOCK). There is no direct equivalent of using variables like in MySQL in DSQL (Dynamic SQL, the normal 'SQL' you use with Firebird).
The closest alternative is to use the context variables using RDB$GET_CONTEXT and RDB$SET_CONTEXT. The near equivalent of your code would be
select RDB$SET_CONTEXT('USER_TRANSACTION', 'var', 10) from RDB$DATABASE;
select RDB$GET_CONTEXT('USER_TRANSACTION', 'var') from RDB$DATABASE;
Be aware, a context variable is stored and retrieved as VARCHAR(255), so if you need to retrieve an integer, you will need to explicitly cast it.
Instead of 'USER_TRANSACTION', with scope limited to the current transaction, you can also use 'USER_SESSION', with scope limited to the current connection.

There is no such thing as "Editor context". Notepad, Word, SynWrite - you can edit anything in them, SQL or not. An editor is just an editor, there is no special context, SQL-wise, in it.
In editor you edit all the SQL texts. DSQL statements, PSQL scripts, everything. Editor does not matter, only difference between DSQL vs PSQL matters here, so read about them in the documentation. Editor has no its own SQL context.
Language-native variables only exist in PSQL context, that is "Procedural" SQL, language used for writing stored procedures, triggers, execute blocks, etc. Also, SQL functions, starting with Firebird 3.
Example from https://firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-psql.html
CREATE OR ALTER PROCEDURE DEPT_BUDGET (
DNO CHAR(3))
RETURNS (
TOT DECIMAL(12,2))
AS
DECLARE VARIABLE SUMB DECIMAL(12,2);
DECLARE VARIABLE RDNO CHAR(3);
DECLARE VARIABLE CNT INTEGER;
BEGIN
TOT = 0;
SELECT
BUDGET
FROM
DEPARTMENT
WHERE DEPT_NO = :DNO
INTO :TOT;
SELECT
COUNT(BUDGET)
FROM
DEPARTMENT
WHERE HEAD_DEPT = :DNO
INTO :CNT;
IF (CNT = 0) THEN
SUSPEND;
FOR
SELECT
DEPT_NO
FROM
DEPARTMENT
WHERE HEAD_DEPT = :DNO
INTO :RDNO
DO
BEGIN
EXECUTE PROCEDURE DEPT_BUDGET(:RDNO)
RETURNING_VALUES :SUMB;
TOT = TOT + SUMB;
END
SUSPEND;
END
Apart from PSQL in Firebird there are more languages ("contexts"): DSQL (Dynamic SQL), ESQL (Embedded SQL) and pre-SQL native query language. Of those only DSQL is of any general use nowadays.
In DSQL you have no language-level variables, but you have functions to set/fetch parts of context: RDB$GET_CONTEXT() and RDB$SET_CONTEXT().
https://firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-functions-scalarfuncs.html#fblangref25-functions-workcontext
select rdb$set_context('USER_SESSION', 'MyVar', 493) from rdb$database
and later
select rdb$get_context('USER_SESSION', 'MyVar') from rdb$database
You can not change 'USER_SESSION' here, as that is the only context you are allowed to write something, other contexts for reading only. But you are free to change the 2nd parameter as you like - that is the name of textual environment variable you set and retrieve.
UPD. I was wrong, there is one more writable context, 'USER_TRANSACTION', see 2.0.7 Release Notest at https://firebirdsql.org/file/documentation/release_notes/html/rlsnotes207.html#dml-dsql-context-ns
Read docs by the two links above and also read the file situated like
c:\Program Files (x86)\Firebird\Firebird_2_1\doc\sql.extensions\README.context_variables2.txt
adjust the path for specific Firebird version on your computer. You can see there full list of contexts (first parameter allowed values) available in your FB version.
However, since you want to use language-native variables, think about, using PSQL instead - see EXECUTE BLOCK in Firebird documentation and my first example. If you do not need to return more than one different datasets from your script, then wrapping it all into one EB for the sake of having variables may suit you better.

Related

Stored procedure in db2 with cursor return type

I am developing a stored procedure (SP) in db2 which will return some data in the form of output cursor but field lengths for different field may vary. I am facing issues as I am not able to make SP compile for this requirement. Below is the code for reference
create table employee(id bigint,first_name varchar(128),last_name varchar(128));
create table employee_designation(id bigint, emp_id bigint,
designation varchar(128));
create type empRowType as row(first_name varchar(128),last_name varchar(128),
designation varchar(128));
create type empCursorType as empRowType cursor;
create procedure emp_designation_lookup(in p_last_name varchar(128), out emp_rec empCursorType)
result sets 0
language SQL
begin
set emp_rec = cursor for select a.first_name,a.last_name,b.designation
from employee a, employee_designation b
where a.id=b.EMP_ID
and a.LAST_NAME = p_last_name;
end;
the above SP compiles and return the result as intended. However if I change the row definition as below
create type empRowType as row(first_name varchar(120),last_name varchar(128),
designation varchar(128));
On recompiling the SP, I get the following error
BMS sample -- [IBM][CLI Driver][DB2/NT64] SQL0408N A value is not compatible with the
data type of its assignment target. Target name is "EMP_REC". LINE NUMBER=5. SQLSTATE=42821
The error is coming as first_name defined in cursor is not of same length in the table employee(cursor has 120 whereas table has 128)
However for my actual work, I need the return values computed based on some logic and hence the length specified in the cursor will be different from what is there in the table. Also I have some new columns in the cursor which are not related with table's column (for example determining the bonus amount or should the employee be promoted etc).
I want to know if there is indeed some solution to such scenario specifically to db2. I am new to db2 and am using version 10.5.7. I also explored multiple articles in IBM docs but not able to find the exact resolution. Any help of pointers will be of great help.
When you use a strongly typed cursor, then any assignment involving that cursor must exactly match the relevant type. Otherwise the compiler will throw an exception, which is your symptom.
Db2 SQL PL also supports weak cursors, and an SQL PL procedure output parameter type can be a weak cursor. This means that a stored procedure declaration can use ...OUT p_cur CURSOR (so there is no preassigned user defined type linked to that cursor) , and then assign that output parameter from different queries ( set p_cur = CURSOR FOR SELECT ... ) . In my case the caller is always SQL (not jdbc), but you might experiment with jdbc as IBM gives an example in the Db2-LUW v11.5 documentation.
Most people use simple result-sets (instead of returned cursors) to harvest the output from queries in stored procedures. These result-sets are consumable by all kinds of client applications (jdbc, odbc, cli) and languages that uses those interfaces (java, .net, python, php, perl, javascript, command-line/scripting etc.). So the simple result sets offer more general purpose usability that returned cursor parameters.
IBM publishes various Db2 samples in different places (on github, in the samples directory-tree of your Db2 server instance directory, in the Knowledge Center etc.).

What does this select statement actually do?

I'm reviewing log of executed PostgreSQL statements and stumble upon one statement I can't totally understand. Can somebody explain what PostgreSQL actually do when such query is executed? What is siq_query?
select *
from siq_query('', '21:1', '', '("my search string")', False, True, 'http://siqfindex:8080/storediq/findex')
I'm running PostgreSQL 9.2
siq_query(...) is a server-side function taking 7 input parameters (or more). It's not part of any standard Postgres distribution I know (certainly not mainline Postgres 9.2), so it has to be user-defined or part of some extension you installed. It does whatever is defined in the function. This can include basically anything your Postgres user is allowed to do. Unless it's a SECURITY DEFINER function, then it ca do whatever the owner of the function is allowed to do.
The way it is called (SELECT * FROM), only makes sense if it returns multiple rows and/or columns, most likely a set of rows, making it a "set-returning function", which can be used almost like a table in SQL queries.
Since the function name is not schema-qualified, it has to reside in a visible schema. See:
How does the search_path influence identifier resolution and the "current schema"
Long story short, you need to see the function definition to know what it does exactly. You can use psql (\df+ siq_query), pgAdmin (browse and select it to see its definition in the SQL pane) or any other client tool to look it up. Or query the system catalog pg_proc directly:
SELECT * FROM pg_proc WHERE proname = 'siq_query';
Pay special attention to the column prosrc, which holds the function body for some languages like plpgsql.
There might be multiple variants of that name, Postgres allows function overloading.

Is it possible to define global variables in postgresql

I am using postgresql 9.4 and while writing functions I want to use self-defined error_codes (int). However I may want to change the exact numeric values later.
For instance
-1 means USER_NOT_FOUND.
-2 means USER_DOES_NOT_HAVE_PERMISSION.
I can define these in a table codes_table(code_name::text, code_value::integer) and use them in functions as follows
(SELECT codes_table.code_value FROM codes_table WHERE codes_table.code_name = 'USER_NOT_FOUND')
Is there another way for this. Maybe global variables?
Postgres does not have global variables.
However you can define custom configuration parameters.
To keep things clear define your own parameters with a given prefix, say glb.
This simple function will make it easier to place the parameter in queries:
create or replace function glb(code text)
returns integer language sql as $$
select current_setting('glb.' || code)::integer;
$$;
set glb.user_not_found to -1;
set glb.user_does_not_have_permission to -2;
select glb('user_not_found'), glb('user_does_not_have_permission');
User-defined parameters are local in the session, therefore the parameters should be defined at the beginning of each session.
Building on #klin's answer, there are a couple of ways to persist a configuration parameter beyond the current session. Note that these require superuser privieges.
To set a value for all connections to a particular database:
ALTER DATABASE db SET abc.xyz = 1;
You can also set a server-wide value using the ALTER SYSTEM command, added in 9.4. It only seems to work for user-defined parameters if they have already been SET in your current session. Note also that this requires a configuration reload to take effect.
SET abc.xyz = 1;
ALTER SYSTEM SET abc.xyz = 1;
SELECT pg_reload_conf();
Pre-9.4, you can accomplish the same thing by adding the parameter to your server's postgresql.conf file. In 9.1 and earlier, you also need to register a custom variable class.
You can use a trick and declare your variables as a 1-row CTE, which you then CROSS JOIN to the rest. See example:
WITH
variables AS (
SELECT 'value1'::TEXT AS var1, 10::INT AS var2
)
SELECT t.*, v.*
FROM
my_table AS t
CROSS JOIN variables AS v
WHERE t.random_int_column = var2;
Postgresql does not support global variables on the DB level. Why not add it:
CREATE TABLE global_variables (
key text not null PRIMARY KEY
value text
);
INSERT INTO global_variables (key, value) VALUES ('error_code_for_spaceship_engine', '404');
If different types may be the values, consider JSON to be the type for value, but then deserialization code is required for each type.
You can use this
CREATE OR REPLACE FUNCTION globals.maxCities()
RETURNS integer AS
$$SELECT 100 $$ LANGUAGE sql IMMUTABLE;
.. and directly use globals.maxCities() in the code.

User defined variables in PostgreSQL

I have the following MySQL script which I want to implement in PostgreSQL.
SET #statement = search_address_query;
PREPARE dynquery FROM #statement;
EXECUTE dynquery;
DEALLOCATE PREPARE dynquery;
How can I define user defined variable "#statement" using PostgreSQL.
Postgres does not normally use variables in plain SQL. But you can do that, too:
SET foo.test = 'SELECT bar FROM baz';
SELECT current_setting('foo.test');
Read about Customized Options in the manual.
In PostgreSQL 9.1 or earlier you needed to declare custom_variable_classes before you could use that.
However, You cannot EXECUTE dynamic SQL without a PL (procedural language). You would use a DO command for executing ad-hoc statements (but you cannot return data from it). Or use CREATE FUNCTION to create a function that executes dynamic SQL (and can return data in any fashion imaginable).
Be sure to safeguard against SQL injection when using dynamic SQL.
Related:
Is there a way to define a named constant in a PostgreSQL query?

PostgreSQL execute statement conditionally by server version

I'm currently writing some installer script that fires SQL files against different database types depending on the system's configuration (the webapplication supports multiple database server like MySQL, MSSQL and PostgreSQL).
One of those types is PostgreSQL. I'm not fluent with it and I would like to know if it's possible to make a statement into a define/populate SQL file that makes an SQL query conditional to a specific PostgreSQL server version.
How to make an SQL statement conditionally in plain PGSQL so that it is only executed in version 9? The command is:
ALTER DATABASE dbname SET bytea_output='escape';
The version check is to compare the version with 9.
Postgres does have version() function, however there is no major_vesion(). Assuming that output string always includes version number as number(s).number(s).number(s) you could write your own wrapper as:
CREATE OR REPLACE FUNCTION major_version() RETURNS smallint
AS $BODY$
SELECT substring(version() from $$(\d+)\.\d+\.\d+$$)::smallint;
$BODY$ LANGUAGE SQL;
Example:
=> Select major_version();
major_version
---------------
9
(1 row)
However real issue here is that AFAIK you can't execute your commands conditionally in "pure" SQL and best what you can do is to write some stored function like this:
CREATE OR REPLACE FUNCTION conditionalInvoke() RETURNS void
AS $BODY$
BEGIN
IF major_version() = 9 THEN
ALTER DATABASE postgres SET bytea_output='escape';
END IF;
RETURN;
END;
$BODY$ LANGUAGE plpgsql;
I think that you should rather use some scripting language and generate appropriate SQL with it.
Or you could just use
select setting from pg_settings where name = 'server_version'
Or
select setting from pg_settings where name = 'server_version_num'
If you need major version only
select Substr(setting, 1, 1) from pg_settings where name = 'server_version_num'
or
select Substr(setting, 1, strpos(setting, '.')-1) from pg_settings where name = 'server_version'
if you want it to be compatible with two digit versions.
Maybe you could make things dependent on the output of
select version();
(probably you'll have to trim and substring that a bit)
BTW (some) DDL statements may not be issued from within functions; maybe you'll have to escape to shell-programming and here-documents.