[tmp]古いSQLite記事のアーカイブ(0.1.0)

SQLiteに関する改訂版記事を書いたので、古くなった記事をスクラップにアーカイブしておきます。

DenoでSQLite使うのは非常に簡単で、Windowsで試す場合必要なファイルはdeno.exeランタイムとindex.tsファイルだけでした。
やっていきます。
import { DB } from "https://deno.land/x/sqlite/mod.ts";
const users = ["a", "b", "c"];
const db = new DB("sqlite.db");
db.query("CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY AUTOINCREMENT, user TEXT)");
for (const user of users) db.query("INSERT INTO users(user) VALUES(?)", [user]);
for (const user of db.query("SELECT * FROM users")) console.log(user);
db.close();
> dir
...
2022/02/20 17:57 <DIR> .
2022/02/20 17:57 <DIR> ..
2021/12/16 18:56 57,516,032 deno.exe
2022/02/20 17:57 389 index.ts
...
> deno run --allow-read --allow-write index.ts
...
[ 1, "a" ]
[ 2, "b" ]
[ 3, "c" ]
簡単ですね。index.tsをウェブサイトにアップロードして読み込めばランタイムだけで動かせます。
$ deno run --allow-read --allow-write https://tkithrta.gitlab.io/u/deno-sqlite-gen.ts
ちなみにtsファイルはアップロードするウェブサイトによってはContent-Typeがapplication/octet-streamやvideo/mp2tになったりして、たまにDeno側でTypeScriptではなくJavaScriptとして解釈されてしまうので注意が必要です。
少し前に紹介したDistrolessコンテナ上でやってみます。
$ docker run -it gcr.io/distroless/cc:debug-nonroot
~ $ wget https://github.com/denoland/deno/releases/download/v1.18.1/deno-x86_64-unknown-linux-gnu.zip
Connecting to github.com (192.30.255.112:443)
Connecting to objects.githubusercontent.com (185.199.108.133:443)
saving to 'deno-x86_64-unknown-linux-gnu.zip'
deno-x86_64-unknown- 100% |**********| 31.4M 0:00:00 ETA
'deno-x86_64-unknown-linux-gnu.zip' saved
~ $ unzip deno-x86_64-unknown-linux-gnu.zip
Archive: deno-x86_64-unknown-linux-gnu.zip
inflating: deno
~ $ rm deno-x86_64-unknown-linux-gnu.zip
~ $ ls -lah
total 85M
drwx------ 1 nonroot nonroot 38 Feb 20 13:40 .
drwxr-xr-x 1 nonroot nonroot 21 Jan 1 1970 ..
-rw------- 1 nonroot nonroot 183 Feb 20 13:40 .ash_history
-rwxr-xr-x 1 nonroot nonroot 84.6M Feb 20 13:40 deno
~ $ ./deno run --allow-read --allow-write https://tkithrta.gitlab.io/u/deno-sqlite-gen.ts
Download https://tkithrta.gitlab.io/u/deno-sqlite-gen.ts
Download https://deno.land/x/sqlite/mod.ts
Warning Implicitly using latest version (v3.2.1) for https://deno.land/x/sqlite/mod.ts
Download https://deno.land/x/sqlite@v3.2.1/mod.ts
Download https://deno.land/x/sqlite@v3.2.1/build/sqlite.js
Download https://deno.land/x/sqlite@v3.2.1/src/constants.ts
Download https://deno.land/x/sqlite@v3.2.1/src/db.ts
Download https://deno.land/x/sqlite@v3.2.1/src/error.ts
Download https://deno.land/x/sqlite@v3.2.1/src/query.ts
Download https://deno.land/x/sqlite@v3.2.1/src/wasm.ts
Download https://deno.land/x/sqlite@v3.2.1/build/vfs.js
Download https://deno.land/x/sqlite@v3.2.1/build/sqlite.d.ts
Check https://tkithrta.gitlab.io/u/deno-sqlite-gen.ts
[ 1, "a" ]
[ 2, "b" ]
[ 3, "c" ]
~ $ ./deno run --allow-read --allow-write https://tkithrta.gitlab.io/u/deno-sqlite-gen.ts
[ 1, "a" ]
[ 2, "b" ]
[ 3, "c" ]
[ 4, "a" ]
[ 5, "b" ]
[ 6, "c" ]
~ $ ./deno run --allow-read --allow-write https://tkithrta.gitlab.io/u/deno-sqlite-gen.ts
[ 1, "a" ]
[ 2, "b" ]
[ 3, "c" ]
[ 4, "a" ]
[ 5, "b" ]
[ 6, "c" ]
[ 7, "a" ]
[ 8, "b" ]
[ 9, "c" ]
~ $ ls -lah
total 85M
drwx------ 1 nonroot nonroot 69 Feb 20 13:41 .
drwxr-xr-x 1 nonroot nonroot 21 Jan 1 1970 ..
-rw------- 1 nonroot nonroot 277 Feb 20 13:41 .ash_history
drwxr-xr-x 3 nonroot nonroot 18 Feb 20 13:40 .cache
-rwxr-xr-x 1 nonroot nonroot 84.6M Feb 20 13:40 deno
-rw-r--r-- 1 nonroot nonroot 12.0K Feb 20 13:41 sqlite.db
~ $ ls /usr/sbin
tzconfig
~ $ ls /usr/bin
c_rehash openssl
プログラミング言語でSQLite扱うのは多分これが一番早いと思います。
お試しあれ。

2022/04/17 [WebAssembly]かんたんindex.htmlでSQLite(sql.js, cdnjs)
最近ようやくViteをはじめてドキュメントを読みまくっているのですが、特徴のページに
プリコンパイルされた .wasm ファイルは直接インポートできます
https://ja.vitejs.dev/guide/features.html#webassembly
みたいなことが書かれていたので.wasmファイルが必要になり、色々調べた結果sql.jsが配布しているSQLiteのWASMバイナリをcdnjsから引っ張ってくるのが一番早かったのでダウンロードしてきました。
まあそれはそれとして今回はViteの解説ではなく、sql.jsを使えばindex.htmlだけで.wasmファイル読み込めるみたいなことがREADMEに書かれていたので、試したことを残しておきます。
cdnjsの場合1.4.0までは./dist/${file}
に.wasmファイルがありますが、
1.5.0以降./${file}
に.wasmファイルがあるため注意が必要です。
今回は1.6.2を使用しています。
ちなみに引数fileはsql-wasm.wasm
なので、上記URLから.wasmファイルを読み込んでいることになります。
リンクをクリックすれば.wasmファイルが降ってきますね。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<title>SQLite</title>
<meta name="description" content="Zenn" />
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.6.2/sql-wasm.min.js"></script>
<script>
initSqlJs({
locateFile: file => `https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.6.2/${file}`
}).then(SQL => {
const db = new SQL.Database();
document.body.innerText = JSON.stringify(db.exec("SELECT sqlite_version();"));
});
</script>
</body>
</html>
こんな感じでindex.htmlを書けばSQLiteのVersionを表示できるので、誰でも簡単にWebAssemblyを始めることができます。
おわり。
Viteの勉強はつづく。

2022/09/19 [JavaScript]Node.jsとDenoとBunでSQLite書き比べ(Pythonもあるよ)
Intro
Cloudflare D1の発表もあり、最近SQLiteが話題になることが増えましたがORMを使うユースケースが多く、ドライバーからSQL文を直接書いてやるやり方について毎回ググるのも面倒になってきたので、書き比べてこれをインポートしてこのメソッドをこう書いて引数と戻り値の型はこんな感じなんだな~みたいなことをまとめてインターネットに放流します。
6種類書きます。やっていきます。
node-sqlite3
// import fs from "fs";
// import sqlite3 from "sqlite3";
// const fs = require("fs");
const sqlite3 = require("sqlite3");
const users = ["a", "b", "c"];
const db = new sqlite3.Database("sqlite.db");
// const sql = fs.readFileSync("schema.sql", "utf8");
db.serialize(() => {
// db.exec(sql);
db.run("CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY AUTOINCREMENT, user TEXT)");
for (const user of users) db.run("INSERT INTO users(user) VALUES(?)", user);
db.each("SELECT * FROM users", (_err, row) => {
console.log(row);
});
db.each("SELECT COUNT(*) FROM users WHERE user = ?", "a", (_err, counta) => {
console.log(counta);
});
});
db.close();
$ npm i sqlite3
$ node node-sqlite3-gen.js
おそらく最も歴史があるNode.js用パッケージな気がします。
そのせいか古き良きコールバックを多用しており、普通に動かすと並列実行されてしまうのでsqlite.dbファイルが作られていないエラーが出たり後に書いた処理が先に実行されたりします。
Database#serialize()
のコールバック内に書けばそのような事象を防止できます。
better-sqlite3
// import fs from "fs";
// import Database from "better-sqlite3";
// const fs = require("fs");
const Database = require("better-sqlite3");
const users = ["a", "b", "c"];
const db = new Database("sqlite.db");
// const sql = fs.readFileSync("schema.sql", "utf8");
// db.exec(sql);
db.prepare("CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY AUTOINCREMENT, user TEXT)").run();
for (const user of users) db.prepare("INSERT INTO users(user) VALUES(?)").run(user);
for (const row of db.prepare("SELECT * FROM users").iterate()) console.log(row);
for (const counta of db.prepare("SELECT COUNT(*) FROM users WHERE user = ?").iterate("a")) console.log(counta);
db.close();
$ npm i better-sqlite3
$ node better-sqlite3-gen.js
先に書いたnode-sqlite3の使いづらい点を解消してしかも速いNode.js用パッケージです。
ベターを名乗るぐらいなので分かりやすく使いやすいコードを書くことができますが、StatementクラスありきなのでSQL文へバインドする場合はDatabase#prepare()
から始める必要があります。
Statement#all()
を使えばSELECT文で書いたDBを配列で出力してくれますが途中でループを抜け出すような処理をする場合はイテレーターを返すStatement#iterate()
を使ったほうが速いです。
deno-sqlite
import { DB } from "https://deno.land/x/sqlite@v3.5.0/mod.ts";
const users = ["a", "b", "c"];
const db = new DB("sqlite.db");
// const sql = await Deno.readTextFile("schema.sql");
// db.execute(sql);
db.query("CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY AUTOINCREMENT, user TEXT)");
for (const user of users) db.query("INSERT INTO users(user) VALUES(?)", [user]);
for (const row of db.queryEntries("SELECT * FROM users")) console.log(row);
for (const counta of db.prepareQuery("SELECT COUNT(*) FROM users WHERE user = ?").iterEntries(["a"])) {
console.log(counta);
}
db.close(true);
$ deno run --allow-read --allow-write deno-sqlite-gen.ts
かんたんDeno用パッケージです。
WebAssemblyを使用しており、OSやCPUが異なっていても簡単に動かせる素晴らしい利点がありますがクラスの名称が他のパッケージと大きく異なっており、DBとPreparedQueryを使います。
またイテレーターでバインドなど行う際PreparedQueryを使うことになるのですが、自動的にfinalizeされないので簡単にcloseできません。
なのでcloseの引数forceにtrueを入れて無理やりfinalizeできるようになっています。うーん。
deno-sqlite3
import { Database } from "https://deno.land/x/sqlite3@v0.5.3/mod.ts";
const users = ["a", "b", "c"];
const db = new Database("sqlite.db");
// const sql = await Deno.readTextFile("schema.sql");
// db.exec(sql);
db.exec("CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY AUTOINCREMENT, user TEXT)");
for (const user of users) db.exec("INSERT INTO users(user) VALUES(?)", user);
for (const row of db.prepare("SELECT * FROM users")) console.log(row);
for (const counta of db.prepare("SELECT COUNT(*) FROM users WHERE user = ?").bind("a")) console.log(counta);
db.close();
$ curl -O https://github.com/denodrivers/sqlite3/releases/download/v0.5.3/libsqlite3.so
$ DENO_SQLITE_PATH=libsqlite3.so deno run --unstable --allow-env --allow-ffi deno-sqlite3-gen.ts
先に書いたWebAssembly版Deno用パッケージだとWALモード使えないし書き込み遅い!
色々欠点があるのでモダン? なFFIを使ったDeno用パッケージができました。
様々なパッケージの良いところ取りをしており新し目のTypeScript構文を使うことで非常にシンプルで多様な使い方のできるAPIを使えるところもいいですね。
ただUnstableなFFIを使っているせいかまだv1に到達しておらず--unstable
オプションも必要になります。
またWindowsの場合はsqlite3.dllを別途入れる必要があります。
bun-sqlite
// import fs from "fs";
// import { Database } from "bun:sqlite";
// const fs = require("fs");
const { Database } = require("bun:sqlite");
const users = ["a", "b", "c"];
const db = new Database("sqlite.db");
// const sql = fs.readFileSync("schema.sql", "utf8");
// db.run(sql);
db.run("CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY AUTOINCREMENT, user TEXT)");
for (const user of users) db.run("INSERT INTO users(user) VALUES(?)", user);
for (const row of db.query("SELECT * FROM users").all()) console.log(row);
for (const counta of db.query("SELECT COUNT(*) FROM users WHERE user = ?").all("a")) console.log(counta);
db.close();
$ bun run bun-sqlite-gen.js
SQLiteとFFIが組み込まれていて恐ろしい速さを誇るランタイムです(最近はDenoも頑張っているのでそうでもない?)
モジュールですがパッケージと同じようにインポートして使います。
ドキュメントではbetter-sqlite3に基づいていると書かれていますがメソッドが結構異なっており、Deno用パッケージふたつに近い簡単さとシンプルさを感じました。
ただ何分機能が少なくイテレーターなしで配列などの結果を即返すような仕組みなので、速いのはいいけどメモリ管理とか大丈夫なのか不安になります。
とにかくまだオープンになってから数ヶ月しか経っていないですしランタイム自体v1に到達していないので今後に期待したいところです。
python-sqlite3
from contextlib import closing
import sqlite3
users = ["a", "b", "c"]
with closing(sqlite3.connect("file:sqlite.db", uri=True)) as conn:
c = conn.cursor()
# with open("schema.sql", encoding="utf-8") as f:
# sql = f.read()
# c.executescript(sql)
c.execute("CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY AUTOINCREMENT, user TEXT)")
for user in users:
c.execute("INSERT INTO users(user) VALUES(?)", (user,))
conn.commit()
for row in c.execute("SELECT * FROM users"):
print(row)
for counta in c.execute("SELECT COUNT(*) FROM users WHERE user = ?", ("a",)):
print(counta)
$ python python-sqlite3-gen.py
以前からよく書くPythonのコードも置いておきます。
PythonはSQLite3パッケージが標準でついてくるので、Raspberry Pi OSでもこのように書けば動きます。
ただcloseの方法が様々あり、(isolation_levelをNoneにしない場合は)DMLのcommitが必要になるので人によっては好き嫌いが分かれるかもしれません。
init
いつものGitpod Ubuntu 20.04で検証しています。DenoはWindowsでも動作確認しています。
$ arch
x86_64
$ node -v
v16.17.0
$ npm -v
8.15.0
$ python -V
Python 3.8.13
$ pip -V
pip 22.2.2 ...
$ curl -fsSL https://deno.land/x/install/install.sh | sh
...
$ export DENO_INSTALL="$HOME/.deno"
$ export PATH="$DENO_INSTALL/bin:$PATH"
$ deno --version
deno 1.25.3 (release, x86_64-unknown-linux-gnu)
v8 10.6.194.5
typescript 4.7.4
$ curl https://bun.sh/install | bash
...
$ export BUN_INSTALL="$HOME/.bun"
$ export PATH="$BUN_INSTALL/bin:$PATH"
$ bun -v
0.1.13
$ touch node-sqlite3-gen.js better-sqlite3-gen.js deno-sqlite-gen.ts deno-sqlite3-gen.ts bun-sqlite-gen.js python-sqlite3-gen.py
$ ls -la
total 0
drwxr-xr-x 2 gitpod gitpod 175 Sep 19 10:00 .
drwxr-xr-x 3 gitpod gitpod 15 Sep 19 10:00 ..
-rw-r--r-- 1 gitpod gitpod 0 Sep 19 10:00 better-sqlite3-gen.js
-rw-r--r-- 1 gitpod gitpod 0 Sep 19 10:00 bun-sqlite-gen.js
-rw-r--r-- 1 gitpod gitpod 0 Sep 19 10:00 deno-sqlite3-gen.ts
-rw-r--r-- 1 gitpod gitpod 0 Sep 19 10:00 deno-sqlite-gen.ts
-rw-r--r-- 1 gitpod gitpod 0 Sep 19 10:00 node-sqlite3-gen.js
-rw-r--r-- 1 gitpod gitpod 0 Sep 19 10:00 python-sqlite3-gen.py
SQL
SQLite CLIから直接SQL文叩く際は"?"に値を入れてください。
CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY AUTOINCREMENT, user TEXT);
INSERT INTO users(user) VALUES(?);
SELECT * FROM users;
SELECT COUNT(*) FROM users WHERE user = ?;
Node.jsの表示
{ id: 1, user: 'a' }
{ id: 2, user: 'b' }
{ id: 3, user: 'c' }
...
{ 'COUNT(*)': 1 }
DenoとBunの表示
{ id: 1, user: "a" }
{ id: 2, user: "b" }
{ id: 3, user: "c" }
...
{ "COUNT(*)": 1 }
Pythonの表示
(1, 'a')
(2, 'b')
(3, 'c')
...
(1,)
package.json
CJS版
{
"dependencies": {
"better-sqlite3": "^7.6.2",
"sqlite3": "^5.1.1"
}
"engines": {
"node": "16.x"
}
}
ESM版
{
"dependencies": {
"better-sqlite3": "^7.6.2",
"sqlite3": "^5.1.1"
},
"type": "module",
"engines": {
"node": "16.x"
}
}
Outro
こう見てみるとやっていることはほとんど変わらず、DatabaseとStatementのクラスに関するソースコードやAPIドキュメントを読めば簡単に分かる内容ばかりでした。
ただ微妙に異なる点があり、クラスに関連したコードが増えれば増えるほどソースコードのマイグレーションが難しくなっていきそうに思えます。
CloudflareとDenoとWinterCGなんとかしてくれ~。
Ref