When multiple statements are submitted together --separated by semicolons(;) but in the same string-- and are NOT wrapped in an explicit transaction, is only a single implicit transaction created or is an implicit transaction created for each statement separately? Further, if one of the later statements fail and an automatic rollback is performed, are all of the statements rolled back?
This other answer almost satisfies my question, but wording in the official documentation leaves me puzzled. In fact, this may seem like a duplicate, but I am specifically wondering about implicit transactions for multiple statements. The other answer does not explicitly address this particular case.
As an example (borrowing from the other question), the following are submitted as a single string:
INSERT INTO a (x, y) VALUES (0, 0);
INSERT INTO b (x, y) VALUES (1, 2); -- line 3 error here, b doesn't have column x
The documentation says
Automatically started transactions are committed when the last query finishes. (emphasis added)
and
An implicit transaction (a transaction that is started automatically, not a transaction started by BEGIN) is committed automatically when the last active statement finishes. A statement finishes when its prepared statement is reset or finalized. (emphasis added)
The keyword last implies to me the possibility of multiple statements. Of course if an implicit transaction is started for each individual statement, then taken individually each statement will be the "last" statement to be executed, but in context of individual statements then it should just say the statement to emphasize the context being one single statement at a time.
Or is there there a difference between prepared statements and unprepared SQL strings? (But as I understand, all statements are prepared even if the calling application doesn't preserve the prepared statement for reuse, so I'm not sure this even matters.)
In the case of all statements being successful, the result of a single commit or multiple commits are essentially the same, but the docs only mention that the single failing statement is automatically rolled back, but doesn't mention other statements submitted together.
The sqlite3_prepare interface compiles the first SQL statement in a query string. The pzTail parameter to these functions returns a pointer to the beginning of the unused portion of the query string.
For example, if you call sqlite3_prepare with the multi-statement SQL string in your example, the first statement is the only one that is active for the resulting prepared statement. The pzTail pointer, if provided, points to the beginning of the second statement. The second statement is not compiled as a prepared statement until you call sqlite3_prepare again with the pzTail pointer.
So, no, multiple statements are not rolled back. Each implicit transaction created by the SQLite engine encompasses a single prepared statement.
Related
I'm building this DB about the University for one of my course classes and I'm trying to create a trigger that doesn't allow for a professor to be under 21yo.
I have a Person class and then a Professor subclass.
What I want to happen is, you create a Person object, then a Professor object using that Person object's id, but, if the Person is under 21yo, delete this Professor object, then delete the Person object.
Everything works fine up until the "delete the Person object" part where this doesn't happen and I'm not sure why. Any help?
This is the sqlite code I have:
AFTER INSERT ON Professor
FOR EACH ROW
WHEN strftime('%J', 'now') - strftime('%J', (SELECT dateOfBirth from Person WHERE personId = NEW.personId)) < 7665 -- 21 years in days
BEGIN
SELECT RAISE(ROLLBACK, 'Professor cant be under 21');
DELETE FROM Person WHERE (personId= new.personId);
END;```
One common issue is that there many not be a current transaction scope to rollback to, which would result in this error:
Error: cannot rollback - no transaction is active
If that occurs, then the trigger execution will be aborted and the delete never executed.
If ROLLBACK does succeed, then this creates a paradox, by rolling back to before the trigger was executed in a strictly ACID environment it would not be valid to continue executing the rest of this trigger, because the INSERT never actually occurred. To avoid this state of ambiguity, any call to RAISE() that is NOT IGNORE will abort the processing of the trigger.
CREATE TRIGGER - The RAISE()
When one of RAISE(ROLLBACK,...), RAISE(ABORT,...) or RAISE(FAIL,...) is called during trigger-program execution, the specified ON CONFLICT processing is performed and the current query terminates. An error code of SQLITE_CONSTRAINT is returned to the application, along with the specified error message.
NOTE: This behaviour is different to some other RDBMS, for instance see this explanation on MS SQL Server where execution will specifically continue in the trigger.
As OP does not provide calling code that demonstrates the scenario it is worth mentioning that in SQLite on RAISE(ROLLBACK,)
If no transaction is active (other than the implied transaction that is created on every command) then the ROLLBACK resolution algorithm works the same as the ABORT algorithm.
Generally, if you wanted to Create a Person and then a Professor as a single operation, you would Create a Stored Procedure that would validate the inputs first, preventing the original insert at the start.
To maintain referential integrity, even if an SP is used, you could still add a check constraint on the Professor record or raise an ABORT from a BEFORE trigger to prevent the INSERT from occurring in the first place:
BEFORE INSERT ON Professor
FOR EACH ROW
WHEN strftime('%J', 'now') - strftime('%J', (SELECT dateOfBirth from Person WHERE personId = NEW.personId)) < 7665 -- 21 years in days
BEGIN
SELECT RAISE(ABORT, 'Professor can''t be under 21');
END
This way it is up to the calling process to manage how to handle the error. The ABORT can be caught in the calling logic and would effectively result in rolling back the outer transaction, but the point is that the calling logic should handle negative side effects. As a general rule triggers that cascade logic should only perform positive side effects, that is to say they should only affect data if the inserted row succeeds. In this case we are rolling back the insert, so it becomes hard to identify why the Person would be deleted.
There is this documentation for sqlite: https://www.sqlite.org/lang_transaction.html which doesn't say anything about what happens in case I have case like:
BEGIN;
INSERT INTO a (x, y) VALUES (0, 0);
INSERT INTO b (x, y) VALUES (1, 2); -- line 3 error here, b doesn't have column x
COMMIT;
What happens in this case? Does it commit or rollback if there is error in line 3? I would expect automatic rollback, but I would like to be sure about it.
SQL statements are executed individually.
When a statement fails, any effects of this single statement are rolled back, but the transaction stays open and active.
When the application receives the error code, it must decide whether it wants to roll back the transaction, or retry, or do something else.
If you are using a function that executes multiple SQL statements, nothing changes; the effect is the same as if you had executed all statements up to the failing one individually.
The worst aspect of the Interactive Report (IR) is that you cannot create it using a PL/SQL returning SQL statement. I have gotten around this using two methods:
1) APEX_COLLECTION.CREATE_COLLECTION in the Before Header Process, which takes a SQL statement (that is constructed in PL/SQL in the process), and have the IR's source be select c001 alias1, c002 alias2 ... from apex_collections a where collection_name = '...'
2) Make a badass pipeline function with a parameter list as long as you need and then have the IR's source be select * from table(package_name.pipelined_function_name(:P1_parameter1, :P1_Parameter2))
Is there a performance difference? I originally used the first method but then ran into an occurrence where it was giving me a bug so I tried the pipelined function and found I just liked it better and have tended to use them ever since unless it was inappropriate to do so (namely when there is a large number of items to be passed to the parameter).
First method gives you opportunity to cache data by re-creating the collection only when you need it. Using n00X and d00X columns will give you some additional performance and right column types for the report definition. You can also create a view based on that collection with type casting and column aliases to add more convenience:
create or replace view apx_my_report
as
select n001 id, c001 data, d001 some_date
from apex_collections
where collection_name = 'MY_REPORT'
/
In that case you report source will be like that:
select id, data, some_date from apx_my_report
/
On the other hand, when you need to execute an ad-hoc query every time when page is rendered, it leads to the unavoidable re-creation of a such collection, therefore the performance goes down because of unwanted transaction maintaining: undo, redo etc.
So, it depends.
I am interested in using transactions for performance.
This is the process I'm currently doing:
* ~~~BEGIN TRANSACTION~~~
* Instance a command object and set the command text.
* Prepare command text so parameters can be added.
* In a loop, I set the values of said parameters and execute the command.
* ~~~COMMIT~~~
I am doing this because, as far as I understand, when you prepare a statement, you ARE communicating with the SQLite engine, so maybe encompassing that in the transaction helps in some way??? (I'm just speculating.)
.
Question: Should I change it to the following process?
* Instance a command object and set the command text.
* Prepare command text so parameters can be added.
* ~~~BEGIN TRANSACTION~~~
* In a loop, I set the values of said parameters and execute the command.
* ~~~COMMIT~~~
I am asking about this because, maybe, making the transaction contain ONLY series of homogenous commands seems like it might be better??? (Again, I have no idea; I'm just speculating.)
There is no advantage to preparing statements inside BEGIN (with caveat below).
There is a small advantage to preparing the statements outside the BEGIN since the transaction will be slightly smaller in time (with second caveat below), thereby permitting more concurrency.
In either case, be sure to reset the statement before reusing it, and finalize the statement before closing the database.
Caveat 1: if the database schema changes, the statement will need to be re-prepared. If you use the recommended sqlite3_prepare_v2() then SQLite will do this for you. You can avoid schema changes by preparing inside a transaction, but note that you will need to use BEGIN IMMEDIATE to be sure the database is locked.
Caveat 2: Since you use BEGIN rather than BEGIN IMMEDIATE the database lock is not actually taken until the first statement of the transaction is stepped. So, there isn't really a concurrency advantage unless you are using BEGIN IMMEDIATE.
There are other advantages to preparing statements outside transactions, e.g., you can use them in multiple transactions without preparing multiple times. However, the logic to maintain their lifetimes becomes more complex/disbursed.
I want to restrict the execution of my PL/SQL code from repetition. That is, I have written a PL/SQL code with three input parameters viz, Month, Year and a Flag. I have executed the procedure with the following values for the parameters:
Month: March
Year : 2011
Flag: Y
Now, If I am trying to execute the procedure with the same values to the parameters as above, I want to write some code in the PL/SQL to restrict the unwanted second execution. Can anyone help. I hope the question is no ambiguous.
You can use the function result cache: http://www.oracle-developer.net/display.php?id=504 . So Oracle can do this for you.
I would create another table that would store the 3 parameters of each request. When your procedure is called it would first check the "parameter request" table to see if the calling parameters have beem used before. If found, then exit the procedure. If not found, then save the parameters and execute the rest of the procedure.
Your going to need to keep "State" of the last call somewhere. I would recommend creating a table with a datetime column.
When your procedure is called update this table. So, next time when your procedure is called.. check this table to see when was the last time your procedure was called and then proceed accordingly.
Why not set up a table to track what arguments you've already executed it with?
In your procedure, first check that table to see if similar parameters have already been processed. If so, exit (with or without an error).
If not, insert them and do the processing necessary.
Depending on how tight the requirements are, you'll need to get a exclusive lock on that table to prevent concurrent execution.
A nice plus would be an extra column with "in progress"/"done"/"error" status so that you can check if things are going on properly. (Maybe a timestamp too if that's important/interesting.)
This setup allows you to easily clear some of the executions (by deleting some rows) if you find things need to be re-done for whatever reason.
Make an insert in the beginning of the procedure, and do a select for update tolock the table so no one else can process any data and if everything goes ok with the procedure, commit and release the table 😀