C++でプレースホルダを利用したsql文作成(sqlite3)
先日C++でsqlite3を用いてDBを操作してみました。
elsammit-beginnerblg.hatenablog.com
elsammit-beginnerblg.hatenablog.com
こちらのブログでは実行するsql文を下記の通りべた書きで記載していました。
char sql2[30] = "select * from huga;";
sqlite3_prepare_v2(db, sql2, strlen(sql2), &stmt, NULL);ですが、sql文を作成するにあたり変数を用いたい場合があるかと思います。
今回は、sql文を生成させるにあたり変数をどのように与えればよいかをまとめたいと思います。
■プレースホルダについて
sqlite3で引数を渡す場合、プレースホルダとバインドを利用する必要があります。
プレースホルダを用いずに、
snprintf(sqlstr,30,"select * from huga where name=%s",huga); ret = sqlite3_exec(db, sqlstr, NULL, NULL, &err);
とした場合、hugaにSQLインジェクションが可能になってしまいます。
プレースホルダを用いることにより与える変数がSQL文と認識・実行されなることがなくなります。
このため、プレースホルダを用い、プレースホルダとして指定された位置にバインドにより変数を与えることが一般的になります。
【プレースホルダとは?】
正式な値が入るまで一時的に場所を確保しておく方法のことを指します。
今回の場合、sql文で引数を与えたい箇所を仮置きで値を挿入しておくことを指します。
■条件
今回も前回と同様にsqlite3を用いかつ、テーブルは下記を用いることにします。

■プレースホルダを用いた実装
では各sql文の実装をまとめていきたいと思います!!
まずはselect文から。
コードはこちら。
int SelectDB(){
sqlite3 *db = NULL;
char* err = NULL;
sqlite3_stmt *stmt;
int rc;
int ret = sqlite3_open("data.db", &db);
if(ret != SQLITE_OK){
printf("ERROR(%d) %s\n",rc,sqlite3_errmsg(db));
return -1;
}
unsigned int id = 5;
rc = sqlite3_prepare_v2(db,"select * from tableA where id=?", -1, &stmt, 0);
if(rc != SQLITE_OK){
printf("ERROR(%d) %s\n",rc, sqlite3_errmsg(db));
sqlite3_close(db);
return -1;
}
sqlite3_bind_int(stmt, 1, id);
rc = sqlite3_step(stmt);
while(rc == SQLITE_ROW){
printf("%d %s %s %lld %lld %lld \n",
sqlite3_column_int(stmt,0),
sqlite3_column_text(stmt,1),
sqlite3_column_text(stmt,2),
sqlite3_column_int64(stmt,3),
sqlite3_column_int64(stmt,4),
sqlite3_column_int64(stmt,5)
);
rc = sqlite3_step(stmt);
}
rc = sqlite3_finalize(stmt);
if(rc != SQLITE_OK){
printf("ERROR(%d) %s\n",rc,sqlite3_errmsg(db));
}
sqlite3_close(db);
return 0;
}プレースホルダを用いるにあたり、sql文をこちらのように定義しております。
rc = sqlite3_prepare_v2(db,"select * from tableA where id=?", -1, &stmt, 0);
引数として与えたい位置を"?"とすることでプレースホルダとすることが出来ます。
こちらのプレースホルダに値を入れる際には、
sqlite3_bind_int(stmt, 1, id);
というようにバインドさせればOKです。
次にinsert文です。
int InsertDB(){
sqlite3 *db = NULL;
char* err = NULL;
sqlite3_stmt *stmt;
int rc;
int ret = sqlite3_open("data.db", &db);
if(ret != SQLITE_OK){
printf("[%s] FILE Open Error \n", __func__);
return -1;
}
unsigned int id = 1;
rc = sqlite3_prepare_v2(db,"insert into tableA (id,name,type,cost,size,weight) values "
"(?,?,?,?,?,?)", -1, &stmt, 0);
if(rc != SQLITE_OK){
printf("ERROR(%d) %s\n",rc, sqlite3_errmsg(db));
sqlite3_close(db);
return -1;
}
sqlite3_bind_int(stmt, 1, id);
sqlite3_bind_text(stmt, 2, "nameA", -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 3, "typeB", -1, SQLITE_STATIC);
sqlite3_bind_int64(stmt, 4, "100000");
sqlite3_bind_int(stmt, 5, "30");
sqlite3_bind_int(stmt, 6, "15");
rc = sqlite3_step(stmt);
if(rc != SQLITE_DONE){
printf("ERROR(%d) %s\n",rc,sqlite3_errmsg(db));
}
rc = sqlite3_finalize(stmt);
if(rc != SQLITE_OK){
printf("ERROR(%d) %s\n",rc,sqlite3_errmsg(db));
}
sqlite3_close(db);
return 0;
}プレースホルダは先ほどのselect文と同様に、
rc = sqlite3_prepare_v2(db,"insert into tableA (id,name,type,cost,size,weight) values "
"(?,?,?,?,?,?)", -1, &stmt, 0);といった形で変数を与えたい位置を"?"という形でプレースホルダを定義すればOKです。
プレースホルダに値を与える処理は、
sqlite3_bind_int(stmt, 1, id);
sqlite3_bind_text(stmt, 2, "nameA", -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 3, "typeB", -1, SQLITE_STATIC);
sqlite3_bind_int64(stmt, 4, "100000");
sqlite3_bind_int(stmt, 5, "30");
sqlite3_bind_int(stmt, 6, "15");といった処理となります。
ここで、int、文字列、long型はそれぞれ、
【int】 sqlite3_bind_int()関数 【文字列】 sqlite3_bind_text()関数 【long型】 sqlite3_bind_int64()関数
を使い分けてbind関数を用いる必要があります。