step-3:日本語対応のためのコード追加
SQLiteがサポートする文字コードはUTF-8とUTF-16です。上記サンプル「sample.cpp」は、SQLiteのUTF-8版インターフェイス(関数)を呼び出しています。テーブル名やカラム名、各レコード内の文字列がすべてASCIIコードなのでUTF-8版を使っても問題なく動作します(ASCIIはUTFのサブセットですから)。
日本語のテーブル名/カラム名/文字列を使いたいとなれば、UTF-16すなわちUnicode、char*
ではなくwchar_t*
を引数/返値とするUTF-16版インターフェイスを呼び出すことになります(SQLiteのワイド文字列インターフェイスではwchar_t*
ではなくvoid*
になっています)。
例えば上記サンプルで用いたSQLiteのUTF-8版ライブラリ関数sqlite3_open
、sqlite3_prepare
、sqlite3_bind_text
などなどにはそれぞれUTF-16版であるsqlite3_open16
、sqlite3_prepare16
、sqlite3_bind_text16
が用意されています。
ところが、SQLを実行する最も簡単でお手軽な関数sqlite3_exec
には対応するUTF-16版関数sqlite3_exec16
がないのです。
ですが、sqlite3_exec
の実装を読んでみたところ、コードをそのままコピーし数箇所の修正を加えることでUTF-16版sqlite3_exec16
が作れることが分かりました。
まず、「sqlite3.h」の165行目あたりに以下の宣言を書き加えます。
typedef int (*sqlite3_callback16)(void*,int,__wchar_t**, __wchar_t**); int sqlite3_exec16( sqlite3*, /* An open database */ const __wchar_t *sql, /* SQL to be executed */ sqlite3_callback16, /* Callback function */ void *, /* 1st argument to callback function */ __wchar_t **errmsg /* Error msg written here */ );
次に「sqlite3.c」の48524行目(legacy.cの次)に以下の実装を追加します。
/********** Begin file legacy16.c ***********************************/ typedef int (*sqlite3_callback16)(void*,int,__wchar_t**, __wchar_t**); int sqlite3_exec16( sqlite3 *db, /* The database on which the SQL executes */ const __wchar_t *zSql, /* The SQL to be executed */ sqlite3_callback16 xCallback, /* Invoke this callback routine */ void *pArg, /* First argument to xCallback() */ __wchar_t **pzErrMsg /* Write error messages here */ ){ int rc = SQLITE_OK; const __wchar_t *zLeftover; sqlite3_stmt *pStmt = 0; __wchar_t **azCols = 0; int nRetry = 0; int nChange = 0; int nCallback; if( zSql==0 ) return SQLITE_OK; while( (rc==SQLITE_OK || (rc==SQLITE_SCHEMA && (++nRetry)<2)) && zSql[0] ){ int nCol; __wchar_t **azVals = 0; pStmt = 0; rc = sqlite3_prepare16(db, zSql, -1, &pStmt, &zLeftover); assert( rc==SQLITE_OK || pStmt==0 ); if( rc!=SQLITE_OK ){ continue; } if( !pStmt ){ /* this happens for a comment or white-space */ zSql = zLeftover; continue; } db->nChange += nChange; nCallback = 0; nCol = sqlite3_column_count(pStmt); azCols = sqliteMalloc(2*nCol*sizeof(const __wchar_t *) + 1); if( azCols==0 ){ goto exec_out; } while( 1 ){ int i; rc = sqlite3_step(pStmt); /* Invoke the callback function if required */ if( xCallback && (SQLITE_ROW==rc || (SQLITE_DONE==rc && !nCallback && db->flags&SQLITE_NullCallback)) ){ if( 0==nCallback ){ for(i=0; i<nCol; i++){ azCols[i] = (__wchar_t*)sqlite3_column_name16(pStmt, i); } nCallback++; } if( rc==SQLITE_ROW ){ azVals = &azCols[nCol]; for(i=0; i<nCol; i++){ azVals[i] = (__wchar_t*)sqlite3_column_text16(pStmt, i); } } if( xCallback(pArg, nCol, azVals, azCols) ){ rc = SQLITE_ABORT; goto exec_out; } } if( rc!=SQLITE_ROW ){ rc = sqlite3_finalize(pStmt); pStmt = 0; if( db->pVdbe==0 ){ nChange = db->nChange; } if( rc!=SQLITE_SCHEMA ){ nRetry = 0; zSql = zLeftover; while( iswspace((wint_t)zSql[0]) ) zSql++; } break; } } sqliteFree(azCols); azCols = 0; } exec_out: if( pStmt ) sqlite3_finalize(pStmt); if( azCols ) sqliteFree(azCols); rc = sqlite3ApiExit(0, rc); if( rc!=SQLITE_OK && rc==sqlite3_errcode(db) && pzErrMsg ){ *pzErrMsg = malloc(wcslen((const __wchar_t*)sqlite3_errmsg16(db)+1) *sizeof(__wchar_t)); if( *pzErrMsg ){ wcscpy(*pzErrMsg, sqlite3_errmsg16(db)); } }else if( pzErrMsg ){ *pzErrMsg = 0; } return rc; } /********* End of legacy16.c ****************************************/