Storage

  • リビジョンの URL スラッグ: Storage
  • リビジョンのタイトル: Storage
  • リビジョンの ID: 182890
  • 作成日:
  • 作成者: OGINO Masanori
  • 現行リビジョン いいえ
  • コメント /* 命令を実行する */

このリビジョンの内容

{{ 翻訳中() }}

Storage は Firefox 2 の データベース API で、 sqlite を基礎としています。これは chrome のコードや 拡張機能 などの信頼される呼び出し元から利用でき、web ページからは利用できません。このAPIは現在の未確定であり、APIはいまだに変更される余地があるという事です。API は Firefox 2 alpha 2 から Firefox 2 リリース版の間に多少変わるでしょうし、またFirefox 2 から Firefox 3 の間でも多少変わるでしょう。

Storage は Web ページの永続的なデータの保持に使われる Firefox 2 のWHATWG DOM storage 機能と混乱されてしまうでしょう。 Storage API は拡張機能の作者と Firefox のコンポーネントのためだけの物です。

このドキュメントは mozStorage API と sqlite からいくらか変わっている点について述べます。SQL についてと 一般的な sqlite については述べません。これらやあなたのお気に入りの SQL リファレンスをお使いください。もしかすると sqlite documentation や特に query language understood by sqlite を見ておく必要があるかもしれません。mozStorage API について助けが必要であれば、 news.mozilla.org ニュースサーバの mozilla.dev.apps.firefox に投稿する事が可能です。バグをレポートするなら、 Bugzilla (product "Toolkit", component "Storage") を使ってください。

データベース接続のパフォーマンスを良くするためには Storage:Performance をご覧ください。

SQLite Database Browser は多くのプラットフォームで利用可能な役にたつフリーのツールです。これは既存のデータベースを試したり、SQL の命令をテストするのに役に立つでしょう。

{{ 英語版章題("Getting started") }}

はじめに

mozStorageは他の多くのデータベースシステムと同じように設計されています。利用のための手順の概要は次のようになります:

  • あなたの選択でデータベースとの接続を開きます。
  • 接続上で実行する命令を作ります。
  • 必要に応じてパラメータを命令に束ねます。
  • 命令を実行します。
  • 命令をリセットします。

{{ 英語版章題("Opening a connection") }}

接続を開く

Storageサービスの最初の初期化はメインスレッドから行わなければなりません。もしもあなたが別のスレッドから一回目の初期化を行ったならば、エラーを受けとるでしょう。したがって、もしもあなたがスレッドからサービスを使いたいならば、サービスが作られていることを確実にするためにgetServiceをメインスレッドから呼び出すように気をつけて下さい。

C++でユーザプロファイルディレクトリにある"asdf.sqlite"と接続を開く例は次のようになります:

nsCOMPtr<nsIFile> dbFile;
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
                            getter_AddRefs(dbFile));
NS_ENSURE_SUCCESS(rv, rv);
rv = dbFile->Append(NS_LITERAL_STRING("asdf.sqlite"));
NS_ENSURE_SUCCESS(rv, rv);

mDBService = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBService->OpenDatabase(dbFile, getter_AddRefs(mDBConn));
NS_ENSURE_SUCCESS(rv, rv);

MOZ_STORAGE_SERVICE_CONTRACTIDは{{ Source("storage/build/mozStorageCID.h") }}で定義されています。その値は"@mozilla.org/storage/service;1"です。

JavaScriptでの例は次のようになります:

var file = Components.classes["@mozilla.org/file/directory_service;1"]
                     .getService(Components.interfaces.nsIProperties)
                     .get("ProfD", Components.interfaces.nsIFile);
file.append("asdf.sqlite");

var storageService = Components.classes["@mozilla.org/storage/service;1"]
                        .getService(Components.interfaces.mozIStorageService);
var mDBConn = storageService.openDatabase(file);
メモ: OpenDatabase関数は変更の必要があります。おそらくこの関数はトラブルにもっと陥りにくくするように強化され、また単純化されるでしょう。

ひょっとしたらあなたのデータベースの名前の最後をsqlite databaseを由来にして".sdb"にしたくなるかもしれませんが、それは推奨できません。この拡張子はWindowsでは"Application Compatibility Database"の拡張子として特別に扱われ、そして変更は『システムの復元』機能の一部として自動的にシステムからバックアップされます。これはファイル管理により多くのオーバーヘッドをもたらすことになります。

{{ 英語版章題("Creating a statement") }}

命令を作る

命令を作る方法は二つあります。もしもパラメータがなく、命令が何のデータも返さないのならば、mozIStorageConnection.executeSimpleSQLを使います。

C++:
rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE TABLE foo (a INTEGER)"));

JS:
mDBConn.executeSimpleSQL("CREATE TABLE foo (a INTEGER)");

そうでなければ、あなたはmozIStorageConnection.createStatementを使って命令を準備すべきです。

C++:
nsCOMPtr<mozIStorageStatement> statement;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("SELECT * FROM foo WHERE a = ?1"),
                              getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);

JS:
var statement = mDBConn.createStatement("SELECT * FROM foo WHERE a = ?1");

この例は{{ 原語併記("束ねられた", "bound") }}パラメータとして"?1"というプレースホルダを使います(次の章を見て下さい)。

一度命令を準備したら、あなたはパラメータを束ねたり、実行したり、また作ったりを何度も何度もできます。もしもあなたが何度も命令を実行するならば、SQLクエリがその度にコンパイルを必要としないので、プリコンパイルされた命令を使うことははあなたに顕著なパフォーマンスの進歩を与えるでしょう。

もしもあなたがsqliteに詳しいならば、準備された命令はデータベースのスキーマを変更した時に無効化されることを知っているかもしれません。幸運にも、mozIStorageStatementはエラーを推測して必要に応じて命令を再コンパイルするでしょう。それゆえに、一度命令を作ったら、あなたはスキーマを変更した時のことを心配する必要はありません。すべての命令は透過的に作業を継続するでしょう。

{{ 英語版章題("Binding paameters") }}

パラメータを束ねる

一般的に、すべてのパラメータを別々に束ねる方が、パラメータを含んだSQL文字列を慌ただしく組み立てるよりは幾分有益です。とりわけ、束ねられたパラメータは決してSQLとして実行されないために、この方法はSQLインジェクション攻撃を妨げます。

あなたはプレースホルダを持つ命令にパラメータを束ねます。プレースホルダは"?1"から始まり、それから"?2"……といった索引で場所が指定されています。あなたはBindXXXParameter(0) BindXXXParameter(1)……といった関数をそれらのプレースホルダへパラメータを束ねるために使います。

気をつけましょう: プレースホルダの索引は1から数えます。束ねるための関数において変化する整数は0から数えます。これは"?1"が0のパラメータと対応、"?2"が1のパラメータと対応……となることを意味します。

プレースホルダはSQL文字列の中で複数回登場でき、そしてすべてのインスタンスは束ねられた値に置き換えられるでしょう。束ねられていないパラメータはNULLとして解釈されるでしょう。

mozIStorageStatement({{ Source("storage/public/mozIStorageStatement.idl") }}を見てください)で利用できる束ねるための関数には次のようなものがあります:

  • bindUTF8StringParameter(in unsigned long aParamIndex, in AUTF8String aValue)
  • bindStringParameter(in unsigned long aParamIndex, in AString aValue)
  • bindDoubleParameter(in unsigned long aParamIndex, in double aValue)
  • bindInt32Parameter(in unsigned long aParamIndex, in long aValue)
  • bindInt64Parameter(in unsigned long aParamIndex, in long long aValue)
  • bindNullParameter(in unsigned long aParamIndex)
  • bindBlobParameter(in unsigned long aParamIndex, {{ mediawiki.external('array,const,size_is(aValueSize)') }} in octet aValue, in unsigned long ValueSize) (バイナリデータ用)

C++での例:

nsCOMPtr<mozIStorageStatement> statement;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("SELECT * FROM foo WHERE a = ?1 AND b > ?2"),
                              getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindUTF8StringParameter(0, "hello"); // "hello" will be substituted for "?1"
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindInt32Parameter(1, 1234); // 1234 will be substituted for "?2"
NS_ENSURE_SUCCESS(rv, rv);

JavaScriptでの例:

var statement = mDBConn.createStatement("SELECT * FROM foo WHERE a = ?1 AND b > ?2");
statement.bindUTF8StringParameter(0, "hello");
statement.bindInt32Parameter(1, 1234);

{{ 英語版章題("Executing a statement") }}

命令を実行する

命令を実行する主な方法はmozIStorageStatement.executeStepを伴います。この関数はあなたの命令が生産するすべての結果の並びを列挙させ、そしてこれ以上の結果がない時にあなたに知らせるでしょう。

executeStepを呼んだ後は、mozIStorageValueArray({{ Source("storage/public/mozIStorageValueArray.idl") }}を見てください)にあるゲッタ関数を使えます。mozIStorageStatementはmozIStorageValueArrayを提供します。これらの関数があります:

  • long getInt32(in unsigned long aIndex);
  • long long getInt64(in unsigned long aIndex);
  • double getDouble(in unsigned long aIndex);
  • AUTF8String getUTF8String(in unsigned long aIndex);
  • AString getString(in unsigned long aIndex);
  • void getBlob(in unsigned long aIndex, out unsigned long aDataSize, {{ mediawiki.external('array,size_is(aDataSize)') }} out octet aData);注意: もしもdataSizeが0ならば、データはNULLでしょう。
  • boolean getIsNull(in unsigned long aIndex); もしもそのセルがNULL(空文字列とは違います)ならばtrueを返します。

あなたはmozIStorageValueArray.getTypeOfIndexから値の型を取得することができ、これは指定された列の型を返します。気をつけてください: sqliteは{{ 原語併記("型付け", "typed") }}データベースではありません。どんな型でも列に宣言した型に無頓着にどの欄に入れることもできます。もしあなたが異なる型を要求したならば、sqliteはそれらの変換に最善を尽くし、そしてそれが不可能な場合はデフォルトの値を処理するでしょう。それゆえに、型エラーを得ることは不可能ですが、奇妙な値の出力を得るかもしれません。

C++のコードはAsInt32AsDoubleなどを使うこともできます。 functions which return the value as a more convenient C++ return value. Watch out, though, because you won't get any errors if your index is invalid. Other errors are impossible, because sqlite will always convert types, even if they don't make sense.

C++ example:

PRBool hasMoreData;
while (NS_SUCCEEDED(statement->ExecuteStep(&hasMoreData)) && hasMoreData) {
  PRInt32 value = statement->AsInt32(0);
  // use the value...
}

Javascript example:

while (statement.executeStep()) {
  var value = statement.getInt32(0);
  // use the value...
}

mozIStorageStatement.execute() is a convenience function for when you are getting no data out of the statement. It steps the statement once and resets it. This can be useful for insert statements because it really cleans up the code:

var statement = mDBConn.createStatement("INSERT INTO my_table VALUES (?1)");
statement.bindInt32Parameter(52);
statement.execute();

SQLのSELECT文をデータベースに対して実行する方法の簡潔で、しかも完全な例を見るには、A Storage Example in XUL using SQL Select and an XUL Gridをご覧ください。

{{ 英語版章題("Resetting a statement") }}

命令をリセットする

It is important to reset statements that are no longer being used. Un-reset write statements will keep a lock on the tables and will prevent other statements from accessing it. Un-reset read statements will prevent writes.

When the statement object is freed, its corresponding database statement is closed. If you are using C++ and you know that all references will be destroyed, you don't have to explicitly reset the statement. Also, if you use mozIStorageStatement.execute(), you don't need to explicitly reset the statement; this function will reset it for you. Otherwise, call mozIStorageStatement.reset().

JavaScript callers should ensure that statements are reset. Be particularly careful about exceptions. You will want to make sure to reset your statements even if an exception is fired, or subsequent access to the database may not be possible. Resetting a statement is relatively lightweight, and nothing bad happens if it's already reset, so don't worry about unnecessary resets.

var statement = connection.createStatement(...);
try {
  // use the statement...
} finally {
  statement.reset();
}

C++ callers must do the same. There is a scoped object in {{ Source("storage/public/mozStorageHelper.h") }} called mozStorageStatementScoper which will ensure that a given statement is reset when the enclosing scope is exited. It is hightly recommended that you use this object if possible.

void someClass::someFunction()
{
  mozStorageStatementScoper scoper(mStatement)
  // use the statement
}

{{ 英語版章題("Transactions") }}

トランザクション

mozIStorageConnection has functions for beginning and ending transactions. If you do not explicitly use transactions, an implicit transaction will be created for you for each statement. This has major performance implications. There is overhead for each transaction, especially for commits. You will therefore see a large performance win when you are doing multiple statements in a row if you put them in a transaction. See Storage:Performance for more performance information.

The major difference between other database systems is that sqlite does not support nested transactions. This means that once a transaction is open, you can not open another transaction. You can check mozIStorageConnection.transactionInProgress to see if a transaction is currently in progress.

You can also just execute "BEGIN TRANSACTION" and "END TRANSACTION" directly as SQL statements (this is what the connection does when you call the functions). However, use of mozIStorageConnection.beginTransaction and related functions are strongly recommended because it stores transaction state in the connection. Otherwise, the attribute transactionInProgress will have the wrong value.

sqlite has several types of transactions:

  • mozIStorageConnection.TRANSACTION_DEFERRED: The default. The database lock is acquired when needed (usually the first time you execute a statement in the transaction).
  • mozIStorageConnection.TRANSACTION_IMMEDIATE: Get a read lock on the database immediately.
  • mozIStorageConnection.TRANSACTION_EXCLUSIVE: Get a write lock on the database immediately.

You can pass this type of transaction to mozIStorageConnection.beginTransactionAs to determine what kind of transaction you need. Keep in mind that if another transaction has already started, this operation will not succeed. Generally, the default TRANSACTION_DEFERRED type is sufficient and you shouldn't use the other types unless you really know why you need them. For more information, see the sqlite documentation about BEGIN TRANSACTION and locking.

var ourTransaction = false;
if (mDBConn.transactionInProgress) {
  ourTransaction = true;
  mDBConn.beginTransactionAs(mDBConn.TRANSACTION_DEFERRED);
}

// ... use the connection ...

if (ourTransaction)
  mDBConn.commitTransaction();

From C++ code, you can use the mozStorageTransaction helper class defined in {{ Source("storage/public/mozStorageHelper.h") }}. This class will begin a transaction of the specified type on the specified connection when it comes into scope, and will either commit or rollback the transaction when it goes out of scope. If a transaction is already in progress, the transaction helper class will not do anything.

It also has functions for explicitly committing. The typical use is that you create the class defaulting to rollback, and then explicitly commit the transaction when processing has succeeded:

nsresult someFunction()
{
  // deferred transaction (the default) with rollback on failure
  mozStorageTransaction transaction(mDBConn, PR_FALSE);

  // ... use the connection ...

  // everything succeeded, now explicitly commit
  return transaction.Commit();
}

{{ 英語版章題("How to corrupt your database") }}

あなたのデータベースを破壊する方法

  • Open more than one connection to the same file with names that aren't exactly the same as determined by strcmp. This includes "my.db" and "../dir/my.db" or, on Windows (case-insensitive) "my.db" and "My.db". Sqlite tries to handle many of these cases, but you shouldn't count on it.
  • Access a database from a symbolic or hard link.
  • Open connections to the same database from more than one thread (see "Thread safety" below).
  • Access a connection or statement from more than one thread (see "Thread safety" below).
  • Open the database from an external program while it is open in Mozilla. Our caching breaks the normal file-locking in sqlite that allows this to be done safely.

{{ 英語版章題("Thread safety") }}

スレッドセーフティ

The mozStorage service and sqlite are threadsafe. However, no other mozStorage or sqlite objects or operations are threadsafe.

  • The storage service must be created on the main thread. If you want to access the service from another thread, you should be sure that you call getService from the main thread ahead of time.
  • You can not access a connection or statement from multiple threads. These storage objects are not threadsafe, and the sqlite representations of them are not threadsafe either. Even if you do locking and ensure that only one thread is doing something at once, there may be problems. This case hasn't been tested, and there may be some internal per-thread state in sqlite. It is strongly advised that you don't do this.
  • You can not access a single database from multiple connections from different threads. Normally, sqlite allows this. However, we do sqlite3_enable_shared_cache(1); (see sqlite shared-cache mode) which makes multiple connections share the same cache. This is important for performance. However, there is no lock for cache access, meaning it will break if you use if from more than one thread.

{{ 英語版章題("SQLite Locking") }}

SQLiteのロック

SQLite locks the entire database; that is, any active readers will cause an attempt to write to return SQLITE_BUSY, and an active writer will cause any attempt to read to return SQLITE_BUSY. A statement is considered active from the first step() until reset() is called. execute() calls step() and reset() in one go. A common problem is forgetting to reset() a statement after you've finished step()'ing through.

While a given SQLite connection is capable of having multiple statements open, its locking model limits what these statements can do concurrently (reading or writing). It is in fact possible for multiple statements to be actively reading at one time. It is not possible, however, for multiple statements to be reading and writing at one time on the same table -- even if they are derived from the same connection.

SQLite has a two-tiered locking model: connection level and table level. Most people are familiar with the connection (database) level locking: multiple readers but only one writer. The table-level (B-Tree) locks are what can sometimes be confusing. (Internally, each table in the database has its own B-Tree, so "table" and "B-Tree" are technically synonymous).

{{ 英語版章題("Table-level locks") }}

テーブルレベルのロック

You would think that if you have only one connection, and it locks the database for writing, you could use multiple statements to do whatever you want. Not entirely. You must be aware of table-level (B-Tree) locks, which are maintined by statement handles traversing the database (i.e. open SELECT statements).

The general rule is this: a statement handle may not modify a table (B-Tree) which other statement handles are reading (have open cursors on) -- even if that statement handle shares the same connection (transaction context, database lock, etc.) with the other statement handles. Attempts to do so will still block (or return SQLITE_BUSY).

This problem often crops up when you attempt to iterate over a table with one statement and modify records within it using another statement. This will not work (or carries a high probability of not working, depending on the optimizer's involvement (see below)). The modifying statement will block because the reading statement has an open cursor on the table.

{{ 英語版章題("Working around locking problems") }}

ロックの問題への対処法

The solution is to follow (1) as described above. Theoretically, (2) actually shouldn't work with SQLite 3.x. In this scenario, database locks come into play (with multiple connections) in addition to table locks. Connection 2 (modifying connection) will not be able to modify (write to) the database while the Connection 1 (reading connection) is reading it. Connection 2 will require an exclusive lock to execute a modifying SQL command, which it cannot get as long as Connection 1 has active statements reading the database (Connection 1 has a shared read lock during this time which prohibits any other connections from getting an exclusive lock).

Another option is to use a temporary table. Create a temporary table containing the results of the table of interest, iterate over it (putting the reading statement's table lock on the temp table) and then the modifing statement can make changes to the real table without any problem. This can be done with statements derived from a single connection (transaction context). This scenario sometimes happens behind the scenes anyway as ORDER BY can produce temporary tables internally. However, it is not safe to assume that the optimizer will do this in all cases. Explicitly creating a temporary table is only safe way to do perform this latter option.

{{ languages( { "en": "en/Storage", "es": "es/Almacenamiento", "fr": "fr/Storage", "pl": "pl/Storage" } ) }}

このリビジョンのソースコード

<p>
</p><p>{{ 翻訳中() }}
</p><p>Storage は <a href="ja/Firefox_2">Firefox 2</a> の データベース API で、 <a class="external" href="http://www.sqlite.org/">sqlite</a> を基礎としています。これは <a href="ja/Chrome">chrome</a> のコードや <a href="ja/Extensions">拡張機能</a> などの信頼される呼び出し元から利用でき、web ページからは<i>利用できません</i>。このAPIは現在の<i>未確定</i>であり、APIはいまだに変更される余地があるという事です。API は Firefox 2 alpha 2 から Firefox 2 リリース版の間に多少変わるでしょうし、またFirefox 2 から Firefox 3 の間でも多少変わるでしょう。
</p><p>Storage は Web ページの永続的なデータの保持に使われる Firefox 2 の<a class="external" href="http://www.whatwg.org/specs/web-apps/current-work/#scs-client-side">WHATWG DOM storage</a> 機能と混乱されてしまうでしょう。 Storage API は拡張機能の作者と Firefox のコンポーネントのためだけの物です。
</p><p>このドキュメントは mozStorage API と sqlite からいくらか変わっている点について述べます。SQL についてと <i>一般的な</i> sqlite については<i>述べません</i>。これらやあなたのお気に入りの SQL リファレンスをお使いください。もしかすると <a class="external" href="http://www.sqlite.org/docs.html">sqlite documentation</a> や特に <a class="external" href="http://www.sqlite.org/lang.html">query language understood by sqlite</a> を見ておく必要があるかもしれません。mozStorage API について助けが必要であれば、 news.mozilla.org ニュースサーバの mozilla.dev.apps.firefox に投稿する事が可能です。バグをレポートするなら、 <a class="link-https" href="https://bugzilla.mozilla.org/enter_bug.cgi?product=Toolkit&amp;component=Storage">Bugzilla</a> (product "Toolkit", component "Storage") を使ってください。
</p><p>データベース接続のパフォーマンスを良くするためには <a href="ja/Storage/Performance">Storage:Performance</a> をご覧ください。
</p><p><a class="external" href="http://sqlitebrowser.sourceforge.net/">SQLite Database Browser</a> は多くのプラットフォームで利用可能な役にたつフリーのツールです。これは既存のデータベースを試したり、SQL の命令をテストするのに役に立つでしょう。
</p><p>{{ 英語版章題("Getting started") }}
</p>
<h4 id=".E3.81.AF.E3.81.98.E3.82.81.E3.81.AB" name=".E3.81.AF.E3.81.98.E3.82.81.E3.81.AB"> はじめに </h4>
<p>mozStorageは他の多くのデータベースシステムと同じように設計されています。利用のための手順の概要は次のようになります:
</p>
<ul><li> あなたの選択でデータベースとの接続を開きます。
</li><li> 接続上で実行する命令を作ります。
</li><li> 必要に応じてパラメータを命令に束ねます。
</li><li> 命令を実行します。
</li><li> 命令をリセットします。
</li></ul>
<p>{{ 英語版章題("Opening a connection") }}
</p>
<h4 id=".E6.8E.A5.E7.B6.9A.E3.82.92.E9.96.8B.E3.81.8F" name=".E6.8E.A5.E7.B6.9A.E3.82.92.E9.96.8B.E3.81.8F"> 接続を開く </h4>
<p>Storageサービスの最初の初期化はメインスレッドから行わなければなりません。もしもあなたが別のスレッドから一回目の初期化を行ったならば、エラーを受けとるでしょう。したがって、もしもあなたがスレッドからサービスを使いたいならば、サービスが作られていることを確実にするためにgetServiceをメインスレッドから呼び出すように気をつけて下さい。
</p><p>C++でユーザプロファイルディレクトリにある"asdf.sqlite"と接続を開く例は次のようになります:
</p>
<pre>nsCOMPtr&lt;nsIFile&gt; dbFile;
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
                            getter_AddRefs(dbFile));
NS_ENSURE_SUCCESS(rv, rv);
rv = dbFile-&gt;Append(NS_LITERAL_STRING("asdf.sqlite"));
NS_ENSURE_SUCCESS(rv, rv);

mDBService = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &amp;rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBService-&gt;OpenDatabase(dbFile, getter_AddRefs(mDBConn));
NS_ENSURE_SUCCESS(rv, rv);
</pre>
<p><code>MOZ_STORAGE_SERVICE_CONTRACTID</code>は{{ Source("storage/build/mozStorageCID.h") }}で定義されています。その値は<code>"@mozilla.org/storage/service;1"</code>です。
</p><p>JavaScriptでの例は次のようになります:
</p>
<pre>var file = Components.classes["@mozilla.org/file/directory_service;1"]
                     .getService(Components.interfaces.nsIProperties)
                     .get("ProfD", Components.interfaces.nsIFile);
file.append("asdf.sqlite");

var storageService = Components.classes["@mozilla.org/storage/service;1"]
                        .getService(Components.interfaces.mozIStorageService);
var mDBConn = storageService.openDatabase(file);
</pre>
<dl><dd><div class="note">メモ: OpenDatabase関数は変更の必要があります。おそらくこの関数はトラブルにもっと陥りにくくするように強化され、また単純化されるでしょう。</div>
</dd></dl>
<p>ひょっとしたらあなたのデータベースの名前の最後を<b>s</b>qlite <b>d</b>ata<b>b</b>aseを由来にして".sdb"にしたくなるかもしれませんが、それは<i>推奨できません</i>。この拡張子はWindowsでは"Application Compatibility Database"の拡張子として特別に扱われ、そして変更は『システムの復元』機能の一部として自動的にシステムからバックアップされます。これはファイル管理により多くのオーバーヘッドをもたらすことになります。
</p><p>{{ 英語版章題("Creating a statement") }}
</p>
<h4 id=".E5.91.BD.E4.BB.A4.E3.82.92.E4.BD.9C.E3.82.8B" name=".E5.91.BD.E4.BB.A4.E3.82.92.E4.BD.9C.E3.82.8B"> 命令を作る </h4>
<p>命令を作る方法は二つあります。もしもパラメータがなく、命令が何のデータも返さないのならば、<code>mozIStorageConnection.executeSimpleSQL</code>を使います。
</p>
<pre>C++:
rv = mDBConn-&gt;ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE TABLE foo (a INTEGER)"));

JS:
mDBConn.executeSimpleSQL("CREATE TABLE foo (a INTEGER)");
</pre>
<p>そうでなければ、あなたは<code>mozIStorageConnection.createStatement</code>を使って命令を準備すべきです。
</p>
<pre>C++:
nsCOMPtr&lt;mozIStorageStatement&gt; statement;
rv = mDBConn-&gt;CreateStatement(NS_LITERAL_CSTRING("SELECT * FROM foo WHERE a = ?1"),
                              getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);

JS:
var statement = mDBConn.createStatement("SELECT * FROM foo WHERE a = ?1");
</pre>
<p>この例は{{ 原語併記("束ねられた", "bound") }}パラメータとして"?1"というプレースホルダを使います(次の章を見て下さい)。
</p><p>一度命令を準備したら、あなたはパラメータを束ねたり、実行したり、また作ったりを何度も何度もできます。もしもあなたが何度も命令を実行するならば、SQLクエリがその度にコンパイルを必要としないので、プリコンパイルされた命令を使うことははあなたに顕著なパフォーマンスの進歩を与えるでしょう。
</p><p>もしもあなたがsqliteに詳しいならば、準備された命令はデータベースのスキーマを変更した時に無効化されることを知っているかもしれません。幸運にも、mozIStorageStatementはエラーを推測して必要に応じて命令を再コンパイルするでしょう。それゆえに、一度命令を作ったら、あなたはスキーマを変更した時のことを心配する必要はありません。すべての命令は透過的に作業を継続するでしょう。
</p><p>{{ 英語版章題("Binding paameters") }}
</p>
<h4 id=".E3.83.91.E3.83.A9.E3.83.A1.E3.83.BC.E3.82.BF.E3.82.92.E6.9D.9F.E3.81.AD.E3.82.8B" name=".E3.83.91.E3.83.A9.E3.83.A1.E3.83.BC.E3.82.BF.E3.82.92.E6.9D.9F.E3.81.AD.E3.82.8B"> パラメータを束ねる </h4>
<p>一般的に、すべてのパラメータを別々に束ねる方が、パラメータを含んだSQL文字列を慌ただしく組み立てるよりは幾分有益です。とりわけ、束ねられたパラメータは決してSQLとして実行されないために、この方法はSQLインジェクション攻撃を妨げます。
</p><p>あなたはプレースホルダを持つ命令にパラメータを束ねます。プレースホルダは"?1"から始まり、それから"?2"……といった索引で場所が指定されています。あなたはBindXXXParameter(0) BindXXXParameter(1)……といった関数をそれらのプレースホルダへパラメータを束ねるために使います。
</p>
<dl><dd><div class="note">気をつけましょう: プレースホルダの索引は1から数えます。束ねるための関数において変化する整数は0から数えます。これは"?1"が0のパラメータと対応、"?2"が1のパラメータと対応……となることを意味します。</div>
</dd></dl>
<p>プレースホルダはSQL文字列の中で複数回登場でき、そしてすべてのインスタンスは束ねられた値に置き換えられるでしょう。束ねられていないパラメータはNULLとして解釈されるでしょう。
</p><p><code>mozIStorageStatement</code>({{ Source("storage/public/mozIStorageStatement.idl") }}を見てください)で利用できる束ねるための関数には次のようなものがあります:
</p>
<ul><li> <code>bindUTF8StringParameter(in unsigned long aParamIndex, in AUTF8String aValue)</code>
</li><li> <code>bindStringParameter(in unsigned long aParamIndex, in AString aValue)</code>
</li><li> <code>bindDoubleParameter(in unsigned long aParamIndex, in double aValue)</code>
</li><li> <code>bindInt32Parameter(in unsigned long aParamIndex, in long aValue)</code>
</li><li> <code>bindInt64Parameter(in unsigned long aParamIndex, in long long aValue)</code>
</li><li> <code>bindNullParameter(in unsigned long aParamIndex)</code>
</li><li> <code>bindBlobParameter(in unsigned long aParamIndex, {{ mediawiki.external('array,const,size_is(aValueSize)') }} in octet aValue, in unsigned long ValueSize)</code> (バイナリデータ用)
</li></ul>
<p>C++での例:
</p>
<pre>nsCOMPtr&lt;mozIStorageStatement&gt; statement;
rv = mDBConn-&gt;CreateStatement(NS_LITERAL_CSTRING("SELECT * FROM foo WHERE a = ?1 AND b &gt; ?2"),
                              getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
rv = statement-&gt;BindUTF8StringParameter(0, "hello"); // "hello" will be substituted for "?1"
NS_ENSURE_SUCCESS(rv, rv);
rv = statement-&gt;BindInt32Parameter(1, 1234); // 1234 will be substituted for "?2"
NS_ENSURE_SUCCESS(rv, rv);
</pre>
<p>JavaScriptでの例:
</p>
<pre>var statement = mDBConn.createStatement("SELECT * FROM foo WHERE a = ?1 AND b &gt; ?2");
statement.bindUTF8StringParameter(0, "hello");
statement.bindInt32Parameter(1, 1234);
</pre>
<p>{{ 英語版章題("Executing a statement") }}
</p>
<h4 id=".E5.91.BD.E4.BB.A4.E3.82.92.E5.AE.9F.E8.A1.8C.E3.81.99.E3.82.8B" name=".E5.91.BD.E4.BB.A4.E3.82.92.E5.AE.9F.E8.A1.8C.E3.81.99.E3.82.8B"> 命令を実行する </h4>
<p>命令を実行する主な方法は<code>mozIStorageStatement.executeStep</code>を伴います。この関数はあなたの命令が生産するすべての結果の並びを列挙させ、そしてこれ以上の結果がない時にあなたに知らせるでしょう。
</p><p><code>executeStep</code>を呼んだ後は、mozIStorageValueArray({{ Source("storage/public/mozIStorageValueArray.idl") }}を見てください)にあるゲッタ関数を使えます。mozIStorageStatementはmozIStorageValueArrayを提供します。これらの関数があります:
</p>
<ul><li> <code>long getInt32(in unsigned long aIndex);</code>
</li><li> <code>long long getInt64(in unsigned long aIndex);</code>
</li><li> <code>double getDouble(in unsigned long aIndex);</code>
</li><li> <code>AUTF8String getUTF8String(in unsigned long aIndex);</code>
</li><li> <code>AString getString(in unsigned long aIndex);</code>
</li><li> <code>void getBlob(in unsigned long aIndex, out unsigned long aDataSize, {{ mediawiki.external('array,size_is(aDataSize)') }} out octet aData);</code>注意: もしもdataSizeが0ならば、データはNULLでしょう。
</li><li> <code>boolean getIsNull(in unsigned long aIndex);</code> もしもそのセルがNULL(空文字列とは違います)ならばtrueを返します。
</li></ul>
<p>あなたは<code>mozIStorageValueArray.getTypeOfIndex</code>から値の型を取得することができ、これは指定された列の型を返します。気をつけてください: sqliteは{{ 原語併記("型付け", "typed") }}データベースではありません。どんな型でも列に宣言した型に無頓着にどの欄に入れることもできます。もしあなたが異なる型を要求したならば、sqliteはそれらの変換に最善を尽くし、そしてそれが不可能な場合はデフォルトの値を処理するでしょう。それゆえに、型エラーを得ることは不可能ですが、奇妙な値の出力を得るかもしれません。
</p><p>C++のコードは<code>AsInt32</code>や<code>AsDouble</code>などを使うこともできます。 functions which return the value as a more convenient C++ return value. Watch out, though, because you won't get any errors if your index is invalid. Other errors are impossible, because sqlite will always convert types, even if they don't make sense.
</p><p>C++ example:
</p>
<pre>PRBool hasMoreData;
while (NS_SUCCEEDED(statement-&gt;ExecuteStep(&amp;hasMoreData)) &amp;&amp; hasMoreData) {
  PRInt32 value = statement-&gt;AsInt32(0);
  // use the value...
}
</pre>
<p>Javascript example:
</p>
<pre>while (statement.executeStep()) {
  var value = statement.getInt32(0);
  // use the value...
}
</pre>
<p><code>mozIStorageStatement.execute()</code> is a convenience function for when you are getting no data out of the statement. It steps the statement once and resets it. This can be useful for insert statements because it really cleans up the code:
</p>
<pre>var statement = mDBConn.createStatement("INSERT INTO my_table VALUES (?1)");
statement.bindInt32Parameter(52);
statement.execute();
</pre>
<p>SQLのSELECT文をデータベースに対して実行する方法の簡潔で、しかも完全な例を見るには、<a href="ja/A_Storage_Example_in_XUL_using_SQL_Select_and_an_XUL_Grid">A Storage Example in XUL using SQL Select and an XUL Grid</a>をご覧ください。
</p><p>{{ 英語版章題("Resetting a statement") }}
</p>
<h4 id=".E5.91.BD.E4.BB.A4.E3.82.92.E3.83.AA.E3.82.BB.E3.83.83.E3.83.88.E3.81.99.E3.82.8B" name=".E5.91.BD.E4.BB.A4.E3.82.92.E3.83.AA.E3.82.BB.E3.83.83.E3.83.88.E3.81.99.E3.82.8B"> 命令をリセットする </h4>
<p>It is important to reset statements that are no longer being used. Un-reset write statements will keep a lock on the tables and will prevent other statements from accessing it. Un-reset read statements will prevent writes.
</p><p>When the statement object is freed, its corresponding database statement is closed. If you are using C++ and you know that all references will be destroyed, you don't have to explicitly reset the statement. Also, if you use <code>mozIStorageStatement.execute()</code>, you don't need to explicitly reset the statement; this function will reset it for you. Otherwise, call <code>mozIStorageStatement.reset()</code>.
</p><p>JavaScript callers should ensure that statements are reset. Be particularly careful about exceptions. You will want to make sure to reset your statements even if an exception is fired, or subsequent access to the database may not be possible. Resetting a statement is relatively lightweight, and nothing bad happens if it's already reset, so don't worry about unnecessary resets.
</p>
<pre>var statement = connection.createStatement(...);
try {
  // use the statement...
} finally {
  statement.reset();
}
</pre>
<p>C++ callers must do the same. There is a scoped object in {{ Source("storage/public/mozStorageHelper.h") }} called mozStorageStatementScoper which will ensure that a given statement is reset when the enclosing scope is exited. It is hightly recommended that you use this object if possible.
</p>
<pre>void someClass::someFunction()
{
  mozStorageStatementScoper scoper(mStatement)
  // use the statement
}
</pre>
<p>{{ 英語版章題("Transactions") }}
</p>
<h4 id=".E3.83.88.E3.83.A9.E3.83.B3.E3.82.B6.E3.82.AF.E3.82.B7.E3.83.A7.E3.83.B3" name=".E3.83.88.E3.83.A9.E3.83.B3.E3.82.B6.E3.82.AF.E3.82.B7.E3.83.A7.E3.83.B3"> トランザクション </h4>
<p>mozIStorageConnection has functions for beginning and ending transactions. If you do not explicitly use transactions, an implicit transaction will be created for you for each statement. This has major performance implications. There is overhead for each transaction, especially for commits. You will therefore see a large performance win when you are doing multiple statements in a row if you put them in a transaction. See <a href="ja/Storage/Performance">Storage:Performance</a> for more performance information.
</p><p>The major difference between other database systems is that sqlite does not support nested transactions. This means that once a transaction is open, you can not open another transaction. You can check <code>mozIStorageConnection.transactionInProgress</code> to see if a transaction is currently in progress.
</p><p>You can also just execute "BEGIN TRANSACTION" and "END TRANSACTION" directly as SQL statements (this is what the connection does when you call the functions). However, use of <code>mozIStorageConnection.beginTransaction</code> and related functions are <i>strongly</i> recommended because it stores transaction state in the connection. Otherwise, the attribute <code>transactionInProgress</code> will have the wrong value.
</p><p>sqlite has several types of transactions:
</p>
<ul><li> mozIStorageConnection.TRANSACTION_DEFERRED: The default. The database lock is acquired when needed (usually the first time you execute a statement in the transaction).
</li></ul>
<ul><li> mozIStorageConnection.TRANSACTION_IMMEDIATE: Get a read lock on the database immediately.
</li></ul>
<ul><li> mozIStorageConnection.TRANSACTION_EXCLUSIVE: Get a write lock on the database immediately.
</li></ul>
<p>You can pass this type of transaction to <code>mozIStorageConnection.beginTransactionAs</code> to determine what kind of transaction you need. Keep in mind that if another transaction has already started, this operation will not succeed. Generally, the default TRANSACTION_DEFERRED type is sufficient and you shouldn't use the other types unless you really know why you need them. For more information, see the sqlite documentation about <a class="external" href="http://www.sqlite.org/lang_transaction.html">BEGIN TRANSACTION</a> and <a class="external" href="http://www.sqlite.org/lockingv3.html">locking</a>.
</p>
<pre>var ourTransaction = false;
if (mDBConn.transactionInProgress) {
  ourTransaction = true;
  mDBConn.beginTransactionAs(mDBConn.TRANSACTION_DEFERRED);
}

// ... use the connection ...

if (ourTransaction)
  mDBConn.commitTransaction();
</pre>
<p>From C++ code, you can use the mozStorageTransaction helper class defined in {{ Source("storage/public/mozStorageHelper.h") }}. This class will begin a transaction of the specified type on the specified connection when it comes into scope, and will either commit or rollback the transaction when it goes out of scope. If a transaction is already in progress, the transaction helper class will not do anything.
</p><p>It also has functions for explicitly committing. The typical use is that you create the class defaulting to rollback, and then explicitly commit the transaction when processing has succeeded:
</p>
<pre>nsresult someFunction()
{
  // deferred transaction (the default) with rollback on failure
  mozStorageTransaction transaction(mDBConn, PR_FALSE);

  // ... use the connection ...

  // everything succeeded, now explicitly commit
  return transaction.Commit();
}
</pre>
<p>{{ 英語版章題("How to corrupt your database") }}
</p>
<h4 id=".E3.81.82.E3.81.AA.E3.81.9F.E3.81.AE.E3.83.87.E3.83.BC.E3.82.BF.E3.83.99.E3.83.BC.E3.82.B9.E3.82.92.E7.A0.B4.E5.A3.8A.E3.81.99.E3.82.8B.E6.96.B9.E6.B3.95" name=".E3.81.82.E3.81.AA.E3.81.9F.E3.81.AE.E3.83.87.E3.83.BC.E3.82.BF.E3.83.99.E3.83.BC.E3.82.B9.E3.82.92.E7.A0.B4.E5.A3.8A.E3.81.99.E3.82.8B.E6.96.B9.E6.B3.95"> あなたのデータベースを破壊する方法 </h4>
<ul><li> Read this document: <a class="external" href="http://www.sqlite.org/lockingv3.html">File locking and concurrency in sqlite version 3</a>, especially the section on corruption.
</li></ul>
<ul><li> Open more than one connection to the same file with names that aren't exactly the same as determined by <code>strcmp</code>. This includes "my.db" and "../dir/my.db" or, on Windows (case-insensitive) "my.db" and "My.db". Sqlite tries to handle many of these cases, but you shouldn't count on it.
</li></ul>
<ul><li> Access a database from a symbolic or hard link.
</li></ul>
<ul><li> Open connections to the same database from more than one thread (see "Thread safety" below).
</li></ul>
<ul><li> Access a connection or statement from more than one thread (see "Thread safety" below).
</li></ul>
<ul><li> Open the database from an external program while it is open in Mozilla. Our caching breaks the normal file-locking in sqlite that allows this to be done safely.
</li></ul>
<p>{{ 英語版章題("Thread safety") }}
</p>
<h4 id=".E3.82.B9.E3.83.AC.E3.83.83.E3.83.89.E3.82.BB.E3.83.BC.E3.83.95.E3.83.86.E3.82.A3" name=".E3.82.B9.E3.83.AC.E3.83.83.E3.83.89.E3.82.BB.E3.83.BC.E3.83.95.E3.83.86.E3.82.A3"> スレッドセーフティ </h4>
<p>The mozStorage service and sqlite are threadsafe. However, no other mozStorage or sqlite objects or operations are threadsafe.
</p>
<ul><li> The storage service must be created on the main thread. If you want to access the service from another thread, you should be sure that you call getService from the main thread ahead of time.
</li></ul>
<ul><li> You can not access a connection or statement from multiple threads. These storage objects are not threadsafe, and the sqlite representations of them are not threadsafe either. Even if you do locking and ensure that only one thread is doing something at once, there may be problems. This case hasn't been tested, and there may be some internal per-thread state in sqlite. It is strongly advised that you don't do this.
</li></ul>
<ul><li> You can not access a single database from multiple connections from different threads. Normally, sqlite allows this. However, we do <code>sqlite3_enable_shared_cache(1);</code> (see <a class="external" href="http://www.sqlite.org/sharedcache.html">sqlite shared-cache mode</a>) which makes multiple connections share the same cache. This is important for performance. However, there is no lock for cache access, meaning it will break if you use if from more than one thread.
</li></ul>
<p>{{ 英語版章題("SQLite Locking") }}
</p>
<h4 id="SQLite.E3.81.AE.E3.83.AD.E3.83.83.E3.82.AF" name="SQLite.E3.81.AE.E3.83.AD.E3.83.83.E3.82.AF"> SQLiteのロック </h4>
<p>SQLite locks the entire database; that is, any active readers will cause an attempt to write to return SQLITE_BUSY, and an active writer will cause any attempt to read to return SQLITE_BUSY. A statement is considered active from the first step() until reset() is called. execute() calls step() and reset() in one go. A common problem is forgetting to reset() a statement after you've finished step()'ing through.
</p><p>While a given SQLite connection is capable of having multiple statements open, its locking model limits what these statements can do concurrently (reading or writing). It is in fact possible for multiple statements to be actively reading at one time. It is not possible, however, for multiple statements to be reading and writing at one time <i>on the same table</i> -- even if they are derived from the same connection.
</p><p>SQLite has a two-tiered locking model: connection level and table level. Most people are familiar with the connection (database) level locking: multiple readers but only one writer. The table-level (B-Tree) locks are what can sometimes be confusing. (Internally, each table in the database has its own B-Tree, so "table" and "B-Tree" are technically synonymous).
</p><p>{{ 英語版章題("Table-level locks") }}
</p>
<h5 id=".E3.83.86.E3.83.BC.E3.83.96.E3.83.AB.E3.83.AC.E3.83.99.E3.83.AB.E3.81.AE.E3.83.AD.E3.83.83.E3.82.AF" name=".E3.83.86.E3.83.BC.E3.83.96.E3.83.AB.E3.83.AC.E3.83.99.E3.83.AB.E3.81.AE.E3.83.AD.E3.83.83.E3.82.AF"> テーブルレベルのロック </h5>
<p>You would think that if you have only one connection, and it locks the database for writing, you could use multiple statements to do whatever you want. Not entirely. You must be aware of table-level (B-Tree) locks, which are maintined by statement handles traversing the database (i.e. open SELECT statements).
</p><p>The general rule is this: a statement handle may <b>not</b> modify a table (B-Tree) which other statement handles are reading (have open cursors on) -- even if that statement handle shares the same connection (transaction context, database lock, etc.) with the other statement handles. <i>Attempts to do so will still block (or return SQLITE_BUSY)</i>.
</p><p>This problem often crops up when you attempt to iterate over a table with one statement and modify records within it using another statement. This will not work (or carries a high probability of not working, depending on the optimizer's involvement (see below)). The modifying statement will block because the reading statement has an open cursor on the table.
</p><p>{{ 英語版章題("Working around locking problems") }}
</p>
<h5 id=".E3.83.AD.E3.83.83.E3.82.AF.E3.81.AE.E5.95.8F.E9.A1.8C.E3.81.B8.E3.81.AE.E5.AF.BE.E5.87.A6.E6.B3.95" name=".E3.83.AD.E3.83.83.E3.82.AF.E3.81.AE.E5.95.8F.E9.A1.8C.E3.81.B8.E3.81.AE.E5.AF.BE.E5.87.A6.E6.B3.95"> ロックの問題への対処法 </h5>
<p>The solution is to follow (1) as described above. Theoretically, (2) actually shouldn't work with SQLite 3.x. In this scenario, database locks come into play (with multiple connections) in addition to table locks. Connection 2 (modifying connection) will not be able to modify (write to) the database while the Connection 1 (reading connection) is reading it. Connection 2 will require an exclusive lock to execute a modifying SQL command, which it cannot get as long as Connection 1 has active statements reading the database (Connection 1 has a shared read lock during this time which prohibits any other connections from getting an exclusive lock).
</p><p>Another option is to use a temporary table. Create a temporary table containing the results of the table of interest, iterate over it (putting the reading statement's table lock on the temp table) and then the modifing statement can make changes to the real table without any problem. This can be done with statements derived from a single connection (transaction context). This scenario sometimes happens behind the scenes anyway as ORDER BY can produce temporary tables internally. However, it is not safe to assume that the optimizer will do this in all cases. Explicitly creating a temporary table is only safe way to do perform this latter option.
</p>{{ languages( { "en": "en/Storage", "es": "es/Almacenamiento", "fr": "fr/Storage", "pl": "pl/Storage" } ) }}
Revert to this revision