PLS-00357 sequence.nextval not allowed - triggers

I'm trying to create a trigger and am getting the error "[Error] PLS-00357: PLS-00357: Table, View Or Sequence reference 'table_data_seq.nextval' not allowed in this context"
I have read a lot of information on the error and cannot find the difference between the PL/SQL that people say works and mine. Below is my code for creating the trigger ( keeping it as basic as possible to get it working ):
create or replace trigger tr_tabData
before insert on table_data
for each row
DECLARE
seq_value int;
begin
select table_data_sq.nextval into seq_value from dual;
end;
Oracle version is 10.2.0.5
As requested here it the script for the sequence:
DROP SEQUENCE DATA_ADMIN.TABLE_DATA_SQ;
CREATE SEQUENCE DATA_ADMIN.TABLE_DATA_SQ
START WITH 1000
MAXVALUE 999999999999999999999999999
MINVALUE 1
NOCYCLE
CACHE 20
NOORDER;

This is not possible before 11g. You can use sequence_name.NEXTVAL in regular assignments from 11g not before that, and that by the following:
select TABLE_DATA_SQ.NEXTVAL into :NEW.YourID from dual;

It turned out that this was a bug with the TOAD version and my Oracle database version. The same code in SQL*Plus and SQL Developer worked as expected.

Related

PostgreSQL: Creating a Trigger that tries to do work on a non existing table

as we start to migrate our Application from using Oracle to PostgreSQL we ran into the following problem:
A lot of our Oracle scripts create triggers that work on Oracle specific tables which dont exist in PostgreSQL. When running these scripts on the PG database they will not throw an error.
Only when the trigger is triggered an error is thrown.
Example code:
-- Invalid query under PostgreSQL
select * from v$mystat;
-- Create a view with the invalid query does not work (as expected)
create or replace view Invalid_View as
select * from v$mystat;
-- Create a test table
create table aaa_test_table (test timestamp);
-- Create a trigger with the invalid query does(!) work (not as expected)
create or replace trigger Invalid_Trigger
before insert
on aaa_test_table
begin
select * from v$mystat;
end;
-- Insert fails if the trigger exists
insert into aaa_test_table (test) values(sysdate);
-- Select from the test table
select * from aaa_test_table
order by test desc;
Is there a way to change this behavior to throw an error on trigger creation instead?
Kind Regards,
Hammerfels
Edit:
I was made aware, that we actually dont use basic PostgreSQL but EDB instead.
That would probably explain why the syntax for create trigger seems wrong.
I'm sorry for the confusion.
It will trigger an error, unless you have configured Postgres to postpone validation when creating functions.
Try issuing this before creating the trigger:
set check_function_bodies = on;
Creating the trigger should show
ERROR: syntax error at or near "trigger"
LINE 1: create or replace trigger Invalid_Trigger

Updating generator value issue

I'm currently working on modifying a Firebird v. 1.5 database.
The database structure will be modified running queries from a delphi application using interbase components, the problem I'm facing is that I need to run a lot of queries, some of which include creating generators and updating the generator value, the problem is that I need to achieve this in as few queries as possible, but it seems(at least to me) that this is not really possible, what I'm trying to do is the following:
/* this command creates a generator to be used for table TABLENAME */
CREATE GENERATOR GEN_TABLENAME;
So I've created a generator, now I need to set it's value at the current max id from table TABLENAME, like so:
/* one would expect that the following command would work, well it doesn't */
SET GENERATOR GEN_TABLENAME TO (SELECT MAX(ID) FROM TABLENAME);
Now, is there any workaround for this, or am I forced to:
create the generator
get the max id
update the generator value
and repeat process for every table?
I also expected that
SELECT
SELECT MAX(ID) AS ID_TABLENAME_1 FROM TABLENAME_1,
...
SELECT MAX(ID) AS ID_TABLENAME_N FROM TABLENAME_N
would be a workaround to get the max id's from every table in one command, but it doesn't.
Statement
SET GENERATOR GEN_TABLENAME TO (SELECT MAX(ID) FROM TABLENAME);
mixes DDL (SET GENERATOR) and DML (SELECT), AFAIK this is not generally supported and Firebird doesn't support it for sure.
If you can upgrade to the latest version of Firebird then you could use EXECUTE BLOCK and / or EXECUTE STATEMENT to do it all "in one statement" and server side, but with Firebird 1.5 you have to settle for the long way (one statement to get the current max, then another one update the generator).
With the following trick you can set the generator value to the maximum ID value of a table with one SQL statement in Firebird:
SELECT GEN_ID( GEN_TABLENAME,
(SELECT MAX(ID) FROM TABLENAME) - GEN_ID(GEN_TABLENAME, 0)) FROM RDB$DATABASE;
That works, because GEN_ID( <GeneratorName>, <increment>) gets the generator value and increments it by <increment>. This should work in Firebird 1.5 as well as in newer versions.
You could create a stored procedure and call it from Delphi:
create procedure update_generators
as
declare variable max_id integer;
declare variable table_name char(31);
declare variable generator_name char(31);
begin
/* assuming generator naming convention GEN_XXX -> table name XXX */
for select
trim(g.rdb$generator_name),
substring(trim(g.rdb$generator_name) from 5)
from rdb$generators g
where (coalesce(g.rdb$system_flag, 0) = 0)
into
:generator_name,
:table_name
do
begin
/* assuming that the field name is always ID */
execute statement 'select max(id) from ' || :table_name into :max_id;
execute statement 'set generator ' || :generator_name || ' to ' || :max_id;
end
end^
It looks like execute statement is supported by Firebird 1.5 already.
In Firebird 2.0 and later, you could also wrap the code in a execute block and avoid creating a stored procedure.

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.

DB2 compound statement using ADO.NET

I want to execute multiple statements from my data access layer using C# dan IBM's DB2 data provider.
(environment: DB2/AS400 os version: V5R4)
eg in TSQL:
declare varA integer;
select varA= count(*) from tableA;
select * from tableB where col1 <= varA
with SQL server ; I can concatenate those 3 statements into a string
and assign the text to DBCommand.CommandText.
How to execute multiple statements(compound statement) against DB2 database via DBCommand (using IBM DB2 data provider)
I tried using begin and end block but still failed
BEGIN
statement1;
statement2;
statement3;
END
Thank you
I do not think it's possible.
I had already tried something similar some time ago, and the only solution I found is to dynamically create a stored procedure, calling it, and finally delete it.

Creating a DB2 sequence with a specific START WITH value

In Oracle we can do it like this:
declare current_max_value NUMBER;
begin select last_number+1 into current_max_value from USER_SEQUENCES where sequence_name = 'HIBERNATE_SEQUENCE';
execute immediate 'CREATE SEQUENCE SEQ__NEW_SEQUENCE MINVALUE 1 MAXVALUE 999999999999999999999999999 INCREMENT BY 1 START WITH '||current_max_value|| ' CACHE 20 NOORDER NOCYCLE';
Is there a DB2 equivalent?
DB2 has vaugely equivalent functionality.
If you just need to generate uninque keys then:-
CREATE TABLE MYTABLE (
GENERATED_KEY BIGINT
GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1, CACHE 100),
MY_DATA VARCHAR(1000) ...........
On the create table statement will accomplish this without too much fuss. Anytime a null value is encountered on an insert an new number will be generated.
If you need an actual sequence number to be used over several tables then:
CREATE SEQUENCE ORG_SEQ
START WITH 1
INCREMENT BY 1
NO MAXVALUE
NO CYCLE
CACHE 24
will define a sequence you then use the "NEXTVAL" keyword anywhere you want the next number in you sql:
NEXTVAL FOR ORG_SEQ
After a great difficulty I was able to figure this out with DB2 syntax and here we go
CREATE PROCEDURE SEQ_NAME
LANGUAGE SQL
DYNAMIC RESULT SETS 1
BEGIN ATOMIC
DECLARE MAX_VAL_NO INTEGER;
SELECT MAX(COL_NAME)+1 INTO MAX_VAL_NO FROM TABLE_NAME;
execute immediate 'CREATE SEQUENCE SEQ_NAME NO MAXVALUE NO CYCLE START WITH '|| MAX_VAL_NO;
END
GO
Could any one tell me why should we use LANGUAGE SQL and DYNAMIC RESULT SETS 1
And what is the syntax used here, frankly speaking I really dont have an Idea but I hit it through trial and error method. Eagerly waiting to know what is the syntax is it either ANSI C or some other.
Appreciate if you could answer this. Also provide some links for good study.(Not regular IBM links)