sqlite3的API以及命令行

sqlite是目前最流行的嵌入式数据库。

所谓嵌入式,就是足够简单,可以嵌入到我们自己开发的应用程序之中。

在Linux系统中,sqlite的使用只需要使用它的API,连接它的动态连接库,甚至都不用连接,sqlite的实现只使用一个C语言源程序,直接编译进自己的应用里面就好。

现在sqlite的最新版本是3,所以我们后面会以这个版本为例。

API

sqlite的API简单到什么程序呢?

当我们开发应用的时候,通常的情况下,只需要使用sqlite3_open、sqlite3_exec与sqlite3_close这三个函数就够了。

它们的原型为:

int sqlite3_open(const char *filename, sqlite3 **ppDb);int sqlite3_close(sqlite3*);int sqlite3_exec(  sqlite3*,                                  /* An open database */  const char *sql,                           /* SQL to be evaluated */  int (*callback)(void*,int,char**,char**),  /* Callback function */  void *,                                    /* 1st argument to callback */  char **errmsg                              /* Error msg written here */  
);

这三个函数都使用了一个sqlite3的指针的指针,指向了我们要操作的数据库实体的指针,不用解释。

sqlite3_open的第一个参数,表示要创建的数据库的路径。

值得一提的是sqlite3_exec。它有一个回调函数以及参数,当有多个返回值的时候,比如select语句被执行之后,将通过callback函数把每一条记录返回。另外还有一个错误字符串,如果出错将被赋值,需要应用层释放

如:


static int  
get_id_callback (void *para, int n_column, char **column_value,  char **column_name)  
{  int *id = (int *)para;  if (column_value[0] != NULL)  {  *id = strtoull (column_value[0], NULL, 10);  }  return 0;  
}int main (char *argc, char *argv[])  
{  gchar *dirname;  sqlite3 *db;char *errMsg;int rc;if (sqlite3_open ("/tmp/test.sqlite", &db) != 0)  {  printf ("Open sqlite db failed: %s.", file, strerror (errno));  return 1;  }  rc = sqlite3_exec (db, "select * from media", get_id_callback, NULL, &errMsg);  if (rc != SQLITE_OK)  {  printf ("SQL error: %s in [%s]", errMsg, text);  sqlite3_free (errMsg);  return 2;}sqlite3_close (db);  return 0;  
}  

转义

sqlite3_exec执行的sql语句里面,如果有值字符串中含有’,需要进行转义。

转义的格式是使用两个’,即’'。

如:

const char *lan = "It's a secret !";

这里的lan需要转义为:

const char *lan = "It''s a secret !";

可以写一个简单的转化函数实现这个功能:

int convert_str(const char *str, char *output, size_t out_size)
{*p;  int trans_len;for (trans_len = 0, p = str; *p && trans_len + 2 < out_size; trans_len++, p++)  {  output[trans_len] = *p;  if (*p == '\'')  {  output[trans_len + 1] = *p;  trans_len++;  }  }if (trans_len + 1 >= out_size) {printf("str is too long\n");return -1;}output[trans_len] = '\0';return trans_len;
}

除了这样转义,还有没有更好的方法呢?

有的。

方法就是使用预编译执行。

预编译执行

所谓预编译执行,就是生成一个sql语句的结构,再赋值进去,之后执行。

这样做有多个好处。

首先是字符串不用转义了。

其次是,生成的sql语句结构可以复用,每次重置,再赋值,可以极大地提升性能。

预编译执行的方法也很简单,就是三步:

  1. 使用sqlite3_prepare生成一个sqlite3_stmt结构。
  2. 使用sqlite3_bind绑定值。
  3. 使用sqlite3_step执行。

sqlite3_prepare_v2

sqlite3_prepare_v2是建议的新版本,其实还有一个为了兼容性的sqlite3_prepare以及更新的sqlite3_prepare_v3。基于“性价比”考量,我们使用sqlite3_prepare_v2就行了。

sqlite3_prepare_v2的原型为:

SQLITE_API 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 */  
);

其中,db是我们打开的数据库实例,zSql是我们要执行的sql语句,语句中的变量使用?进行占位,而ppStmt是我们生成的sqlite3_stmt结构的指针的指针。

sqlite3_bind_*

为了绑定不同的值,有几个不同的函数可以使用,如:sqlite3_bind_intsqlite3_bind_doublesqlite3_bind_text等。

SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));  
SQLITE_API int sqlite3_bind_blob64(sqlite3_stmt*, int, const void*, sqlite3_uint64,  void(*)(void*));  
SQLITE_API int sqlite3_bind_double(sqlite3_stmt*, int, double);  
SQLITE_API int sqlite3_bind_int(sqlite3_stmt*, int, int);  
SQLITE_API int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);  
SQLITE_API int sqlite3_bind_null(sqlite3_stmt*, int);  
SQLITE_API int sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*));
// 省略其它

这些函数都是第一个是前面生成的sqlite3_stmt的指针,第二个是绑定的值的位置,第三个以及后面是值信息。

值得注意的是,这些函数的位置参数,即索引,从1开始。

比如,我们使用了

sqlite3_bind_int (stmt, 1, 1024);

就是把生成stmt的sql中的第1个?赋值了,不要使用0。

sqlite3_step

赋值完成后,就使用sqlite3_step执行。

这个函数原型特别简单,就是:

SQLITE_API int sqlite3_step(sqlite3_stmt*);

但是这个函数的返回值很丰富。不同的返回值,需要做不同的处理。

比如,在不出错的情况下,如果返回SQLITE_DONE,表示执行成功。

如果返回SQLITE_ROW,表示返回了一行数据,我们可以使用sqlite_column_*族的函数取得列的值。之后需要再次调用sqlite3_step,直到最后返回了SQLITE_DONE

sqlite3_column_*

SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);  
SQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol);  
SQLITE_API int sqlite3_column_int(sqlite3_stmt*, int iCol);  
SQLITE_API sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);  
SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
// 省略其它

可以看到,这些函数基本上跟sqlite3_bind_*是对应的。

唯一需要注意的是,这些函数的索引是从0开始的。

另外,sqlite3_data_count可以取得当前行的列数。

示例

最后来一个示例,总结一下:

int insert_medias(sqlite3_database *db, struct media *medias, size_t count)
{int ret;sqlite3_stmt *stmt;const char *sql = "INSERT INTO media(path, size) VALUES(?,?);";if (sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL) != SQLITE_OK) {printf ("sqlite3_prepare_v2() error: %s\n", sqlite3_errmsg (db));return -1;}for(int i = 0; i < count; i++){sqlite3_bind_text(stmt, 1, medias[i].name, strlen (medias[i].name), NULL);sqlite3_bind_int(pstmt, 2, medias[i].size);if (sqlite3_step(pstmt) != SQLITE_DONE) {printf ("sqlite3_step() error: %s\n", sqlite3_errmsg (db));return i;}sqlite3_reset(pstmt);}sqlite3_finalize(pstmt);return count;
}void query_media (sqlite3_database *db) 
{int ret;sqlite3_stmt *stmt;const char *sql = "SELECT * FROM media;";if (sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL) != SQLITE_OK) {printf ("sqlite3_prepare_v2() error: %s\n", sqlite3_errmsg (db));return -1;}ret = sqlite3_step (stmt);while (ret == SQLITE_ROW) {printf("get media: %s, size: %d\n", sqlite3_coumn_text (stmt, 0), sqlite3_column_int (stmt, 1));ret = sqlite3_step (stmt);}if (ret != SQLITE_DONE) {printf ("sqlite3_step() error: %s\n", sqlite3_errmsg (db)); }
}

命令行

sqlite3有一个命令行工具,就叫sqlite3。

我们使用sqlite3 /tmp/test.sqlite3就可以打开前面创建的数据库。

在这个命令终端里,以.开头的是内置的命令。

如:

> .help 显示帮助
> .tables 显示数据表
> .schema TableName 显示创建数据表的语句,类似mysql里的describe
> .open 文件名 关闭当前,打开另一个数据库
> .quit 退出
> .save 文件名 另存当前数据库到文件
> .headers on/off 在查询结果前面,是否显示列名

执行sql语句,就直接输入,加;回车执行就行了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/76227.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Allure测试报告按测试终端和测试类型智能分类查看

以下是实现Allure测试报告按测试终端和测试类型智能分类的完整方案: 一、测试框架分层设计 # 项目结构 project/ ├── api_tests/ # API测试 │ └── test_order.py ├── app_tests/ # 移动端测试 │ ├── android/ │ └── ios/ ├── pc_te…

Spine-Leaf 与 传统三层架构:全面对比与解析

本文将详细介绍Spine-Leaf架构&#xff0c;深入对比传统三层架构&#xff08;Core、Aggre、Access&#xff09;&#xff0c;并探讨其与Full-mesh网络和软件定义网络&#xff08;SDN&#xff09;的关联。通过通俗易懂的示例和数据中心网络分析&#xff0c;我将帮助您理解Spine-L…

图像预处理-图像噪点消除

一.基本介绍 噪声&#xff1a;指图像中的一些干扰因素&#xff0c;也可以理解为有那么一些点的像素值与周围的像素值格格不入。常见的噪声类型包括高斯噪声和椒盐噪声。 滤波器&#xff1a;也可以叫做卷积核 - 低通滤波器是模糊&#xff0c;高通滤波器是锐化 - 低通滤波器就…

安卓手机如何改ip地址教程

对于安卓手机用户而言&#xff0c;ip修改用在电商、跨境电商、游戏搬砖、社交软件这些需要开多个账号的项目。因为多个设备或账号又不能在同一ip网络下&#xff0c;所以修改手机的IP地址防检测成为一个必要的操作。以下是在安卓手机上更改IP地址的多种方法及详细步骤&#xff0…

对象池模式在uniapp鸿蒙APP中的深度应用

文章目录 对象池模式在uniapp鸿蒙APP中的深度应用指南一、对象池模式核心概念1.1 什么是对象池模式&#xff1f;1.2 为什么在鸿蒙APP中需要对象池&#xff1f;1.3 性能对比数据 二、uniapp中的对象池完整实现2.1 基础对象池实现2.1.1 核心代码结构2.1.2 在Vue组件中的应用 2.2 …

本地部署大模型实现扫描版PDF文件OCR识别!

在使用大模型处理书籍 PDF 时&#xff0c;有时你会遇到扫描版 PDF&#xff0c;也就是说每一页其实是图像形式。这时&#xff0c;大模型需要先从图片中提取文本&#xff0c;而这就需要借助 OCR&#xff08;光学字符识别&#xff09;技术。 像 Gemini 2.5 这样的强大模型&#x…

《Operating System Concepts》阅读笔记:p700-p732

《Operating System Concepts》学习第 60 天&#xff0c;p700-p732 总结&#xff0c;总计 33 页。 一、技术总结 1.Virtual machine manager (VMM) The computer function that manages the virtual machine; also called a hypervisor. VMM 也称为 hypervisor。 2.types …

软件项目验收报告模板

软件项目验收报告 一、项目基本信息 项目名称XX智能仓储管理系统开发单位XX科技有限公司验收单位XX物流集团合同签订日期2023年3月15日项目启动日期2023年4月1日验收日期2024年1月20日 二、验收范围 入库管理模块&#xff08;包含RFID识别、库存预警&#xff09;出库调度模…

深度学习笔记39_Pytorch文本分类入门

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 一、我的环境 1.语言环境&#xff1a;Python 3.8 2.编译器&#xff1a;Pycharm 3.深度学习环境&#xff1a; torch1.12.1cu113torchvision…

二分查找-LeetCode

题目 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target&#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0c;否则返回 -1。 示例 1: 输入: nums [-1,0,3,5,9,12], target 9 输出: 4 解释: …

从 Ext 到 F2FS,Linux 文件系统与存储技术全面解析

与 Windows 和 macOS 操作系统不同&#xff0c;Linux 是由爱好者社区开发的大型开源项目。它的代码始终可供那些想要做出贡献的人使用&#xff0c;任何人都可以根据个人需求自由调整它&#xff0c;或在其基础上创建自己的发行版本。这就是为什么 Linux 存在如此多的变体&#x…

leetcode:3210. 找出加密后的字符串(python3解法)

难度&#xff1a;简单 给你一个字符串 s 和一个整数 k。请你使用以下算法加密字符串&#xff1a; 对于字符串 s 中的每个字符 c&#xff0c;用字符串中 c 后面的第 k 个字符替换 c&#xff08;以循环方式&#xff09;。 返回加密后的字符串。 示例 1&#xff1a; 输入&#xff…

JVM详解(曼波脑图版)

(✪ω✪)&#xff89; 好哒&#xff01;曼波会用最可爱的比喻给小白同学讲解JVM&#xff0c;准备好开启奇妙旅程了吗&#xff1f;(๑˃̵ᴗ˂̵)و &#x1f4cc; 思维导图 ━━━━━━━━━━━━━━━━━━━ &#x1f34e; JVM是什么&#xff1f;&#xff08;苹果式比…

ZStack文档DevOps平台建设实践

&#xff08;一&#xff09;前言 对于软件产品而言&#xff0c;文档是不可或缺的一环。文档能帮助用户快速了解并使用软件&#xff0c;包括不限于特性概览、用户手册、API手册、安装部署以及场景实践教程等。由于软件与文档紧密耦合&#xff0c;面对业务的瞬息万变以及软件的飞…

Git创建分支操作指南

1. 创建新分支但不切换&#xff08;仅创建&#xff09; git branch <分支名>示例&#xff1a;创建一个名为 new-feature 的分支git branch new-feature2. 创建分支并立即切换到该分支 git checkout -b <分支名> # 传统方式 # 或 git switch -c <分支名&g…

package.json 中的那些版本数字前面的符号是什么意思?

1. 语义化版本&#xff08;SemVer&#xff09; 语义化版本的格式是 MAJOR.MINOR.PATCH&#xff0c;其中&#xff1a; MAJOR&#xff1a;主版本号&#xff0c;表示不兼容的 API 修改。MINOR&#xff1a;次版本号&#xff0c;表示新增功能但保持向后兼容。PATCH&#xff1a;修订号…

如何有效防止服务器被攻击

首先&#xff0c;我们要明白服务器被攻击的危害有多大。据不完全统计&#xff0c;每年因服务器遭受攻击而导致的经济损失高达数十亿。这可不是一个小数目&#xff0c;就好比您辛苦积攒的财富&#xff0c;瞬间被人偷走了一大半。 要有效防止服务器被攻击&#xff0c;第一步就是…

Chainlit 快速构建Python LLM应用程序

背景 chainlit 是一款简单易用的Web UI goggle&#xff0c;它支持使用 Python 语言快速构建 LLM 应用程序&#xff0c;提供了丰富的功能&#xff0c;包括文本分析&#xff0c;情感分析等。 这里我们以官网openai提供的例子&#xff0c;快速的开发一个带有UI的聊天界面&#xf…

华为OD机试真题——硬件产品销售方案(2025A卷:100分)Java/python/JavaScript/C++/C语言/GO六种最佳实现

2025 A卷 100分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析&#xff1b; 并提供Java、python、JavaScript、C、C语言、GO六种语言的最佳实现方式&#xff01; 2025华为OD真题目录全流程解析/备考攻略/经验分享 华为OD机试真题《硬件产品…

【数据结构_6】双向链表的实现

一、实现MyDLinkedList&#xff08;双向链表&#xff09; package LinkedList;public class MyDLinkedList {//首先我们要创建节点&#xff08;因为双向链表和单向链表的节点不一样&#xff01;&#xff01;&#xff09;static class Node{public String val;public Node prev…