データベース†
4 個の関数。
SQLOPEN†
書式†
SQLOPEN( path [, mode] )
引数†
| Parameter | Description |
|---|---|
| path | データベースファイルのパス(yaya.dll からの相対パス、または絶対パス)。":memory:" ならメモリ上のデータベース |
| mode | 開き方(省略可。下記参照)。省略時は "rwc" |
mode†
| mode | 動作 |
|---|---|
"rwc" |
読み書き。ファイルが無ければ作る(省略時) |
"rw" |
読み書き。ファイルが無ければ失敗する |
"r" |
読み取り専用。ファイルが無ければ失敗する |
戻り値†
- 1: 成功
- 0: 失敗
- 2: すでに開いている
解説†
SQLite のデータベースを開く。開いたデータベースには、SQLEXEC と SQLQUERY で SQL を実行する。
FOPEN と同じく、データベースは開いたときのパスで識別する。以降の SQLEXEC・SQLQUERY・SQLCLOSE には、SQLOPEN と同じ書き方のパスを渡す。"data.db" と "./data.db" のように書き方が違うと、同じファイルでも別のものとして扱われ、2つ目の接続が開く(SQLite のロックで守られるので中身は壊れないが、閉じるときはそれぞれ閉じる必要がある)。
- 複数のデータベースを同時に開ける
":memory:"のデータベースは1つだけ開ける。閉じると中身は消える- 開いたデータベースは、SQLCLOSE で閉じるか、unload のときに自動で閉じる。終わっていないトランザクションはロールバックされる
- 別のプロセス(ほかのゴーストなど)が書き込み中でロックされているときは、最大1秒待つ。それでも解除されなければ、その SQL はエラー(database is locked)になる
データベースの中身(表の作り方、ジャーナルの方式など)は SQL で指定する。たとえば書き込みの多い用途では、開いた直後に SQLEXEC(path, "PRAGMA journal_mode=WAL") を実行しておくとよい。
SQLite の拡張機能の読み込み(load_extension)は使えない。
エラー†
| 状況 | 警告 | GETLASTERROR |
|---|---|---|
| 引数がない | W0008 | 8 |
| path または mode が文字列でない | W0009 | 9 |
| path が空文字列 | W0010 | 10 |
| mode が不正 | W0012 | 12 |
| データベースを開けない | W0029 | 29 |
W0029 の警告には SQLite のエラーメッセージが付く。
使用例†
if SQLOPEN("ghost.db") == 0 {
// 開けなかった
}
SQLEXEC("ghost.db", "CREATE TABLE IF NOT EXISTS talk(id INTEGER PRIMARY KEY, text TEXT)")
SQLOPEN(":memory:") // メモリ上の作業用データベース
SQLOPEN("ref.db", "r") // 読み取り専用
互換性†
- YAYA: Tc602-11以降
関連項目†
SQLCLOSE†
書式†
SQLCLOSE( path )
引数†
| Parameter | Description |
|---|---|
| path | SQLOPEN で開いたデータベースのパス |
戻り値†
返り値はない(VOID)。
解説†
SQLOPEN で開いたデータベースを閉じる。終わっていないトランザクションはロールバックされる。
閉じ忘れたデータベースは unload のときに自動で閉じる。
エラー†
| 状況 | 警告 | GETLASTERROR |
|---|---|---|
| 引数がない | W0008 | 8 |
| path が文字列でない | W0009 | 9 |
| 開いていない | W0015 | 15 |
使用例†
SQLOPEN("ghost.db")
SQLEXEC("ghost.db", "INSERT INTO talk(text) VALUES(?)", "こんにちは")
SQLCLOSE("ghost.db")
互換性†
- YAYA: Tc602-11以降
関連項目†
SQLEXEC†
書式†
SQLEXEC( path, sql [, param...] )
引数†
| Parameter | Description |
|---|---|
| path | SQLOPEN で開いたデータベースのパス |
| sql | 実行する SQL。; で区切って複数の文を書ける |
| param | SQL のパラメータに入れる値(省略可。下記の パラメータ) |
戻り値†
- 成功時: INSERT・UPDATE・DELETE で変更された行数の合計(それ以外の文だけなら 0)
- 失敗時: -1
解説†
SQL を実行する。表を作る、行を追加・変更・削除する、トランザクションを始める、といった結果の行を使わない SQL に使う。SELECT の結果を受け取るときは SQLQUERY を使う(SQLEXEC で SELECT を実行すると、結果の行は捨てられる)。
sql に複数の文があれば、先頭から順に実行する。途中の文でエラーになったら、そこで止まる。それまでに実行した文は取り消されないので、まとめて取り消したいときはトランザクション(BEGIN 〜 COMMIT)の中で実行する。
パラメータ†
SQL の中の ?(または ?1 のような番号付き)や :名前 @名前 $名前 の位置に値を入れられる。値を SQL の文字列に直接埋め込むと、' を含む文字列でエラーになったり、意図しない SQL が実行されたりする(SQL インジェクション)。外から受け取った文字列は必ずパラメータで渡すこと。
param の渡し方は2通りある。
- 値を並べる: param がすべて整数・実数・文字列・VOID のとき、1回だけ実行する。
?に先頭から順に入る - 1回分ずつ配列かハッシュにする: param がすべて配列かハッシュのとき、1つを1回分として、その数だけ実行する
- ハッシュ:
:名前@名前$名前に、キーが「名前」の値が入る - 配列:?に要素が先頭から順に入る
値が足りないパラメータや、ハッシュにキーが無いパラメータには NULL が入る。値とハッシュ・配列を混ぜて渡すと W0009 になる。
逆に、値がパラメータより多いときは、余った値は使われず、W0030 の警告が出る(実行はする)。1 の形では param の数が、2 の形では配列の要素数が、? の数(?3 のような番号付きなら最大の番号)より多いと警告になる。ハッシュは使われなかったキーがあっても警告しない。
関数に配列を渡すと、いちばん外側の配列は引数に展開される(値の入れ子と多次元代入)。そのため、値の配列 (1, "a") を渡すと 1 の「値を並べる」になり、ハッシュの配列や、配列の配列(PARSEJSON などで得たもの)を渡すと 2 の「1回分ずつ」になる。
(1, 2), (3, 4) と書いたり、値の配列の変数を2つ並べたりしても、, で1つの配列につながるので「1回分ずつ」にはならず、1 の形で1回だけ実行される(? が 2 つなら 3 と 4 は使われず、W0030 になる)。1回分ずつにしたいときは、配列の配列やハッシュの配列を作って渡す。
sql に複数の文があるときは、どの文にも同じパラメータが使われる(? の番号は文ごとに 1 から数える)。前の文の続きの値が次の文に入るわけではない。INSERT INTO t VALUES (?); INSERT INTO u VALUES (?) に "first", "second" を渡すと、どちらの文にも "first" が入り、"second" は使われない(W0030)。文ごとに違う値を入れたいときは、文を分けて実行するか、ハッシュで :名前 を使う。
2 の形で多くの行を追加するときは、BEGIN と COMMIT で囲むと、1行ずつファイルに書き込まれないので速い。
値の対応†
| YAYA | SQLite | YAYA に戻したとき |
|---|---|---|
| 整数 | INTEGER | 整数 |
| 実数 | REAL | 実数 |
| 文字列 | TEXT(UTF-8) | 文字列 |
| 空(VOID) | NULL | 空(VOID) |
| ― | BLOB | 16進数の文字列(hex() と同じ大文字) |
- 空文字列
""は NULL ではなく、長さ 0 の TEXT になる。NULL を入れたいときは、パラメータを省くか、ハッシュにそのキーを入れない - 配列やハッシュは1つのパラメータに入れられない(W0009)。JSON として保存したいときは DUMPJSON で文字列にしてから渡す。SQLite の JSON 関数(
json_extractや->>、json_eachなど)で中身を検索できる - BLOB を入れたいときは、16進数の文字列を渡して SQL 側で
unhex(?)にする
準備済みの文の再利用†
同じ sql の文字列で実行すると、前回 SQLite が解析した結果(準備済みの文)を使い回すので速い。パラメータで値を変えながら同じ sql を何度も実行するとよい。値を SQL の文字列に埋め込むと毎回別の sql になり、使い回せない。
エラー†
| 状況 | 警告 | GETLASTERROR |
|---|---|---|
| 引数が足りない | W0008 | 8 |
| path または sql が文字列でない、param が上記のどちらの形でもない | W0009 | 9 |
| データベースを開いていない | W0015 | 15 |
| SQL の実行に失敗した(文法の誤り、制約違反、読み取り専用など) | W0029 | 29 |
| param の値がパラメータより多い(実行は成功する) | W0030 | 30 |
W0029 の警告には SQLite のエラーメッセージが付く(例: SQLEXEC : UNIQUE constraint failed: talk.id)。W0030 の警告にはパラメータの数と値の数が付く(例: SQLEXEC : parameters 2, values 4)。
W0030 は実行に成功したときに出るので、返値は成功時と同じ(変更された行数)。GETLASTERROR は成功しても 0 に戻らないので、W0030 を調べるときは先に SETLASTERROR(0) しておく。
使用例†
SQLOPEN("ghost.db")
SQLEXEC("ghost.db", "CREATE TABLE IF NOT EXISTS talk(id INTEGER PRIMARY KEY, text TEXT, score REAL); CREATE INDEX IF NOT EXISTS talk_score ON talk(score)")
// 値を並べる
SQLEXEC("ghost.db", "INSERT INTO talk(text, score) VALUES(?, ?)", "こんにちは", 1.5) // 1
// ハッシュで名前を指定する
_h = IHASH("text", "こんばんは", "score", 2)
SQLEXEC("ghost.db", "INSERT INTO talk(text, score) VALUES(:text, :score)", _h) // 1
// ハッシュの配列で、まとめて追加する
_rows = (IHASH("text", "a", "score", 3), IHASH("text", "b", "score", 4))
SQLEXEC("ghost.db", "BEGIN")
SQLEXEC("ghost.db", "INSERT INTO talk(text, score) VALUES(:text, :score)", _rows) // 2
SQLEXEC("ghost.db", "COMMIT")
SQLEXEC("ghost.db", "UPDATE talk SET score = score + 1 WHERE score >= ?", 3) // 2
SQLEXEC("ghost.db", "DELETE FROM talk WHERE text = ?", "a") // 1
// 直前に追加した行の id は SQL の関数で得る
SQLQUERY("ghost.db", "SELECT last_insert_rowid() AS id")[0]["id"]
互換性†
- YAYA: Tc602-11以降
- Tc602-13: 値がパラメータより多いときに警告 W0030 を出すようにした
関連項目†
SQLQUERY†
書式†
SQLQUERY( path, sql [, param...] )
引数†
| Parameter | Description |
|---|---|
| path | SQLOPEN で開いたデータベースのパス |
| sql | 実行する SQL(主に SELECT) |
| param | SQL のパラメータに入れる値(省略可。SQLEXEC と同じ) |
戻り値†
- 成功時: 結果の行の汎用配列。1行が「列名 → 値」のハッシュになる。行が無ければ空の汎用配列
- 失敗時: 空(VOID)
解説†
SQL を実行して、結果の行を返す。パラメータの渡し方、値の対応、エラーは SQLEXEC と同じ。
- 列名は SELECT に書いた名前になる。式の列や、同じ名前の列が複数あるとき(
a.idとb.idなど)は、ASで別の名前を付けること。同じ名前の列は後のものだけが残る - NULL の列は空(VOID)になる。ハッシュにキーは残る
- sql に複数の文があれば、結果の行を順につなげて返す。1回分ずつ配列・ハッシュで param を渡したときも、すべての回の行をつなげて返す
- 行はすべてメモリに読み込んでから返す。行の多い表を読むときは
LIMITやWHEREで絞ること。SQLite の処理中は SHIORI の応答が止まる
空の結果(空の汎用配列)と失敗(VOID)は GETTYPE で見分けられる(それぞれ 4 と 0)。
使用例†
SQLOPEN("ghost.db")
_rows = SQLQUERY("ghost.db", "SELECT id, text, score FROM talk WHERE score >= ? ORDER BY score DESC LIMIT 10", 2)
ARRAYSIZE(_rows) // 行数
_rows[0]["text"] // 1行目の text
foreach _rows; _r {
// _r["id"] _r["text"] _r["score"]
}
// 集計には AS で名前を付ける
SQLQUERY("ghost.db", "SELECT count(*) AS n, avg(score) AS avg FROM talk")[0]["n"]
// JSON で保存した列を SQLite の JSON 関数で検索する
SQLEXEC("ghost.db", "CREATE TABLE IF NOT EXISTS ev(id TEXT PRIMARY KEY, json TEXT)")
_ev = PARSEJSON('{"id":"abc","tags":[["p","xyz"],["e","def"]]}')
SQLEXEC("ghost.db", "INSERT OR IGNORE INTO ev VALUES(?, ?)", _ev["id"], DUMPJSON(_ev))
SQLQUERY("ghost.db", "SELECT ev.id, t.value->>1 AS v FROM ev, json_each(ev.json, '$.tags') t WHERE t.value->>0 = ?", "e")
// → 要素1つ:IHASH("id", "abc", "v", "def")
互換性†
- YAYA: Tc602-11以降
- Tc602-13: 値がパラメータより多いときに警告 W0030 を出すようにした