常见软件---SQLite3的C语言下使用

使用背景

最近在研究Wazuh的时候,发现服务端默认也使用了sqlite,原以为只有嵌入式使用,其实在这种本地化的软件中,使用sqlite也是一种很好的选择,至少软件的依赖性就少了很多,sqlite可以直接编译到产品中,不依赖mysql这种软件。
在这里插入图片描述

源码下载

访问《这里》可以下载sqlite3的源码。
源码就是4个文件
在这里插入图片描述
其中shell.c在不使用命令行的时候,无需编译进自己的产品。
在这里插入图片描述

构建环境

今天学了一个新的工程搭建方式,在开源软件上经常用到的CMake方式。

[root@localhost sqlite3]# tree
.
├── build
├── CMakeLists.txt
├── sqlite
│   ├── shell.c
│   ├── sqlite3.c
│   ├── sqlite3ext.h
│   └── sqlite3.h
└── src
    └── main.c

3 directories, 6 files

CMakeLists.txt内容如下

cmake_minimum_required (VERSION 3.5)
project(demo)
include_directories (sqlite)

add_executable(main ${PROJECT_SOURCE_DIR}/src/main.c ${PROJECT_SOURCE_DIR}/sqlite/sqlite3.c)
target_link_libraries (main pthread dl)

add_executable(sqliteShell ${PROJECT_SOURCE_DIR}/sqlite/shell.c ${PROJECT_SOURCE_DIR}/sqlite/sqlite3.c)
target_link_libraries (sqliteShell pthread dl)

这个还挺有意思的,直接就能够根据后面几行,自动生成Makefile,并且编译出main和sqliteShell两个可执行程序。
看来以后要深入学习一下。
在这里插入图片描述
编译方式与运行

cd build
cmake .. && make
./main

main函数就简单的获取一个版本就可以进行测试了。

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

int main(void)
{
    printf("%s\n", sqlite3_libversion());
    return 0;
}
[root@localhost sqlite3]# cd build/
[root@localhost build]# cmake ..
-- The C compiler identification is GNU 8.5.0
-- The CXX compiler identification is GNU 8.5.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/sqlite3/build
[root@localhost build]# make
[ 16%] Building C object CMakeFiles/main.dir/src/main.c.o
[ 33%] Building C object CMakeFiles/main.dir/sqlite/sqlite3.c.o
[ 50%] Linking C executable main
[ 50%] Built target main
[ 66%] Building C object CMakeFiles/sqliteShell.dir/sqlite/shell.c.o
[ 83%] Building C object CMakeFiles/sqliteShell.dir/sqlite/sqlite3.c.o
[100%] Linking C executable sqliteShell
[100%] Built target sqliteShell
[root@localhost build]# ./main 
3.39.3

以上内容参考《C语言操作SQLite3简明教程》
在这里插入图片描述

C语言方式

这里主要学习一下sqlite3的一些API函数
可以参考文章《sqlite3接口API函数备注(2)》,下面介绍一些有意思的调用。
在这里插入图片描述

数据库打开

值得注意的是数据库的打开时候的参数,可以根据情况,例如多线程,进行选择。

SQLITE_API int sqlite3_open_v2(
  const char *filename,   /* Database filename (UTF-8) */
  sqlite3 **ppDb,         /* OUT: SQLite db handle */
  int flags,              /* Flags */
  const char *zVfs        /* Name of VFS module to use */
);

第一个参数为数据库文件名字,值得注意的点是:

  • 如果文件名为“:memory:”,则会为连接创建一个专用的临时内存数据库。数据库连接关闭时,内存中的数据库将消失。SQLite的未来版本可能会使用以“:”字符开头的其他特殊文件名。建议当数据库文件名实际上以“:”字符开头时,应在文件名前面加上路径名,如“/”以避免歧义。
  • 如果文件名为空字符串,则将创建一个专用的临时磁盘数据库。一旦数据库连接关闭,此专用数据库将自动删除。

第三个参数flags,含义如下

参数含义是否必选
SQLITE_OPEN_READONLY只读模式,如果数据库不存在,会返回错误Y
SQLITE_OPEN_READWRITE读写模式,如果数据库不存在,会返回错误Y
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE读写方式打开。如果数据库不存在,会自动创建,sqlite3_open() and sqlite3_open16()函数就是这种模式Y
SQLITE_OPEN_URI如果设置了此标志,则文件名可以解释为URIN
SQLITE_OPEN_MEMORY数据库将作为内存数据库打开。如果启用了共享缓存模式,则出于缓存共享的目的,数据库由“filename”参数命名,但在其他情况下忽略“filename”N
SQLITE_OPEN_NOMUTEX新数据库连接将使用“多线程”[线程模式])这意味着允许单独的线程同时使用SQLite,只要每个线程使用不同的[数据库连接]N
SQLITE_OPEN_FULLMUTEX新数据库连接将使用“序列化”[线程模式])这意味着多个线程可以安全地尝试同时使用相同的数据库连接。(互斥锁将阻止任何实际的并发,但在这种模式下,尝试不会有任何危害。)N
SQLITE_OPEN_SHAREDCACHE数据库启用共享缓存,覆盖[sqlite3_enable_shared_cache()]提供的默认共享缓存设置。)N
SQLITE_OPEN_PRIVATECACHE数据库禁用共享缓存,覆盖[sqlite3_enable_shared_cache()]提供的默认共享缓存设置。)N
SQLITE_OPEN_EXRESCODE数据库连接在“扩展结果代码模式”下出现。换句话说,数据库行为具有如果[sqlite3_extended_result_codes(db,1)],其中在连接创建后立即调用数据库连接。除了设置扩展结果代码模式外,该标志还导致[sqlite3_open_v2()]返回扩展结果代码。N
SQLITE_OPEN_NOFOLLOW数据库名称不能是符号链接N

部分内容翻译的很直白,见谅见谅
在这里插入图片描述

预编译下的增删改操作

这里主要讲一下数据库数据操作的预编译。

以前用数据库,就是老老实实每次都调用文本的sql语句,实际上每次都会进行语句的编译再执行,如果进行三次操作,就需要进行三次编译,那么我们可以将重复执行或者部分重复执行的sql语句,提前编译好,然后调用的时候,直接调用,就省去了一部分时间,提升了效率。
在这里插入图片描述

请看函数

// 准备语句 
int sqlite3_prepare_v2(
  sqlite3 *db,            /* Database handle */
  const char *zSql,       /* SQL statement, UTF-8 encoded */
  int nByte,              /* Maximum length of zSql in bytes. */
  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
  const char **pzTail     /* OUT: Pointer to unused portion of zSql */
);
// 执行 
int sqlite3_step(sqlite3_stmt*);
// 完成
int sqlite3_finalize(sqlite3_stmt *pStmt);

就是将zSql语句,编译成ppStmt,然后执行的时候就可以直接运行这个编译过的命令。
如果带有参数的话,需要通过下面的函数,进行参数设置

// 部分绑定函数接口
int sqlite3_bind_double(sqlite3_stmt*, int, double);
int sqlite3_bind_int(sqlite3_stmt*, int, int);
int sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*));
int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
// 语句重置
int sqlite3_reset(sqlite3_stmt *pStmt);

直接来一个创建数据库并且增删改查数据的例子吧。
代码较长,请多指教。
在这里插入图片描述

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


int callback(void *NotUsed, int argc, char **argv, char **azColName)
{
    NotUsed = NULL;

    for (int i = 0; i < argc; ++i)
    {
        printf("%s = %s\n", azColName[i], (argv[i] ? argv[i] : "NULL"));
    }

    printf("\n");

    return 0;
}

int exec_sql(sqlite3 *db,char*   sqltxt)
{
    char *err_msg = NULL;
	int rc;
    printf("sqltxt:%s\n",sqltxt);
	
    rc = sqlite3_exec(db, sqltxt, callback, NULL, &err_msg);
    if (rc != SQLITE_OK ) 
	{
        
        fprintf(stderr, "Failed to select data\n");
        fprintf(stderr, "SQL error: %s\n", err_msg);

        sqlite3_free(err_msg);
        sqlite3_close(db);
        
        return 1;
    }
	return 0;
}

int main(void) 
{
    sqlite3 *db = NULL;
    char *err_msg = NULL;
	int rc = 0;
	int i = 0;
	char text[50] = {0};
    const char *sql_init = "DROP TABLE IF EXISTS Testtable;" 
                      "CREATE TABLE Testtable(Id INT, Data TEXT);";

					  
	const char *sql_insert = "insert into Testtable(Id,Data) values(?,?)";
	const char *sql_update = "update Testtable set Data = ? where Id = ?;";
	const char *sql_delete = "delete from Testtable where Id = ?;";
	const char *sql_search = "select * from Testtable";
	
	sqlite3_stmt *stmt_insert;
	sqlite3_stmt *stmt_update;
	sqlite3_stmt *stmt_delete;
	sqlite3_stmt *stmt_search;

	//打开数据库,不存在就创建
	rc = sqlite3_open("test.db", &db);

    if (rc != SQLITE_OK)
    {
        fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);
        
        return 1;
    }

	printf("Autocommit: %d\n", sqlite3_get_autocommit(db));

	//创建数据库和表
    rc = sqlite3_exec(db, sql_init, NULL, NULL, &err_msg);
    if (rc != SQLITE_OK)
    {
        fprintf(stderr, "SQL error: %s\n", err_msg);
        sqlite3_free(err_msg);
        sqlite3_close(db);

        return 1;
    }

	//查看数据
    printf("新建表\n");
	exec_sql(db,sql_search);

	// 插入三条数据
	rc = sqlite3_prepare_v2(db, sql_insert, -1, &stmt_insert, NULL);
	for (i = 0; i < 3; i++)
	{
		
		sqlite3_bind_int(stmt_insert, 1, i);
		sprintf(text, "test%d", i);
		sqlite3_bind_text(stmt_insert, 2, text, -1, NULL);
		rc = sqlite3_step(stmt_insert);
		if (rc != SQLITE_DONE)
	    {
	        printf("execution failed: %s\n", sqlite3_errmsg(db));
	    }
		sqlite3_reset(stmt_insert);
	}
	sqlite3_finalize(stmt_insert);

	//查看数据
    printf("插入三条数据\n");
	exec_sql(db,sql_search);
	
	//修改第二条
	rc = sqlite3_prepare_v2(db, sql_update, -1, &stmt_update, NULL);
	sqlite3_bind_int(stmt_update, 2, 1);
	strcpy(text, "change");
	sqlite3_bind_text(stmt_update, 1, text, -1, NULL);
	rc = sqlite3_step(stmt_update);
	if (rc != SQLITE_DONE)
    {
        printf("execution failed: %s\n", sqlite3_errmsg(db));
    }
	sqlite3_reset(stmt_update);
	//查看数据
    printf("修改第二条数据\n");
	exec_sql(db,sql_search);

	//删除第三条数据
	rc = sqlite3_prepare_v2(db, sql_delete, -1, &stmt_delete, NULL);
	sqlite3_bind_int(stmt_delete, 1, 2);
	rc = sqlite3_step(stmt_delete);
	if (rc != SQLITE_DONE)
    {
        printf("execution failed: %s\n", sqlite3_errmsg(db));
    }	sqlite3_reset(stmt_delete);
	
    printf("删除第三条数据\n");
	exec_sql(db,sql_search);

    sqlite3_close(db);

    return 0;
}

Blob数据类型存储

Bolb是二进制长对象的意思,通常用于存储大文件,通过二进制数据保存到数据库里,并可以从数据库里恢复指定文件。
常见的就是存储一些图片数据。
在这里插入图片描述

这里用存储并读取几个个结构体数据为例,内部涉及了读取多条数据结果的操作。

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


typedef struct 
{
    int64_t time;
    int32_t value;
} myData;


int main(void)
{
    sqlite3 *db = NULL;
    char *err_msg = NULL;
    sqlite3_stmt *pStmt = NULL;
	int rc = 0;
	int i = 0;
    int bytes = 0;
	myData *pData = NULL;
    // 创建名为Images的表
    const char *sql_init = "DROP TABLE IF EXISTS Testtable;" 
                      "CREATE TABLE Testtable(Id INTEGER PRIMARY KEY, Data BLOB);";
    const char *sql_insert = "INSERT INTO Testtable(Data) VALUES(?)"; // 向Data列插入新值
    const char *sql_get = "SELECT Data FROM Testtable;";

    myData data = {10000, 200}; // 准备要写入的值
    
	rc = sqlite3_open("test.db", &db);
    if (rc != SQLITE_OK) 
	{
        
        fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
        return 1;
    }
    

    rc = sqlite3_exec(db, sql_init, NULL, NULL, &err_msg);
    if (rc != SQLITE_OK ) {
        
        fprintf(stderr, "Failed to select data\n");
        fprintf(stderr, "SQL error: %s\n", err_msg);
		goto exit;
	}

    rc = sqlite3_prepare_v2(db, sql_insert, -1, &pStmt, NULL);
    if (rc != SQLITE_OK) 
	{
        fprintf(stderr, "Cannot prepare statement: %s\n", sqlite3_errmsg(db));
		goto exit;
    }

    sqlite3_bind_blob(pStmt, 1, &data, sizeof(data), SQLITE_STATIC); // 绑定需要写入的值
	for(i=0;i<5;i++)
	{
		data.value=i;
	    rc = sqlite3_step(pStmt);
	    if (rc != SQLITE_DONE)
	    {
	        printf("execution failed: %s", sqlite3_errmsg(db));
			goto exit;
	    }

	}
    sqlite3_finalize(pStmt);    

    rc = sqlite3_prepare_v2(db, sql_get, -1, &pStmt, NULL);
     if (rc != SQLITE_OK ) {
        
        fprintf(stderr, "Failed to prepare statement\n");
        fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
		goto exit;
    } 
    
	rc = sqlite3_step(pStmt);
	
    while (rc == SQLITE_ROW)
    {
        bytes = sqlite3_column_bytes(pStmt, 0);
		pData = (myData*)sqlite3_column_blob(pStmt, 0);
		printf("bytes: %d, %lld, %d\n", bytes, pData->time, pData->value);
	    rc = sqlite3_step(pStmt);
		if(rc == SQLITE_DONE)
			break;
    }

    rc = sqlite3_finalize(pStmt);   

exit:
	if(err_msg)
	{
		sqlite3_free(err_msg);
	}
	if(db)
	{
		sqlite3_close(db);
	}
    return 0;

}

其他操作

SQLite也和mysql一样,支持命令行的操作,详细方式可以参考。
《一文掌握SQLite3基本用法》
《SQLite 教程》
在这里插入图片描述

结束语

好些天没写了,最近有点沉迷工作,荒废了学业。马上就周末了,更新一篇。
在这里插入图片描述
最近大家都在玩羊了个羊,大家都集中到了两个困惑上
1.怎么跳过第一关
2.怎么能过第二关
其实我就怀疑这个游戏的内容,全是随机的,根本没有考虑你这套组合,能不能完成,除非你看广告刷够了道具。
在这里插入图片描述
不过这也算是个成功的游戏,一个成功的吸引住你的游戏。至于能不能过关,他并不关心。咱们也不必苛责人家,毕竟都是程序员,赚的也不是用户的钱。
在这里插入图片描述

  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胖哥王老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值