Elsaの技術日記(徒然なるままに)

主に自分で作ったアプリとかの報告・日記を記載

MENU

sqlite3でDBを操作してみる(C++編)

以前sqlについて簡単にまとめました。
今までnodejsやpython、go言語などでsqlを用いてDBの操作したことがあったのですが、
CやC++sqlを利用したことがなかったので今回使ってみました!!
elsammit-beginnerblg.hatenablog.com

ちょっとばかしクセのある使い方だったので備忘録として残しておこうと思います。
f:id:Elsammit:20210529125301j:plain



■環境構築

今回用いる環境ですが"Ubuntu 20.04"を用いました。

C言語C++でsqlite3 を用いる場合には、

sqlite3.h

が必要になるのですが、標準ではこららのライブラリが未インストールになります。
そこでこちらのコマンドを実行してライブラリのインストールを行います。

$ sudo apt-get install sqlite3
$ sudo apt-get install libsqlite3-dev

これで環境構築は完了です。

また今回はデータベースとして、
hoge.db
を用意し、こちらにテーブルの作成やテーブル内にデータを挿入したりしたいと思います。
データベースですが、

sqlite3 hoge.db

とコマンド実行頂ければ生成可能です。

■sqlite3操作(データベースを開く)

では実装していきます。
まずは先ほど作成したデータベースをオープンしてみたいと思います。
コードはこちら。

#include <sqlite3.h>
#include <stdio.h>

int main(void){
    sqlite3 *db = NULL;
    int ret = sqlite3_open("./hoge.db", &db);
    if(ret != SQLITE_OK){
        printf("FILE Open Error \n");
        return -1;
    }
    sqlite3_close(db);
    printf("Finish \n");
    return 0;
}

まずsqlite3型のポインタを生成します。
この変数ですが、データベースハンドルと呼ばれ、以降データベースを操作する際の核となる変数です。
こちらのハンドルを生成しデータベースと紐づけるがsqlite3_open関数になります。
sqlite3_open関数は
 ・第1引数:データベースファイルパス
 ・第2引数:データベースハンドル
になります。

オープンしたデータベースはクローズすることが必要なので、

sqlite3_close(db);

を最後に実行しています。

ではこちらを実行していきたいと思います。
ビルドを実行するにあたり、libsqlite3.soのリンクが必要になります。
例えばこちらのようにビルドを実行すればOKです。

$ g++ -o execute sql_test.cpp -lsqlite3

無事ビルドが完了したら生成物を実行してみてください。
finishという文字列がコンソール上に表示されればOKです。

■sqlite3操作(テーブルを作成する)

次にテーブルを作成していきます。
今回作成するテーブルはこちらとしました(理由はないです)。
f:id:Elsammit:20210529115441p:plain

こちらのテーブルを生成するコードですがこちらになります。

#include <sqlite3.h>
#include <stdio.h>

int main(void){
    sqlite3 *db = NULL;
    char* err = NULL;
    
int ret = sqlite3_open("./hoge.db", &db);
    if(ret != SQLITE_OK){
        printf("FILE Open Error \n");
        return -1;
    }

    ret = sqlite3_exec(db, "create table huga(id INTEGER, name TEXT, category TEXT, cost INTEGER, size INTEGER, weight INTEGER);", NULL, NULL, &err);
    if(ret != SQLITE_OK){
        printf("Execution Err \n");
        sqlite3_close(db);
        sqlite3_free(err);
        return -1;
    }

    sqlite3_close(db);
    printf("Finish \n");
    return 0;
}

テーブル作成にあたりsqlite3_exec関数を用いました。
sqlite3_exec関数ですが、引数として与えられたデータベースハンドルに対して、引数として与えているsql文が実行できる関数になります。

sqlite3_exec関数の引数ですが、こちらの通りになっております。

sqlite3_exec(データベースハンドル, sql文, コールバック関数, コールバック関数に渡す引数, エラーメッセージ);

今回コールバック関数を使用する必要がないので、NULLにしております。

こちらを実行した後に下記を実行すると、
データベース内にテーブルが作成されていることが確認できるかと思います。

$sqlite3 hoge.db
sqlite> .table

■sqlite3操作(データを挿入)

ではでは続いて作成したテーブルにデータを挿入してみます。
と言ってもこちらは先ほどのテーブル作成と同様にsqlite3_exec関数を用いればOKです。

#include <sqlite3.h>
#include <stdio.h>

int main(void){
    sqlite3 *db = NULL;
    char* err = NULL;
    
    int ret = sqlite3_open("./hoge.db", &db);
    if(ret != SQLITE_OK){
        printf("FILE Open Error \n");
        return -1;
    }

    char insertMsg[100] ="insert into huga values(1, 'AAA', 'aaa', 10, 100, 111);" ;
    ret = sqlite3_exec(db, insertMsg, NULL, NULL, &err);

    sqlite3_close(db);
    printf("Finish \n");
    return 0;
}

■sqlite3操作(テーブル有無チェック)

ここで、データ挿入の前にテーブルの有無をチェックしたい場合があるかと思います。
その場合にはこちらのようなコードにすればOKです。

#include <sqlite3.h>
#include <stdio.h>
#include <iostream>

int CallBack_CheckTable(void* param, int col_cnt, char** row_txt, char** col_name){
    *(int*)param = atoi(row_txt[0]);
    return 0;
}

int main(void){
    sqlite3 *db = NULL;
    char* err = NULL;
    int count = 0;
    
    int ret = sqlite3_open("./hoge.db", &db);
    if(ret != SQLITE_OK){
        printf("FILE Open Error \n");
        return -1;
    }

    char sql[100] = "select count(*) from sqlite_master  where type='table' and name='huga';";
    ret = sqlite3_exec(db, sql, CallBack_CheckTable, (void*)(&count), &err);

    if(count <= 0){
        ret = sqlite3_exec(db, "create table huga(id INTEGER, name TEXT, category TEXT, cost INTEGER, size INTEGER, weight INTEGER);", NULL, NULL, &err);
        if(ret != SQLITE_OK){
            printf("Execution Err \n");
            sqlite3_close(db);
            sqlite3_free(err);
            return -1;
        }
    }

    char insertMsg[100] ="insert into huga values(1, 'AAA', 'aaa', 10, 100, 111);" ;
    ret = sqlite3_exec(db, insertMsg, NULL, NULL, &err);

    sqlite3_close(db);
    printf("Finish \n");
    return 0;
}

急に長くなってしまいましたね。。。
今までsqlite3_exec関数のコールバック関数を利用していなかったのですが、今回はsql文により得られた結果を用いて判定が必要だったので利用しました。

まずsql文のおさらいですが、

select count(*) from sqlite_master  where type='table' and name='huga';

を実行するとnameで与えたテーブル名の個数がカウントされます。
こちらを用いれば、テーブルがなければ0が返ってきますし、テーブルがあれば1が返ってくる形になります。

こちらのsql文をsqlite3_exec関数で実行します。
そして実行した結果テーブル有無をチェックするためにコールバック関数CallBack_CheckTableを用意しました。
テーブル有無の結果を格納するためにコールバック側で利用する変数としてcountをセットしました。

  int count = 0;
 char sql[100] = "select count(*) from sqlite_master  where type='table' and name='huga';";
 ret = sqlite3_exec(db, sql, CallBack_CheckTable, (void*)(&count), &err);

コールバック関数CallBack_CheckTableはこちらになります。

int CallBack_CheckTable(void* param, int col_cnt, char** row_txt, char** col_name){
    *(int*)param = atoi(row_txt[0]);
    return 0;
}

コールバック関数の引数ですがあらかじめ決められており、

int コールバック関数名(void* sqlite3_execの第4引数, int 列数, char** 行内容, char** 列名);

になります。
今回1行・1列目にテーブル有無数が格納されております。
このため、テーブルが存在すれば引数として与えていたparamに1を、なければ0を入れております。

ここまでがテーブル有無チェックでした。
そしてここからテーブルがなければテーブルの作成を行っていくのですが、
こちらは下記の通り、結果を見て未作成であれば先ほどのテーブル作成を実行すればOKです。

    if(count <= 0){
        ret = sqlite3_exec(db, "create table huga(id INTEGER, name TEXT, category TEXT, cost INTEGER, size INTEGER, weight INTEGER);", NULL, NULL, &err);
        if(ret != SQLITE_OK){
            printf("Execution Err \n");
            sqlite3_close(db);
            sqlite3_free(err);
            return -1;
        }
    }

■最後に

今回はC++でのsql文を用いたDB操作をまとめてみました。
本当はselect * from文の利用方法についてもまとめたいと思ったのですが、、、
ここまで長くなってしまったのでまた後日まとめたいと思います!!