基于 SQLite 3 的 C 学习:1-开发流程 与 基本函数
背景
SQLite 是 一个 常用于 嵌入式平台的 轻量级的 关系型数据库。
我们已经介绍了 移植 SQLite 3 ,这一讲我们来介绍它的开发,这里仅仅涉及最基本的开发。
高级api:https://blog.csdn.net/yxtxiaotian/article/details/89037128
数据库有关概念
在介绍开发之前,我们需要明确有关概念。
数据库
(DataBase): “按照数据结构来组织、存储和管理数据的仓库”。是一个长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集合。
数据库管理系统
数据库概念的演变与诞生经历了漫长的发展过程,从最开始的人工管理,到文件系统,再到数据库系统。每一个阶段的到来都伴随着新的技术突破。
数据库管理系统(DataBase Managerment System, DBMS):管理数据库的一个软件,它充当所有数据的知识库,并对它的存储、安全、一致性、并发操作、恢复和访问负责。是对数据库的一种完整和统一的管理和控制机制。DBMS有一个数据字典(有时被称为系统表),用于贮存它拥有的每个事物的相关信息,例如名字、结构、位置和类型,这种关于数据的数据也被称为元数据(metadata)。
DBMS支持的3种数据模型:(这里不提 面向对象模型)
- 层次模型:若用图来表示,层次模型是一棵倒立的树。在现实世界中,事物之间的联系更多的是非层次关系的,用层次模型表示非树型结构是很不直接的。
在数据库中,满足以下条件的数据模型称为层次模型:
a.有且仅有一个节点无父节点,这个节点称为根节点;
b.其他节点有且仅有一个父节点。桌面型的关系模型数据库
- 网状模型:网状模型是一个网络。在数据库中,满足以下两个条件的数据模型称为网状模型。网状模型构成了比层次结构复杂的网状结构,适宜表示多对多的联系。
a.允许一个以上的节点无父节点;
b.一个节点可以有多于一个的父节点。
- 关系模型:以二维表的形式表示实体和实体之间联系的数据模型称为关系数据模型。
从模型的三要素角度看,关系模型的内容为:
a.数据结构:一张二维表格。
b.数据操作:数据表的定义、检索、维护、计算等。
c.数据约束条件:表中列的取值范围即域值的限制条件。
表
关系模型数据库采用表组织数据(表称为“关系”),一个数据库由许多个表组成,多个表数据之间存在着关系,在这些表上的数据操作依赖于关系,关系用来描述多个表之间的数据依存,包括了一对一、一对多、多对多的关系
字段
在数据库中,大多数时,表的“列”称为“字段”(Attribute) ,每个字段包含某一专题的信息。就像“通讯录”数据库中,“姓名”、“联系电话”这些都是表中所有行共有的属性,所以把这些列称为“姓名”字段和“联系电话”字段。
字段 = 字段名 + 类型
sqlite 中的 数据类型
- NULL :空类型(相当exel表格当中的:常规格式)
- INTEGER :整型(整型数据:24 366 14)
- REAL :IEEE浮点数(3.24 36.14)
- TEXT :文本型(表示字符:"224")
- BLOB :按照二进制值存储(图片,音频)
主关键字(primary key):
表中的一个或多个字段,它的值用于唯一地标识表中的某一条记录。在两个表的关系中,主关键字用来在一个表中引用来自于另一个表中的特定记录。主关键字是一种唯一关键字,表定义的一部分。一个表的主键可以由多个关键字共同组成,并且主关键字的列不能包含空值。主关键字是可选的,并且可在 CREATE TABLE
或 ALTER TABLE
语句中定义。一个表中只能有一个主键,最好为每个表都设置一个主键。
开发流程
在Sqlite提供了两个重要对象:database_connection(数据库连接)和prepared_statement)处理命令:
database_connection:
- database_connection对象是由sqlite3_open接口函数创建并返回的。
- 在其他应用程序调用Sqlite3相关接口时,都需要这个对象作为输入来完成操作。
prepared_statement:
- 编译后的SQL语句。是select、insect、updata···
- 所有和SQL语句执行相关的函数也都需要 该对象作为输入参数以完成指定的SQL操作。
0)创建数据库、在数据库中创建表
1)打开数据库
2)在表中插入数据、修改、查询、删除一行记录
3)关闭数据库
有关函数
所有的函数都需要包含:#include "sqlite3.h"
,以下不再赘述。
数据库的操作无非就是CURD,我们一个个来看
打开/创建 数据库 sqlite3_open
int sqlite3_open(const char *filename,sqlite3 **db);
int sqlite3_open16(
const void *filename, /* Database filename (UTF-16) */
sqlite3 **db /* OUT: SQLite db handle */
);
int sqlite3_open_v2(
const char *filename, /* Database filename (UTF-8) */
sqlite3 **db, /* OUT: SQLite db handle */
int flags, /* Flags */
const char *zVfs /* Name of VFS module to use */
);
描述:打开对应的数据库文件,如果不存在则创建
参数解析:
filename:数据库文件名
db:用于接收数据库对象的指针
flags :
- SQLITE_OPEN_NOMUTEX: 设置数据库连接运行在多线程模式(没有指定单线程模式的情况下)
- SQLITE_OPEN_FULLMUTEX:设置数据库连接运行在串行模式。
- SQLITE_OPEN_SHAREDCACHE:设置运行在共享缓存模式。
- SQLITE_OPEN_PRIVATECACHE:设置运行在非共享缓存模式。
- SQLITE_OPEN_READWRITE:指定数据库连接可以读写。
- SQLITE_OPEN_CREATE:如果数据库不存在,则创建。
zvfs : 一个 sqlite3_vfs 对象(定义了数据库使用的操作系统接口),如果为NULL则使用默认值
注:传递db时不需要为db申请内存。
例程:
int main(int argc, char *argv[])
{
int ret;
sqlite3 *db = NULL;
//打开数据库
ret = sqlite3_open("example.db",&db);
if(ret != SQLITE_OK)
{
printf("sqlite3_open failure!\n");
printf("%s\n", sqlite3_errmsg(db));
return -1;
}
return 0;
}
关闭数据库 sqlite3_close
int sqlite3_close(sqlite3* db);
描述:关闭数据库
参数解析:db : 持有数据库对象的指针
返回值:返回0
例程:
ret = sqlite3_close(db);
获取错误信息 sqlite3_errmsg / sqlite3_errcode
const char *sqlite3_errmsg(sqlite3* db);
int sqlite3_errcode(sqlite3 *db)
描述:返回操作数据库时的错误信息
参数解析:db : 持有数据库对象的指针
注:使用
sqlite3_close
以后调用sqlite3_errmsg
会导致library routine called out of sequence
返回值:成功时返回对应类型的错误信息。
例程:
int main(int argc, char *argv[])
{
int ret;
sqlite3 *db = NULL;
ret = sqlite3_open("example.db",&db);
if(ret != SQLITE_OK)
{
printf("sqlite3_open failure!\n");
printf("%s\n", sqlite3_errmsg(db)); // 打印错误信息
return -1;
}
return 0;
}
格式化SQLite语句 sqlite3_mprintf
char *sqlite3_mprintf(const char*,...);
void sqlite3_free(void*);
描述:
将结果写入到sqlite3_malloc()获取的内存中,例程返回的字符串应该使用sqlite3_free()进行释放,如果无法申请到足够的内存,则会返回NULL指针;
它同c库函数 sprintf()类似,实现一些额外的格式化。对所有常用的printf()格式化选项都适用。另外还有非常用选项:%q, %Q, %z;
%q选项的作用类似于%s,它会替换了参数列表中以空字符结尾的字符串%q,同时他会将单引号字符转义,有助于防止SQL注入攻击
作为一般规则,在将文本插入字符串文字时,应始终使用%q而不是%s。
- %Q选项的作用类似于%q,除了它还在整个字符串的外部添加单引号。此外,如果参数列表中的参数是NULL指针,则%Q替换文本“NULL”(不带单引号)
- “%z”格式化选项的作用类似于“%s”,但添加了在读取字符串并将其复制到结果中之后,在输入字符串上调用
sqlite3_free
。 - 当需要插入的字符为NULL时,%s 并没有将NULL插入到表中,但是%q, %Q能够将NULL插入到表中,只是大小写不一样
执行 SQLite 语句
一共有6个函数(3组) 可以实现执行语句: sqlite3_exec
、 sqlite3_get_table/sqlite3_free_table
、sqlite3_prepare_v2/sqlite3_step/sqlite3_finalize
它们的区别在于
(1).sqlite3_exec方式接口使用很简单,实现同样的功能,比sqlite3_perpare_v2接口代码量少。
(2).sqlite3_prepare方式更高效,因为只需要编译一次,就可以重复执行N次。
(3).sqlite3_prepare方式支持参数化SQL。
鉴于两种方式的差异,对于简单的PRAGMA设置语句(PRAGMA cache_size=2000),事务设置语句(BEGIN TRANSACTION,COMMIT,ROLLBACK)使用sqlite3_exec方式,更简单;而对于批量的更新、查询语句,则使用sqlite3_prepare方式,更高效。
用ltrace跟踪
sqlite3_get_table
时,发现效率比较低,因为它封装了sqlite3_exec
这些函数,而sqlite3_exec
又调用了sqlite3_prepare
等,看来还是得在后两者中选择一个了。不过如果查询不是很多,应该说来sqlite3_get_table就好了,而且使用简单。
sqlite3_exec
int sqlite3_exec(
sqlite3* db, const char *sql,
int (*callback)(void*,int,char**,char**),
void *arg, char **errmsg
);
int callback(void *execarg, int ncols, char ** values, char **attribute);
/*
execarg : sqlite3_exec() 传进来的参数
ncols : 字段个数
values : 字段值
attribute:字段名
*/
描述:执行sql指定的数据库命令操作。
参数解析:
db :持有数据库对象的指针
sql :SQL命令,可以有多条命令组成
callback:执行完该函数的回调函数
arg : 执行callback回调函数的参数(execarg)
errmsg : 获取函数错误是的错误码
注意:每成功执行的一次select执行操作,就会调用一次回调函数;插入,删除,更新不执行回调函数。 这个特征具体可以参考sqlite3交互式程序。
sqlite3_exec 通常用于执行不返回数据的查询,如insert、update、delete
#include "sqlite3.h"
int sqlite_callback(void *data, int ncols, char ** values, char **attribute)
{
printf("sqlite_callback\n");
printf("Data addr is %p\n", data);
printf("ncols is %d\n", ncols);
for(int i = 0; i< ncols; i++)
{
printf("%p %s %s\n", attribute, attribute[i], values[i]);
}
printf("\n");
printf("\n");
return 0;
}
int main(int argc, char *argv[])
{
int ret;
sqlite3 *db;
//打开数据库
ret = sqlite3_open("example.db",&db);
printf("%d\n", ret);
if(ret != SQLITE_OK)
{
printf("sqlite3_open failure!\n");
printf("%s\n", sqlite3_errmsg(db));
return -1;
}
char *errmsg;
// 执行 n 次 回调函数
char *command_select = "SELECT * FROM table1";
int data = 2;
printf("%p\n", &data);
ret = sqlite3_exec(db, command_select, sqlite_callback, &data,0);
if(ret != SQLITE_OK)
{
printf("sqlite3_open failure!\n");
return -1;
}
ret = sqlite3_close(db);
return 0;
}
sqlite3_get_table/sqlite3_free_table
int SQLite3_get_table(
sqlite3 *db, const char *sql,
char ***result, int *nrow, int *ncolumn, char **errmsg);
void sqlite3_free_table(char **result);
描述:功能与sqlite3_exec
一样,只是无需回调函数处理,使用get以后要free,不然会导致内存泄漏。该函数接收SQL语句返回的所有记录,由于结果集可能非常大,会导致内存撑爆,因此对于大结果集的查询,不建议采用这种方式。 (这是一个历史遗留的接口,已经不推荐使用)
如果sqlite3_get_table在内部调用的 sqlie3_exec 发生错误时,错误信息无法通过sqlite3_errcode/sqlite3_errmsg获取;
参数解析:
db : 持有数据库对象的指针
sql : SQL语句
result :存放结果集的指针
nrow : 存放行数获取结果的容器
ncolumn :存放列数获取结果的容器
errmsg:存放错误信息的指针
例程
#include <stdio.h>
#include <string.h>
#include "sqlite3.h"
int main(void)
{
int ret,i,j;
sqlite3 *db = NULL;
char *errmsg;
char **result;
int nrow, ncolumn;
// 打开数据库,若数据库不存在,则创建
ret = sqlite3_open("example.db",&db);
if(ret != SQLITE_OK)
{
printf("sqlite3_open failure!\n");
return -1;
}
// 创建表、执行创建表的命令
char *sql = "CREATE TABLE IF NOT EXISTS student(id integer primary key, name text,age integer,sex text,phone text);";
ret = sqlite3_exec(db,sql,0,0,&errmsg);
// 执行命令,并获取结果
sqlite3_get_table(db, "SELECT * FROM student;", &result, &nrow, &ncolumn, &errmsg);
printf("nrow = %d\n", nrow); //行 --> 6
printf("ncolumn = %d\n", ncolumn);//列 --> 5
for(i=0; i<nrow; i++)
{
for(j=0; j<ncolumn; j++)
{
printf("字段:%s 字段值:%s\n",presult[j], presult[ncolumn] );
index++;
}
printf("===================================\n");
}
//释放空间
sqlite3_free_table(result);
sqlite3_close(db);
return 0;
}
sqlite3_prepare_v2/sqlite3_step/sqlite3_finalize
sqlite3_exec实际上是将编译,执行进行了封装,与之等价的一组函数是
sqlite3_prepare_v2()
,sqlite3_step()
和sqlite3_finalize()
。
sqlite3_prepare_v2()
编译SQL语句生成VDBE执行码,sqlite3_step()
执行,sqlite3_finalize()
关闭语句句柄,释放资源。
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 **stmt, /* OUT: Statement handle */
const char **pzTail /* OUT: Pointer to unused portion of zSql */
);
int sqlite3_step(sqlite3_stmt* stmt);
描述:如果既不想写回调函数,又想避免sqlite3_get_table
之后麻烦的一维数组遍历,那么利用sqlite3_prepare_v2执行sql select
语句,然后sqlite3_step
遍历select
执行的返回结果是一个非常方便的解决方案。
当然,你必须要明白
sqlite3_prepare_v2
不仅仅能够执行table的query selection,也能方便地进行sql Delete, Insert, Update
等其他一些操作。它能帮你把sql语句的执行操作变的更加优雅。
参数详解:
db : 持有数据库对象的指针
zSql : SQL语句,新旧版本使用不同的编码(正常使用即可)
nByte:若为负,从zSql 中取出,直到第一个0终止符终止;如果为非负,它则是这个函数能读取Zsql的最大字节数。
stmt:能够作为sqlite3_step()
执行的编译好的准备语句的指针,如果错误发生,它被置为NULL,如假如输入的文本不包括SQL语句,调用过程必须负责在编译好的sql语句完成使用后使用sqlite3_finalize()
删除它。
pzTail:上面提到zSql在遇见终止符或者是达到设定的nByte之后结束,假如zSql还有剩余的内容,那么这些剩余的内容被存放到pZTail中,不包括终止符.
例程1 : 简单做一下查询
const char *selectSql="select id,name from persons";
int id;
char *name;
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(db, selectSql, -1, &statement, NULL) !=SQLITE_OK)
{
printf("select error: %s", sqlie3_errmsg(db));
}
while (sqlite3_step(statement)==SQLITE_ROW)
{
int id=sqlite3_column_int(statement, 0);
name = (char *)sqlite3_column_text(statement, 1);
printf("row >>id %d, name %s",id, name); // 由于 这个接口使用了 UTF-8 所以打印中文可能会有乱码
}
sqlite3_finalize(statement);
例程2 : 执行多sql
prepare方式执行多sql的例子,pNext初始化在sql语句首部,执行完一个sql后,移动到下一个sql首部。
const char *pNext = (const char *)sql;
while (pNext && strlen(pNext) > 0) {
rc = sqlite3_prepare_v2(db, pNext, -1, &stmt, &pNext);
if(SQLITE_OK != rc){
错误处理
break;
}
rc = sqlite3_step(stmt);
if(SQLITE_OK != rc && SQLITE_DONE != rc){
错误处理
break;
}
rc = SQLITE_OK;
/*统计影响记录数目*/
resultCount += sqlite3_changes(db);
/* 清理语句句柄,准备执行下一个语句*/
sqlite3_finalize(stmt);
}
例程3 : 查询语句
prepare方式同样支持查询语句,主要分为3个阶段,编译,执行和结果集处理。前面更新SQL部分已经描述了prepare的基本步骤,这里主要讲结果集处理部分。首先通过sqlite3_column_count()
可以得到结果集的列数目,通过sqlite3_column_type()
可以得到具体某列的存储类型,方便我们调用合适的sqlite3_column_xxx接口处理字段值。主要有以下几类:
- sqlite3_column_int
- sqlite3_column_int64
- sqlite3_column_double
- sqlite3_column_text
- sqlite3_column_blob
int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
//获取列数目
int n_columns = sqlite3_column_count(stmt);
do{
ret = sqlite3_step(stmt);
if (ret == SQLITE_ROW)
{
//处理每一列
for (i = 0; i < n_columns; i++)
{
/*获取列存储类型*/
type = sqlite3_column_type(stmt,i);
switch(type)
{
case SQLITE_INTEGER:
/*处理整型*/
sqlite3_column_int(stmt,i);
break;
case SQLITE_FLOAT:
/*处理浮点数*/
sqlite3_column_double(stmt,i);
break;
case SQLITE_TEXT:
/*处理字符串*/
sqlite3_column_text(stmt,i);
break;
case SQLITE_BLOB:
/*处理二进制*/
sqlite3_column_blob(stmt, i));
break;
case SQLITE_NULL:
/*处理空*/
}
}
}
else if (ret == SQLITE_DONE) //结束
{
break;
}
}while(true);
例程4 : 参数绑定
SQLite通过prepare接口可以支持参数化的SQL语句,即带问号的SQL语句。比如查询语句select * from t where id=?
,或者插入语句 insert into t(a,b,c) values(?,?,?)
。通过参数化SQL,可以实现一次编译多次执行的目的,由于问号是没有意义的,因此需要调用sqlite3_bind_xxx接口来绑定具体的参数。主要有以下几类:
- sqlite3_bind_int
- sqlite3_bind_int64
- sqlite3_bind_double
- sqlite3_bind_text
- sqlite3_bind_blob
- sqlite3_bind_null
关于绑定参数这里提一点,对于sqlite3_bind_text和sqlite3_bind_blob接口,绑定参数占据的存储空间是否可以被SQLite重用。接口中通过最后一个参数指定,参数值可以为SQLITE_STATIC和SQLITE_TRANSIENT。
- SQLITE_STATIC:通知bind函数,参数使用空间是常量,不会改变,sqlite内部无需拷贝副本。
- SQLITE_TRANSIENT:通知bind函数,参数使用空间可能会改变,sqlite内部需要有自己的副本。
//begin a transaction
if(sqlite3_exec(pdb, "begin", NULL, NULL, &errmsg) != SQLITE_OK)
{
错误处理
return ERROR;
}
sqlite3_prepare_v2(pdb, "insert into t1 values(?,?,?);", &stmt);
for (i = 0; i < n_rows; i++)
{
for (j = 0; j < n_columns; j++)
{
switch(type)
{
case SQLITE_INTEGER:
/*处理整型*/
sqlite3_bind_int()
break;
case SQLITE_FLOAT:
/*处理浮点型*/
sqlite3_bind_double()
break;
case SQLITE_TEXT:
/*处理字符串类型*/
sqlite3_bind_text()
break;
case SQLITE_BLOB:
/*处理二进制类型*/
sqlite3_bind_blob
break;
case SQLITE_NULL:
sqlite3_bind_null(stmt, index);
break;
}
}
sqlite3_step(stmt); //执行
sqlite3_reset(stmt); //将已编译的SQL语句恢复到初始状态,保留语句相关的资源
}
sqlite3_finalize(stmt); //结束语句,释放语句句柄
if(sqlite3_exec(pdb, "commit", NULL, NULL, &errmsg) != SQLITE_OK)
{
错误处理 return ERROR;
}
SQLite3 语法:
创建表
CREATE TABLE [IF NOT EXISTS] table_name(
column1 datatype PRIMARY KEY(one or more columns),
column2 datatype[,
column3 datatype,
.....
columnN datatype,]
);
删除表
DROP TABLE database_name.table_name;
添加一行数据
INSERT INTO TABLE_NAME [(column1, column2, column3,...columnN)]
VALUES (value1, value2, value3,...valueN);
查询
SELECT 语句用于从 SQLite 数据库表中获取数据,以结果表的形式返回数据。这些结果表也被称为结果集。
SELECT column1, column2, columnN FROM table_name;
删除数据
DELETE 查询用于删除表中已有的记录。可以使用带有 WHERE 子句的 DELETE 查询来删除选定行,否则所有的记录都会被删除。
DELETE FROM table_name WHERE [condition];
修改数据
SQLite 的 UPDATE 查询用于修改表中已有的记录。可以使用带有 WHERE 子句的 UPDATE 查询来更新选定行,否则所有的行都会被更新。
UPDATE table_name
SET column1 = value1, column2 = value2...., columnN = valueN
WHERE [condition];
交互式Sqlite3工具的使用
$ sqlite3 example.db # 运行sqlite3时顺便打开一个数据库
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> .tables
table1
sqlite> select * from table1;
100|text
101|text
102|text
103|text
104|text
105|text
106|text
107|text
108|text
109|text
sqlite>
附录:sqlite3 错误码表
#define SQLITE_OK 0 /* 成功 | Successful result */
/* 错误码开始 */
#define SQLITE_ERROR 1 /* SQL错误 或 丢失数据库 | SQL error or missing database */
#define SQLITE_INTERNAL 2 /* SQLite 内部逻辑错误 | Internal logic error in SQLite */
#define SQLITE_PERM 3 /* 拒绝访问 | Access permission denied */
#define SQLITE_ABORT 4 /* 回调函数请求取消操作 | Callback routine requested an abort */
#define SQLITE_BUSY 5 /* 数据库文件被锁定 | The database file is locked */
#define SQLITE_LOCKED 6 /* 数据库中的一个表被锁定 | A table in the database is locked */
#define SQLITE_NOMEM 7 /* 某次 malloc() 函数调用失败 | A malloc() failed */
#define SQLITE_READONLY 8 /* 尝试写入一个只读数据库 | Attempt to write a readonly database */
#define SQLITE_INTERRUPT 9 /* 操作被 sqlite3_interupt() 函数中断 | Operation terminated by sqlite3_interrupt() */
#define SQLITE_IOERR 10 /* 发生某些磁盘 I/O 错误 | Some kind of disk I/O error occurred */
#define SQLITE_CORRUPT 11 /* 数据库磁盘映像不正确 | The database disk image is malformed */
#define SQLITE_NOTFOUND 12 /* sqlite3_file_control() 中出现未知操作数 | Unknown opcode in sqlite3_file_control() */
#define SQLITE_FULL 13 /* 因为数据库满导致插入失败 | Insertion failed because database is full */
#define SQLITE_CANTOPEN 14 /* 无法打开数据库文件 | Unable to open the database file */
#define SQLITE_PROTOCOL 15 /* 数据库锁定协议错误 | Database lock protocol error */
#define SQLITE_EMPTY 16 /* 数据库为空 | Database is empty */
#define SQLITE_SCHEMA 17 /* 数据结构发生改变 | The database schema changed */
#define SQLITE_TOOBIG 18 /* 字符串或二进制数据超过大小限制 | String or BLOB exceeds size limit */
#define SQLITE_CONSTRAINT 19 /* 由于约束违例而取消 | Abort due to constraint violation */
#define SQLITE_MISMATCH 20 /* 数据类型不匹配 | Data type mismatch */
#define SQLITE_MISUSE 21 /* 不正确的库使用 | Library used incorrectly */
#define SQLITE_NOLFS 22 /* 使用了操作系统不支持的功能 | Uses OS features not supported on host */
#define SQLITE_AUTH 23 /* 授权失败 | Authorization denied */
#define SQLITE_FORMAT 24 /* 附加数据库格式错误 | Auxiliary database format error */
#define SQLITE_RANGE 25 /* 传递给sqlite3_bind()的第二个参数超出范围 | 2nd parameter to sqlite3_bind out of range */
#define SQLITE_NOTADB 26 /* 被打开的文件不是一个数据库文件 | File opened that is not a database file */
#define SQLITE_ROW 100 /* sqlite3_step() 已经产生一个行结果 | sqlite3_step() has another row ready */
#define SQLITE_DONE 101 /* sqlite3_step() 完成执行操作 | sqlite3_step() has finished executing */
/* 错误码结束 */
基于 SQLite 3 的 C 学习:1-开发流程 与 基本函数的更多相关文章
- 基于AOP和HashMap原理学习,开发Mysql分库分表路由组件!
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 什么?Java 面试就像造火箭 单纯了! 以前我也一直想 Java 面试就好好面试呗 ...
- Android应用---基于NDK的samples例程hello-jni学习NDK开发
Android应用---基于NDK的samples例程hello-jni学习NDK开发 NDK下载地址:http://developer.android.com/tools/sdk/ndk/index ...
- 学习嵌入式开发板的Android平台体系结构和源码结构
本文转自迅为论坛资料:http://www.topeetboard.com 推荐学习嵌入式开发板平台:iTOP-4412开发板 下面这张图出自Google官方,展示了Android系统的主要组成部分. ...
- 基于SageMath的数学网站——本科毕业开发项目
1 绪论 1.1研究背景 我国是一个拥有15亿人口的大国.其中,据2017年的统计,全国共有大学生2600万左右.如此数量众多的大学生,都会有着学习基础数理课程的需求.而在高校的数学教学中,教授最多最 ...
- 从C#到Objective-C,循序渐进学习苹果开发(3)--分类(category)和协议Protocal的理解
本随笔系列主要介绍从一个Windows平台从事C#开发到Mac平台苹果开发的一系列感想和体验历程,本系列文章是在起步阶段逐步积累的,希望带给大家更好,更真实的转换历程体验.本文继续上一篇随笔<从 ...
- 【零基础学习iOS开发】【转载】
原文地址:http://www.cnblogs.com/mjios/archive/2013/04/24/3039357.html 本文目录 一.什么是iOS 二.主流手机操作系统 三.什么是iOS开 ...
- 基于Spring的可扩展Schema进行开发自定义配置标签支持
一.背景 最近和朋友一起想开发一个类似alibaba dubbo的功能的工具,其中就用到了基于Spring的可扩展Schema进行开发自定义配置标签支持,通过上网查资料自己写了一个demo.今天在这里 ...
- 10个很棒的学习Android 开发的网站(转)
看到江湖旅人 写的<10个很棒的学习iOS开发的网站 - 简书>,所以就忍不住写Android 啦,也希望对大家有帮助.我推荐的网站,都是我在学习Android 开发过程中发现的好网站,给 ...
- 翻译:打造基于Sublime Text 3的全能python开发环境
原文地址:https://realpython.com/blog/python/setting-up-sublime-text-3-for-full-stack-python-development/ ...
- 那些在学习iOS开发前就应该知道的事(part 2)
英文原文:Things I wish I had known before starting iOS development—Part 2 http://www.cocoachina.com/ios/ ...
随机推荐
- Codeforces 题解集
Codeforces Round 940 (Div. 2) and CodeCraft-23 Codeforces Round 940 (Div. 2) and CodeCraft-23 (A-E)
- C#开发的CPU使用率小应用 - 开源研究系列文章 - 个人小作品
这次用C#编写一个CPU使用率的小应用.想了一下,大概需要两个内容:一个是获取CPU使用率:一个是托盘图标的动画效果.这两个内容在上次的博文中有介绍了,此博文为具体的应用的例子. 对于要实现的应用,首 ...
- 使用beego/bee热启动gin框架
目录 1.需要关闭gomod 2.安装 bee 3.再开启gomod 4.启动服务 效果: 1.需要关闭gomod export GO111MODULE=off 2.安装 bee go get -u ...
- Python基础知识——缩进、标识符、保留字
标识符 标识符就是程序中,使用的各种名称,例如:变量名.常量名.类名等等. 在 Python 中,对标识符格式的要求与 C/C++.Java 等差不多: 第一个字符必须是字母表中的字母或下划线 _ ; ...
- List集合中获取重复元素
一.方法1 ## 测试数据 List<String> words = Arrays.asList("a", "b", "c", ...
- Unity新的MeshData API学习
在新版本的Unity中提供了MeshDataArray和MeshData等多个API,使Mesh数据操作支持多线程:以更好的支持DOTS. API文档:https://docs.unity3d.com ...
- OpenTelemetry agent 对 Spring Boot 应用的影响:一次 SPI 失效的
背景 前段时间公司领导让我排查一个关于在 JDK21 环境中使用 Spring Boot 配合一个 JDK18 新增的一个 SPI(java.net.spi.InetAddressResolverPr ...
- PageOffice 在线编辑 office文件,回调父页面
一.子页面调用父页面的方法 var value=window.external.CallParentFunc("ParentFunName(Arguments);");//父页面的 ...
- C# Log4net 组件无法写日志 IsDebuged、IsInfoEnabled、IsErrorEnabled 全部为false
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "Log4Net.config", Watch = true)] 如果 ...
- 【c#】 重复鼠标键盘动作
这个先录制好要重复的鼠标和键盘的操作,然后就能重复的执行保存的这些动作,这个是我从csdn下载的,原本不支持录制键盘动作. + 符号 开始/暂停录制 / 播放/暂停动作 - 退出程序 主要是用到了 ...