1. 程式人生 > >嵌入式環境下使用SQLite

嵌入式環境下使用SQLite

環境搭建

下載SQLite的原始碼,https://www.sqlite.org/download.html,下載名為sqlite-autoconf的包,在交叉編譯環境下(我的是Ubuntu系統)使用arm編譯器進行編譯。

# 將包內的檔案提取出來,假定提取後的資料夾名為sqlite-autoconf-3250200

# cd到此目錄,使用./configure --host=arm-arago-linux-gnueabi --prefix=/home/root/Desktop/sqlite/build
PS:我的交叉編譯工具鏈中gcc全名為arm-arago-linux-gnueabi-gcc,所以host就填寫為arm-arago-linux-gnueabi,configure用到的gcc、g++、strip等都會以arm-arago-linux-gnueabi為字首自動引用到。prefix就是生成的so和標頭檔案的路徑,可以自己定義

# 等待configure完成後,執行make && make install

# make完成後將build目錄下,lib資料夾中的檔案拷貝到開發板系統中的/lib目錄下,將bin中的檔案拷貝到開發板系統的/bin目錄下

# 在開發板系統中執行sqlite3 test.db命令,如果可以執行成功,則說明移植成功,執行成功出現

SQLite version 3.25.2 2018-09-25 15:08:22
Enter ".help" for usage hints.
sqlite> 

我第一次執行並沒有成功,因為依賴libreadline.so.5,既然能編譯成功,說明交叉編譯環境中是有這個庫的,使用locate libreadline查詢庫的位置,並拷貝到開發板系統的/lib目錄下。
注意:如果出現了多個libreadline庫的位置,需要拷貝交叉編譯器目錄下的庫,不能使用當前系統中的libreadline庫。我的libreadline庫位置在/usr/local/arm/cross/am335xt3/devkit/arm-arago-linux-gnueabi/usr/lib/下,大家根據自己的環境查詢。

# 開發板的環境搭建好了,還需要將生成的build/lib目錄下的檔案拷貝一份到交叉編譯器的lib下,使之可以編譯c/c++程式碼成功,並且將build/include目錄下的標頭檔案拷貝到交叉編譯器的include目錄下,我的include目錄在/usr/local/arm/cross/am335xt3/devkit/arm-arago-linux-gnueabi/usr/include

# 這時,交叉編譯環境下的sqlite也搭建好了,注意編譯時需要連結sqlite3庫,例如arm-arago-linux-gnueabi-gcc test.c -lsqlite3

示例程式碼

建立表、插入表項

#include <stdio.h>
#include <sqlite3.h>
int main( void )
{
	sqlite3 *db=NULL;
	char *error_msg = NULL;
	int ret = 0;
	char *sql;
	//開啟資料庫檔案,如果不存在將建立一個數據庫檔案
    sqlite3_initialize();
	ret = sqlite3_open_v2("test.db", &db,SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE,NULL); 
	if
( ret ) { sqlite3_close_v2(db); sqlite3_shutdown(); return 0; } //建立表,如果表存在,則不建立,並給出提示資訊,儲存在 error_msg 中 //表中只有一個自增鍵 每次插入自動+1 sql = "CREATE TABLE test_table(\ ID INTEGER PRIMARY KEY, \ );"; sqlite3_exec( db, sql, 0, 0, &zErrMsg ); //插入資料 sql = "INSERT INTO \"test_table\" VALUES(null);"
; //執行sql語句 sqlite3_exec( db, sql, 0, 0, &zErrMsg ); sqlite3_exec( db, sql, 0, 0, &zErrMsg ); sqlite3_exec( db, sql, 0, 0, &zErrMsg ); sqlite3_exec( db, sql, 0, 0, &zErrMsg ); sqlite3_exec( db, sql, 0, 0, &zErrMsg ); sqlite3_exec( db, sql, 0, 0, &zErrMsg ); sqlite3_close_v2(db); //關閉資料庫 sqlite3_shutdown(); return 0; }

查詢表項

接上邊的插入好的資料庫檔案

#include <stdio.h>
#include <sqlite3.h>
int main()
{
	sqlite3 *db = NULL;
	char *error_msg = NULL;
	int ret = 0;
	sqlite3_initialize();
	ret = sqlite3_open_v2("test.db", &db,SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE,NULL); 
	if( ret )
	{
		sqlite3_close_v2(db);
        sqlite3_shutdown();
		return 0;
	}
	char **table_array;
	int nrow,ncolumn;
	int i,j,index;
	ret = sqlite3_get_table(db,"SELECT * FROM test_table",&table_array,&nrow,&ncolumn,&error_msg);
	if(ret == SQLITE_OK)
	{
		index = 0;
		//列印列名 此例只有一項“ID”
		printf("%s\n",table_array[index++]);
		for(i=0;i<nrow;i++)
		{
			for(j=0;j<ncolumn;j++)
			{
				printf("%s\n",table_array[index++]);
			}
			printf("\n");
		}
		sqlite3_free_table(table_array);
	}
	sqlite3_close_v2(db);
	sqlite3_shutdown();
	return 0;
}

刪除表項

#include <stdio.h>
#include <sqlite3.h>
int main()
{
	sqlite3 *db = NULL;
	char *sql = NULL;
	char *error_msg = NULL;
	int ret = 0;
	sqlite3_initialize();
	ret = sqlite3_open_v2("test.db", &db,SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE,NULL); 
	if( ret )
	{
		sqlite3_close_v2(db);
        sqlite3_shutdown();
		return 0;
	}
	//刪除ID為5的表項
	sql = "DELETE FROM test_table where ID = 5";
	//刪除按ID排序的前三項
	//sql = "DELETE FROM test_table where ID in(SELECT * FROM test_table order by ID limit 3)";
	//刪除表中全部內容
	//sql = "DELETE FROM test_table;"
	int ret = sqlite3_exec(db,sql,0,0,&error_msg);
	if(ret == SQLITE_OK)
	{
		//作用是重新構建資料庫檔案,回收空白空間,減小資料庫檔案的大小。
		sqlite3_exec(db,"VACUUM;",0,0,&error_msg);
	}
	sqlite3_close_v2(db);
	sqlite3_shutdown();
	return 0;
}

工作需求

在開發板中插入SD卡存放資料庫檔案來儲存資料,以實現方便快捷的增刪改查,要求SD卡滿了將早期資料刪除再存放新插入的資料。但是經過測試,SD卡滿了以後再刪除資料就會報錯,會提示database or disk is full。

  • 解決方案

    每次插入資料時檢查一次SD卡空間,當SD卡佔用達到一定程度(暫定80%)時,進行一次早期資料刪除。

#include <stdio.h>
#include <sys/vfs.h>
#include <sqlite3.h>

int check_space();
int free_some_space();

int main(void)
{
	sqlite3 *db = NULL;
	char *error_msg = NULL;
	int ret = 0;
	char *sql;
	//開啟資料庫檔案,如果不存在將建立一個數據庫檔案
	sqlite3_initialize();
	ret = sqlite3_open_v2("test.db", &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
	if (ret)
	{
		sqlite3_close_v2(db);
		sqlite3_shutdown();
		return 0;
	}
	//建立表,如果表存在,則不建立,並給出提示資訊,儲存在 error_msg 中
	//表中只有一個自增鍵 每次插入自動+1
	sql = "CREATE TABLE test_table(\
		ID INTEGER PRIMARY KEY, \
		);";
	sqlite3_exec(db, sql, 0, 0, &zErrMsg);

	//插入資料 
	sql = "INSERT INTO \"test_table\" VALUES(null);";
	//執行sql語句
	while (1)
	{
		//SD卡佔用空間達到80%時 刪除一些早期的資料 釋放空間
		if (check_space() == -1)
		{
			ret = free_some_space(db);
			if (ret != SQLITE_OK)
			{
				printf("error code is %d\n", ret);
				goto End;
			}
		}
		ret = sqlite3_exec(db, sql, 0, 0, &error_msg);
		printf("ret: %d  msg: %s\n", ret, error_msg);
	}

End:
	sqlite3_close_v2(db); //關閉資料庫
	sqlite3_shutdown();
	return 0;
}

int check_space()
{
	struct statfs sfs;
	statfs("/media/mmcblk0p1", &sfs);
	int percent = (sfs.f_blocks - sfs.f_bfree) * 100 / (sfs.f_blocks - sfs.f_bfree + sfs.f_bavail) + 1;
	//SD卡佔用空間達到80%時 返回-1
	if (percent > 80)
		return -1;
	return 0;
}

int free_some_space()
{
	char *sql = ;
	char *error_msg = "DELETE FROM test_table where ID in(SELECT * FROM test_table order by ID limit 20);";
	int ret = sqlite3_exec(db, sql, 0, 0, &error_msg);
	if (ret == SQLITE_OK)
	{
		//重新構建資料庫檔案,回收空白空間,減小資料庫檔案的大小。
		ret = sqlite3_exec(db, "VACUUM;", 0, 0, &error_msg);
	}
	return ret;
}

參考資料: