sqlite之我见--C/C++ API接口示例

2024-02-07 16:32

本文主要是介绍sqlite之我见--C/C++ API接口示例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转载自:http://blog.csdn.net/wzzfeitian/article/details/7993686

在之前的两篇博文中,分别介绍了SQLITE的基础知识和操作,C/C++ 的一些常用API

sqlite之我见--简单介绍与基本操作

sqlite之我见--C/C++ API接口介绍 


本文中,我会给大家用几个小程序示例SQLITE C/C++ API的使用。

1.我们看下最简单的sqlite程序,通过sqlite3_open, sqlite3_exec, sqlite3_close来实现一个简单的数据库操作。

/* 
* File:   sqlite_test.cpp
* Author: Carl
*
* Created on September 20, 2012, 3:28 PM
*/
#include <cstdlib>
#include <cstdio>
#include <sqlite3.h>
static int _sql_callback(void *notused, int argc, char **argv, char **szColName)
{
int i = 0;
printf("notused:0x%x, argc:%d\n", notused, argc);
for (i = 0; i < argc; i++)
{
printf("%s = %s\n", szColName[i], argv[i] == 0 ? "NULL" : argv[i]);
}
printf("\n");
return 0;
}
/*
* 
*/
int main(int argc, char** argv)
{
const char *sSQL1 = "create table users(userid varchar(20) PRIMARY KEY, age int, birthday datetime);";
const char *sSQL2 = "insert into users values('wang', 20, '1989-5-4');";
const char *sSQL3 = "select * from users;";
sqlite3 *db = 0;
char *pErrMsg = 0;
int ret = 0;
//连接数据库
ret = sqlite3_open("./test.db", &db);
if (ret != SQLITE_OK)
{
fprintf(stderr, "无法打开数据库:%s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
printf("数据库连接成功\n");
//执行建表SQL
ret = sqlite3_exec(db, sSQL1, _sql_callback, 0, &pErrMsg);
if (ret != SQLITE_OK)
{
fprintf(stderr, "SQL create error: %s\n", pErrMsg);
sqlite3_free(pErrMsg); //这个要的哦,要不然会内存泄露的哦!!!
sqlite3_close(db);
return 1;
}
printf("数据库建表成功!!\n");
//执行插入数据
ret = sqlite3_exec(db, sSQL2, _sql_callback, 0, &pErrMsg);
if (ret != SQLITE_OK)
{
fprintf(stderr, "SQL insert error: %s\n", pErrMsg);
sqlite3_free(pErrMsg); //这个要的哦,要不然会内存泄露的哦!!!
sqlite3_close(db);
return 1;
}
printf("数据库插入数据成功!\n");
//执行查询操作
ret = sqlite3_exec(db, sSQL3, _sql_callback, 0, &pErrMsg);
if (ret != SQLITE_OK)
{
fprintf(stderr, "SQL error: %s\n", pErrMsg);
sqlite3_free(pErrMsg);
sqlite3_close(db);
return 1;
}
printf("数据库查询成功!!\n");
//关闭数据库
sqlite3_close(db);
db = 0;
return 0;
}


 

运行结果如下结果:

[carl@Fedora sqlite]$ g++ sqlite_test.cpp -lsqlite3
[carl@Fedora sqlite]$ ./a.out 
数据库连接成功
数据库建表成功!!
数据库插入数据成功!
notused:0x0, argc:3
userid = wang
age = 20
birthday = 1989-5-4
数据库查询成功!!
[carl@Fedora sqlite]$


 

2. 我们再看一个在sqlite上是有事务来实现原子操作的的例子,

代码如下:

/* 
* File:   sqlite_test.cpp
* Author: Carl
*
* Created on 2012年9月22日, 上午7:50
*/
#include <cstdlib>
#include <cstdio>
#include <sqlite3.h>
static int _sql_callback(void *notused, int argc, char **argv, char **szColName)
{
int i = 0;
printf("notused:0x%x, argc:%d\n", notused, argc);
for (i = 0; i < argc; i++)
{
printf("%s = %s\n", szColName[i], argv[i] == 0 ? "NULL" : argv[i]);
}
printf("\n");
return 0;
}
/*
* 
*/
int main(int argc, char** argv)
{
const char *sSQL1 = "create table test_for_cpp (id int, name varchar(10), age int);";
char sql[100] = {0};
sqlite3 *db = NULL;
char *pErrMsg = NULL;
int ret = 0;
bool is_success = true;
const char *sSQL3 = "select * from test_for_cpp;";  
ret = sqlite3_open("./test.db", &db);
if (SQLITE_OK != ret)
{
fprintf(stderr, "无法打开数据库: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
printf("数据库连接成功\n");  
ret = sqlite3_exec(db, sSQL1, NULL, 0, &pErrMsg);
if (SQLITE_OK != ret)
{
fprintf(stderr, "SQL create error: %s\n", pErrMsg);
sqlite3_free(pErrMsg);
sqlite3_close(db);
return 1;
}
printf("数据库建表成功!!\n");  
sqlite3_exec(db, "begin;", _sql_callback, 0, &pErrMsg); //开启事务
if (SQLITE_OK != ret)
{
fprintf(stderr, "SQL begin error: %s\n", pErrMsg);
sqlite3_free(pErrMsg);
sqlite3_close(db);
return 1;
}
printf("数据库开启事务成功!!\n");  
for (int i = 0; i < 10; i++)
{
sprintf(sql, "insert into test_for_cpp(id, name, age) values(%d, \"%s\", %d);", i, "Carl", i);
ret = sqlite3_exec(db, sql, _sql_callback, 0, &pErrMsg);
if (SQLITE_OK != ret)
{
is_success = false;
fprintf(stderr, "for %d time error: %s\n", i, pErrMsg);
sqlite3_free(pErrMsg);
break;
}
}
if (is_success)
{
sqlite3_exec(db, "commit;", 0, 0, 0);
printf("数据库插入数据成功!\n");  
}
else
{
sqlite3_exec(db, "rollback;", 0, 0, 0);
printf("数据库插入数据失败!\n");  
}
ret = sqlite3_exec(db, sSQL3, _sql_callback, 0, &pErrMsg);
if (SQLITE_OK != ret)
{
fprintf(stderr, "SQL ERROR: %s\n", pErrMsg);
sqlite3_free(pErrMsg);
sqlite3_close(db);
return 1;
}
printf("数据库查询成功!!\n");  
sqlite3_close(db);
db = 0;
return 0;
}


运行结果:

[carl@Fedora sqlite]$ g++ sqlite_test.cpp -lsqlite3
[carl@Fedora sqlite]$ ./a.out 
数据库连接成功
数据库建表成功!!
数据库开启事务成功!!
数据库插入数据成功!
notused:0x0, argc:3
id = 0
name = Carl
age = 0
notused:0x0, argc:3
id = 1
name = Carl
age = 1
notused:0x0, argc:3
id = 2
name = Carl
age = 2
notused:0x0, argc:3
id = 3
name = Carl
age = 3
notused:0x0, argc:3
id = 4
name = Carl
age = 4
notused:0x0, argc:3
id = 5
name = Carl
age = 5
notused:0x0, argc:3
id = 6
name = Carl
age = 6
notused:0x0, argc:3
id = 7
name = Carl
age = 7
notused:0x0, argc:3
id = 8
name = Carl
age = 8
notused:0x0, argc:3
id = 9
name = Carl
age = 9
数据库查询成功!!
[carl@Fedora sqlite]$ 


 

3. 我们接着看一下如何能够更好的使用语句参数来操作sqlite数据库,用sqlite3_prepare_v2, sqlite3_bind_*, sqlite3_step, sqlite3_column_*等接口来实现对数据库的操作。

代码如下:里面的注释,有兴趣的可以试着打开试一下,但要记得注释掉相关的重复功能的语句哦。

/* 
* File:   sqlite_test2.cpp
* Author: Carl
*
* Created on September 21, 2012, 3:12 PM
*/
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <sqlite3.h>
static int _sql_callback(void *notused, int argc, char **argv, char **szColName)
{
int i = 0;
printf("notused:0x%x, argc:%d\n", notused, argc);
for (i = 0; i < argc; i++)
{
printf("%s = %s\n", szColName[i], argv[i] == 0 ? "NULL" : argv[i]);
}
printf("\n");
return 0;
}
/*
* 
*/
int main(int argc, char** argv)
{
sqlite3 *conn = NULL;
sqlite3_stmt *stmt = NULL;
char *err_msg = NULL;
int ret = 0;
char col_types[][10] = {"", "Interger", "Float", "Text", "Blob", "NULL"};
ret = sqlite3_open("./test.db", &conn);
if (SQLITE_OK != ret)
{
fprintf(stderr, "sqlite open err, %d\n", ret);
return 1;
}
printf("打开数据库成功!!!\n");
//    ret = sqlite3_prepare_v2(conn, "SELECT * FROM [test_for_cpp] WHERE [name]==:name", -1, &stmt, (const char **)&err_msg);
ret = sqlite3_prepare_v2(conn, "SELECT * FROM [test_for_cpp] WHERE [name]==?2", -1, &stmt, (const char **)&err_msg);
if (SQLITE_OK != ret)
{
fprintf(stderr, "sqlite prepare error: %s\n", err_msg);
sqlite3_free(err_msg);
sqlite3_close(conn);
return 1;
}
//    printf("数据库语句对象编译成功!!!%d\n", sqlite3_bind_parameter_index(stmt, ":name"));
printf("数据库语句对象编译成功!!!\n");
ret = sqlite3_bind_text(stmt, 2, "Carl", 4, SQLITE_STATIC);
if (SQLITE_OK != ret)
{
fprintf(stderr, "sqlite bind error: %d\n", ret);
sqlite3_close(conn);
return 1;
}
printf("数据库语句对象bind成功!!!\n");
while (ret = sqlite3_step(stmt), ret == SQLITE_ROW)
{
int col_count = sqlite3_column_count(stmt); //结果集中列的数量
printf("列数:%d\t", col_count);
const char *col_0_name = sqlite3_column_name(stmt, 0); //获取列名
printf("列名:%s\t", col_0_name);
int id = sqlite3_column_int(stmt, 0);
printf("id值:%d\t", id);
int id_type = sqlite3_column_type(stmt, 0); //获取列数据类型
printf("id类型:%d\t", id_type);
const char *col_2_name = sqlite3_column_name(stmt, 2);
int age = sqlite3_column_int(stmt, 2);
int age_type = sqlite3_column_type(stmt, 2);
const char *col_1_name = sqlite3_column_name(stmt, 1);
char name[80];
strncpy(name, (const char *)sqlite3_column_text(stmt, 1), 80);
int name_type = sqlite3_column_type(stmt, 1);
//打印结果
printf("col_count: %d, %s = %d(%s), %s = %s(%s), %s = %d(%s)\n", 
col_count, col_0_name, id, col_types[id_type], col_1_name, name,
col_types[name_type], col_2_name, age, col_types[age_type]);
}
fprintf(stderr, "sqlite step exit with %d\n", ret);
sqlite3_finalize(stmt);
sqlite3_close(conn);
return 0;
}


运行结果如下:

[carl@Fedora sqlite]$ g++ sqlite_test2.cpp -lsqlite3
[carl@Fedora sqlite]$ ./a.out 
打开数据库成功!!!
数据库语句对象编译成功!!!
数据库语句对象bind成功!!!
列数:3	列名:id	id值:0	id类型:1	col_count: 3, id = 0(Interger), name = Carl(Text), age = 0(Interger)
列数:3	列名:id	id值:1	id类型:1	col_count: 3, id = 1(Interger), name = Carl(Text), age = 1(Interger)
列数:3	列名:id	id值:2	id类型:1	col_count: 3, id = 2(Interger), name = Carl(Text), age = 2(Interger)
列数:3	列名:id	id值:3	id类型:1	col_count: 3, id = 3(Interger), name = Carl(Text), age = 3(Interger)
列数:3	列名:id	id值:4	id类型:1	col_count: 3, id = 4(Interger), name = Carl(Text), age = 4(Interger)
列数:3	列名:id	id值:5	id类型:1	col_count: 3, id = 5(Interger), name = Carl(Text), age = 5(Interger)
列数:3	列名:id	id值:6	id类型:1	col_count: 3, id = 6(Interger), name = Carl(Text), age = 6(Interger)
列数:3	列名:id	id值:7	id类型:1	col_count: 3, id = 7(Interger), name = Carl(Text), age = 7(Interger)
列数:3	列名:id	id值:8	id类型:1	col_count: 3, id = 8(Interger), name = Carl(Text), age = 8(Interger)
列数:3	列名:id	id值:9	id类型:1	col_count: 3, id = 9(Interger), name = Carl(Text), age = 9(Interger)
sqlite step exit with 101 //101意思为SQLITE_DONE
[carl@Fedora sqlite]$ 


下面解释下为什么要学会这种能够细致控制sqlite的方法(即使用语句参数),主要有以下几个优点:

(1) 使用“语句参数”方式,具有更高的安全性,可以有效防止“SQL注入攻击”。 “SQL注入攻击”要想达到目的,就必须让attack value随着SQL命令字符串一起传送进SQL解析器。黑客如果在一条SQL命令字符串被送入到sqlite3_prepare函数之前,利用c字符串处理函数等途径将attack value注入其中,而在sqlite3_prepare函数之中进行解析(parse),就可以达到攻击目的。而使用“语句参数”方式,被传送到sqlite3_prepare函数的只是SQL命令字符串中的参数符号(如:“?”),而不是具体的值。在sqlite3_prepare函数执行之后,才会使用bind函数给参数符号绑定具体的值,这就可以避免attack value随着SQL命令字符串一起在sqlite3_prepare函数中被解析,从而有效躲避“SQL注入攻击”。
(2)使用“语句参数”方式,可以更快的完成值替换。

(3)使用“语句参数”方式,更节省内存。原因是使用如snprintf函数,需要一个SQL命令模板,一块足够大的输出缓存,而且字符串处理函数需要工作内存(working memory),除此之外对于整形,浮点型,特别是BLOBs,经常会占用更多的空间。

 

这篇关于sqlite之我见--C/C++ API接口示例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/688298

相关文章

MySQL 日期时间格式化函数 DATE_FORMAT() 的使用示例详解

《MySQL日期时间格式化函数DATE_FORMAT()的使用示例详解》`DATE_FORMAT()`是MySQL中用于格式化日期时间的函数,本文详细介绍了其语法、格式化字符串的含义以及常见日期... 目录一、DATE_FORMAT()语法二、格式化字符串详解三、常见日期时间格式组合四、业务场景五、总结一、

C#集成DeepSeek模型实现AI私有化的流程步骤(本地部署与API调用教程)

《C#集成DeepSeek模型实现AI私有化的流程步骤(本地部署与API调用教程)》本文主要介绍了C#集成DeepSeek模型实现AI私有化的方法,包括搭建基础环境,如安装Ollama和下载DeepS... 目录前言搭建基础环境1、安装 Ollama2、下载 DeepSeek R1 模型客户端 ChatBo

mysql线上查询之前要性能调优的技巧及示例

《mysql线上查询之前要性能调优的技巧及示例》文章介绍了查询优化的几种方法,包括使用索引、避免不必要的列和行、有效的JOIN策略、子查询和派生表的优化、查询提示和优化器提示等,这些方法可以帮助提高数... 目录避免不必要的列和行使用有效的JOIN策略使用子查询和派生表时要小心使用查询提示和优化器提示其他常

Qt实现发送HTTP请求的示例详解

《Qt实现发送HTTP请求的示例详解》这篇文章主要为大家详细介绍了如何通过Qt实现发送HTTP请求,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、添加network模块2、包含改头文件3、创建网络访问管理器4、创建接口5、创建网络请求对象6、创建一个回复对

C++实现回文串判断的两种高效方法

《C++实现回文串判断的两种高效方法》文章介绍了两种判断回文串的方法:解法一通过创建新字符串来处理,解法二在原字符串上直接筛选判断,两种方法都使用了双指针法,文中通过代码示例讲解的非常详细,需要的朋友... 目录一、问题描述示例二、解法一:将字母数字连接到新的 string思路代码实现代码解释复杂度分析三、

Spring Boot整合消息队列RabbitMQ的实现示例

《SpringBoot整合消息队列RabbitMQ的实现示例》本文主要介绍了SpringBoot整合消息队列RabbitMQ的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的... 目录RabbitMQ 简介与安装1. RabbitMQ 简介2. RabbitMQ 安装Spring

golang panic 函数用法示例详解

《golangpanic函数用法示例详解》在Go语言中,panic用于触发不可恢复的错误,终止函数执行并逐层向上触发defer,最终若未被recover捕获,程序会崩溃,recover用于在def... 目录1. panic 的作用2. 基本用法3. recover 的使用规则4. 错误处理建议5. 常见错

Python进行PDF文件拆分的示例详解

《Python进行PDF文件拆分的示例详解》在日常生活中,我们常常会遇到大型的PDF文件,难以发送,将PDF拆分成多个小文件是一个实用的解决方案,下面我们就来看看如何使用Python实现PDF文件拆分... 目录使用工具将PDF按页数拆分将PDF的每一页拆分为单独的文件将PDF按指定页数拆分根据页码范围拆分

javaScript在表单提交时获取表单数据的示例代码

《javaScript在表单提交时获取表单数据的示例代码》本文介绍了五种在JavaScript中获取表单数据的方法:使用FormData对象、手动提取表单数据、使用querySelector获取单个字... 方法 1:使用 FormData 对象FormData 是一个方便的内置对象,用于获取表单中的键值

Node.js net模块的使用示例

《Node.jsnet模块的使用示例》本文主要介绍了Node.jsnet模块的使用示例,net模块支持TCP通信,处理TCP连接和数据传输,具有一定的参考价值,感兴趣的可以了解一下... 目录简介引入 net 模块核心概念TCP (传输控制协议)Socket服务器TCP 服务器创建基本服务器服务器配置选项服