Le ven. 4 août 2017 à 02:42, Nico Williams <n...@cryptonector.com> a écrit :
> On Fri, Aug 04, 2017 at 12:35:56AM +0200, Sylvain Pointeau wrote: > > void sp_seq_init(sqlite3_context *context, int argc, sqlite3_value > **argv) { > > int rc = 0; > > sqlite3_stmt *stmt; > > sqlite3 *db = sqlite3_context_db_handle(context); > > If you use sqlite3_create_function*() with nArg == -1 then you can have > just one UDF from sp_seq_init(). You'd have to check argc here though. > yes this is a good idea, I will do that as well as testing the arguments as also Petern mentioned. > > > const unsigned char* seq_name = sqlite3_value_text(argv[0]); > > long seq_init_val = sqlite3_value_int64(argv[1]); > > seq_init_val could be optional and default to 0 or maybe 1. > good idea, defaukt to 1 > > > long seq_inc_val = argc < 3 ? 1 : sqlite3_value_int64(argv[2]); > > I think some type checking should be done. > yes agree > You could just take the argv[] values and bind them directly to the > insert below, and use CHECK constraints on the SP_SEQUENCE table. > Or you could use sqlite3_value_type() to check that each argument is of > the expected value. > > I like the idea of using a CHECK constraint. It simplifies the C code > by having SQLite3 do more of the work. > I like also the check constraint, I am just wondering if the error, if any, would descriptive enough. I will test it at least.. > > rc = sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS SP_SEQUENCE ( " \ > > " SEQ_NAME TEXT NOT NULL PRIMARY KEY, " \ > > " SEQ_VAL INTEGER NOT NULL, " \ > > " SEQ_INC INTEGER NOT NULL " \ > > " )", 0, 0, 0); > > You might want to add WITHOUT ROWID here (unless you want this to work > with older versions of SQLite3). You could make this conditional on a C > pre-processor macro. it is a good idea, however it does not seem critical > > if( rc != SQLITE_OK ) { > > sqlite3_result_error(context, sqlite3_errmsg(db), -1); > > return; > > } > > > > > > sqlite3_prepare_v2(db, "insert or replace into sp_sequence (seq_name, > > seq_val, seq_inc) values (?, ?, ?)", -1, &stmt, 0); > > Should this init function re-initialize sequences if called more than > once for the same sequence name? > yes I did it on purpose > Because that's what INSERT OR REPLACE will do (since on conflict it will > delete the old row and insert a new one). If you don't mean to > re-initialize then use INSERT OR IGNORE. > > (When I want INSERT OR UPDATE I just run two statements: > > UPDATE .. WHERE ..; > INSERT OR IGNORE ..;) > > > sqlite3_bind_text(stmt, 1, seq_name, -1, SQLITE_STATIC); > > sqlite3_bind_int64(stmt, 2, seq_init_val); > > sqlite3_bind_int64(stmt, 3, seq_inc_val); > > > > rc = sqlite3_step(stmt); > > > > sqlite3_finalize(stmt); > > > > if (rc != SQLITE_DONE) { > > sqlite3_result_error(context, sqlite3_errmsg(db), -1); > > return; > > } > > > > sqlite3_result_int64( context, seq_init_val ); > > } > > > > void sp_seq_nextval(sqlite3_context *context, int argc, sqlite3_value > > **argv) { > > int rc = 0; > > sqlite3_stmt *stmt; > > sqlite3 *db = sqlite3_context_db_handle(context); > > long seq_val = 0; > > > > const unsigned char* seq_name = sqlite3_value_text(argv[0]); > > > > sqlite3_prepare_v2(db, "update sp_sequence set seq_val = seq_val + > > seq_inc where seq_name = ?", -1, &stmt, 0); > > > > sqlite3_bind_text(stmt, 1, seq_name, -1, SQLITE_STATIC); > > > > rc = sqlite3_step(stmt); > > > > sqlite3_finalize(stmt); > > > > if (rc != SQLITE_DONE) { > > sqlite3_result_error(context, sqlite3_errmsg(db), -1); > > return; > > } > > > > sqlite3_prepare_v2(db, "select seq_val from sp_sequence where seq_name > = > > ?", -1, &stmt, 0); > > > > sqlite3_bind_text(stmt, 1, seq_name, -1, SQLITE_STATIC); > > <aside> > > If ever SQLite3 got higher write concurrency (it almost certainly won't, > but derivatives might), then a simple way to ensure atomicity here might > be to do the SELECT first then an UPDATE with a WHERE seq_val = > :the_value_just_read, and loop as necessary. > > do { > current_value = <run SELECT here>; > <run update here using WHERE constraint to ensure that the > current value hasn't changed>; > } while (<updated row count == 0>); > > Though if one is going to have higher write concurrency then one ought > to have SELECT .. FOR UPDATE. > > Anyways, no change needed here. > should I use the mutex lock to ensure atomicity? > </aside> > > > rc = sqlite3_step(stmt); > > > > if( rc == SQLITE_ROW) { > > seq_val = sqlite3_column_int64(stmt, 0); > > } > > > > sqlite3_finalize(stmt); > > > > if (rc != SQLITE_ROW) { > > if( rc == SQLITE_DONE ) sqlite3_result_error(context, "sequence name > > does not exist", -1); > > else sqlite3_result_error(context, sqlite3_errmsg(db), -1); > > return; > > } > > > > sqlite3_result_int64( context, seq_val ); > > } > > > > > > int sqlite3_extension_init( > > sqlite3 *db, > > char **pzErrMsg, > > const sqlite3_api_routines *pApi > > ){ > > SQLITE_EXTENSION_INIT2(pApi) > > I'd create just one UDF using sp_seq_init(), make the narg -1, and check > the argc counts in sp_seq_init(). > yes I will do > > > sqlite3_create_function(db, "seq_init_inc", 3, SQLITE_UTF8, 0, > > sp_seq_init, 0, 0); > > sqlite3_create_function(db, "seq_init", 2, SQLITE_UTF8, 0, sp_seq_init, > 0, > > 0); > > If you made seq_next() have nArg == -1 then you could have a default > sequence for the no-arguments case... You don't have to -- you could > say that's just creeping featuritis! > :-) I will keep on specifying the sequence > > > sqlite3_create_function(db, "seq_nextval", 1, SQLITE_UTF8, 0, > > sp_seq_nextval, 0, 0); > > return 0; > > } > > Nice! Did you test it? yes! I tested it with mingw compiler (and cmake), and the sqlite3 shell to test the seq functions. I will do the code update and will probably create a github repository thanks a lot for your review. _______________________________________________ sqlite-users mailing list sqlite-users@mailinglists.sqlite.org http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users