1. 要求
在sqlite表结构发生变化后,现场升级数据库db文件,能够自动完成。并要求旧表中的数据不能丢失或破坏。
sqlite表结构发生变化,指的是数据库表个数发生变化,或者表中的字段(字段名字,字段类型,字段个数,字段顺序)发生变化。
2. 设计
2.1 升级条件
在现有数据库表字段与最新数据库表字段不相同的情况下,需要升级该表。
相同:指的是字段名称和顺序相同。
2.2 新旧表结构是否相同的情况
上述A~H 8种情况,可分为两类,A~D是第一类顺序相同名称不同,E~H 是第二类顺序和名称都不相同。
8种情况处理如下:
A情况:不用变动
B情况:给旧表新增列
C情况:将旧表通过swap方法升级为新表
D情况:处理同C
E情况:处理同C。E其实和A是相同的。但是因为现在软件代码中插入记录使用的是bind方法,bind方法要求插入的字段和顺序与数据库表中的字段和顺序相同。所以才不得不将E单独处理。
F情况:处理同C。F同B
G情况:处理同C。G同C
H情况:处理同C。H同D
2.3 Swap方法
1、 将被升级的旧表重命名
2、 创建新表
3、 将被升级表的数据插入到新表中
4、 删除旧表
3. 代码实现
sqlite基于3.8.4.3版本。仅给出主要数据结构设计。主要的类设计有两个:Sqlite3 和 DB_upgrade
Sqlite3 类负责打开、关闭db文件,并将db文件中的表和字段都读取出来。
DB_upgrade 类负责对db文件升级过程的整体调度、判断和执行。它会将旧db升级为新db;如果db文件不存在则会直接创建该db。
class Sqlite3:public CppSQLite3DB
{
public:
Sqlite3(){};
~Sqlite3(){};
void Open(std::string dbname);
void Close();
void Load();
public:
enum ColumnStatus
{
cs_equal, //0-新旧表都有,且位置相同
cs_not_exist_in_old, //1-旧表有新表无
cs_not_exist_in_new, //2-旧表无新表有
cs_pos_diffrent, //3-新旧表都有,但位置不同
};
enum TableStatus
{
/*
Table_old表字段 Table_new表字段
x,y,z x,y,z A情况
x,y x,y,z B情况
x,y,w x,y,z C情况
x,y,z,w x,y,z D情况
x,z,y x,y,z E情况
x,z,y x,y,z,w F情况
x,z,w x,y,z G情况
x,z,y,w x,y,z H情况
对于情况A,不需要变动Table_old表。
对于情况B,只需要给Table_old表新增列即可。
对于情况C/D/E/F/G/H,都可采用swap方法实现升级。
swap方法见设计文档。
*/
ts_A, //A情况
ts_B, //B情况
ts_C, //C情况
ts_D, //D情况
ts_E, //E情况
ts_F, //F情况
ts_G, //G情况
ts_H, //H情况
ts_Q, //一个表在新库中有但在旧库中没有,标记为本值。
ts_default, //初始值
};
//sqlite_master实际存储的是每个表的信息
struct sqlite_master
{
std::string type; //有"index"/"table"两个值
std::string name;
std::string tbl_name;
int rootpage;
std::string sql; //对于表是建表语句
};
//table_info实际存储的是每个表中所有字段的信息
struct table_info
{
int cid;
std::string name;
std::string type;
int notnull;
std::string dflt_value;
int pk;
ColumnStatus cs;
};
struct TableInfo
{
sqlite_master table;
std::vector<table_info> columns;
TableStatus ts;
};
/* m_mapMaster
key:tbl_name
value:TableInfo
*/
typedef std::map<std::string,TableInfo> T_TableInfo;
T_TableInfo m_mapMaster;
std::string m_dbname;
};
class DB_upgrade
{
public:
DB_upgrade();
~DB_upgrade();
void Set(std::string dbSrc,std::string dbTmp,int tableNums,const char **tableNameArray,const char **tableSqlArray);
/* 构建或升级数据库表结构 */
void CreateOrUpgradeTable();
private:
void CreateTable();
void UpgradeTable();
private:
void CreateTmpTable();
void DeleteTmpTable();
bool CheckCaseA(Sqlite3::TableInfo &src,Sqlite3::TableInfo &tmp);
bool CheckCaseB(Sqlite3::TableInfo &src,Sqlite3::TableInfo &tmp);
void AddNewColumns(Sqlite3 &sqto,Sqlite3::TableInfo &to,Sqlite3::TableInfo &from);
void Swap(Sqlite3 &sqto,Sqlite3::TableInfo &to,Sqlite3 &sqfrom,Sqlite3::TableInfo &from);
void KeepSameColumns(std::vector<Sqlite3::table_info> &to,std::vector<Sqlite3::table_info> &from);
private:
std::string m_dbTmp;
std::string m_dbSrc;
int m_tableNums;
char **m_tableNameArray;
char **m_tableSqlArray;
};