SQLITE: stop execution if select returns specific value - sql

Is there any way to write an SQL input file for sqlite that would somehow "throw" an error, eg. exited the transaction with rollback, if a condition isn't met?
I have a script that is supposed to do something, but only if there is a certain row in one table. If it's not there, the execution of the script might have fatal results and corrupt the db.
The script is only started on demand right now, but I would prefer to add a fail-safe which would prevent its execution in case there is some issue.
Basically what I need is something like
/* IF */ SELECT value FROM meta WHERE key = 'version' /* != hardcoded_version_string THROW SOME EXCEPTION */
Is there any way to accomplish that? In Postgre / Oracle this could be done using PLSQL but I am not sure if sqlite support any such a thing?

Triggers can use the RAISE function to generate errors:
CREATE VIEW test AS SELECT NULL AS value;
CREATE TRIGGER test_insert
INSTEAD OF INSERT ON test
BEGIN
SELECT RAISE(FAIL, 'wrong value')
WHERE NEW.value != 'fixed_value';
END;
INSERT INTO test SELECT 'fixed_value';
INSERT INTO test SELECT 'whatever';
Error: wrong value

Is there any way to write an SQL input file for sqlite that would
somehow "throw" an error, eg. exited the transaction with rollback, if
a condition isn't met?
One workaround may be to create dummy table and explicitly violate NULL constraint:
CREATE TABLE meta("key" VARCHAR(100));
INSERT INTO meta("key") VALUES ('version');
CREATE TEMPORARY TABLE dummy(col INT NOT NULL);
Transaction:
BEGIN TRANSACTION;
INSERT INTO dummy(col)
SELECT NULL -- explicit insert of NULL
FROM meta
WHERE "key" = 'version';
-- Error: NOT NULL constraint failed: dummy.col
-- rest code
INSERT INTO meta("key")
VALUES ('val1');
INSERT INTO meta("key")
VALUES ('val2');
-- ...
COMMIT;
SqlFiddleDemo
Keep in mind that SQLite is not procedural language and this solution is a bit ugly.

Related

insert statement inside stored procedure failing

I have written a oracle stored procedure and creating an error log table if that doesn't exist.
SELECT COUNT(*)
INTO v_count
FROM all_tables
WHERE TABLE_NAME = 'ERROR_LOG';
IF v_count =0 THEN
cr_table := 'CREATE TABLE ERROR_LOG ( ERROR_LOG_ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY, IDENTIFIER VARCHAR2(100), ERROR_MESSAGE VARCHAR2(1000),created_by varchar2(100 ), created_date TIMESTAMP DEFAULT systimestamp )';
execute immediate cr_table;
then issuing an insert statement below in the code as
INSERT
INTO error_log
(
identifier,
error_message,
created_by
)
VALUES
(
v_identifier,
'Success',
v_user
);
But the SP is throwing compilation error with
PL/SQL: ORA-00942: table or view does not exist
If I create the table manually, offline, and compile then it works.
any help?
if you use execute immediate to create a table, it exists in the PL/SQL context, not in SQL context, you have to execute the INSERT by using execute immedation too.
Or you have to make the insert inside an other BEGIN.. END block.
Before using the stored procedure, please create a view or table where you are going to insert the values using the SP. So it will avoid the compiler error in the run time.
This is too long for a comment.
The table doesn't exist when the code is compiled. Hence, you are getting a compile-time error. At compilation time, Oracle doesn't know that the table will exist when executed.
One solution is to use dynamic SQL for the insert as well.
A better solution is to set up the database with the appropriate tables before the code can ever be executed. Creating permanent tables dynamically in conditional code is usually a sign of a poorly designed application.

PostgreSQL FOUND for CREATE TABLE statements

I am creating a function that will create a new table and insert informations about that table into other tables.
To create that table I am using the
CREATE TABLE IF NOT EXISTS
statement. Sadly it does not update the FOUND special variable in PostgreSQL nor can i find any other variable that would be updated.
Is there any way in PL/PGSQL to know whether that statement created a table or not?
The target of it is to not to double the informations in the other tables.
You may use CREATE TABLE AS in combination with ON_ERROR_ROLLBACK:
BEGIN;
-- Do inital stuff
\set ON_ERROR_ROLLBACK on
CREATE TABLE my_table AS
SELECT id, name FROM (VALUES (1, 'Bob'), (2, 'Mary')) v(id, name);
\set ON_ERROR_ROLLBACK off
-- Do remaining stuff
END;
To put it bluntly, with \set ON_ERROR_ROLLBACK on postgres will create a savepoint before each statement and automatically rollback to this savepoint or releasing it depending on the success of that statement.
The code above will execute initial and remaining stuff even if the table creation fails.
No, there are not any information if this command created table or not. The found variable is updated after query execution - not after DDL command. There is guaranteed so after this command, the table will be or this command fails to an exception.

sql query inside if stage with exists

I want to check if the id I want to insert into tableA exists in tableB into an if statement
Can I do something like this
if new.id exists (select id from tableB where stat = '0' ) then
some code here
end if;
When I try this I get an error message, any thoughts?
Why not do it like this? I'm not very knowledgeable about PostgreSQL but this would work in T-SQL.
INSERT INTO TargetTable(ID)
SELECT ID
FROM TableB
WHERE ID NOT IN (SELECT DISTINCT ID FROM TargetTable)
This is usually done with a trigger. A trigger function does the trick:
CREATE FUNCTION "trf_insert_tableA"() RETURNS trigger AS $$
BEGIN
PERFORM * FROM "tableB" WHERE id = NEW.id AND stat = '0';
IF FOUND THEN
-- Any additional code to go here, optional
RETURN NEW;
ELSE
RETURN NULL;
END IF;
END; $$ LANGUAGE plpgsql;
CREATE TRIGGER "tr_insert_tableA"
BEFORE INSERT ON "tableA"
FOR EACH ROW EXECUTE PROCEDURE "trf_insert_tableA"();
A few notes:
Identifiers in PostgreSQL are case-insensitive. PostgreSQL by default makes them lower-case. To maintain the case, use double-quotes. To make your life easy, use lower-case only.
A trigger needs a trigger function, this is always a two-step affair.
In an INSERT trigger, you can use the NEW implicit parameter to access the column values that are attempted to be inserted. In the trigger function you can modify these values and those values are then inserted. This only works in a BEFORE INSERT trigger, obviously; AFTER INSERT triggers are used for side effects such as logging, auditing or cascading inserts to other tables.
The PERFORM statement is a special form of a SELECT statement to test for the presence of data; it does not return any data, but it does set the FOUND implicit parameter that you can use in a conditional statement.
Depending on your logic, you may want the insert to succeed or to fail. RETURN NEW to make the insert succeed, RETURN NULL to make it fail.
After you defined the trigger, you can simply issue an INSERT statement: the trigger function is invoked automatically.
Presumably, you want something like this:
if exists (select 1 from tableB b where stat = '0' and b.id = new.id) then
some code here
end if;

how to create a trigger in oracle which will restrict insertion and update queries on a table based on a condition

I have account table as this--
create table account
(
acct_id int,
cust_id int,
cust_name varchar(20)
)
insert into account values(1,20,'Mark');
insert into account values(2,23,'Tom');
insert into account values(3,24,'Jim');
I want to create a trigger which will ensure that no records can be inserted or update in account table having acct_id as 2 and cust_id as 23.
My code is --
create trigger tri_account
before insert or update
on account
for each row
begin
IF (:new.acct_id == 2 and :new.cust_id == 23) THEN
DBMS_OUTPUT.PUT_LINE('No insertion with id 2 and 23.');
rollback;
END IF;
end;
so this trigger is created , but with compilation error.
now when I insert any record with acct_id as 2 and cust_id as 23,it doesent allow.
But I get an error saying
ORA-04098: trigger 'OPS$0924769.TRI_ACCOUNT' is invalid and failed re-validation
I don't understand this.I also want to show a message that dis insertion is not possible.
please Help...
The equality operator in Oracle is =, not ==.
You cannot commit or rollback in a trigger. You can throw an exception which causes the triggering statement to fail and to be rolled back (though the existing transaction will not necessarily be rolled back).
It does not appear that this trigger compiled successfully when you created it. If you are using SQL*Plus, you can type show errors after creating a PL/SQL object to see the compilation errors.
You should never write code that depends on the caller being able to see the output from DBMS_OUTPUT. Most applications will not so most applications would have no idea that the DML operation failed if your trigger simply tries to write to the DBMS_OUTPUT buffer.
Putting those items together, you can write something like
create trigger tri_account
before insert or update
on account
for each row
begin
IF (:new.acct_id = 2 and :new.cust_id = 23) THEN
raise_application_error( -20001, 'No insertion with id 2 and 23.');
END IF;
end;
A trigger is more flexible, but you can also accomplish this through the use of a CHECK CONSTRAINT:
ALTER TABLE account ADD CONSTRAINT check_account CHECK ( acct_id != 2 OR cust_id != 23 )
ENABLE NONVALIDATE;
The NONVALIDATE clause will ensure that the check constraint does not attempt to validate existing data, though it will validate all future data.
Hope this helps.
IF (:new.acct_id = 2 and :new.cust_id = 23) THEN
must be OR, not and.
While using conditional checks you don't need to use colons (:). This will always cause errors.
Note: Exclude the colon only in cases where condition checking is performed.

Nested transactions in postgresql 8.2?

I'm working on scripts that apply database schema updates. I've setup all my SQL update scripts using start transaction/commit. I pass these scripts to psql on the command line.
I now need to apply multiple scripts at the same time, and in one transaction. So far the only solution I've come up with is to remove the start transaction/commit from the original set of scripts, then jam them together inside a new start transaction/commit block. I'm writing perl scripts to do this on the fly.
Effectively I want nested transactions, which I can't figure out how to do in postgresql.
Is there any way to do or simulate nested transactions for this purpose? I have things setup to automatically bail out on any error, so I don't need to continue in the top level transaction if any of the lower ones fail.
Well you have the possibility to use nested transactions inside postgresql using SavePoints.
Take this code example:
CREATE TABLE t1 (a integer PRIMARY KEY);
CREATE FUNCTION test_exception() RETURNS boolean LANGUAGE plpgsql AS
$$BEGIN
INSERT INTO t1 (a) VALUES (1);
INSERT INTO t1 (a) VALUES (2);
INSERT INTO t1 (a) VALUES (1);
INSERT INTO t1 (a) VALUES (3);
RETURN TRUE;
EXCEPTION
WHEN integrity_constraint_violation THEN
RAISE NOTICE 'Rollback to savepoint';
RETURN FALSE;
END;$$;
BEGIN;
SELECT test_exception();
NOTICE: Rollback to savepoint
test_exception
----------------
f
(1 row)
COMMIT;
SELECT count(*) FROM t1;
count
-------
0
(1 row)
Maybe this will help you out a little bit.
I've ended up 'solving' my problem out of band - I use a perl script to re-work the input scripts to eliminate their start transaction/commit calls, then push them all into one file, which gets it's own start transaction/commit.