MDN may have intermittent access issues April 18 13:00 - April 19 01:00 UTC. See whistlepig.mozilla.org for all notifications.

mozilla

Revision 100067 of Storage

  • 리비전 슬러그: Storage
  • 리비전 제목: Storage
  • 리비전 아이디: 100067
  • 제작일시:
  • 만든이: Jeongkyu
  • 현재 리비전인가요? 아니오
  • 댓글 /* Last insert id */

리비전 내용

{{template.Fx_minversion_header(2)}}

저장소(Storage)SQLite 데이터베이스 API입니다. 이는 신뢰할 수 있는 호출자에게 제공되는데 여기에는 확장과 Firefox 콤포넌트만 해당합니다. 데이터베이스 연결 인터페이스의 모든 메소드와 속성에 대한 전체 레퍼런스는 mozIStorageConnection를 참고하십시오.

API는 현재 "확정되지 않은 상태(unfrozen)"로서 언제든지 바뀔 수 있습니다. Firefox 2와 Firefox 3 사이에 API가 바뀔 가능성이 높습니다.


참고: 저장소는 웹 페이지가 영속적인 데이터를 저장하는데 사용하는 DOM:Storage 기능이나 (확장이 사용하기 위한 XPCOM 저장소 유틸리티인) Session store API 기능과는 다릅니다.


시작하기

이 문서는 mozStorage API와 sqlite의 몇 가지 특성에 대해 다룹니다. SQL이나 "일반적인" sqlite에 대해서는 다루지 않습니다. 하지만, 참고 섹션에서 유용한 링크를 찾을 수 있습니다. mozStorage API에 대한 도움을 얻으려면 news.mozilla.org 뉴스 서버의 mozilla.dev.apps.firefox 그룹으로 질문을 올릴 수 있습니다. 버그를 신고하려면 Bugzilla (product "Toolkit", component "Storage")를 이용하십시오.

이제 시작하겠습니다. mozStorage는 많은 다른 데이터베이스 시스템과 유사하게 설계되었습니다. 사용을 위한 전반적인 절차는 다음과 같습니다.

  • 선택한 데이터베이스로 접속을 엽니다.
  • 접속에서 실행할 구문을 생성합니다.
  • 필요한 경우에 매개 변수를 구문에 대입합니다.
  • 구문을 실행합니다.
  • 오류를 확인합니다.
  • 구문을 초기화합니다.

접속 열기

C++ 사용자: 저장소 서비스의 첫 초기화는 주 스레드에서 해야 합니다. 다른 스레드에서 처음으로 저장소를 초기화하면 오류가 발생합니다. 그러므로, 스레드에서 서비스를 사용하려면 주 스레드에서 getService를 호출하여 서비스가 생성된 것을 확실하게 합니다.

사용자 프로파일 디렉토리의 "asdf.sqlite"로 접속을 여는 C++ 예제:

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 는 {{template.Source("storage/build/mozStorageCID.h")}}에서 정의하고 있습니다. 그 값은 "@mozilla.org/storage/service;1"입니다.

자바스크립트 예제:

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

var storageService = Components.classes["@mozilla.org/storage/service;1"]
                        .getService(Components.interfaces.mozIStorageService);
var mDBConn = storageService.openDatabase(file);
참고: OpenDatabase 함수는 바뀔 수 있습니다. 문제가 발생하지 않도록 개선되고 단순하게 될 가능성이 높습니다.

데이터베이스의 이름을 sqlite database를 의미하는 ".sdb"로 끝나게 하고 싶을 수도 있지만 이는 바람직하지 않습니다. 윈도우는 이 확장자를 "애플리케이션 호환성 데이터베이스(Application Compatibility Database)"를 위한 알려진 확장자로 특별하게 간주하여 시스템 복구 기능의 일부로서 변경 사항을 자동으로 백업하게 됩니다. 이는 부하가 매우 큰 파일 작업을 야기합니다.

구문

아래 단계에 따라 SQLite 데이터베이스에 구문을 생성하고 실행할 수 있습니다. 전체 레퍼런스는 mozIStorageStatement를 참고하십시오.

구문 생성

구문을 생성하는 데에는 두 가지 방법이 있습니다. 매개 변수가 없고 구문이 어떠한 데이터도 반환하지 않는 경우에는 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");

이 예제에서는 나중에 대입할 매개 변수에 대하여 "?1"라는 플레이스홀더를 사용하고 있습니다(다음 섹션을 참고하십시오).

구문의 준비를 마치면 매개 변수를 대입하여 실행하고 초기화하는 것을 계속 반복할 수 있습니다. 구문을 여러 번 실행하는 경우, 미리 컴파일한 구문을 사용하면 SQL 질의를 매번 분석할 필요가 없으므로 현저한 성능의 향상을 얻을 수 있습니다.

여러분이 sqlite에 대해 잘 알고 있다면, 데이터베이스의 스키마가 바뀌는 경우 준비된 구문이 무효가 된다는 것을 알고있을 지도 모릅니다. 다행히 mozIStorageStatement는 이 오류를 감지하여 필요하다면 구문을 다시 컴파일합니다. 그러므로 구문을 생성하고 나면 스키마를 변경할 때 걱정할 필요가 없습니다. 모든 구문은 투명하게 계속 작동하게 됩니다.

매개 변수 대입

일반적으로 실행 중에 매개 변수를 포함한 SQL 문자열을 생성하는 것보다 모든 매개 변수를 별도로 대입하는 것이 가장 좋은 방법입니다. 다른 무엇보다도 이는 SQL 주입 공격을 방지할 수 있는데, 대입한 매개 변수는 절대 SQL로 실행되지 않기 때문입니다.

여러분은 플레이스홀더를 포함한 구문에 매개 변수를 대입합니다. 플레이스홀더는 색인으로 참조하는데, "?1"로 시작하고 그 다음 "?2"...와 같습니다. 플레이스홀더를 대입하려면 구문 함수 BindXXXParameter(0) BindXXXParameter(1)... 를 사용합니다.

주의: 플레이스홀더의 색인은 1부터 시작합니다. 대입 함수로 전달하는 정수는 0부터 시작합니다. 이는 "?1"가 매개 변수 0에 대응하고 "?2"가 매개 변수 1에 대응한다는 것을 의미합니다.

"?xx" 대신 ":example"와 같은 이름있는 매개 변수를 사용할 수도 있습니다.

플레이스홀더는 하나의 SQL 구문에 여러 번 나타날 수 있으며 모든 인스턴스는 대입한 값으로 대체합니다. 대입하지 않은 매개 변수는 NULL로 해석합니다.

아래 예제는 bindUTF8StringParameter()bindInt32Parameter()만 사용하고 있습니다. 모든 대입 함수 목록은 mozIStorageStatement를 참고하시기 바랍니다.

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);

자바스크립트 예제:

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

이름있는 매개 변수를 사용하는 경우에는 getParameterIndex 메소드를 사용하여 이름있는 매개 변수의 색인을 얻어야 합니다. 자바스크립트 예제는 다음과 같습니다.

var statement = mDBConn.createStatement("SELECT * FROM foo WHERE a = :myfirstparam AND b > :mysecondparam");

var firstidx = statement.getParameterIndex(":myfirstparam");
statement.bindUTF8StringParameter(firstidx, "hello");

var secondidx = statement.getParameterIndex(":mysecondparam");
statement.bindInt32Parameter(secondidx, 1234);

같은 질의에 이름있는 매개 변수와 색인된 매개 변수를 혼합할 수도 있습니다.

var statement = mDBConn.createStatement("SELECT * FROM foo WHERE a = ?1 AND b > :mysecondparam");

statement.bindUTF8StringParameter(0, "hello");
// you can also use
// var firstidx = statement.getParameterIndex("?1");
// statement.bindUTF8StringParameter(firstidx, "hello");

var secondidx = statement.getParameterIndex(":mysecondparam");
statement.bindInt32Parameter(secondidx, 1234);

IN ( value-list ) 표현식과 함께 WHERE 절을 사용하는 경우에 대입은 동작하지 않게 됩니다. 대신 문자열을 생성하시기 바랍니다. 사용자 입력을 처리하는 경우가 아니라면 보안 문제는 없습니다.

var ids = "3,21,72,89";
var sql = "DELETE FROM table WHERE id IN ( "+ ids +" )";

구문 실행

구문을 실행하는 주 방법은 mozIStorageStatement.executeStep입니다. 이 함수는 구문이 생성하는 모든 결과 행을 나열할 수 있도록 해주고 더 이상 결과가 없을 때를 알려줍니다.

executeStep를 호출할 후에 mozIStorageValueArray에서 적절한 getter 함수를 사용하여 결과 행의 값을 얻을 수 있습니다(mozIStorageStatement는 mozIStorageValueArray를 구현합니다). 아래의 예제는 getInt32()만 사용하고 있습니다.

값의 형식은 지정한 열의 형식을 반환하는 mozIStorageValueArray.getTypeOfIndex로 구할 수 있습니다. 그러나, 주의하십시오. sqlite는 형식있는 데이터베이스가 아닙니다. 열에 선언한 형식과 무관하게 모든 셀에 아무 형식이나 입력할 수 있습니다. 다른 형식을 요청하면 sqlite는 최선을 다하여 그것을 변환하고 변환이 불가능하면 기본 값으로 처리합니다. 그러므로 형식 오류를 얻을 수 없으며 이상한 데이터 출력을 얻을 수도 있습니다.

C++ 코드는 AsInt32, AsDouble과 같은 함수를 이용할 수도 있는데, 이는 더 편리한 C++ 반환 값으로 값을 반환합니다. 하지만, 색인이 잘못된 경우에도 오류가 발생하지 않으므로 주의하십시오. 다른 오류가 발생하는 것도 불가능한데, sqlite는 사리에 맞지 않는 경우에도 항상 형식을 변환하기 때문입니다.

C++ 예제:

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

자바스크립트 예제:

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

mozIStorageStatement.execute()는 구문에서 얻을 데이터가 없는 경우에 편리한 함수입니다. 이는 구문을 한 번 실행하고 초기화합니다. 이는 삽입 구문에 대해 유용한데 코드를 매우 간단하게 하기 때문입니다.

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

파일 Image:TTRW2.zip은 데이터베이스에 대하여 SQL SELECT를 실행하는 간단하지만 완전한 자바스크립트입니다.

구문 초기화

더 이상 사용하지 않는 구문을 초기화하는 것은 중요합니다. 초기화되지 않은 쓰기 구문은 테이블에 잠금을 유지하여 다른 구문이 테이블을 접근하는 것을 막게 됩니다. 초기화되지 않은 읽기 구문은 쓰기를 막게 됩니다.

구문 개체가 해제되면 해당 데이터베이스 구문은 닫힙니다. C++을 사용 중인 경우, 모든 참조가 소멸된다는 것을 알고 있다면 따로 구문을 초기화할 필요가 없습니다. 또한 mozIStorageStatement.execute()를 사용하는 경우에도 따로 구문을 초기화할 필요가 없습니다. 이 함수는 여러분을 대신하여 구문을 초기화합니다. 나머지 경우에는 mozIStorageStatement.reset()를 호출하십시오.

자바스크립트 호출자는 확실하게 구문을 초기화해야 합니다. 특히 예외에 대해서 주의하십시오. 예외가 발생하거나 데이터베이스에 접근하는 것이 불가능해진 경우에도 구문을 초기화하는 것을 확실하게 해야 합니다. 구문 초기화는 비교적 가벼운 작업이고 이미 초기화된 경우에도 아무런 문제가 발생하지 않기 때문에 불필요한 초기화에 대해서 걱정할 필요는 없습니다.

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

C++ 호출자도 같은 일을 해야 합니다. {{template.Source("storage/public/mozStorageHelper.h")}}에는 mozStorageStatementScoper라고 불리우는 유효 영역이 있는 개체가 있는데, 이 개체는 둘러싼 영역을 빠져 나갈 때 주어진 구문이 초기화되는 것을 보장합니다. 가능하면 이 개체를 사용하는 것이 바람직합니다.

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

최종 insert 아이디

연결의 lastInsertRowID 속성을 이용하면 데이터베이스의 마지막 INSERT 작업에서 할당한 아이디(rowid)를 구할 수 있습니다.
이는 여러분이 테이블에 INTEGER PRIMARY KEYINTEGER PRIMARY KEY AUTOINCREMENT로 지정된 열을 가지고 있을 때 유용한데, 이 경우 SQLite는 여러분이 값을 제공하지 않으면 삽입하는 각 행에 대하여 자동으로 값을 할당합니다. 반환 값은 자바스크립트에서는 number 형식이고 C++에서는 long long입니다.

lastInsertRowID를 이용하는 자바스크립트 예제는 다음과 같습니다.

var sql = "INSERT INTO contacts_table (number_col, name_col) VALUES (?1, ?2)"
var statement = mDBConn.createStatement(sql);
    statement.bindUTF8StringParameter(0, number);
    statement.bindUTF8StringParameter(1, name);
    statement.execute();
    statement.reset();
    
var rowid = mDBConn.lastInsertRowID;

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 {{template.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.

SQLite Locking

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.

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 thread safe 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.

It's worth noting, however, that authors of JavaScript browser extensions are less impacted by these restrictions than it might first appear. If a database is created and used exclusively from within JavaScript, thread safety usually will not be an issue. SpiderMonkey (the JavaScript engine run within Firefox) executes JavaScript from a single persistent thread, except when the JavaScript runs in a different thread or is executed from a callback made on a different thread (e.g. via some networking or stream interfaces). Barring incorrect use of multi-threaded JavaScript, problems should occur only if a database already in use by a non-JavaScript, system-level thread is accessed through mozStorage.

See also

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

리비전 소스

<p>
{{template.Fx_minversion_header(2)}}
</p><p><b>저장소(Storage)</b>는 <a class="external" href="http://www.sqlite.org/">SQLite</a> 데이터베이스 API입니다. 이는 신뢰할 수 있는 호출자에게 제공되는데 여기에는 확장과 Firefox 콤포넌트만 해당합니다. 데이터베이스 연결 인터페이스의 모든 메소드와 속성에 대한 전체 레퍼런스는 <a href="ko/MozIStorageConnection">mozIStorageConnection</a>를 참고하십시오.
</p><p>API는 현재 "확정되지 않은 상태(unfrozen)"로서 언제든지 바뀔 수 있습니다. Firefox 2와 Firefox 3 사이에 API가 바뀔 가능성이 높습니다.
</p><p><br>
</p>
<div class="note"><b>참고:</b> 저장소는 웹 페이지가 영속적인 데이터를 저장하는데 사용하는 <a href="ko/DOM/Storage">DOM:Storage</a> 기능이나 (확장이 사용하기 위한 <a href="ko/XPCOM">XPCOM</a> 저장소 유틸리티인) <a href="ko/Session_store_API">Session store API</a> 기능과는 다릅니다.</div>
<p><br>
</p>
<h2 name=".EC.8B.9C.EC.9E.91.ED.95.98.EA.B8.B0"> 시작하기 </h2>
<p>이 문서는 mozStorage API와 sqlite의 몇 가지 특성에 대해 다룹니다. SQL이나 "일반적인" sqlite에 대해서는 다루지 <i>않습니다</i>. 하지만, <a href="#See_also"> 참고 섹션</a>에서 유용한 링크를 찾을 수 있습니다. mozStorage API에 대한 도움을 얻으려면 news.mozilla.org 뉴스 서버의 mozilla.dev.apps.firefox 그룹으로 질문을 올릴 수 있습니다. 버그를 신고하려면 <a class="external" href="https://bugzilla.mozilla.org/enter_bug.cgi?product=Toolkit&amp;component=Storage">Bugzilla</a> (product "Toolkit", component "Storage")를 이용하십시오.
</p><p>이제 시작하겠습니다. mozStorage는 많은 다른 데이터베이스 시스템과 유사하게 설계되었습니다. 사용을 위한 전반적인 절차는 다음과 같습니다.
</p>
<ul><li> 선택한 데이터베이스로 접속을 엽니다.
</li><li> 접속에서 실행할 구문을 생성합니다.
</li><li> 필요한 경우에 매개 변수를 구문에 대입합니다.
</li><li> 구문을 실행합니다.
</li><li> 오류를 확인합니다.
</li><li> 구문을 초기화합니다.
</li></ul>
<h2 name=".EC.A0.91.EC.86.8D_.EC.97.B4.EA.B8.B0"> 접속 열기 </h2>
<p>C++ 사용자: 저장소 서비스의 첫 초기화는 주 스레드에서 해야 합니다. 다른 스레드에서 처음으로 저장소를 초기화하면 오류가 발생합니다. 그러므로, 스레드에서 서비스를 사용하려면 주 스레드에서 getService를 호출하여 서비스가 생성된 것을 확실하게 합니다.
</p><p>사용자 프로파일 디렉토리의 "asdf.sqlite"로 접속을 여는 C++ 예제:
</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> 는 {{template.Source("storage/build/mozStorageCID.h")}}에서 정의하고 있습니다. 그 값은 <code>"@mozilla.org/storage/service;1"</code>입니다.
</p><p>자바스크립트 예제:
</p>
<pre>var file = Components.classes["@mozilla.org/file/directory_service;1"]
                     .getService(Components.interfaces.nsIProperties)
                     .get("ProfD", Components.interfaces.nsIFile);
file.append("my_db_file_name.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>. 윈도우는 이 확장자를 "애플리케이션 호환성 데이터베이스(Application Compatibility Database)"를 위한 알려진 확장자로 특별하게 간주하여 시스템 복구 기능의 일부로서 변경 사항을 자동으로 백업하게 됩니다. 이는 부하가 매우 큰 파일 작업을 야기합니다.
</p>
<h2 name=".EA.B5.AC.EB.AC.B8"> 구문 </h2>
<p>아래 단계에 따라 SQLite 데이터베이스에 구문을 생성하고 실행할 수 있습니다. 전체 레퍼런스는 <a href="ko/MozIStorageStatement">mozIStorageStatement</a>를 참고하십시오.
</p>
<h3 name=".EA.B5.AC.EB.AC.B8_.EC.83.9D.EC.84.B1"> 구문 생성 </h3>
<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>이 예제에서는 나중에 대입할 매개 변수에 대하여 "?1"라는 플레이스홀더를 사용하고 있습니다(다음 섹션을 참고하십시오).
</p><p>구문의 준비를 마치면 매개 변수를 대입하여 실행하고 초기화하는 것을 계속 반복할 수 있습니다. 구문을 여러 번 실행하는 경우, 미리 컴파일한 구문을 사용하면 SQL 질의를 매번 분석할 필요가 없으므로 현저한 성능의 향상을 얻을 수 있습니다.
</p><p>여러분이 sqlite에 대해 잘 알고 있다면, 데이터베이스의 스키마가 바뀌는 경우 준비된 구문이 무효가 된다는 것을 알고있을 지도 모릅니다. 다행히 mozIStorageStatement는 이 오류를 감지하여 필요하다면 구문을 다시 컴파일합니다. 그러므로 구문을 생성하고 나면 스키마를 변경할 때 걱정할 필요가 없습니다. 모든 구문은 투명하게 계속 작동하게 됩니다.
</p>
<h3 name=".EB.A7.A4.EA.B0.9C_.EB.B3.80.EC.88.98_.EB.8C.80.EC.9E.85"> 매개 변수 대입 </h3>
<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>"?xx" 대신 ":example"와 같은 이름있는 매개 변수를 사용할 수도 있습니다.
</p><p>플레이스홀더는 하나의 SQL 구문에 여러 번 나타날 수 있으며 모든 인스턴스는 대입한 값으로 대체합니다. 대입하지 않은 매개 변수는 NULL로 해석합니다.
</p><p>아래 예제는 <code>bindUTF8StringParameter()</code>와 <code>bindInt32Parameter()</code>만 사용하고 있습니다. 모든 대입 함수 목록은 <a href="ko/MozIStorageStatement#Binding_functions">mozIStorageStatement</a>를 참고하시기 바랍니다.
</p><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>자바스크립트 예제:
</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>이름있는 매개 변수를 사용하는 경우에는 <code>getParameterIndex</code> 메소드를 사용하여 이름있는 매개 변수의 색인을 얻어야 합니다. 자바스크립트 예제는 다음과 같습니다.
</p>
<pre>var statement = mDBConn.createStatement("SELECT * FROM foo WHERE a = :myfirstparam AND b &gt; :mysecondparam");

var firstidx = statement.getParameterIndex(":myfirstparam");
statement.bindUTF8StringParameter(firstidx, "hello");

var secondidx = statement.getParameterIndex(":mysecondparam");
statement.bindInt32Parameter(secondidx, 1234);
</pre>
<p>같은 질의에 이름있는 매개 변수와 색인된 매개 변수를 혼합할 수도 있습니다.
</p>
<pre>var statement = mDBConn.createStatement("SELECT * FROM foo WHERE a = ?1 AND b &gt; :mysecondparam");

statement.bindUTF8StringParameter(0, "hello");
// you can also use
// var firstidx = statement.getParameterIndex("?1");
// statement.bindUTF8StringParameter(firstidx, "hello");

var secondidx = statement.getParameterIndex(":mysecondparam");
statement.bindInt32Parameter(secondidx, 1234);
</pre>
<p><code>IN ( value-list )</code> 표현식과 함께 <code>WHERE</code> 절을 사용하는 경우에 대입은 동작하지 않게 됩니다. 대신 문자열을 생성하시기 바랍니다. 사용자 입력을 처리하는 경우가 아니라면 보안 문제는 없습니다.
</p>
<pre>var ids = "3,21,72,89";
var sql = "DELETE FROM table WHERE id IN ( "+ ids +" )";
</pre>
<h3 name=".EA.B5.AC.EB.AC.B8_.EC.8B.A4.ED.96.89"> 구문 실행 </h3>
<p>구문을 실행하는 주 방법은 <code>mozIStorageStatement.executeStep</code>입니다. 이 함수는 구문이 생성하는 모든 결과 행을 나열할 수 있도록 해주고 더 이상 결과가 없을 때를 알려줍니다.
</p><p><code>executeStep</code>를 호출할 후에 <a href="ko/MozIStorageValueArray">mozIStorageValueArray</a>에서 <b>적절한 getter 함수를 사용하여</b> 결과 행의 값을 얻을 수 있습니다(mozIStorageStatement는 mozIStorageValueArray를 구현합니다). 아래의 예제는 <code>getInt32()</code>만 사용하고 있습니다.
</p><p>값의 형식은 지정한 열의 형식을 반환하는 <code>mozIStorageValueArray.getTypeOfIndex</code>로 구할 수 있습니다. 그러나, 주의하십시오. sqlite는 형식있는 데이터베이스가 아닙니다. 열에 선언한 형식과 무관하게 모든 셀에 아무 형식이나 입력할 수 있습니다. 다른 형식을 요청하면 sqlite는 최선을 다하여 그것을 변환하고 변환이 불가능하면 기본 값으로 처리합니다. 그러므로 형식 오류를 얻을 수 없으며 이상한 데이터 출력을 얻을 수도 있습니다.
</p><p>C++ 코드는 <code>AsInt32</code>, <code>AsDouble</code>과 같은 함수를 이용할 수도 있는데, 이는 더 편리한 C++ 반환 값으로 값을 반환합니다. 하지만, 색인이 잘못된 경우에도 오류가 발생하지 않으므로 주의하십시오. 다른 오류가 발생하는 것도 불가능한데, sqlite는 사리에 맞지 않는 경우에도 항상 형식을 변환하기 때문입니다.
</p><p>C++ 예제:
</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>자바스크립트 예제:
</p>
<pre>while (statement.executeStep()) {
  var value = statement.getInt32(0); // use the correct function!
  // use the value...
}
</pre>
<p><code>mozIStorageStatement.execute()</code>는 구문에서 얻을 데이터가 없는 경우에 편리한 함수입니다. 이는 구문을 한 번 실행하고 초기화합니다. 이는 삽입 구문에 대해 유용한데 코드를 매우 간단하게 하기 때문입니다.
</p>
<pre>var statement = mDBConn.createStatement("INSERT INTO my_table VALUES (?1)");
statement.bindInt32Parameter(52);
statement.execute();
</pre>
<p>파일 <img alt="Image:TTRW2.zip" src="File:ko/Media_Gallery/TTRW2.zip">은 데이터베이스에 대하여 SQL SELECT를 실행하는 간단하지만 완전한 자바스크립트입니다.
</p>
<h3 name=".EA.B5.AC.EB.AC.B8_.EC.B4.88.EA.B8.B0.ED.99.94"> 구문 초기화 </h3>
<p>더 이상 사용하지 않는 구문을 초기화하는 것은 중요합니다. 초기화되지 않은 쓰기 구문은 테이블에 잠금을 유지하여 다른 구문이 테이블을 접근하는 것을 막게 됩니다. 초기화되지 않은 읽기 구문은 쓰기를 막게 됩니다.
</p><p>구문 개체가 해제되면 해당 데이터베이스 구문은 닫힙니다. C++을 사용 중인 경우, 모든 참조가 소멸된다는 것을 알고 있다면 따로 구문을 초기화할 필요가 없습니다. 또한 <code>mozIStorageStatement.execute()</code>를 사용하는 경우에도 따로 구문을 초기화할 필요가 없습니다. 이 함수는 여러분을 대신하여 구문을 초기화합니다. 나머지 경우에는 <code>mozIStorageStatement.reset()</code>를 호출하십시오.
</p><p>자바스크립트 호출자는 확실하게 구문을 초기화해야 합니다. 특히 예외에 대해서 주의하십시오. 예외가 발생하거나 데이터베이스에 접근하는 것이 불가능해진 경우에도 구문을 초기화하는 것을 확실하게 해야 합니다. 구문 초기화는 비교적 가벼운 작업이고 이미 초기화된 경우에도 아무런 문제가 발생하지 않기 때문에 불필요한 초기화에 대해서 걱정할 필요는 없습니다.
</p>
<pre>var statement = connection.createStatement(...);
try {
  // use the statement...
} finally {
  statement.reset();
}
</pre>
<p>C++ 호출자도 같은 일을 해야 합니다. {{template.Source("storage/public/mozStorageHelper.h")}}에는 mozStorageStatementScoper라고 불리우는 유효 영역이 있는 개체가 있는데, 이 개체는 둘러싼 영역을 빠져 나갈 때 주어진 구문이 초기화되는 것을 보장합니다. 가능하면 이 개체를 사용하는 것이 바람직합니다.
</p>
<pre>void someClass::someFunction()
{
  mozStorageStatementScoper scoper(mStatement)
  // use the statement
}
</pre>
<h2 name=".EC.B5.9C.EC.A2.85_insert_.EC.95.84.EC.9D.B4.EB.94.94"> 최종 insert 아이디 </h2>
<p>연결의 <code>lastInsertRowID</code> 속성을 이용하면 데이터베이스의 마지막 <code>INSERT</code> 작업에서 할당한 아이디(rowid)를 구할 수 있습니다.<br>
이는 여러분이 테이블에 <code>INTEGER PRIMARY KEY</code>나 <code>INTEGER PRIMARY KEY AUTOINCREMENT</code>로 지정된 열을 가지고 있을 때 유용한데, 이 경우 SQLite는 여러분이 값을 제공하지 않으면 삽입하는 각 행에 대하여 자동으로 값을 할당합니다.<a class="external" href="http://www.sqlite.org/capi3ref.html#sqlite3_last_insert_rowid"></a><a class="external" href="http://www.sqlite.org/faq.html#q1"> 반환 값은 자바스크립트에서는 <code>number</code> 형식이고 C++에서는 <code>long long</code>입니다.
</a></p><p><a class="external" href="http://www.sqlite.org/faq.html#q1"><code>lastInsertRowID</code>를 이용하는 자바스크립트 예제는 다음과 같습니다.
</a></p><a class="external" href="http://www.sqlite.org/faq.html#q1">
<pre>var sql = "INSERT INTO contacts_table (number_col, name_col) VALUES (?1, ?2)"
var statement = mDBConn.createStatement(sql);
    statement.bindUTF8StringParameter(0, number);
    statement.bindUTF8StringParameter(1, name);
    statement.execute();
    statement.reset();
    
var rowid = mDBConn.lastInsertRowID;
</pre>
<h2 name="Transactions"> Transactions </h2>
</a><p><a class="external" href="http://www.sqlite.org/faq.html#q1">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><a href="ko/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 {{template.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>
<h2 name="How_to_corrupt_your_database"> How to corrupt your database </h2>
<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>
<h2 name="SQLite_Locking"> SQLite Locking </h2>
<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>
<h3 name="Table-level_locks"> Table-level locks </h3>
<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>
<h3 name="Working_around_locking_problems"> Working around locking problems </h3>
<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>
<h2 name="Thread_safety"> Thread safety </h2>
<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 thread safe 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>It's worth noting, however, that authors of JavaScript browser extensions are less impacted by these restrictions than it might first appear. If a database is created and used exclusively from within JavaScript, thread safety usually will not be an issue.  SpiderMonkey (the JavaScript engine run within Firefox) executes JavaScript from a single persistent thread, except when the JavaScript runs in a different thread or is executed from a callback made on a different thread (e.g. via some networking or stream interfaces).  Barring incorrect use of multi-threaded JavaScript, problems should occur only if a database already in use by a non-JavaScript, system-level thread is accessed through mozStorage.
</p>
<h2 name="See_also"> See also </h2>
<ul><li><a href="ko/MozIStorageConnection">mozIStorageConnection</a> Database connection to a specific file or in-memory data storage
</li><li><a href="ko/MozIStorageStatement">mozIStorageStatement</a> Create and execute SQL statements on a SQLite database.
</li><li><a href="ko/MozIStorageValueArray">mozIStorageValueArray</a> Wraps an array of SQL values, such as a result row.
</li><li><a href="ko/MozIStorageFunction">mozIStorageFunction</a> Create a new SQLite function.
</li><li><a href="ko/MozIStorageAggregateFunction">mozIStorageAggregateFunction</a> Create a new SQLite aggregate function.
</li><li><a href="ko/MozIStorageProgressHandler">mozIStorageProgressHandler</a> Monitor progress during the execution of a statement.
</li><li><a href="ko/MozIStorageStatementWrapper">mozIStorageStatementWrapper</a> Storage statement wrapper
</li></ul>
<ul><li><a href="ko/Storage/Performance">Storage:Performance</a> How to get your database connection performing well.
</li><li><a class="external" href="https://addons.mozilla.org/en-US/firefox/addon/3072">Storage Inspector Extension</a> Makes it easy to view any sqlite database files in the current profile.
</li><li><a class="external" href="http://www.sqlite.org/lang.html">SQLite Syntax</a> Query language understood by SQLite
</li><li><a class="external" href="http://sqlitebrowser.sourceforge.net/">SQLite Database Browser</a> is a capable free tool available for many platforms. It can be handy for examining existing databases and testing SQL statements.
</li><li><a class="external" href="https://addons.mozilla.org/en-US/firefox/addon/5817">SQLite Manager Extension</a> helps manage sqlite database files on your computer.
</li></ul>
<div class="noinclude">
</div>
{{ wiki.languages( { "en": "en/Storage", "es": "es/Almacenamiento", "fr": "fr/Storage", "ja": "ja/Storage", "pl": "pl/Storage" } ) }}
현재 리비전 복원