SQLite3 C 编程学习
文件概览
本笔记包含四个SQLite3 C语言程序,涵盖数据库基本操作、数据导入、查询和参数化查询等核心功能。
一、sqli.c - 数据库连接与简单插入
完整代码
cpp
#include <sqlite3.h>
#include <stdio.h>
int main(int argc, char** argv)
{
sqlite3* db = NULL;
int ret = sqlite3_open("123.db", &db);
if (ret != SQLITE_OK)
{
fprintf(stderr, "sqlite3_open %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
char* errmsg = NULL;
char sql_cmd[512] = "insert into user values(6,'lvbu',31);";
ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
fprintf(stderr, "sqlite3_exec %s\n", errmsg);
sqlite3_free(errmsg);
sqlite3_close(db);
return 1;
}
sqlite3_close(db);
return 0;
}
详细解释
-
头文件包含
-
sqlite3.h: SQLite数据库操作头文件 -
stdio.h: 标准输入输出头文件
-
-
数据库连接
sqlite3* db = NULL; // 数据库句柄指针 int ret = sqlite3_open("123.db", &db); // 打开数据库文件-
sqlite3_open(): 打开或创建数据库文件 -
返回值:
SQLITE_OK表示成功,其他值为错误代码 -
错误处理:使用
sqlite3_errmsg()获取错误信息
-
-
SQL语句执行
char sql_cmd[512] = "insert into user values(6,'lvbu',31);"; ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);-
sqlite3_exec(): 执行SQL语句的通用函数 -
参数说明:
-
db: 数据库连接 -
sql_cmd: 要执行的SQL语句 -
第3个参数:回调函数(此处为NULL)
-
第4个参数:回调函数参数(此处为NULL)
-
&errmsg: 错误信息指针
-
-
-
资源清理
-
sqlite3_free(errmsg): 释放错误信息内存 -
sqlite3_close(db): 关闭数据库连接
-
二、sql_dict.c - 从文件导入数据到数据库
完整代码
cpp
#include <sqlite3.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv)
{
// drop table
// create table
// insert
sqlite3* db = NULL;
int ret = sqlite3_open("123.db", &db);
if (ret != SQLITE_OK)
{
fprintf(stderr, "sqlite3_open %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
char* errmsg = NULL;
char sql_cmd[512] = {0};
strcpy(sql_cmd, "drop table dict;");
sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
bzero(sql_cmd, sizeof(sql_cmd));
strcpy(sql_cmd, "create table dict(id int,word char, mean text);");
sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
FILE* fp = fopen("/home/linux/dict.txt", "r");
if (NULL == fp)
{
perror("fopen");
return 1;
}
int i = 1;
while (1)
{
char buf[1024]={0};
if(NULL == fgets(buf,sizeof(buf),fp))
{
break;
}
char *word = strtok(buf," ");
char* mean=strtok(NULL,"\r");
bzero(sql_cmd,sizeof(sql_cmd));
sprintf( sql_cmd,"insert into dict values(%d,\"%s\",\"%s\");",i++,word,mean);
ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
fprintf(stderr, "sqlite3_exec %s , sql_cmd:%s\n", errmsg,sql_cmd);
sqlite3_free(errmsg);
sqlite3_close(db);
return 1;
}
}
sqlite3_close(db);
fclose(fp);
return 0;
}
详细解释
-
表格管理
// 删除已存在的表 strcpy(sql_cmd, "drop table dict;"); sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg); // 创建新表 bzero(sql_cmd, sizeof(sql_cmd)); strcpy(sql_cmd, "create table dict(id int,word char, mean text);");- 表结构:id(整数)、word(字符)、mean(文本)
-
文件读取与数据解析
FILE* fp = fopen("/home/linux/dict.txt", "r"); while (1) { fgets(buf, sizeof(buf), fp); // 逐行读取文件 char *word = strtok(buf, " "); // 用空格分隔单词和释义 char* mean = strtok(NULL, "\r"); // 用回车符结束 } -
动态SQL语句构建
sprintf(sql_cmd, "insert into dict values(%d,\"%s\",\"%s\");", i++, word, mean);- 注意:存在SQL注入风险(后续会讨论)
-
批量插入
- 循环读取文件内容并逐条插入数据库
三、sql_select.c - 数据库查询与结果显示
完整代码
cpp
#include <sqlite3.h>
#include <stdio.h>
// show 函数会调用多次。 次数取决于结果集,有几条记录。
//有几条记录,show就调用几次。每次result指向不同结果(记录)。
int show(void* arg, int col, char** result, char** title)
{
static int flag = 0;
int i = 0;
if (0 == flag)
{
flag = 1;
for (i = 0; i < col; i++)
{
printf("%s\t\t", title[i]);
}
printf("\n");
}
for (i = 0; i < col; i++)
{
printf("%s\t\t", result[i]);
}
printf("\n");
return 0;
}
int main(int argc, char** argv)
{
sqlite3* db = NULL;
int ret = sqlite3_open("123.db", &db);
if (ret != SQLITE_OK)
{
fprintf(stderr, "sqlite3_open %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
char* errmsg = NULL;
char sql_cmd[512] = "select * from user ;";
ret = sqlite3_exec(db, sql_cmd, show, NULL, &errmsg);
if (ret != SQLITE_OK)
{
fprintf(stderr, "sqlite3_exec %s\n", errmsg);
sqlite3_free(errmsg);
sqlite3_close(db);
return 1;
}
sqlite3_close(db);
return 0;
}
详细解释
-
回调函数机制
int show(void* arg, int col, char** result, char** title)-
arg: 用户传递的参数(此处未使用) -
col: 结果集的列数 -
result: 当前行的数据数组 -
title: 列名数组
-
-
静态变量使用
static int flag = 0;-
静态变量只在第一次调用时初始化为0
-
用于确保列标题只打印一次
-
-
查询执行
ret = sqlite3_exec(db, sql_cmd, show, NULL, &errmsg);-
第三个参数
show:查询结果的回调函数 -
第四个参数
NULL:传递给回调函数的参数
-
-
结果显示格式
-
先打印列标题
-
然后逐行打印数据
-
使用制表符对齐
-
四、sql_arg.c - 带参数的数据库查询(存在SQL注入漏洞)
完整代码
cpp
#include <sqlite3.h>
#include <stdio.h>
#include <string.h>
// show 函数会调用多次。 次数取决于结果集,有几条记录。
//有几条记录,show就调用几次。每次result指向不同结果(记录)。
int show(void* arg, int col, char** result, char** title)
{
*(int*)arg = 1;
return 0;
}
int main(int argc, char** argv)
{
char name[50]={0};
char pass[50]={0};
printf("input name:");
fgets(name,sizeof(name),stdin);
printf("input pass:");
fgets(pass,sizeof(pass),stdin);
name[strlen(name)-1]='\0';
pass[strlen(pass)-1]='\0';
sqlite3* db = NULL;
int ret = sqlite3_open("123.db", &db);
if (ret != SQLITE_OK)
{
fprintf(stderr, "sqlite3_open %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
char* errmsg = NULL;
int flag = 0;
char sql_cmd[512] ={0};
sprintf( sql_cmd,"select * from user where name like '%s' and age ='%s';",name,pass);
ret = sqlite3_exec(db, sql_cmd, show, &flag, &errmsg);
if (ret != SQLITE_OK)
{
fprintf(stderr, "sqlite3_exec %s\n", errmsg);
sqlite3_free(errmsg);
sqlite3_close(db);
return 1;
}
if(0 == flag)
{
printf("not't find person %s \n",name);
}
else
{
printf("name & pass correct \n");
}
sqlite3_close(db);
return 0;
}
详细解释
-
用户输入处理
fgets(name, sizeof(name), stdin); // 安全读取输入 name[strlen(name)-1] = '\0'; // 移除换行符 -
回调函数设计
int show(void* arg, int col, char** result, char** title) { *(int*)arg = 1; // 通过参数修改标志位 return 0; }-
当查询到结果时,将flag设置为1
-
利用参数指针在回调函数中修改主函数变量
-
-
SQL注入漏洞
sprintf(sql_cmd, "select * from user where name like '%s' and age ='%s';", name, pass);-
高危漏洞:直接拼接用户输入到SQL语句
-
攻击示例:
-
用户名输入:
admin' -- -
密码随意输入
-
生成的SQL:
select * from user where name like 'admin' -- ' and age ='anything'; -
--在SQL中是注释符,会注释掉后面的条件
-
-
-
认证逻辑
-
通过flag判断是否找到匹配记录
-
找到则认证成功,否则失败
-
安全建议
-
使用参数化查询(预编译语句)
// 推荐使用sqlite3_prepare_v2()和sqlite3_bind_xxx() sqlite3_stmt *stmt; const char *sql = "SELECT * FROM user WHERE name = ? AND age = ?"; sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); sqlite3_bind_text(stmt, 1, name, -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 2, pass, -1, SQLITE_STATIC); -
输入验证
-
验证用户输入长度和格式
-
过滤特殊字符
-
-
错误处理改进
-
生产环境中不应将详细错误信息返回给用户
-
使用日志记录错误
-
总结
这四个程序展示了SQLite3在C语言中的基本使用:
-
基础操作:连接、插入、关闭
-
数据导入:从文件批量导入数据
-
数据查询:使用回调函数处理结果集
-
交互查询:处理用户输入(存在安全问题)
注意 :sql_dict.c和sql_arg.c中的字符串拼接方式存在SQL注入风险,在实际开发中应使用参数化查询来避免。