2016年2月25日木曜日

MySQLとSQLiteを切り替える

今週はデータベースの勉強期間なのでデータベースネタをもう一つ。たぶんこれでデータベース系は最後です。

僕、SQLiteって軽くて高速なので、大好きでよく使います。
だけれど、スマートフォンなどのクライアント側はSQLiteが主に使われているのに、サーバ側はMySQLがメインで、使用するデータベースが違っていてサーバとクライアントで共通化できなくて困ります。
SQLiteでさくっとプロトタイプを作って、あとからMySQLに移行してリプリケーションさせてスケールさせることもよくあります。
MySQLって商用のサーバ運用するときにはとっても便利なんですが、ローカルでWebAPIとかを開発するときは自分のPCにインストールするのがめんどくさい。

また、どの言語のどのインタフェースで書くかにもよるのですが、MySQLとSQLiteの両方で動くようにするには関数のインタフェースって微妙に違っていて困ります。JavaだとJDBCがあるのですが、JDOとかPDOとかODBCとか共通化規格がそもそも共通化されてないし。データベースってSQL文投げて複数の行が返ってくるだけなのに、なんでこう関数が違うんでしょうね。簡単なラッパーでよいんでない?

ということで、MySQLとSQLiteを直接切り替えて呼び出せるラッパー関数を作ってみました。僕は高速に動作するC/C++が大好きなので、実装はいつものようにC/C++ですが、ほかの言語でも同じようにすれば複数のデータベースを併用することができます。

前回のブログで書いたMySQLの関数の動的呼び出しを行っているので、ビルド時にMySQLのライブラリはいりません。

これで簡単にデータベースを切り替えられる!
ローカルで開発とかデバッグするときはSQLiteで行い、最終運用はMySQLで行うというようにデータベースを使い分けることや、サーバとクライアントでコードを共通化することもできます。
C言語なのでAndroid、iPhone、Linuxサーバ、Windowsサーバどれでも動くしとても便利。



mysql_sqlite.h
------------------------------------------------------------
#ifndef MY_SQL_SQLITE_H_
#define MY_SQL_SQLITE_H_


#ifdef __cplusplus
extern "C"
{
#endif

typedef void* HDBSQL;
typedef void* HDBCOL;


HDBSQL mydb_connect(const char* server,const char* user,const char* pass,const char* dbname);
void mydb_close(HDBSQL hdb);

int mydb_exec(HDBSQL hdb,const char* str);
HDBCOL mydb_prepare(HDBSQL hdb, const char* str);
int mydb_step(HDBCOL col);
void mydb_free_result(HDBCOL col);

int mydb_column_int(HDBCOL col,int n);
const char* mydb_column_char(HDBCOL col,int n);


#ifdef __cplusplus
}
#endif

#endif /* MY_SQL_SQLITE_H_ */
------------------------------------------------------------


mysql_sqlite.c
------------------------------------------------------------
//#define I_USE_MYSQL
#define I_USE_MYSQL_WRAPPER
#define I_USE_SQLITE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef I_USE_MYSQL
#ifdef I_USE_MYSQL_WRAPPER
#include "mysql_wrapper.h"
#else
#include <mysql/mysql.h>
#endif
#endif

#ifdef I_USE_SQLITE
#include "sqlite3.h"
#endif

#include "mysql_sqlite.h"

HDBSQL mydb_connect(const char* server, const char* user, const char* pass, const char* dbname)
{
HDBSQL ret = NULL;
if (server == NULL || user == NULL || pass == NULL || dbname == NULL)goto end;
#ifdef I_USE_MYSQL
MYSQL *conn = NULL;
conn = mysql_init(NULL);
if (!mysql_real_connect(conn, server, user, pass, dbname, 0, NULL, 0)) {
goto end;
}
ret = (HDBSQL)conn;
#endif
#ifdef I_USE_SQLITE
sqlite3 *conn = NULL;
if(SQLITE_OK != sqlite3_open("db_test", &conn)){
goto end;
}
ret = (HDBSQL)conn;
#endif
end:
return ret;
}
void mydb_close(HDBSQL hdb)
{
if (hdb == NULL)return;
#ifdef I_USE_MYSQL
mysql_close((MYSQL*)hdb);
#endif
#ifdef I_USE_SQLITE
sqlite3_close((sqlite3*)hdb);
#endif
}

int mydb_exec(HDBSQL hdb, const char* str)
{
int ret = -1;
if (hdb == NULL || str==NULL)return ret;
#ifdef I_USE_MYSQL
if (mysql_query((MYSQL*)hdb, str)){
//error
ret = -1;
}else {
//ok
ret = 0;
}
#endif
#ifdef I_USE_SQLITE
if (SQLITE_OK != sqlite3_exec((sqlite3*)hdb, str, NULL, NULL, NULL))
{
ret = -1;
}else {
ret = 0;
}

#endif
return ret;
}


struct hdb {
void* resp;
void* row;
};

HDBCOL mydb_prepare(HDBSQL hdb, const char* str)
{
HDBCOL ret = NULL;
if (hdb == NULL || str == NULL)goto end;

#ifdef I_USE_MYSQL
ret = (HDBCOL)malloc(sizeof(struct hdb));
if (ret == NULL)goto end;
memset(ret, 0, sizeof(struct hdb));

if (mysql_query((MYSQL*)hdb, str)) {
//error
free(ret);
goto end;
}
((struct hdb*)ret)->resp=(HDBCOL)mysql_use_result((MYSQL*)hdb);
#endif
#ifdef I_USE_SQLITE
if (SQLITE_OK != sqlite3_prepare((sqlite3*)hdb, str, strlen(str), (sqlite3_stmt**)(&ret), NULL))
{
ret = NULL;
}
#endif
end:
return ret;
}

void mydb_free_result(HDBCOL col)
{
if (col == NULL)return;
#ifdef I_USE_MYSQL
mysql_free_result((MYSQL_RES*)(((struct hdb*)col)->resp));
free(col);
#endif
#ifdef I_USE_SQLITE
sqlite3_finalize((sqlite3_stmt*)col);
#endif
}

int mydb_step(HDBCOL col)
{
int ret = 0;
if (col == NULL)return ret;
#ifdef I_USE_MYSQL
((struct hdb*)col)->row= mysql_fetch_row((MYSQL_RES*)(((struct hdb*)col)->resp));
if (((struct hdb*)col)->row == NULL)return ret;
ret = 1;
#endif
#ifdef I_USE_SQLITE
if (SQLITE_ROW == sqlite3_step((sqlite3_stmt*)col))ret = 1;
#endif
return ret;
}

int mydb_column_int(HDBCOL col,int n)
{
int ret = 0;
const char* p = NULL;
if (col == NULL)return ret;
#ifdef I_USE_MYSQL
if (((struct hdb*)col)->row == NULL)return ret;
p=((MYSQL_ROW)(((struct hdb*)col)->row))[n];
if (p == NULL)p = "0";
ret = atoi(p);
#endif
#ifdef I_USE_SQLITE
ret= sqlite3_column_int((sqlite3_stmt*)col, n);
#endif
return ret;

}
const char* mydb_column_char(HDBCOL col,int n)
{
const char* ret = NULL;
#ifdef I_USE_MYSQL
if (((struct hdb*)col)->row == NULL)return ret;
if (((struct hdb*)col)->row == NULL)return ret;
ret = ((MYSQL_ROW)(((struct hdb*)col)->row))[n];
#endif
#ifdef I_USE_SQLITE
ret = sqlite3_column_text((sqlite3_stmt*)col, n);
#endif
return ret;
}

#if 0

int main(void) {
HDBSQL conn = NULL;
HDBCOL resp = NULL;

char sql_str[255];
char *sql_serv = "localhost";
char *user = "root";
char *passwd = "wanted";
char *db_name = "db_test";


conn = mydb_connect(sql_serv,user,passwd,db_name);
if (conn == NULL) {
printf("mydb_connect error\n");
exit(-1);
}

memset(&sql_str[0], 0x00, sizeof(sql_str));
snprintf(&sql_str[0], sizeof(sql_str) - 1, "select * from tb_test");
resp = mydb_prepare(conn, sql_str);
if (resp==NULL) {
mydb_close(conn);
exit(-1);
}

while (mydb_step(resp)){
printf("%d : %s\n", mydb_column_int(resp,0),mydb_column_char(resp,1));
}
mydb_free_result(resp);
mydb_close(conn);

return 0;
}

#endif
------------------------------------------------------------





0 件のコメント:

コメントを投稿