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を使ってどうにかならないものかなーっと想定しているのだが。

0 コメント:

コメントを投稿