Node.js sqlite3 awaitで思考停止する
db.serialize()
から逃げた
Node.js v10.15.3sqlite3 4.0.9
最新 (2022-09-05)
sqlite を使いましょう。 db.serialize()
は無いし await
つけて実行できます。
以下、古い内容です。
以前の記事
たとえば id
が 1, 2, 3, 4, 5 のレコードを1行ずつ取得して、content
カラムの末尾に『!』を付与してUPDATEするというコードを、db.serialize()
使って書いてみます。
const sqlite3 = require('sqlite3').verbose();
const db = new sqlite3.Database('/path/to/mydatabase');
db.serialize(() => {
const ids = [1,2,3,4,5];
for (let id of ids) {
// 1行取得
db.get(`SELECT * FROM mytable WHERE id = ?`, [id], (err, row) => {
if (err) return console.log('※SELECTでエラー', err);
// 更新
const newContent = row.content + '!';
db.run(`UPDATE mytable SET content = ? WHERE id = ?`, [newContent, id], (err) => {
if (err) return console.log('※UPDATEでエラー', err);
});
});
}
// データベースを閉じる
db.close(err => {
if (err) return console.log('※closeでエラー', err);
});
});
上記のコードは動きません。db.run()
で以下のエラーが出てしまいます。
※UPDATEでエラー { [Error: SQLITE_MISUSE: Database handle is closed] errno: 21, code: 'SQLITE_MISUSE' }
※UPDATEでエラー { [Error: SQLITE_MISUSE: Database handle is closed] errno: 21, code: 'SQLITE_MISUSE' }
※UPDATEでエラー { [Error: SQLITE_MISUSE: Database handle is closed] errno: 21, code: 'SQLITE_MISUSE' }
※UPDATEでエラー { [Error: SQLITE_MISUSE: Database handle is closed] errno: 21, code: 'SQLITE_MISUSE' }
※UPDATEでエラー { [Error: SQLITE_MISUSE: Database handle is closed] errno: 21, code: 'SQLITE_MISUSE' }
そうでしょうね。db.run()
は db.serialize()
の直下で実行してないので、それをやる頃にはすでに db.close()
が流れてしまってるからです。
*上記のコードは db.close()
を消せば一応動きます。*プログラム的にデータベースを閉じるタイミングがわからないので、終了までしばらく待たされますが、動くことは動きます。でもそれでいいのかよっていう。
そもそも db.serialize()
が難しいです。というか、自分ひとりでちょっと使いたいだけだったりすると、結果をコールバック関数で受け取るのがしんどいです。
どうするの
自分ひとりでちょっと使うだけなら、db.serialize()
使わないで、Promise化して await
で呼んでしまいます。こっちの方が、本当に何も考えなくていいです。
const sqlite3 = require('sqlite3').verbose();
const db = new sqlite3.Database('/path/to/mydatabase');
const ids = [1,2,3,4,5];
// awaitはasyncの中じゃないと使えない
(async () => {
for (let id of ids) {
// 1行取得
const row = await get(`SELECT * FROM mytable WHERE id = ?`, [id]);
// 更新
const newContent = row.content + '!';
await run(`UPDATE mytable SET content = ? WHERE id = ?`, [newContent, id]);
}
// データベースを閉じる
db.close(err => {
if (err) return console.log('※closeでエラー', err);
});
})();
function get(sql, params) {
return new Promise((resolve, reject) => {
db.get(sql, params, (err, row) => {
if (err) reject(err);
resolve(row);
});
});
}
function run(sql, params) {
return new Promise((resolve, reject) => {
db.run(sql, params, (err) => {
if (err) reject(err);
resolve();
});
});
}
このサンプル程度の処理だと無駄にコードが長くなるだけですが、もっと複雑なことをやり始めたら、ただただ書いた順番通りに動いてくれるのは楽です。