ラベル Gears/sqlite の投稿を表示しています。 すべての投稿を表示
ラベル Gears/sqlite の投稿を表示しています。 すべての投稿を表示

2013/03/17

[iOS][Objective-C]sqlite3ライブラリについて

Xcodeでライブラリを追加するときに、検索窓に「sqlite」と打つと下のように二つヒットします。

libsqlite3.0.dylib
libsqlite3.dylib
この二つに違いって何だろうか?

同じ疑問を持った人が、stackoverflowに投稿していたようだ。

The tutorial calls for libsqlite3.0.dylib but I noticed another one libsqlite3.dylib. Is the latter just a symlink to the latest v3 library like the convention for package managers on UNIX or is there a difference?

via:What is the difference between libsqlite3.dylib and libsqlite3.0.dylib?
で、答えを読むと、どうやら最新のsqlite3のライブラリが「libsqlite3.dylib」のようです。

これで悩みが解決したのでさっぱり。

2012/06/28

[Python][django]sqlite3の設定時の注意

setting.pyファイルでDATABASESの設定ができるのだが、sqlite3を設定する場合は、ENGINEキーの設定もそうだが、NAMEキーの設定も注意したい。

例えば、windowsで設定する場合は、下のようになる。

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': 'C:/Django-1.4/testsite/database/test.sqlite',
        'USER': '',
        'PASSWORD': '',
        'HOST': '',
        'PORT': '',
    }
}
要注なのだが、windowsの場合でも、NAMEキーのディレクトリ設定は、「\」(エンマーク)ではなく「/」であるところ。

「\」にしてしまうと、正常終了しないので、気をつけたい

2011/03/13

[GoogleGears]開発ストップ

いよいよ、ストップしそうだ。

With all this now available in HTML5, it’s finally time to say goodbye to Gears. There will be no new Gears releases, and newer browsers such as Firefox 4 and Internet Explorer 9 will not be supported. We will also be removing Gears from Chrome in Chrome 12.

via:Stopping the Gears

つまり、ほとんどの機能をHTML5によって実装されてしまったんですね。

なので、ストップというよりもむしろ、役目を終えたというところでしょうか?

下記のような書籍がでていたが、もう、後に続く本は出なさそうだ。




2010/11/20

[sqlite3]日本語入力について

mac os xのターミナルからsqliteに接続し、日本語の情報をテーブルに登録しようとしたところ、どうやらエラーになってしまうようです。

そもそも入力自体ができないっぽい。

そこで、方法を調べたところ。

対応策としては、CSVファイルにして.importコマンドで取り込むと行けるらしい。

via:sqlite3へ日本語データを入れる

で、実際に上の方法で試したところ無事に日本語情報が登録できました。

一件落着、落着。

2010/11/07

[sqlite]sqlite3_exec()について

複数のsql文を一気に実行するのに、sqlite3_exec()を使おうと検討していたところ、ちょっと不便利だなーっと思うところがあったのでメモ代わりに。

sqlite3_exec()そのものの仕様なので、仕方がないといえばそれまでなのだが、定義を読んでみると、どうやら、sql文が実行される度に、毎回、毎回、コールバック関数が呼ばれること。

つまり、select文を発行した場合、結果が2件あった場合、2回コールバックが発生してしまうこと。

しかも複数のsql文の場合、どのsql文からのコールバックなのか、判断できないのがやっぱり微妙。

sql文を実行するたびにコールされるのは、いいのだが、そのときに、実行されたsql文がわかると便利だなーっと思う今日この頃。

2010/10/23

[本]SQLite ポケットリファレンス


第0章 SQLiteの基本仕様

0-1 データベースとは
0-2 SQLiteとは
0-3 SQLiteの仕様
0-4 SQLiteとコマンドライン
0-5 SQLiteでできること,できないこと
0-6 ドットコマンド
0-7 PRAGMAコマンド
0-8 データ型
0-9 関数
0-10 演算子
0-11 エンコーディング
0-12 ファイル構成
0-13 VACUUMコマンド
0-14 予約語
0-15 コメント
第1章 SQLiteの基本操作

1-1 SQLiteのインストール
1-2 SQLiteの起動と終了
1-3 データベースの基本操作
第2章 SQLiteで使えるSQL命令

2-1 データ操作言語(DML)
2-2 データ定義言語(DDL)
2-3 データ制御言語(DCL)
第3章 SQLite独自のコマンド

3-1 コマンドラインのドットコマンド
3-2 データベース管理のドットコマンド
3-3 外部ファイル連携のドットコマンド
3-4 SQLプログラミングのPRAGMAコマンド
3-5 データベース管理のPRAGMAコマンド
第4章 SQLiteの関数

4-1 集計関数
4-2 文字列関数
4-3 日付関数
4-4 変換関数/算術関数/分析関数
4-5 そのほかの関数
第5章 SQLiteの演算子

5-1 演算子とは
5-2 演算子の優先順位
5-3 集合演算子
5-4 算術演算子
5-5 ビット演算子
5-6 比較演算子
5-7 論理演算子
5-8 単項演算子/そのほかの演算子
第6章 SQLiteへの接続

6-1 SQLiteプログラミング
6-2 スマートフォンでSQLiteに接続
6-3 WebブラウザでSQLiteに接続
付録

A-1 GUIツールによる管理と活用
A-2 データのインポートとエクスポート
A-3 パフォーマンスチューニング

via:目次

sqlite初めてのポケットリファレンスということで、そっこーで購入しました。
他のDB関連のポケットリファレンスを買った事がないので、比較できないのですが、本書に限って言えば、sqliteのダウンロードから実際にコマンドを打ってsqlを実行するところまできちんと記載されている。

しかも、iPhoneやandroidといったスマートフォーン上で実行する方法まで書かれている。
が、しかし、iPhoneに限って言えば、本書の内容だけだと若干、不十分である。
かつ、Leopardがあれば、iPhoneの開発ができると書かれているがこれは間違っている。
正確には、snow Leopardではないと作ることができない。

さらに、PHPやPerlなどのサーバーサイドプログラムでの実行方法までサポートしているが、肝心のC言語上での実行方法が書かれていない。
iPhoneでsqliteを走らせるのに必ず必要になるのに。

sql関数や、sqliteコマンドなどほとんど網羅しているのだから、C言語の関数の使い方も掲載してほしかったなーっと、それだけが心残りでした。


2010/10/19

[sqlite][C]言語を使った処理の流れ その2

前回、C言語を使ったsqliteの実行方法について書きましたが、今日は、別バージョン

SQLite3をC言語で使う練習。を参考にすると、下記のような流れになるようです。

sqlite3_open()

sqlite3_mprintf()

sqlite3_exec()

sqlite3_close()

うーん、実に便利!!

2010/10/17

[sqlite]文字列結合

の仕方は、どうやら、oracleと同じように、

select 'hoge'||'foo' from test

でいけちゃうようです。

参考:[sqlite]sqliteで文字列結合

2010/10/13

[sqlite][iPhone sdk]sqliteのマイナーバージョンについてその2

前回、[iPhone sdk]sqliteのマイナーバージョンについてというエントリーで、iPhone sdkに含まれているsqlite3のマイナーバージョンについて書きましたが、具体的な調査方法がわからないままきてしまいました。

で今回、そのマイナーバージョンの確認方法がわかったので、改めて、1エントリー設けることに。

#import <sqlite3.h>
でsqlite3フレームワークをインポートするのですが、その中身を直接参照したところ、
#define SQLITE_VERSION "3.6.22"
と書いてあったので、Xcode 3.2.3のiPhone sdk 3.2環境下では、3.6.22みたいです。
(Xcode上でsqlite3を選択した後、「定義へジャンプ」するとソースコードを閲覧することできるようです。この方法は、他のフレームワークでも使えるので便利かなと。)

2010/10/10

[iPhone sdk]sqliteのマイナーバージョンについて

前回、iPhoneがsqliteをサポートしていることを書きました。

で、少し調査してみると、sqlite3をサポートしていることはわかったのですが、マイナーバージョンってどうなっているのかなーっと気になったので、ぐぐってみると、以下の記事ががヒットしました。

SQLite 3 version numbers from sqlite3.h:

3.4.0 - iPhone OS 2.2.1
3.6.12 - iPhone OS 3.0 / 3.1
3.6.22 - iPhone OS 4.0

via:iPhone iOS 2.0 to iOS 4.0 SQLite compatibility

これを参考に、アプリを作っていきたいと思います。

2010/08/26

[sqlite]version 2時点におけるincrement

かなり前に、AsialさんのPHPセミナーに参加させていただいたときに、sqliteのauto-incrementの方法を教えていただいた上に資料までいただいたのですが、該当部分をすぐに見つけることができなく、やむなく探すことに。
(記憶力が悪すぎるうえに、部屋がちらかりすぎている。。。)

つまり,SQLite で auto-increment なフィールドを作りたければ,

INTEGER PRIMARY KEY

を指定してあげればいいらしい.

via:SQLite で auto-increment なフィールドを作成する方法

なるほど。。。

2010/07/08

[Objective-C][iPhone][sqlite]SELECT文の発行

今までの段階で、INSERT文とCREATE文を書きましたが、今日は、SELECT文の発行について書きたいと思います。
//TestTable.h

#import <Foundation/Foundation.h>
#import <sqlite3.h>

@interface TestTable : NSObject {
sqlite3 *db;
NSString *dbPath;
NSArray *columnNames;
NSString *tableName;
NSMutableDictionary *columnDatum;
}

@property sqlite3 *db;
@property (retain,nonatomic) NSString *dbPath;
@property (retain,nonatomic) NSArray *columnNames;
@property (retain,nonatomic) NSString *tableName;
@property (retain,nonatomic) NSMutableDictionary *columnDatum;

-(id)init;
-(void)setDatum;
-(BOOL)openDataBase;
-(BOOL)createTable;
-(BOOL)insertData;
-(void)selectData;
-(void)closeDataBase;
-(void)errDataBase;
@end

//TestTable.m
#import "TestTable.h"


@implementation TestTable

@synthesize db;
@synthesize dbPath;
@synthesize columnNames;
@synthesize tableName;
@synthesize columnDatum;

-(id)init{
self = [super init];

tableName = @"test";
columnNames = [[NSArray alloc] initWithObjects:
@"column_no1"
, @"column_no2"
, @"column_no3"
, nil
];

//対象Dataを初期化
columnDatum = [NSDictionary dictionary];

//dbが存在しているかどうかの確認
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
dbPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"test.db"];

//ファイルが存在しない場合
if (![[NSFileManager defaultManager] fileExistsAtPath:dbPath]) {
//ファイルを作成する
BOOL result = [[NSFileManager defaultManager] createFileAtPath:dbPath contents:nil attributes:nil];
//ファイル作成が失敗した場合
if (!result) {
}

//sqliteをオープンする
if([self openDataBase]){
//テーブルの作成
[self createTable];

//テーブルのクローズ
[self closeDataBase];
}
}

if([self openDataBase]){
//データを登録
[self insertData];

//テーブルのクローズ
[self closeDataBase];
}
if([self openDataBase]){
//データを取得
[self selectData];
}

return self;
}

//Dataをセットする
-(void)setDatum{
}

//sqliteをオープンする
-(BOOL)openDataBase{
int ret;
ret = sqlite3_open([dbPath UTF8String],&db);
//正常終了
if(ret == SQLITE_OK){
return YES;
//異常終了
}else {
//エラーが発生してしまったので、クローズを行う
sqlite3_close(db);
return NO;
}
}

//tableの作成
-(BOOL)createTable{
//sqlの設定
NSMutableString *createsql = [NSMutableString string];
[createsql appendString:@"CREATE TABLE "];
[createsql appendString:tableName];
[createsql appendString:@"("];
for (NSUInteger i=0; i< [columnNames count]; i++) {
[createsql appendString:((i>0)?@",":@"")];
[createsql appendString:[columnNames objectAtIndex:i]];
}
[createsql appendString:@")"];

//各関数の戻り値
int ret;
//sql文を実行するための変数
sqlite3_stmt *sqlstmt;

ret = sqlite3_prepare_v2(db,[createsql UTF8String],-1,&sqlstmt,NULL);
//実行準備おk
if(ret == SQLITE_OK){
//sqlの実行を行う
ret = sqlite3_step(sqlstmt);
//sql文の解放
sqlite3_finalize(sqlstmt);

//sqlの実行が正常終了した場合
if(ret == SQLITE_DONE){
return YES;
}
}

//エラーメセッドをコール
[self errDataBase];
//dbクローズ
[self closeDataBase];
return NO;

}

-(BOOL)insertData{

NSArray *tmpArray = [[NSArray alloc] initWithObjects:@"test_01"
, @"test_02"
, @"test_03"
, @"test_04"
, nil
];

BOOL q_Ret = NO;

//各関数の戻り値
int ret;

//トランザクションの開始
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL);

//insert文の作成
sqlite3_stmt* sqlstmt;
NSMutableString *inssql = [NSMutableString string];
[inssql appendString:@"insert into "];
[inssql appendString:tableName];
[inssql appendString:@"("];
for (NSUInteger i=0; i<[columnNames count]; i++) {
[inssql appendString:((i>0)?@",":@"")];
[inssql appendString:[columnNames objectAtIndex:i]];
}
[inssql appendString:@")values("];
for (NSUInteger i=0; i<[columnNames count]; i++) {
[inssql appendString:((i>0)?@",":@"")];
[inssql appendString:@"?"];
}
[inssql appendString:@")"];

ret = sqlite3_prepare_v2(db, [inssql UTF8String], -1, &sqlstmt, NULL);
//構文解析の結果問題なし(バインド前)
if (ret == SQLITE_OK) {
for (NSUInteger tmp=1; tmp<3; tmp++) {
//sqlをリセット
sqlite3_reset(sqlstmt);
//バインド変数をクリアー
sqlite3_clear_bindings(sqlstmt);
sqlite3_bind_text(sqlstmt,1,[[tmpArray objectAtIndex:(tmp-1)] UTF8String],-1,SQLITE_TRANSIENT);
sqlite3_bind_text(sqlstmt,2,[[tmpArray objectAtIndex:tmp] UTF8String],-1,SQLITE_TRANSIENT);
sqlite3_bind_text(sqlstmt,3,[[tmpArray objectAtIndex:(tmp+1)] UTF8String],-1,SQLITE_TRANSIENT);
//sql文を実行
ret = sqlite3_step(sqlstmt);

//一回でもエラーが発生した場合はクローズさせて終了
if(ret != SQLITE_DONE){
//sql文の解放
sqlite3_finalize(sqlstmt);
//異常終了(ROLLBACKして処理を終了)
sqlite3_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
//エラーメソッドをコール
[self errDataBase];
//dbをクローズ
[self closeDataBase];
return q_Ret;
}
}
//正常終了(COMMITをして処理を終了)
sqlite3_exec(db, "COMMIT TRANSACTION", NULL, NULL, NULL);
q_Ret = YES;
NSLog(@"OKです");
//構文解析の結果問題あり(バインド前)
}else {
//エラーメソッドをコール
[self errDataBase];
//異常終了(ROLLBACKして処理を終了)
sqlite3_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
}

//sql文の解放
sqlite3_finalize(sqlstmt);

//クローズ
[self closeDataBase];
return q_Ret;
}

-(void)selectData{
sqlite3_stmt *sqlstmt;
char *sql = "SELECT * FROM test";
int ret;
ret = sqlite3_prepare_v2(db, sql, -1, &sqlstmt, NULL);
NSLog(@"整いました。1");
//構文解析の結果問題なし(バインド前)
if (ret == SQLITE_OK) {
NSLog(@"整いました。");
if(SQLITE_ROW == sqlite3_step(sqlstmt)){
NSLog(@"%s",sqlite3_column_text(sqlstmt,0));
NSLog(@"%s",sqlite3_column_text(sqlstmt,1));
NSLog(@"%s",sqlite3_column_text(sqlstmt,2));
}

}else {
[self errDataBase];
}

}

//sqliteをクローズする
-(void)closeDataBase{
sqlite3_close(db);
}

//sqliteのエラー処理
-(void)errDataBase{

UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle:[[NSString alloc] initWithFormat:@"error:%d",sqlite3_errcode(db)]
message:[[NSString alloc] initWithUTF8String:sqlite3_errmsg(db)]
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil
];
[alertView show];
[alertView release];

}

@end

実際は、selectDataメソッドが、SELECT文を実行していて、ビルドしたところ、正常に動作しました。

ここまでくれば、ObjectBrowserとか作ってみたくなるなー。

2010/06/30

[objective-c][iPad][sqlite]動的にCREATEとINSERT文を発行

やっと、バインド変数ではなく、動的にSQL文を実行することに成功したので、ソースを紹介します。
//TestTable.h

#import <Foundation/Foundation.h>
#import <sqlite3.h>

@interface TestTable : NSObject {
sqlite3 *db;
NSString *dbPath;
NSArray *columnNames;
NSString *tableName;
}

@property sqlite3 *db;
@property (retain,nonatomic) NSString *dbPath;
@property (retain,nonatomic) NSArray *columnNames;
@property (retain,nonatomic) NSString *tableName;

-(id)init;
-(BOOL)openDataBase;
-(BOOL)createTable;
-(BOOL)insertData;
-(void)closeDataBase;
-(void)errDataBase;
@end

//TestTable.m
#import "TestTable.h"

@implementation TestTable

@synthesize db;
@synthesize dbPath;
@synthesize columnNames;
@synthesize tableName;
@synthesize columnDatum;

-(id)init{
self = [super init];

tableName = @"test";
columnNames = [[NSArray alloc] initWithObjects:
@"column_no1"
, @"column_no2"
, @"column_no3"
, nil
];


//dbが存在しているかどうかの確認
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
dbPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"test.db"];

//ファイルが存在しない場合
if (![[NSFileManager defaultManager] fileExistsAtPath:dbPath]) {
//ファイルを作成する
BOOL result = [[NSFileManager defaultManager] createFileAtPath:dbPath contents:nil attributes:nil];
//ファイル作成が失敗した場合
if (!result) {

}

//sqliteをオープンする
if([self openDataBase]){
//テーブルの作成
[self createTable];

//テーブルのクローズ
[self closeDataBase];
}

}

if([self openDataBase]){
//データを登録
[self insertData];
//テーブルのクローズ
[self closeDataBase];
}

return self;
}

//sqliteをオープンする
-(BOOL)openDataBase{
int ret;
ret = sqlite3_open([dbPath UTF8String],&db);
//正常終了
if(ret == SQLITE_OK){
return YES;
//異常終了
}else {
//エラーが発生してしまったので、クローズを行う
sqlite3_close(db);
return NO;
}
}

//tableの作成
-(BOOL)createTable{
//sqlの設定
NSMutableString *createsql = [NSMutableString string];
[createsql appendString:@"CREATE TABLE "];
[createsql appendString:tableName];
[createsql appendString:@"("];
for (NSUInteger i=0; i< [columnNames count]; i++) {
[createsql appendString:((i>0)?@",":@"")];
[createsql appendString:[columnNames objectAtIndex:i]];
}
[createsql appendString:@")"];

//各関数の戻り値
int ret;
//sql文を実行するための変数
sqlite3_stmt *sqlstmt;

ret = sqlite3_prepare_v2(db,[createsql UTF8String],-1,&sqlstmt,NULL);
//実行準備おk
if(ret == SQLITE_OK){
//sqlの実行を行う
ret = sqlite3_step(sqlstmt);
//sql文の解放
sqlite3_finalize(sqlstmt);

//sqlの実行が正常終了した場合
if(ret == SQLITE_DONE){
return YES;
}
}

//エラーメセッドをコール
[self errDataBase];
//dbクローズ
[self closeDataBase];
return NO;

}

-(BOOL)insertData{

NSArray *tmpArray = [[NSArray alloc] initWithObjects:@"test_01"
, @"test_02"
, @"test_03"
, @"test_04"
, nil
];

BOOL q_Ret = NO;

//各関数の戻り値
int ret;

//トランザクションの開始
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL);

//insert文の作成
sqlite3_stmt* sqlstmt;
NSMutableString *inssql = [NSMutableString string];
[inssql appendString:@"insert into "];
[inssql appendString:tableName];
[inssql appendString:@"("];
for (NSUInteger i=0; i<[columnNames count]; i++) {
[inssql appendString:((i>0)?@",":@"")];
[inssql appendString:[columnNames objectAtIndex:i]];
}
[inssql appendString:@")values("];
for (NSUInteger i=0; i<[columnNames count]; i++) {
[inssql appendString:((i>0)?@",":@"")];
[inssql appendString:@"?"];
}
[inssql appendString:@")"];

ret = sqlite3_prepare_v2(db, [inssql UTF8String], -1, &sqlstmt, NULL);
//構文解析の結果問題なし(バインド前)
if (ret == SQLITE_OK) {
for (NSUInteger tmp=1; tmp<3; tmp++) {
//sqlをリセット
sqlite3_reset(sqlstmt);
//バインド変数をクリアー
sqlite3_clear_bindings(sqlstmt);
sqlite3_bind_text(sqlstmt,1,[[tmpArray objectAtIndex:(tmp-1)] UTF8String],-1,SQLITE_TRANSIENT);
sqlite3_bind_text(sqlstmt,2,[[tmpArray objectAtIndex:tmp] UTF8String],-1,SQLITE_TRANSIENT);
sqlite3_bind_text(sqlstmt,3,[[tmpArray objectAtIndex:(tmp+1)] UTF8String],-1,SQLITE_TRANSIENT);
//sql文を実行
ret = sqlite3_step(sqlstmt);

//一回でもエラーが発生した場合はクローズさせて終了
if(ret != SQLITE_DONE){
//sql文の解放
sqlite3_finalize(sqlstmt);
//異常終了(ROLLBACKして処理を終了)
sqlite3_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
//エラーメソッドをコール
[self errDataBase];
//dbをクローズ
[self closeDataBase];
return q_Ret;
}
}
//正常終了(COMMITをして処理を終了)
sqlite3_exec(db, "COMMIT TRANSACTION", NULL, NULL, NULL);
q_Ret = YES;
//構文解析の結果問題あり(バインド前)
}else {
//エラーメソッドをコール
[self errDataBase];
//異常終了(ROLLBACKして処理を終了)
sqlite3_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
}

//sql文の解放
sqlite3_finalize(sqlstmt);

//クローズ
[self closeDataBase];
return q_Ret;
}

//sqliteをクローズする
-(void)closeDataBase{
sqlite3_close(db);
}

//sqliteのエラー処理
-(void)errDataBase{

UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle:[[NSString alloc] initWithFormat:@"error:%d",sqlite3_errcode(db)]
message:[[NSString alloc] initWithUTF8String:sqlite3_errmsg(db)]
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil
];
[alertView show];
[alertView release];

}

@end

NSArrayとNSStringクラスを使ってそれぞれカラム名とテーブル名をセットした後、createTableメソッドでテーブルを作成し、insertDataでデータの登録をしています。

かつinitメソッドで設定しているので、initメソッドを拡張させることで、インスタンス変数宣言時に、これらの変数をもっていくことができます。

ただ唯一気になったのが、sqlite3_prepare_v2の第二引数にchar型のsql文を叩き込むのですが、NSMutableStringでセットした変数を一度、char型変数にセットするとコンパイル時に警告がでちゃうんですよね〜。

なぜだろう???

ちなみに、上記を実行した結果、正常終了しました。

ここまでくると、iPad版のObjectBrowser的なものを作りたくなってきた。

ただ、やはり、INSERT文発行時に、登録対象となるデータをインスタンス変数に設定したいなー。
getterとsetterを使ってインスタンス変数経由で、うまくできないものだろうか?

NSDictionaryとNSArrayを使ってどうにかならないものかなーっと想定しているのだが。

2010/06/28

[objective-c][iPad][sqlite]INSERT文の発行その2

前回は、バインド変数を使わずに、INSERT文を実行する方法について書きました。

今日は、長い試行のすえできた、バインド変数を使ったトランザクション処理について書いてみたいと思います。

// TestTable.h

#import <Foundation/Foundation.h>
#import <sqlite3.h>

@interface TestTable : NSObject {
sqlite3 *db;
NSString *dbPath;
}

@property sqlite3 *db;
@property (retain,nonatomic) NSString *dbPath;

-(id)init;
-(BOOL)openDataBase;
-(BOOL)createTable;
-(BOOL)insertData;
-(void)closeDataBase;
-(void)errDataBase;
@end

// TestTable.m
#import "TestTable.h"

@implementation TestTable

@synthesize db;
@synthesize dbPath;

-(id)init{
self = [super init];

//dbが存在しているかどうかの確認
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
dbPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"test.db"];

//ファイルが存在しない場合
if (![[NSFileManager defaultManager] fileExistsAtPath:dbPath]) {
//ファイルを作成する
BOOL result = [[NSFileManager defaultManager] createFileAtPath:dbPath contents:nil attributes:nil];
//ファイル作成が失敗した場合
if (!result) {
}
//BOOL sql_exec_ok = NO;
//sqliteをオープンする
if([self openDataBase]){
//テーブルの作成
[self createTable];

//データを登録
[self insertData];

//テーブルのクローズ
[self closeDataBase];
}
}
return self;
}

//sqliteをオープンする
-(BOOL)openDataBase{
int ret;
ret = sqlite3_open([dbPath UTF8String],&db);
//正常終了
if(ret == SQLITE_OK){
return YES;
//異常終了
}else {
//エラーが発生してしまったので、クローズを行う
sqlite3_close(db);
return NO;
}
}

//tableの作成
-(BOOL)createTable{
//sqlの設定
char *sql = "CREATE TABLE test(column_no1,column_no2)";
//各関数の戻り値
int ret;
//sql文を実行するための変数
sqlite3_stmt *sqlstmt;

ret = sqlite3_prepare_v2(db,sql,-1,&sqlstmt,NULL);
//実行準備おk
if(ret == SQLITE_OK){
//sqlの実行を行う
ret = sqlite3_step(sqlstmt);
//sql文の解放
sqlite3_finalize(sqlstmt);

//sqlの実行が正常終了した場合
if(ret == SQLITE_DONE){
return YES;
}
}

//エラーメセッドをコール
[self errDataBase];
//dbクローズ
[self closeDataBase];
return NO;

}

-(BOOL)insertData{

NSArray *tmpArray = [[NSArray alloc] initWithObjects:@"test_01"
, @"test_02"
, @"test_03"
, @"test_04"
, nil
];

BOOL q_Ret = NO;

//各関数の戻り値
int ret;

//トランザクションの開始
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL);

//insert文の作成
sqlite3_stmt* sqlstmt;
char *sql = "insert into test(column_no1,column_no2)values(?,?)";
ret = sqlite3_prepare_v2(db, sql, -1, &sqlstmt, NULL);
//構文解析の結果問題なし(バインド前)
if (ret == SQLITE_OK) {
for (NSUInteger tmp=1; tmp<3; tmp++) {
//sqlをリセット
sqlite3_reset(sqlstmt);
//バインド変数をクリアー
sqlite3_clear_bindings(sqlstmt);
sqlite3_bind_text(sqlstmt,1,[[tmpArray objectAtIndex:(tmp-1)] UTF8String],-1,SQLITE_TRANSIENT);
sqlite3_bind_text(sqlstmt,2,[[tmpArray objectAtIndex:tmp] UTF8String],-1,SQLITE_TRANSIENT);
//sql文を実行
ret = sqlite3_step(sqlstmt);

//一回でもエラーが発生した場合はクローズさせて終了
if(ret != SQLITE_DONE){
//sql文の解放
sqlite3_finalize(sqlstmt);
//異常終了(ROLLBACKして処理を終了)
sqlite3_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
//エラーメソッドをコール
[self errDataBase];
//dbをクローズ
[self closeDataBase];
return q_Ret;
}
}
//正常終了(COMMITをして処理を終了)
sqlite3_exec(db, "COMMIT TRANSACTION", NULL, NULL, NULL);
q_Ret = YES;
NSLog(@"OKです");
//構文解析の結果問題あり(バインド前)
}else {
//エラーメソッドをコール
[self errDataBase];
//異常終了(ROLLBACKして処理を終了)
sqlite3_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
}

//sql文の解放
sqlite3_finalize(sqlstmt);

//クローズ
[self closeDataBase];
return q_Ret;
}

//sqliteをクローズする
-(void)closeDataBase{
sqlite3_close(db);
}

//sqliteのエラー処理
-(void)errDataBase{

UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle:[[NSString alloc] initWithFormat:@"error:%d",sqlite3_errcode(db)]
message:[[NSString alloc] initWithUTF8String:sqlite3_errmsg(db)]
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil
];
[alertView show];
[alertView release];

}

@end

バインドの方法は、SQLiteで“おこづかいちょう”の部分を読みながら、NSArrayに設定したNSStringをchar型に変換させて、それを、sqlite3_bind_text関数を使ってバインドさせてます。

同関数の第四引数には、SQLITE_TRANSIENTという定数があるが、これは、Binding Values To Prepared Statementsを読んで、SQLITE_STATICかSQLITE_TRANSIENTという選択肢があり、SQLITE_STATICは性的な場合のみ使えと書いてあったので、今回、SQLITE_TRANSIENTを採用しました。

ここからちょっと気になったのだが、SQLiteで“おこづかいちょう”に記載されていたコードには、sql文を解放させるsqlite3_reset関数が使われていたが、バインド変数をリセットするsqlite3_clear_bindingsが使われていなかった。

確かに、sqlite3_clear_bindings関数を使わずに実行をした結果、きちんと登録されているが、Reset A Prepared Statement Objectを読んでみると、

Any SQL statement variables that had values bound to them using the sqlite3_bind_*() API retain their values. Use sqlite3_clear_bindings() to reset the bindings.

via:Reset A Prepared Statement Object

と書いてあるので、やはり、sqlite3_clear_bindings使わないといけないんじゃーないのかなーっと疑問に思いつつ。

やっとこれでバインド変数を使った動的SQL文を作成することができました。

ただ、やはり、前回から指摘しているが、各メソッドに、カラム名及び、テーブル名を直指定してしまっている。

これを、NSStringなどの変数に設定して、動的に、テーブルを作成したり、ドロップしたりすることはできないのだろうか?
そうすれば、もっとプログラム全体が汎用的になるはず。

実は、その方法も発見したのですが、今回は、バインド変数を使った登録までなので、次回に譲りたい。

2010/06/21

[Objective-C][sqlite][iPad]CREATE文の発行

今、sqliteを使ってDBの管理を行おうとしているのですが、やっと、CREATE文の発行ができたので、そのプログラムの紹介です。

参考にさせていただいたのは、SQLite Tutorial - Selecting Data

上のエントリーでは、sqliteのdbをfirefoxのアドオンを使って作成して、それをObjective-Cを使ってiPhoneのアプリ内のディレクトリにコピーしているプログラムなのだが、そのやり方になんだか違和感を感じてしまったので、データベースの作成からObjective-Cで始めて、そこから、CREATE文の実行を行い、ターミナルで確認する作業を紹介します。

では、さっそくいきます。

//dbが存在しているかどうかの確認
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *dbPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"test.db"];
//ファイルが存在しない場合
if (![[NSFileManager defaultManager] fileExistsAtPath:dbPath]) {
//ファイルを作成する
BOOL result = [[NSFileManager defaultManager] createFileAtPath:dbPath contents:nil attributes:nil];
//ファイル作成が失敗した場合
if (!result) {

}
BOOL sql_exec_ok = NO;
sqlite3 *db;
int ret;
//sqliteをオープンする
ret = sqlite3_open([dbPath UTF8String],&db);
//sqliteのオープンに成功した場合
if (ret == SQLITE_OK) {
//sqlの設定
const char *sql = "create table test(column_no1,column_no2)";
//実行するsqlを格納する変数
sqlite3_stmt *sqlstmt;
ret = sqlite3_prepare_v2(db,sql,-1,&sqlstmt,NULL);
//sqlの準備完了の場合
if(ret == SQLITE_OK){
//sqlの実行を行う
ret = sqlite3_step(sqlstmt);
//sqlの実行が正常終了した場合
if(ret == SQLITE_DONE){
//sql文の解放
sqlite3_finalize(sqlstmt);
sql_exec_ok = YES;
}
}
}

//sqliteをクローズする
sqlite3_close(db);

//テーブルの作成が正常に完了できなかった場合
if(!sql_exec_ok){

}
}

まず最初の2行で、iPhoneのDocumentフォルダにあるtest.dbというファイルの確認を行います。

ファイルが存在する場合は、何も処理を行いません。

ファイルが存在しない場合は、一つ目のif文を通かした後の、処理でファイルの作成を行います。
(さらなる詳細については、[Objective-C][iPad]ファイルを書き出すを参照のこと)

で、ここからが重要で、まず、sqlite3_open関数を使って、データベースをオープンします。

オープンに成功した場合は、その勢いに乗っかって、sql文を記述します。

記述完了後、今度は、sqliteを実行するぞーっというのをプログラムに伝えるために、sqlite3_prepare_v2関数を使います。
(ここは、sqlite3_prepareもありますが、ドキュメントでは、sqlite3_prepare_v2が推奨されています。)

if文を使って準備がオッケーだった場合、sqlite3_step関数を使って、いざ実行

実行が正常終了した場合、sqlite3_finalize関数を使って、sql文の解放を行います。

そして、最後に、sqlite3_close関数を使って、データベースをクローズします。

script系言語からdbを触ったことがある方には、sql文の解放作業と準備作業というものがあることに少し気持ち悪さを感じるかもしれません。
(それでも、CFデベロッパーよりもましで、CFの場合、sqlのオープン、クローズの概念がないので、おそらくそれ以上に気持ち悪さを感じると思います。)

これを実行すると所定のフォルダに、データベースファイルができており、ターミナルからそのファイルをsqlite3コマンドを使ってオープンすると、確かに、テーブルができていることが確認できました。

ただ、やはりまだプログラム全体に気持ち悪さを感じてしまう。

データベースのオープンや、クローズは、別メソッドにして、そこで管理したりする方が、汎用性が高いかもしれない。
さらに、今回は、CREATE文だけだったから、この量ですんだものの、INSERT文だったり、UPDATE文だったりしたら、もっと増えると考えると、やはり、もっと柔軟に作り返る必要性があるかもしれない。

そこの辺は、また出来次第、改めて、考察していく。

補足;
上で紹介したエントリーに日本語和訳が、あるみたいです。↓で紹介されていました。
■[iPhone]SQLite Tutorialを日本語に訳した 20:22

でも、エントリーをいくら探しても、日本語訳見つからないんだよなー。。。
どうしたもんでしょ?

2010/06/18

[sqlite]は、[mac]にビルドインされていた

やはり、予想とおり、初期インストールされていた。

Mac OSX 10.6 Snow Leopardには、SQLiteという軽量データベースがはじめからインストールされています。

via:Mac OS X. 10.6 Snow LeopardでSQLite3を動かしてみたよ。

そう思ったのは、とても単純な理由からでPHPが初期インストールされているから。

PHPをインストールするとsqliteがバンドルされているので、ターミナルからおそらく実行できることは、容易に想像できた。

しかし、問題は、どうやって実行するのかというところ。

SQLite3を終了する

.exitまたは.quitで終了することができます。

・・・省略・・・

sqlite> .quit

・・・省略・・・

データベースの作成

・・・省略・・・

$ sqlite3 hoge.db

・・・省略・・・

via:Mac OS X. 10.6 Snow LeopardでSQLite3を動かしてみたよ。

これで、iPhoneで作成したsqliteに本当にデータが登録されているか確認することができるぞと。

2010/06/16

[sqlite][C]言語を使った処理の流れ その1

現在、iPadアプリでsqliteを使う方法を模索しているのですが、調査の結果として、C言語を使ってsqliteのsql文を実行すればいいことまでたどりつき、そもそもC言語の場合におけるsqliteの処理の流れがわかっていないので、その確認から。

An Introduction To The SQLite C/C++ Interfaceを読むと、

sqlite3_open()

sqlite3_prepare()

sqlite3_step()

sqlite3_column()

sqlite3_finalize()

sqlite3_close()

という流れになっています。

まず、sqlite3_open関数を使って、databaseをオープンします。

次に、sqlite3_prepare関数(sqlite3_prepare_v2が、現行では推奨されている。)を使って、今度は、実行対象となるsql文の準備を行います。

で、sqlite3_step関数(fetch作業に相当)を使って、sql文の実行を行い、sqlite3_column関数で、各カラムの値を取得します。

最後に、sqlite3_finalize関数で、sql文のクリアーを行い、sqlite3_close関数で、databaseのクローズをします。

だいたい大まかな流れとしては、こんな感じ。

しかし、若干、必要される処理が多い気がするのですが。。。

上記を簡便するために、sqlite3_exec関数が用意されているようなのですが、うーん、まだ実行していないので、実際ところはどうなのでしょうか?

引き続き、調査を行います。

2009/12/09

[Gears]googleが撤退する可能性がでてきた

clmemo@akaさんの@ Google、Gears から HTML5 へというエントリーを読んでびっくりしたのですが、なんと、GoogleがGearsから手を引くらしいのです。

なぜだ??

いずれリリースされるChrome OSはオフラインでもクライアントアプリケーション並みに協力なwebアプリケーションをサポートするのが必須なのではないのか??

ともう少し先を読んでみると、どうやらhtml5が同様の機能をサポートするみたいなのです。

確かにhtml5が同機能をサポートするなら必要ないとは思うんだけど。。。

なんだか少し寂しい気がするなー。。。

2009/11/21

[Objective-C][iPhone sdk]iPhoneでsqliteを使用について

ColorMakerの次期バージョンでは、いくつかの案があるのですが、その中で、sqliteを使ってデータを保存したいなーと思い、保存方法及びsqliteの使用方法について調べてみることにしました。

が、

ぐぐってみると、

SQLite and CoreData - iPhone SDK Articles

に、OSのバージョン3からはsqliteではなく、Core Dataが推奨されているみたいなのですが、実際、iPhone OS Technology OverviewのSQLiteを読むと、やはりサポートされているのでできればこちらを使いたいです。

というのもCore Dataのマニュアルが膨大すぎて、sqlの方が工数がかからなさそうだと思ったので。。。
(先々にちょっとやりたいなーっと考えているgoogle app engineのことを考えるとやった方がいいのかなと思ったりもするのですが)

なわけで、sqliteで突撃~~~♪

2008/11/13

[gears]sqliteのオブジェクトを取得する

gearというよりも、sqliteの仕様なのだが、objectを取得したい場合、sqlite_masterを使って取得することができます。

select * from sqlite_master;

を実行すれば、以下のカラムをゲットすることができます。

type|name|tbl_name|rootpage|sql

typeと、sqlはわかるのですが、nameとtbl_nameの違いがわからない。。。
rootpageってどこからのルートなのでしょうか?

個人的には、create tableを最初にしたときに、できたtableのroot pageを1とし、次にcreateをしたときは、2とindexのような働きをするのかなと思っています。

この辺は間違っている可能性があるので、間違っていたら、また訂正しようと思っています。