やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

SQLite3学習 ビルドオプション動作確認(SQLITE_ALLOW_URI_AUTHORITY)

 URIが使えた。インメモリと同一プロセス内における共有もできた。

成果物

情報源

DB接続文字列

種類ファイルメモリ共有メモリ
Path/tmp/a.db:memory:不可
URIfile:///tmp/a.dbfile::memory:file::memory:?cache=shared

インタフェースごとの接続

接続 CLI Tcl
ファイル(path) sqlite3 /tmp/a.db sqlite3 db /tmp/a.db
ファイル(URI) sqlite3 file:///tmp/a.db sqlite3 db file:///tmp/a.db -uri true
メモリ sqlite3 sqlite3 db :memory:
共有メモリ(URI) 不可 sqlite3 db file::memory:?cache=shared -uri true
共有メモリ(path) 不可 不可
sqlite3 db file::memory:?cache=shared -uri true

SQLITE_ALLOW_URI_AUTHORITY

 ファイルパスでなくURIでも参照できることを確認した。

$ sqlite3 file:///tmp/work/a.db
...
sqlite> .tables
sqlite> .exit
$ ls
a.db

 URIはパスだけでなく?を付与してクエリを渡せるのが特徴。

インメモリ

 インメモリにも使える。

CLI

$ sqlite3 file::memory:
SQLite version 3.29.0 2019-07-10 17:32:03
Enter ".help" for usage hints.
sqlite> create table AAA(id primary key, name text);
sqlite> insert into AAA values(1, 'A');
sqlite> select * from AAA;
1|A
sqlite> .exit
$ ls
(file::memory:という名のファイルが作成されていないことを確認)

Tcl

sqlite3 db_name file::memory: -uri true

shared

 同一プロセス内なら共有できる。共有しないと接続するたびに別のDBとして扱われる。

rc = sqlite3_open("file::memory:?cache=shared", &db);
ATTACH DATABASE 'file::memory:?cache=shared' AS aux1;

 上記のような記法となる。確かめてみる。

CLI(不可)

 インメモリでAAAテーブルを作成する。そしてテーブル名一覧を出力する。

$ sqlite3 file::memory:?cache=shared "create table AAA(id primary key, name text);" ".tables";
AAA

 テーブル作成と一覧を別の接続に分けて、共有できるか確認してみる。

$ { sqlite3 file::memory:?cache=shared "create table AAA(id primary key, name text);"; sqlite3 file::memory:?cache=shared ".tables"; }
(何も出力されず)

 共有されていない。原因はおそらくsqlite3コマンドを実行するとそれぞれ別プロセスになってしまうから。cache=sharedで共有できるのは同一プロセス内のみ。

Tcl(非共有)

SQLite3 Tclインターフェース

 tclでSQLite3のDBファイルを作成・読込するにはsqlite3コマンドを使う。

sqlite3 変数名 パス オプション

 たとえば変数名とパスを指定するとそのDBファイルを開ける。なければ新規生成。

sqlite3 db_name /tmp/a.db

 たとえばパスにURI機能を使って以下のように指定すると共有インメモリを使える。

sqlite3 db_name file::memory:?cache=shared -uri true

 以下は共有しないとエラーになる。

  1. 接続1でcreate tableする
  2. 接続2で1のテーブルに対してinsertするつもり
  3. 2でテーブルが存在せずエラーとなる(接続ごとに別DBとして扱われるため)

in_memory.tcl

package require sqlite3
# db1とdb2は接続を共有していない!
sqlite3 db1 file::memory: -uri true
db1 eval { CREATE TABLE users (id INT,name TEXT) }
sqlite3 db2 file::memory: -uri true
# 共有してないのでusersテーブルが作成されておらずエラーになる!
db2 eval { INSERT INTO users VALUES (1,'yamada') }

 実行するとエラー。usersテーブルが存在しないのにinsertしようとしたから。db1db2は別のDBとして扱われる。

$ tclsh in_memory.tcl
no such table: users
    while executing
"db2 eval { INSERT INTO users VALUES (1,'yamada') }"
    (file "/tmp/work/Sqlite3.BuildOptions.SQLITE_ALLOW_URI_AUTHORITY.20190805092444/src/in_memory.tcl" line 7)

 まあ、この場合は同じ接続を使えばいいだけなんだけど。

 DBを共有するクエリが?cache=shared。ためしてみる。

Tcl(共有)

 tclでSQLite3のDBファイルを作成・読込するにはsqlite3コマンドを使う。

sqlite3 変数名 パス オプション

 パスにURI機能を使って以下のように指定すると共有インメモリを使える。

sqlite3 db_name file::memory:?cache=shared -uri true

 試してみる。

$ vim sqlite.tcl

sqlite.tcl

package require sqlite3
sqlite3 db1 file::memory:?cache=shared -uri true
db1 eval { CREATE TABLE users (id INT,name TEXT) }
sqlite3 db2 file::memory:?cache=shared -uri true
db2 eval { INSERT INTO users VALUES (1,'yamada') }

set resultset [db1 eval { SELECT * FROM users }]
foreach row $resultset {
  puts $row
}
set resultset [db2 eval { SELECT * FROM users }]
foreach row $resultset {
  puts $row
}

db1 close
db2 close

 実行。

$ tclsh sqlite.tcl
1
yamada
1
yamada

 異なるDB接続db1,db2間で共有できていることがわかった。

shared(名前付き)

 以下のような記法になる。

rc = sqlite3_open("file:memdb1?mode=memory&cache=shared", &db);
ATTACH DATABASE 'file:memdb1?mode=memory&cache=shared' AS aux1;

 確かめてみる。

Tcl

共有できた

$ vim sqlite_shared_name_1.tcl

sqlite_shared_name_1.tcl

package require sqlite3
sqlite3 db1 file:memdb1?mode=memory&cache=shared -uri true
db1 eval { CREATE TABLE users (id INT,name TEXT) }
sqlite3 db2 file:memdb1?mode=memory&cache=shared -uri true 
db2 eval { INSERT INTO users VALUES (1,'yamada') }

set resultset [db1 eval { SELECT * FROM users }]
foreach row $resultset {
  puts $row
}
set resultset [db2 eval { SELECT * FROM users }]
foreach row $resultset {
  puts $row
}

db1 close
db2 close

 実行。できた。

$ tclsh sqlite_shared_name_1.tcl
1
yamada
1
yamada

 だが、これだと名前無しのときと変わらない。名前付きは他のDBと区別できる点が優れている。そこを確認したい。

 つまり、同一プロセス内にて2つ以上のDBを作り、各DBに対して2つ以上の接続をもたせ、共有できているかを確認する。そして各DBは区別されていることも確認する。

$ vim sqlite_shared_name_2.tcl

sqlite_shared_name_2.tcl

package require sqlite3

proc openInMem { db db_name } { sqlite3 $db file:$db_name?mode=memory&cache=shared -uri true }
proc create { db tbl_name } { $db eval "CREATE TABLE $tbl_name (id INT,name TEXT);" }
proc insert { db tbl_name } { $db eval "INSERT INTO $tbl_name VALUES (1,'yamada');" }
proc show { db tbl_name } {
    set result [$db eval "SELECT * FROM $tbl_name;"]
    foreach row $result {
        puts $row
    }
}
openInMem dbA1 "memdbA"
create dbA1 "AAA"
openInMem dbA2 "memdbA"
insert dbA2 "AAA"

show dbA1 "AAA"
show dbA2 "AAA"

openInMem dbB1 "memdbB"
create dbB1 "BBB"
openInMem dbB2 "memdbB"
insert dbB2 "BBB"

show dbB1 "BBB"
show dbB2 "BBB"

# no such table
#dbA1 eval { select * from BBB }
#dbA2 eval { select * from BBB }
#dbB1 eval { select * from AAA }
#dbB2 eval { select * from AAA }

dbA1 close
dbA2 close
dbB1 close
dbB2 close

 実行。できた。

$ tclsh sqlite_shared_name_2.tcl
1
yamada
1
yamada
1
yamada
1
yamada

 上記では以下のことが確認できた。

  • 同一プロセス内で接続を共有できた
    • 名前が同じDBなら異なる接続であっても共有できる
    • 名前が異なるDBとは区別される
      • 名前を指定すれば任意のメモリDBへ接続できる
table DB 接続
AAA memdbA dbA1
AAA memdbA dbA2
BBB memdbB dbB1
BBB memdbB dbB2

 memdbA, memdbBという2種類のDBに、テーブルAAA, BBBを作った。各DBには一方の名前のテーブルしか存在しない。もしmemdbABBBテーブルを探したら存在せずno such tableエラーになる。上記コードのコメントアウトを解除して実行すると以下のようになる。

$ tclsh sqlite_shared_name_2.tcl
no such table: AAA
no such table: BBB

 これで名前付きだとDBを区別することがわかった。

 単に:memory:だけでは「任意のメモリDB」へはアクセスできない。単一のメモリDBのみである。名前付きにすることで、別個のメモリDBを作ることができる。まあ、そんな用途がどれくらいあるかは知らないが。

対象環境

$ uname -a
Linux raspberrypi 4.19.42-v7+ #1218 SMP Tue May 14 00:48:17 BST 2019 armv7l GNU/Linux

前回まで