Hi,

Thanks for your feedback, James.

A generalized way of getting if an ID was inserted on the last performed
query and if so which, is of value for instance when inserting a row into a
table with an AUTOINCREMENT key e.g.

CREATE TABLE t (
     id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
     d  TEXT
);


and then inserting into it, e.g.

"INSERT INTO t (d) VALUES (\"a\");" => inserted id: 1
"INSERT INTO t (d) VALUES (\"a\");" => inserted id: 2
"INSERT INTO t (d) VALUES (\"b\");" => inserted id: 3
"INSERT INTO t (d) VALUES (\"b\");" => inserted id: 4
"INSERT INTO t (d) VALUES (\"c\");" => inserted id: 5


This functionality makes a lot of sense in at least certain SQLite
bindings. In the places where it's not strictly required, it will still
provide clarity which is valuable in debug, and convenience from being
dynamic, please see the example use ~15 rows below for this -


So what I'm looking for is a general mechanism for deriving the ID inserted
for a query. E.g.:

"SELECT a FROM b" => none

"UPDATE b SET a = c" => none

"INSERT IGNORE INTO b (a) VALUES (c)" and the ID was already in there =>
none

"INSERT IGNORE INTO b (a) VALUES (c)" and the ID was not already in there
=> the ID of the inserted row

"INSERT INTO b (a) VALUES (c)" => the ID of the inserted row



Thanks to this, a SQLite binding can be achieved which
dynamically/*automatically* detects not only if it was a readonly or
non-readonly query, but also within the non-readonly query if it was an
update or insert, and thus a generalized query mechanism can be devised
that behaves like:

SELECT query  => The query results
UPDATE query => The number of rows updated
INSERT query => The ID of the row inserted, or none (by null/false/0/etc.)



This is to create a general abstraction for this purpose. Indeed SQLite
performs queries, well, at the level of query and, the meaningful work you
do with SQLite is at the granularity of a query, so it appears reasonable
to me that there would be a mechanism using which you could retrieve the ID
of the row inserted by a particular query.

A generalized "rows changed by this query" abstraction can be implemented
as of today, using sqlite3_changes which returns the number of changed rows
in the *last performed query*, the error reporting (as to check
sqlite3_changes was set on the last query) and possibly secondarily
by stmt_readonly .


The problem with checking the ID of the last inserted row is that the
abstraction for checking it cannot reasonably know that a query is an
insert;

Essentially doing a string match for "INSERT " in the query executed would
be a leaky abstraction.

Getting a copy of SQLite's SQL parser's data could be something, but then -
this is a bit beyond my current SQLite skills - aren't there mechanisms to
make INSERT:s indirectly in SQLite, like through a database-stored
procedure or alike?

That would make any abstraction like this leak, because it wouldn't know if
an INSERT was made during a query, and that knowledge is required as to
know if sqlite3_last_insert_rowid regards the last inserted ID or not.



And to illustrate how sqlite3_changes and sqlite3_last_insert_rowid does
not deliver for automatic query ID retrieval:

Using these two, let's define an attempt at a general mechanism for this,
in pseudocode:

var last_rowId_on_last_query = 0;
function query(query) {
     lock_mutex();
     sqlite_perform_query(query);
     var last_rowId = sqlite_last_rowid();
     var id_of_this_row = (last_rowId != last_rowId_on_last_query) ?
last_rowId : null;
     last_rowId_on_last_query = last_rowId;
     unlock_mutex();
     return id_of_this_row;
}


Now, let's set up these two tables

CREATE TABLE t1 ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, d TEXT );
CREATE TABLE t2 ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, d TEXT );


and now make some inserts with it! The use of query() above is implied here.

"INSERT INTO t1 (d) VALUES (\"a\");" => 1    - Correct!
"INSERT INTO t1 (d) VALUES (\"a\");" => 2    - Correct!
"INSERT INTO t2 (d) VALUES (\"a\");" => 1    - Correct!
"INSERT INTO t2 (d) VALUES (\"a\");" => 2    - Correct!
"INSERT INTO t2 (d) VALUES (\"a\");" => 3    - Correct!
"INSERT INTO t1 (d) VALUES (\"a\");" => null - Error, abstraction leak,
ouch!



All of this leakiness would be undone if before a query, the SQLite user
could reset the sqlite3_last_insert_rowid . By that, the user can just
check the sqlite3_last_insert_rowid right after the query, and by that know
that that was .

It may be a bit too simplistic if a query inserted several ID:s as you
pointed out, though at least it's general unlike the present solution, and
can be made to fit any usecase at least I have in reach, as in SQLite any
INSERT operation can be performed one row per query with OK-to-great
performance.


Introduction of a sqlite3_reset_last_insert_rowid would be complimenting
the current sqlite_int64 sqlite3_last_insert_rowid at sqlite3.c row
114023-114028:

/*
** Return the ROWID of the most recent insert
*/
SQLITE_API sqlite_int64 sqlite3_last_insert_rowid(sqlite3 *db){
  return db->lastRowid;
}


with the similar

SQLITE_API void sqlite3_reset_last_insert_rowid(sqlite3 *db){
  db->lastRowid = 0;
}


and then making it export properly.


The reason I address you on this list directly with this is because it
appears to be of use for SQLite bindings as far as I can see.

I agree that keeping the API minimal is a top priority. Though, for
instance exporting this as a SQL procedure that could be invoked by
preparing "CALL reset_last_insert_rowid();" and then invoking it, wouldn't
that be a bit too costly to do on *every* query however small in a
particular binding?



If you see any other way to solve this with good performance, please feel
free to share, I'm just looking for a general way of getting if the last
query inserted an ID and if so which, it can be implemented any way as long
as it performs so good it can be included in a general query mechanism in a
binding.

Thanks,
Mikael

2013/5/7 James K. Lowden <jklow...@schemamania.org>

> On Mon, 6 May 2013 20:55:01 +0300
> Mikael <mikael.tr...@gmail.com> wrote:
>
> > Currently in the absence of a sqlite3_reset_last_insert_rowid(), the
> > closest you can do to figure out what ID was actually inserted by the
> > most recent SQLite query is:
> >
> >  * Lock mutex
> >  * Get sqlite*'s last insert row ID
> >  * Perform the query
> >  * Get number of changed rows
> >  * Get sqlite*'s last insert row ID
> >  * Unlock mutex
>
> I don't understand.  The process doing the insert presumably knows
> which data it inserted.  Why not turn around and SELECT the ids for the
> inserted values?  No mutex and no guesswork.
>
> Your process seems to imply that the the inserted values don't
> constitute a key, or that you want to compute the row IDs for
> performance.  Those don't strike me as  sufficient reason to perturb
> the API.
>
> --jkl
>
> _______________________________________________
> sqlite-users mailing list
> sqlite-users@sqlite.org
> http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users
>
_______________________________________________
sqlite-users mailing list
sqlite-users@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users

Reply via email to