On 14 Oct 2016, Simon Slavin wrote: > The problem is that SQLite doesn't expect you to make manual changes to > sqlite_master. It doesn't reread the schema to execute every command.
That is factually correct as evidenced by this example: ======= snip ======== (loco2-precise)torsten.landschoff@horatio:~$ /opt/sqlite-dev/bin/sqlite3 example.db SQLite version 3.14.2 2016-09-12 18:50:49 Enter ".help" for usage hints. sqlite> create table foo (name varchar(80), address varchar(32)); sqlite> insert into foo values (null, 'N/A'); sqlite> pragma writable_schema=on; sqlite> update sqlite_master set sql=replace(sql, 'varchar(80),', 'varchar(80) not null,') where name='foo'; sqlite> pragma writable_schema=off; sqlite> insert into foo values (null, 'Hamburg'); sqlite> .schema foo CREATE TABLE foo (name varchar(80) not null, address varchar(32)); sqlite> ======= snip ======== > diagnostic tool, please try closing the file and reopening it after you've > changed sqlite_master. I did. If you look at the original email, I ran a separate shell command to ensure that the table is reread. What I did not mention was that I checked that the constraint actually works for newly inserted data: ======= snip ======== (loco2-precise)torsten.landschoff@horatio:~$ /opt/sqlite-dev/bin/sqlite3 example.db [...] sqlite> insert into foo values (null, 'Frankfurt'); Error: NOT NULL constraint failed: foo.name ======= snip ======== > I'm betting that the integrity check works after that. Fine. Let's say $100? As above I already did that in the original email. But here it is again: ======= snip ======== (loco2-precise)torsten.landschoff@horatio:~$ /opt/sqlite-dev/bin/sqlite3 example.db [...] sqlite> select * from foo where name is null; |N/A |Hamburg sqlite> pragma integrity_check; ok ======= snip ======== I would have expected the integrity_check to report both rows having NULL in the name column. In fact it does if there is an index on that table: ======= snip ======== sqlite> create index ix_demo on foo(address); sqlite> pragma integrity_check; NULL value in foo.name NULL value in foo.name ======= snip ======== > It might also be possible to force re-reading of sqlite_master using the > following command: > > ANALYZE sqlite_master Maybe. I did not care or check before. Let's see: ======= snip ======== (loco2-precise)torsten.landschoff@horatio:~$ rm example.db (loco2-precise)torsten.landschoff@horatio:~$ /opt/sqlite-dev/bin/sqlite3 example.db SQLite version 3.14.2 2016-09-12 18:50:49 Enter ".help" for usage hints. sqlite> create table foo (name varchar(80), address varchar(32)); sqlite> insert into foo values (null, 'N/A'); sqlite> pragma writable_schema=on; sqlite> update sqlite_master set sql=replace(sql, 'varchar(80),', 'varchar(80) not null,') where name='foo'; sqlite> insert into foo values (null, 'Hamburg'); sqlite> analyze sqlite_master; sqlite> insert into foo values (null, 'Berlin'); sqlite> ======= snip ======== So no, it does not make any difference (as I would have expected). The only reference to "analyze sqlite_master" in the documentation that I remember is here: https://www.sqlite.org/lang_analyze.html Quote: | An application can force the query planner to reread the statistics tables by | running ANALYZE sqlite_master. > I'm not clear on why this works, though, so don't take my word for it. One > thing I believe definitely causes rereading of the entire schema would be to > perform any change of schema: > > CREATE dummy_for_reread(t TEXT); > DROP TABLE dummy_for_reread; Interesting idea. I tried this: ======= snip ======== sqlite> CREATE TABLE dummy_for_reread(t TEXT); sqlite> DROP TABLE dummy_for_reread; sqlite> insert into foo values (null, 'Munich'); sqlite> ======= snip ======== So no, it does not cause a reread of the schema. > Again, please test this rather than assuming it. I've not seen it > documented, > I've only seen it in use as an undocumented hack. Better tell them that their hack is not working. ;-) So much about my attempt to report a bug. If you don't want to believe my report, then don't. The problem does not really affect me as I just added a manual check for NULL values after my database upgrade, which is probably much more efficient than running pragma integrity_check over the whole database. I just wanted to give back by telling about a problem in the code. BTW: Here is the reason in code: // source: http://sqlite.org/src/artifact/c8b499756658cb8b82cfdbb5845c22cf11f297aa?ln=1402 case PragTyp_INTEGRITY_CHECK: { int i, j, addr, mxErr; // ... /* Do an integrity check on each database file */ for(i=0; i<db->nDb; i++){ // ... /* Make sure all the indices are constructed correctly. */ for(x=sqliteHashFirst(pTbls); x && !isQuick; x=sqliteHashNext(x)){ // ... if( pTab->pIndex==0 ) continue; // ... /* Verify that all NOT NULL columns really are NOT NULL */ for(j=0; j<pTab->nCol; j++){ // ... It appears to me that the row based primary key index is not counted as an index by pTab->pIndex so the NOT NULL checks are skipped without index. Greetings, Torsten -- $---+----1----+----2----+----3----+----4----+----5----+----6----+ SCALE GmbH Niederlassung Dresden Torsten Landschoff Pohlandstraße 19 01309 Dresden Tel: +49-351-312002-10 Fax: +49-351-312002-29 SCALE GmbH Registergericht und Sitz: Ingolstadt, HRB 6384 Geschäftsführer: Dr.-Ing. Heiner Müllerschön, Dipl.-Math. Ulrich Franz _______________________________________________ sqlite-users mailing list sqlite-users@mailinglists.sqlite.org http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users