How to ensure subquery execution order of sinlge VIEW in SQLITE3? - sqlite

While using sqlite3, I have encountered a strange case.
Firstly I create a database like this:
$ sqlite3 ./test.db
sqlite> CREATE VIRTUAL TABLE dummy USING fts3(tokenize=simple);
sqlite> CREATE VIEW testview AS SELECT(
...> (SELECT fts3_tokenizer('simple', x'deadbeefdeadbeef')) +
...> (SELECT * FROM dummy)
...> );
sqlite> .quit
Then I open it again.
$ sqlite3 ./test.db
sqlite> SELECT * FROM dummy;
sqlite> .quit
Of course nothing happens as dummy is an empty table.
I reclaim tokenizer 'simple' before it.
$ sqlite3 ./test.db
sqlite> SELECT fts3_tokenizer('simple', x'deadbeefdeadbeef');
sqlite> SELECT * FROM dummy;
[1] 33079 segmentation fault (core dumped) ./sqlite3 test.db
It crashs, because I have changed the virtual table of tokenizer 'simple', and when I SELECT the table dummy the first time since sqlite start, sqlite will use fts3 xCREATE function to "recreate" the table.
Strange thing happens as I do this two things in the view, which is just created at the begining - testview.
$ sqlite3 ./test.db
sqlite> SELECT * FROM testview;
sqlite> .quit
Nothing happened. It seems as if the first subquery in testview is just executed after the second, which is counterintuitive. No matter how I change the order, "SELECT * FROM dummy" seems always executed at first.
So my question is, why does it preform like this, and, how can I ensure "SELECT fts3_tokenizer('simple', x'deadbeefdeadbeef');" happens before "SELECT * FROM dummy;", so I can trigger the crash.

Related

Constraint "ON DELETE RESTRICT" not being respected

I'm trying to learn sqlite and am following this tutorial series.
On the terminal, I started this tiny "example" database:
amrsa % sqlite3 example
SQLite version 3.32.3 2020-06-18 14:16:19
Enter ".help" for usage hints.
As a minimal working example to show the unexpected behavior I noticed, I need two tables:
sqlite> CREATE TABLE people(
...> person_id INTEGER PRIMARY KEY,
...> name TEXT NOT NULL
...> );
and
sqlite> CREATE TABLE jobs(
...> job_id INTEGER PRIMARY KEY,
...> description TEXT NOT NULL,
...> person_id INTEGER,
...> FOREIGN KEY (person_id)
...> REFERENCES people (person_id)
...> ON UPDATE RESTRICT
...> ON DELETE RESTRICT
...> );
Now I add some rows to the tables:
sqlite> INSERT INTO people(name)
...> VALUES("Bob"),("Mary");
sqlite> SELECT * FROM people;
1|Bob
2|Mary
Check: Bob and Mary are in the "people" table
sqlite> INSERT INTO jobs(description,person_id)
...> VALUES("job1",1),("job2",1),("job3",2);
sqlite> SELECT * FROM jobs;
1|job1|1
2|job2|1
3|job3|2
two jobs for Bob, and one for Mary: so far so good.
Now, let's try to delete "Bob".
It shouldn't be possible without deleting his jobs from the "jobs" table, given the constraint "ON DELETE RESTRICT", right?
However, it silently deletes the row:
sqlite> DELETE FROM people
...> WHERE person_id = 1;
sqlite> SELECT * FROM people;
2|Mary
sqlite> SELECT * FROM jobs;
1|job1|1
2|job2|1
3|job3|2
sqlite>
Confirmed that Bob is no longer in the "people" table.
Even worse, the jobs referring to him are still in the "jobs" table;
so it's like there was no constraint at all, right?
Am I doing something wrong here?
Or am I assuming something about constraints which is not right?
Thanks in advance.
In SQLite you must enable foreign key support with:
PRAGMA foreign_keys = ON;
because it is disabled by default.
So even if you define foreign key constraints they are ignored, unless you enable them.
See the demo.

Does Sqlite have the concept of a schema in naming tables/views?

From everything I've read and searched on I think the answer is no because it's never mentioned. But I never saw an explicit no.
Does Sqlite allow for a schema like in Sql Server you can have dbo. Employee.FirstName? Or is it limited to Employee.FirstName?
thanks - dave
I don't think sqlite has the exact same concept as schemas have in some other databases but you can attach several databases and operate on them by name.
e.g.
$ sqlite3 mydb1
SQLite version 3.20.1 2017-08-24 16:21:36
sqlite> create table test1 (id int); insert into test1 values (1);
sqlite> .tables
test1
Above we just have 1 database, which is the default database and doesn't need to be prefixed by the name of the database. However the name of this default default database is main , so we can do:
sqlite> select * from test1;
1
sqlite> select * from main.test1;
1
We can attach another database, which will be available under the name myotherdb.
sqlite> attach database 'myotherdb' as 'myotherdb';
sqlite> create table myotherdb.test1 (id int); insert into myotherdb.test1 values (2);
Now we can use myotherdb to refer to this new database, and no prefix or the main prefix to refer to the first/default database
sqlite> select * from myotherdb.test1 where myotherdb.test1.id > 0;
2
sqlite> select * from test1 where test1.id > 0;
1
sqlite> select * from main.test1 where main.test1.id > 0;
1
Note that this will create 2 different database files
sqlite> .databases
main: /tmp/mydb1
myotherdb: /tmp/myotherdb
These 2 databases files can be opened individually later on.

clone a sqlite database skip some data

My application logs user access to sqlite database, the my.db file grows about 5Gb every month. There're tables like "access" which logs every user access, this table should be cleared at every beginning of the month, and it holds almost all the 5gb data. Another table "user" which holds all my users, this table should always keep its data.
So every month I need to:
ctrl+c -> ctrl+v my.db to clone a file copy(for future statistics), this is very slow for 5gb file.
clear the "access" table via "delete from access" and "vacuum", it also takes some time.
It's too slow. I wonder if there is a sql command like "export/import database structure and indices" to clone only the structure to another new.db, and then "copy * from mydb.user to newdb"
Or maybe some existing tool can do this?
Here's the sample database I'll use, representing your old database. I made a table to keep, a table to lose, and an index.
$ sqlite3 old.db
sqlite> create table KeepMe (a TEXT);
sqlite> create table DeleteMe (b TEXT);
sqlite> create index DeleteMe_b on DeleteMe(b);
sqlite> insert into KeepMe values("Hello");
sqlite> insert into DeleteMe values("World");
sqlite> .quit
First copy the schema to a new database:
$ sqlite3 old.db .schema | sqlite3 new.db
Then open the new database, ATTACH to the old database, and copy over the table(s) you want:
$ sqlite3 new.db
sqlite> attach "old.db" as old;
sqlite> insert into KeepMe select * from old.KeepMe;
sqlite> .dump
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE KeepMe (a TEXT);
INSERT INTO "KeepMe" VALUES('Hello');
CREATE TABLE DeleteMe (b TEXT);
CREATE INDEX DeleteMe_b on DeleteMe(b);
COMMIT;
sqlite> .quit

Sqlite via terminal

New in sqlite and terminal commands,so may be this is simple.I was trying to create a DB table via terminal(studying)
sqlite> insert into game values ('fallout','rpg');
sqlite> insert into game values ('final fantasy','rpg');
sqlite> insert into game values ('gaame3','stealth');
sqlite> insert into game values ('hitman','rpg');
sqlite> select * from ame where type='rpg'
...> select * from game
...>
...>
Created a table game and load values into it.When i entered a query to see the data.i mistyped the name of the table and i got this
...>
How can i get back to the normal state ,Cntrl+Z worked but it changed back to the state where i started.Is there a way i can reach back to this.
sqlite>
without typing in
sqlite3 test.sqlite3
again
Ok found the problem:
to the problem.It is that i missed the ; in the code entered so the terminal thinks the command did not terminate.
Solution :
Add the ; into the terminal and the new line of sqlite is open
Thanks joachim-isaksson for the comments

sqlite SELECT returns all records when querying a column for a value with the same name as the column

$ sqlite3 test.db
SQLite version 3.6.21
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> CREATE TABLE test(foo text);
sqlite> INSERT INTO test VALUES ("foo");
sqlite> INSERT INTO test VALUES ("bar");
sqlite> SELECT * FROM test WHERE foo="foo";
foo
bar
sqlite>
It seems that the query treats "foo" as a reference to the name of the column, rather than as a string constant. How do I get this query to only return foo, not bar? Are there options besides renaming the column?
Sqlite3 Keywords
sqlite> SELECT * FROM test WHERE foo='foo';
use single quotes.

Resources