LoginSignup
2
1

More than 5 years have passed since last update.

JXAでSQLite3を使う

Last updated at Posted at 2016-12-12

JXAのObjective-Cブリッジは、いくつかの標準添付Cライブラリへのアクセスを提供している。

この記事では、JXAにおいて、SQLite3のCインターフェイスを介して、SQLite3データベースを操作する方法を説明する。

オープンおよびクローズ

sqlite**型は空のRefで表現できる。var db = ppDb[0]はデリファレンス(sqlite *db = *ppDb)に相当する操作である。

ObjC.import('sqlite3')
var err
var filename = $('~/data.sqlite3').stringByStandardizingPath.js

var ppDb = Ref()
err = $.sqlite3_open(filename, ppDb)
var db = ppDb[0]
if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))

try {
    /* データベース操作 */
} finally {
    err = $.sqlite3_close(db)
    if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))
}

テーブル作成

NULLポインタは空のRef()で表現できる。

sql = 'CREATE TABLE IF NOT EXISTS myTable (id INTEGER PRIMARY KEY, name TEXT); DELETE FROM myTable'
err = $.sqlite3_exec(db, sql, Ref(), Ref(), Ref())
if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))

レコード挿入

// トランザクション
err = $.sqlite3_exec(db,'BEGIN', Ref(), Ref(), Ref())
if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))

sql = 'INSERT INTO myTable (id, name) VALUES (?, ?)'
ppStmt = Ref()
err = $.sqlite3_prepare_v2(db, sql, -1, ppStmt, Ref())
if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))
pStmt = ppStmt[0]

try {
    for (var i = 0; i < 100; i++) {
        err = $.sqlite3_reset(pStmt)
        if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))

        err = $.sqlite3_bind_int(pStmt, 1, i)
        if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))

        err = $.sqlite3_bind_text(pStmt, 2, "First " + i + " unique names", -1, -1)
        if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))

        err = $.sqlite3_step(pStmt)
        if (err != $.SQLITE_DONE) throw new Error($.sqlite3_errmsg(db))
    }

    // トランザクション
    err = $.sqlite3_exec(db,'END', Ref(), Ref(), Ref())
    if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))
} finally {
    err = $.sqlite3_finalize(pStmt)
    if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))
}

注意

型がconst char *の引数に対しては、JavaScriptの文字列を直接渡すことが可能であるが、渡したポインタが指す領域は当該関数呼び出し終了時に解放される模様。
したがって、$.sqlite3_bind_textなどの関数を使用する場合、第5引数にSQLITE_TRANSIENT (-1) を使用し、SQLiteの内部バッファに文字列をコピーしておく必要がある。

レコード検索

sql = 'SELECT id, name FROM myTable'
ppStmt = Ref()
err = $.sqlite3_prepare_v2(db, sql, -1, ppStmt, Ref())
if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))
pStmt = ppStmt[0]

try {
    while ((err = $.sqlite3_step(pStmt)) == $.SQLITE_ROW) {
        console.log($.sqlite3_column_int(pStmt, 0), $.sqlite3_column_text(pStmt, 1))
    }
    if (err != $.SQLITE_DONE) throw new Error($.sqlite3_errmsg(db))
} finally {
    err = $.sqlite3_finalize(pStmt)
    if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))
}

コード

ObjC.import('sqlite3')

var err
var filename = $('~/data.sqlite3').stringByStandardizingPath.js

var ppDb = Ref()
err = $.sqlite3_open(filename, ppDb)
var db = ppDb[0]
if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))

try {
    var sql, ppStmt, pStmt

    sql = 'CREATE TABLE IF NOT EXISTS myTable (id INTEGER PRIMARY KEY, name TEXT); DELETE FROM myTable'
    err = $.sqlite3_exec(db, sql, Ref(), Ref(), Ref())
    if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))

    err = $.sqlite3_exec(db,'BEGIN', Ref(), Ref(), Ref())
    if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))

    sql = 'INSERT INTO myTable (id, name) VALUES (?, ?)'
    ppStmt = Ref()
    err = $.sqlite3_prepare_v2(db, sql, -1, ppStmt, Ref())
    if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))
    pStmt = ppStmt[0]

    try {
        for (var i = 0; i < 100; i++) {
            err = $.sqlite3_reset(pStmt)
            if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))

            err = $.sqlite3_bind_int(pStmt, 1, i)
            if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))

            err = $.sqlite3_bind_text(pStmt, 2, "First " + i + " unique names", -1, -1)
            if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))

            err = $.sqlite3_step(pStmt)
            if (err != $.SQLITE_DONE) throw new Error($.sqlite3_errmsg(db))
        }

        err = $.sqlite3_exec(db,'END', Ref(), Ref(), Ref())
        if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))
    } finally {
        err = $.sqlite3_finalize(pStmt)
        if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))
    }

    sql = 'SELECT id, name FROM myTable'
    ppStmt = Ref()
    err = $.sqlite3_prepare_v2(db, sql, -1, ppStmt, Ref())
    if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))
    pStmt = ppStmt[0]

    try {
        while ((err = $.sqlite3_step(pStmt)) == $.SQLITE_ROW) {
            console.log($.sqlite3_column_int(pStmt, 0), $.sqlite3_column_text(pStmt, 1))
        }
        if (err != $.SQLITE_DONE) throw new Error($.sqlite3_errmsg(db))
    } finally {
        err = $.sqlite3_finalize(pStmt)
        if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))
    }
} finally {
    err = $.sqlite3_close(db)
    if (err != $.SQLITE_OK) throw new Error($.sqlite3_errmsg(db))
}

結果

/* 0 First 0 unique names */
/* 1 First 1 unique names */
...
/* 97 First 97 unique names */
/* 98 First 98 unique names */
/* 99 First 99 unique names */
Result:
0
2
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1