资讯专栏INFORMATION COLUMN

C - SQLite剖析之C接口

MobService / 778人阅读

摘要:对象是由接口函数创建并返回的,在应用程序使用任何其他接口函数之前,必须先调用该函数以便获得对象,在随后的其他调用中,都需要该对象作为输入参数以完成相应的工作。最后需要指出的,对于新的应用程序,可以使用接口函数来替代该函数以完成相同的工作。

SQLite剖析之C/C++接口

菜鸟教程:http://www.runoob.com/sqlite/...
sqlite官方api文档:https://sqlite.com/capi3ref.h...

前言

  SQLite3是SQLite一个全新的版本,它虽然是在SQLite2的代码基础之上开发的,但是使用了和之前的版本不兼容的数据库格式和API。SQLite3是为了满足以下的需求而开发的:支持UTF-16编码、用户自定义的文本比较方法、可以对BLOBs字段建立索引。SQLite 3.X版的和SQLite 2.X版的API非常相似,但是有一些重要的改变需要注意。3.X版的API增加到超过185个,所有API接口函数和数据结构的前缀都由"sqlite_"改为了"sqlite3_",这是为了避免同时使用SQLite 2.X和SQLite 3.X这两个版本的时候发生链接冲突。这里概要地介绍一下SQLite的核心API,详细的API指南参考http://sqlite.com/capi3ref.html。

  由于对于C语言应用什么数据类型来存放UTF-16编码的字符串并没有一致的规范,因此SQLite使用了普通的void类型来指向UTF-16编码的字符串。客户端使用过程中可以把void映射成适合他们的系统的任何数据类型。

 

sqlite3的使用 

一个SQL数据库引擎的首要任务是执行SQL语句以获得我们想要的数据。为了完成这个任务,开发需要知道两个对象数据库连接对象sqlite3和**SQL预处理语句对象.sqlite3_stmt,定义如下:

typedef struct sqlite3 sqlite3;
typedef struct sqlite3_stmt sqlite3_stmt;

  严格地说,SQL预处理语句对象不是必需的,因为有使用方便的包装函数sqlite3_exec或sqlite3_get_table,它们封装并且隐藏了SQL语句对象。不过理解SQL语句对象能更好地使用SQLite。

  数据库连接对象和SQL语句对象由下面几个核心的C/C++接口来控制:sqlite3_open()、sqlite3_prepare()、sqlite3_step()、sqlite3_column()、sqlite3_finalize()、sqlite3_close()。

  使用SQLite3时根据以上函数大概分为几个过程,这几个过程是概念上的说法,而不完全是程序运行的过程,如sqlite3_column()表示的是对查询获得一行里面的数据的列的各个操作统称,实际上在sqlite中并不存在这个函数。在SQLite提供的C/C++接口中,其中5个API属于核心接口。相比于其它数据库引擎提供的API,如OCI、MySQL API等,SQLite提供的接口易于理解和掌握。以上六个C/C++接口及上面的两个对象构成SQLite的核心功能。注意这些接口有些有多个版本,例如sqlite3_open()有三个独立的版本:sqlite3_open(), sqlite3_open16()和sqlite3_open_v2(),它们以稍微不同的方式完成同样的事情。 sqlite3_column()代表一个家族系列:sqlite_column_int(), sqlite_column_blob()等等,用于提取结果集中各种类型的列数据。  

核心对象和接口: 核心对象:

  在SQLite中最主要的两个对象是:database_connection和prepared_statement。database_connection对象是由sqlite3_open()接口函数创建并返回的,在应用程序使用任何其他SQLite接口函数之前,必须先调用该函数以便获得database_connnection对象,在随后的其他API调用中,都需要该对象作为输入参数以完成相应的工作。至于prepare_statement,我们可以简单地将它视为编译后的SQL语句,因此,所有和SQL语句执行相关的函数也都需要该对象作为输入参数以完成指定的SQL操作。

核心接口:

sqlite3_open.
​ 上面已经提到过这个函数了,它是操作SQLite数据库的入口函数。该函数返回的database_connection对象是很多其他SQLite API的句柄参数。注意,我们通过该函数既可以打开已经存在的数据库文件,也可以创建新的数据库文件。对于该函数返回的database_connection对象,我们可以在多个线程之间共享该对象的指针,以便完成和数据库相关的任意操作。然而在多线程情况下,更为推荐的使用方式是,为每个线程创建独立的database_connection对象。对于该函数还有一点也需额外说明,我们没有必要为了访问多个数据库而创建多个数据库连接对象,因为通过SQLite自带的ATTACH命令可以在一个连接中方便的访问多个数据库。

sqlite3_prepare.
​ 该函数将SQL文本转换为prepared_statement对象,并在函数执行后返回该对象的指针。事实上,该函数并不会评估参数指定SQL语句,它仅仅是将SQL文本初始化为待执行的状态。最后需要指出的,对于新的应用程序,可以使用sqlite3_prepare_v2接口函数来替代该函数以完成相同的工作。

sqlite3_step.
​ 该函数用于评估sqlite3_prepare函数返回的
prepared_statement对象,在执行完该函数之后,prepared_statement对象的内部指针将指向其返回的结果集的第一行。如果打算进一步迭代其后的数据行,就需要不断的调用该函数,直到所有的数据行都遍历完毕。然而对于INSERT、UPDATE和DELETE等DML语句,该函数执行一次即可完成。

sqlite3_column.
​ 该函数用于获取当前行指定列的数据,然而严格意义上讲,此函数在SQLite的接口函数中并不存在,而是由一组相关的接口函数来完成该功能,其中每个函数都返回不同类型的数据,如:

    sqlite3_column_blob
    sqlite3_column_bytes
    sqlite3_column_bytes16
    sqlite3_column_double
    sqlite3_column_int
    sqlite3_column_int64
    sqlite3_column_text
    sqlite3_column_text16
    sqlite3_column_type
    sqlite3_column_value
    sqlite3_column_count

​ 其中,sqlite3_column_count函数用于获取当前结果集中的字段数目。下面是使用sqlite3_step和sqlite3_column函数迭代结果集中每行数据的伪代码(注意这里作为示例代码简化了对字段类型的判断): 

int fieldCount = sqlite3_column_count(...);
while (sqlite3_step(...) <> EOF) {
    for (int i = 0; i < fieldCount; ++i) {
        int v = sqlite3_column_int(...,i);
    }
}

sqlite3_finalize.
​ 该函数用于销毁prepared statement对象,否则将会造成内存泄露。

sqlite3_close.
​ 该函数用于关闭之前打开的database_connection对象,其中所有和该对象相关的prepared_statements对象都必须在此之前先被销毁。

参数绑定:

  和大多数关系型数据库一样,SQLite的SQL文本也支持变量绑定,以便减少SQL语句被动态解析的次数,从而提高数据查询和数据操作的效率。要完成该操作,我们需要使用SQLite提供的另外两个接口API,sqlite3_reset和sqlite3_bind。见如下示例:

void test_parameter_binding() {
  //1. 不带参数绑定的情况下插入多条数据。
  char strSQL[128];
  for (int i = 0; i < MAX_ROWS; ++i) {
    sprintf(strSQL,"insert into testtable values(%d)",i);
    sqlite3_prepare_v2(..., strSQL);
    sqlite3_step(prepared_stmt);
    sqlite3_finalize(prepared_stmt);
  }
  //2. 参数绑定的情况下插入多条数据。
  string strSQLWithParameter = "insert into testtable values(?)";
  sqlite3_prepare_v2(..., strSQLWithParameter);
  for (int i = 0; i < MAX_ROWS; ++i) {
    sqlite3_bind(...,i);
    sqlite3_step(prepared_stmt);
    sqlite3_reset(prepared_stmt);
  }
  sqlite3_finalize(prepared_stmt);
}

  这里首先需要说明的是,SQL语句"insert into testtable values(?)"中的问号(?)表示参数变量的占位符,该规则在很多关系型数据库中都是一致的,因此这对于数据库移植操作还是比较方便的。

  通过上面的示例代码,明显可以看出参数绑定写法的执行效率要高于每次生成不同的SQL语句的写法,即(2)在效率上要明显优于(1),下面是针对这两种写法的具体比较:

单单从程序表面来看,前者在for循环中执行了更多的任务,比如字符串的填充、SQL语句的prepare,以及prepared_statement对象的释放。

在SQLite的官方文档中明确的指出,sqlite3_prepare_v2的执行效率往往要低于sqlite3_step的效率。

当插入的数据量较大时,后者带来的效率提升还是相当可观的。

一、打开和关闭数据库连接
int sqlite3_open(  
  const char *filename,   /* Database filename (UTF-8) */  
  sqlite3 **ppDb          /* OUT: SQLite db handle */  
);  
int sqlite3_open16(  
  const void *filename,   /* Database filename (UTF-16) */  
  sqlite3 **ppDb          /* OUT: SQLite db handle */  
);  
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 */  
);  
int sqlite3_close(sqlite3*);  
int sqlite3_close_v2(sqlite3*);  
int sqlite3_errcode(sqlite3 *db);  
int sqlite3_extended_errcode(sqlite3 *db);  
const char *sqlite3_errmsg(sqlite3*);  
const void *sqlite3_errmsg16(sqlite3*); 

  建立到一个SQLite数据库文件的连接,返回连接对象。如果数据库文件不存在,则创建这个文件,函数返回一个整数错误代码。许多SQLite接口需要一个指向连接对象的指针作为第一个参数,这个函数用来创建一个数据库连接对象。sqlite3_open()和sqlite3_open16()的不同之处在于sqlite3_open16()使用UTF-16编码(使用本地主机字节顺序)传递数据库文件名。如果要创建新数据库,sqlite3_open16()将内部文本转换为UTF-16编码,反之sqlite3_open()将文本转换为UTF-8编码。打开或者创建数据库的命令会被缓存,直到这个数据库真正被调用的时候才会被执行。而且允许使用PRAGMA声明来设置如本地文本编码或默认内存页面大小等选项和参数。

  sqlite3_close()关闭数据库连接,在关闭之前所有准备好的SQL语句对象都要被销毁。

  sqlite3_errcode()通常用来获取最近调用的API接口返回的错误代码。sqlite3_errmsg()则用来得到这些错误代码所对应的文字说明。这些错误信息将以UTF-8的编码返回,并且在下一次调用任何SQLiteAPI函数的时候被清除。sqlite3_errmsg16()和sqlite3_errmsg()大体上相同,除了返回的错误信息将以UTF-16本机字节顺序编码。

SQLite的返回码定义如下:

#define SQLITE_OK           0   /* Successful result */  
/* beginning-of-error-codes */  
#define SQLITE_ERROR        1   /* SQL error or missing database */  
#define SQLITE_INTERNAL     2   /* 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   /* A malloc() failed */  
#define SQLITE_READONLY     8   /* Attempt to write a readonly database */  
#define SQLITE_INTERRUPT    9   /* Operation terminated by sqlite3_interrupt()*/  
#define SQLITE_IOERR       10   /* Some kind of disk I/O error occurred */  
#define SQLITE_CORRUPT     11   /* The database disk image is malformed */  
#define SQLITE_NOTFOUND    12   /* 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   /* 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() has another row ready */  
#define SQLITE_DONE        101  /* sqlite3_step() has finished executing */  
/* end-of-error-codes */  
二、编译SQL语句
int sqlite3_prepare(  
  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_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_prepare16(  
  sqlite3 *db,            /* Database handle */  
  const void *zSql,       /* SQL statement, UTF-16 encoded */  
  int nByte,              /* Maximum length of zSql in bytes. */  
  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */  
  const void **pzTail     /* OUT: Pointer to unused portion of zSql */  
);  
int sqlite3_prepare16_v2(  
  sqlite3 *db,            /* Database handle */  
  const void *zSql,       /* SQL statement, UTF-16 encoded */  
  int nByte,              /* Maximum length of zSql in bytes. */  
  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */  
  const void **pzTail     /* OUT: Pointer to unused portion of zSql */  
);  

  把SQL文本编译成一个SQL语句对象并返回这个对象的指针。它只是把含有SQL语句的字符串编译成字节码,并不执行SQL语句。sqlite3_prepare()处理的SQL语句应该是UTF-8编码的,而sqlite3_prepare16()则要求是UTF-16编码的。输入的参数中只有第一个SQL语句会被编译。第四个参数则用来指向输入参数中下一个需要编译的SQL语句存放的SQLite statement对象的指针。任何时候如果调用sqlite3_finalize()将销毁一个准备好的SQL声明。在数据库关闭之前,所有准备好的声明都必须被释放销毁。sqlite3_reset()函数用来重置一个SQL声明的状态,使得它可以被再次执行。

  注意现在sqlite3_prepare()已经不被推荐使用了,在新的应用中推荐使用sqlite3_prepare_v2()。

三、执行SQL语句
int sqlite3_step(sqlite3_stmt*); 

  在SQL声明准备好之后,就可以调用sqlite3_step()来执行这个SQL声明。如果SQL返回了一个单行结果集,sqlite3_step()函数将返回SQLITE_ROW,若要得到结果集的第二行、第三行 ...,则要继续调用sqlite3_step()。如果SQL语句执行成功或者正常将返回SQLITE_DONE,否则将返回错误代码。如果不能打开数据库文件则会返回SQLITE_BUSY。

  执行SQL语句还可以直接便捷的包装函数,这样就无需预先编译SQL语句。如下:

typedef int (*sqlite3_callback)(void*,int,char**, char**);  
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_exec函数依然像它在SQLite 2中一样承担着很多的工作。该函数的第二个参数中可以指定零个或多个SQL语句,查询的结果返回给回调函数,回调函数会作用在结果集的每条记录上。sqlite3_exec函数实际上封装了sqlite3_prepare_v2(),sqlite3_step()和sqlite3_finalize(),因此可以通过一个调用直接执行多条SQL语句,让应用程序省略大量代码,因此在实际应用中一般使用这个函数。

四、获取结果集数据
const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);  
int sqlite3_column_bytes(sqlite3_stmt*, int iCol);  
int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);  
const char *sqlite3_column_decltype(sqlite3_stmt *, int iCol);  
const void *sqlite3_column_decltype16(sqlite3_stmt *, int iCol);  
double sqlite3_column_double(sqlite3_stmt*, int iCol);  
int sqlite3_column_int(sqlite3_stmt*, int iCol);  
sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);  
const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);  
const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);  
int sqlite3_column_type(sqlite3_stmt*, int iCol);  
sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);  
const char *sqlite3_column_name(sqlite3_stmt*, int N);  
const void *sqlite3_column_name16(sqlite3_stmt*, int N);  
int sqlite3_column_count(sqlite3_stmt *pStmt);  
int sqlite3_data_count(sqlite3_stmt *pStmt);  

  如果函数sqlite3_step()的返回值是SQLITE_ROW,那么用以上方法可以获得记录集中的数据。

  sqlite3_column_count()函数返回结果集中包含的列数,sqlite3_column_count()可以在执行了sqlite3_prepare()之后的任何时刻调用。sqlite3_data_count()除了必须在sqlite3_step()之后调用之外,其他跟sqlite3_column_count()大同小异。如果调用sqlite3_step()返回值是SQLITE_DONE或者一个错误代码,则此时调用sqlite3_data_count()将返回0,然而sqlite3_column_count()仍然会返回结果集中包含的列数。

  返回的记录集通过使用其它几个sqlite3_column_xx()函数来提取,所有的这些函数都把列的编号作为第二个参数。列编号从左到右以零起始,和之前那些从1起始的参数不同。

  sqlite3_column_type()函数返回第N列的值的数据类型,具体的返回值如下:

#define SQLITE_INTEGER  1  
#define SQLITE_FLOAT    2  
#define SQLITE_TEXT     3  
#define SQLITE_BLOB     4  
#define SQLITE_NULL     5  

  sqlite3_column_decltype()则用来返回该列在CREATE TABLE语句中声明的类型,它可以用在当返回类型是空字符串的时候。

  sqlite3_column_name()返回第N列的字段名。

  sqlite3_column_bytes()用来返回UTF-8编码的BLOB列的字节数或者TEXT字符串的字节数。

  sqlite3_column_bytes16()对于BLOB列返回同样的结果,但是对于TEXT字符串则按UTF-16的编码来计算字节数。

  sqlite3_column_blob()返回BLOB数据。

  sqlite3_column_text()返回UTF-8编码的TEXT数据。sqlite3_column_text16()返回UTF-16编码的TEXT数据。

  sqlite3_column_int()以本地主机的整数格式返回一个整数值。sqlite3_column_int64()返回一个64位的整数。

  sqlite3_column_double()返回浮点数。

  不一定非要按照sqlite3_column_type()接口返回的数据类型来获取数据,数据类型不同时软件将自动转换。

五、SQL声明对象的销毁和重用
int sqlite3_finalize(sqlite3_stmt*);  
int sqlite3_reset(sqlite3_stmt*); 

  函数sqlite3_finalize()销毁由sqlite3_prepare()创建的SQL声明对象。在数据库关闭之前每个准备好的声明都必须被销毁,以避免内存泄露。sqlite3_reset()则用来重置一个SQL声明的状态,使得它可以被再次执行。例如用sqlite3_step()执行完编译好的SQL声明后,还想再执行它,则可用sqlite3_reset()重置它即可,而无需用sqlite3_prepare()再来编译一个新SQL声明,因为很多SQL声明的编译时间甚至超过执行时间。

六、给SQl语句绑定参数

SQL语句声明中可以包含如下形式的参数:

    ?
    ?NNN
    :AAA
    $AAA
    @AAA

  其中"NNN"是一个整数,"AAA"是一个字符串,这些标记代表一些不确定的字符值(或者说是通配符)。在首次调用sqlite3_step()之前或者刚调用sqlite3_reset()之后,应用程序可以用sqlite3_bind接口来填充这些参数。每一个通配符都被分配了一个编号(由它在SQL声明中的位置决定,从1开始),此外也可以用"NNN"来表示"?NNN"这种情况。允许相同的通配符在同一个SQL声明中出现多次,在这种情况下所有相同的通配符都会被替换成相同的值。没有被绑定的通配符将自动取NULL值。

int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));  
int sqlite3_bind_double(sqlite3_stmt*, int, double);  
int sqlite3_bind_int(sqlite3_stmt*, int, int);  
int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);  
int sqlite3_bind_null(sqlite3_stmt*, int);  
int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*));  
int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*));  
int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);  
int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);  

  以上是sqlite3_bind所包含的全部接口,用来给SQL声明中的通配符赋值。没有绑定的通配符则被认为是空值。已绑定的值不会被sqlite3_reset()函数重置。但是在调用了sqlite3_reset()之后所有的通配符都可以被重新赋值。注意绑定操作是可选的。

七、扩展SQL (1)创建自定义的比较序列:
int sqlite3_create_collation(  
  sqlite3*,   
  const char *zName,   
  int eTextRep,   
  void *pArg,  
  int(*xCompare)(void*,int,const void*,int,const void*)  
);  
int sqlite3_create_collation_v2(  
  sqlite3*,   
  const char *zName,   
  int eTextRep,   
  void *pArg,  
  int(*xCompare)(void*,int,const void*,int,const void*),  
  void(*xDestroy)(void*)  
);  
int sqlite3_create_collation16(  
  sqlite3*,   
  const void *zName,  
  int eTextRep,   
  void *pArg,  
  int(*xCompare)(void*,int,const void*,int,const void*)  
);  
int sqlite3_collation_needed(  
  sqlite3*,   
  void*,   
  void(*)(void*,sqlite3*,int eTextRep,const char*)  
);  
int sqlite3_collation_needed16(  
  sqlite3*,   
  void*,  
  void(*)(void*,sqlite3*,int eTextRep,const void*)  
);  

  这些函数在数据库连接上,为要比较的文本添加、删除或者修改自定义比较规则。第三个参数eTextRep表示SQLite支持的字符编码类型,必须是以下常量之一:

#define SQLITE_UTF8           1  
#define SQLITE_UTF16LE        2  
#define SQLITE_UTF16BE        3  
#define SQLITE_UTF16          4    /* Use native byte order */  
#define SQLITE_ANY            5    /* sqlite3_create_function only */  
#define SQLITE_UTF16_ALIGNED  8    /* sqlite3_create_collation only */  

  sqlite3_create_collation()函数用来声明一个比较序列和实现它的比较函数,比较函数只能用来做文本的比较。同一个自定义的比较规则的同一个比较函数可以有UTF-8、UTF-16LE和UTF-16BE等多个编码的版本。sqlite3_create_collation16()和sqlite3_create_collation()的区别也仅仅在于比较名称的编码是UTF-16还是UTF-8。

  可以使用sqlite3_collation_needed()函数来注册一个回调函数,当数据库引擎遇到未知的比较规则时会自动调用该函数。在回调函数中可以查找一个相似的比较函数,并激活相应的sqlite_3_create_collation()函数。回调函数的第四个参数是比较规则的名称。同样sqlite3_collation_needed采用UTF-8编码,sqlite3_collation_need16()采用UTF-16编码。

(2)创建自定义的SQL函数:
int sqlite3_create_function(  
  sqlite3 *db,  
  const char *zFunctionName,  
  int nArg,  
  int eTextRep,  
  void *pApp,  
  void (*xFunc)(sqlite3_context*,int,sqlite3_value**),  
  void (*xStep)(sqlite3_context*,int,sqlite3_value**),  
  void (*xFinal)(sqlite3_context*)  
);  
int sqlite3_create_function16(  
  sqlite3 *db,  
  const void *zFunctionName,  
  int nArg,  
  int eTextRep,  
  void *pApp,  
  void (*xFunc)(sqlite3_context*,int,sqlite3_value**),  
  void (*xStep)(sqlite3_context*,int,sqlite3_value**),  
  void (*xFinal)(sqlite3_context*)  
);  
int sqlite3_create_function_v2(  
  sqlite3 *db,  
  const char *zFunctionName,  
  int nArg,  
  int eTextRep,  
  void *pApp,  
  void (*xFunc)(sqlite3_context*,int,sqlite3_value**),  
  void (*xStep)(sqlite3_context*,int,sqlite3_value**),  
  void (*xFinal)(sqlite3_context*),  
  void(*xDestroy)(void*)  
);  

  nArg参数用来表明自定义函数的参数个数。如果参数值为0,则表示接受任意个数的参数。用eTextRep参数来表明传入参数的编码形式。SQLite 3允许同一个自定义函数有多种不同的编码参数的版本。数据库引擎会自动选择转换参数编码个数最少的版本使用。

  普通的函数只需要设置xFunc参数,而把xStep和xFinal设为NULL。聚合函数则需要设置xStep和xFinal参数,然后把xFunc设为NULL。该方法和使用sqlite3_create_aggregate() API一样。

  sqlite3_create_function16()和sqlite_create_function()的不同就在于自定义的函数名一个要求是UTF-16编码,而另一个则要求是UTF-8。

  自定函数的参数目前使用sqlite3_value结构体指针替代了SQLite version 2.X中的字符串指针。

  下面的函数用来从sqlite3_value结构体中提取数据,以获得SQL函数的参数值:

const void *sqlite3_value_blob(sqlite3_value*);  
int sqlite3_value_bytes(sqlite3_value*);  
int sqlite3_value_bytes16(sqlite3_value*);  
double sqlite3_value_double(sqlite3_value*);  
int sqlite3_value_int(sqlite3_value*);  
sqlite3_int64 sqlite3_value_int64(sqlite3_value*);  
const unsigned char *sqlite3_value_text(sqlite3_value*);  
const void *sqlite3_value_text16(sqlite3_value*);  
const void *sqlite3_value_text16le(sqlite3_value*);  
const void *sqlite3_value_text16be(sqlite3_value*);  
int sqlite3_value_type(sqlite3_value*);  
int sqlite3_value_numeric_type(sqlite3_value*); 

  上面的函数调用以下的API来获得上下文内容和返回结果:

void *sqlite3_aggregate_context(sqlite3_context*, int nBytes);  
void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*));  
void sqlite3_result_double(sqlite3_context*, double);  
void sqlite3_result_error(sqlite3_context*, const char*, int);  
void sqlite3_result_error16(sqlite3_context*, const void*, int);  
void sqlite3_result_error_toobig(sqlite3_context*);  
void sqlite3_result_error_nomem(sqlite3_context*);  
void sqlite3_result_error_code(sqlite3_context*, int);  
void sqlite3_result_int(sqlite3_context*, int);  
void sqlite3_result_int64(sqlite3_context*, sqlite3_int64);  
void sqlite3_result_null(sqlite3_context*);  
void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*));  
void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*));  
void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*));  
void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*));  
void sqlite3_result_value(sqlite3_context*, sqlite3_value*);  
void sqlite3_result_zeroblob(sqlite3_context*, int n);  
void *sqlite3_user_data(sqlite3_context*);  
void *sqlite3_get_auxdata(sqlite3_context*, int N);  
void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*));  
(3)注册自定义的虚拟表模块:
int sqlite3_create_module(  
  sqlite3 *db,               /* SQLite connection to register module with */  
  const char *zName,         /* Name of the module */  
  const sqlite3_module *p,   /* Methods for the module */  
  void *pClientData          /* Client data for xCreate/xConnect */  
);  
int sqlite3_create_module_v2(  
  sqlite3 *db,               /* SQLite connection to register module with */  
  const char *zName,         /* Name of the module */  
  const sqlite3_module *p,   /* Methods for the module */  
  void *pClientData,         /* Client data for xCreate/xConnect */  
  void(*xDestroy)(void*)     /* Module destructor function */  
);  

  其中第二个参数指定虚拟表模块名称,第三个参数指向虚拟表模块,第四个参数为传给虚拟表模块xCreate/xConnect方法的客户数据。sqlite3_create_module_v2()还有第五个参数,指定对pClientData数据进行析构的函数。若指定析构函数为NULL,则该函数与sqlite3_create_module()等价。

  SQLite的所有内建SQL函数都使用上面这些接口来创建,特别是date.c和func.c中的SQL函数代码。

程序示例
#include 
#include 
#include 
#include 
int main( int argc, char **argv )
{
  sqlite3 *db;
  sqlite3_stmt * stmt;
  const char *zTail;
  //打开数据库
  int r = sqlite3_open("mysqlite.db",&db);
  if(r){
    printf("%s",sqlite3_errmsg(db));
  }
  else
    printf("open db sccess!
");
  //创建Table
  //sqlite3_prepare()将SQL语句编译为sqlite内部一个结构体(sqlite3_stmt),
  //该结构体中包含了将要执行的SQL语句的信息
  //第四个参数用来指向输入参数中下一个需要编译的SQL语句存放的 SQLite statement 对象的指针
  sqlite3_prepare(db,"CREATE TABLE players ( ID INTEGER PRIMARY KEY, name TEXT, age INTEGER);",-1,&stmt,&zTail);
  printf("prepared has done
");
  //调用sqlite3_step(),此时SQL语句才真正执行,执行成功,返回SQLITE_DONE或SQLITE_ROW.
  //每次调用sqlite3_step(),只返回一行数据,使用sqlite3_column_XXX()函数来取出这些数据
  //要取出全部的数据,则需要反复调用sqlite3_step()
  sqlite3_step(stmt);
  //调用sqlite3_finalize(),释放stmt占用的内存,该内存是在sqlite3_prepare()时分配的
  //如果SQL语句要重复使用,可以调用sqlite3_reset()来清除已经绑定的参数
  sqlite3_finalize(stmt);
  printf("create table success!
");
  //插入数据
  sqlite3_prepare(db,"INSERT INTO players (name,age) VALUES(?,?);",-1,&stmt,&zTail);
  char str[] = "Kevin";
  int n = 23;
  //sqlite3_bind_xxx的第四个参数为负,则字符串长度由第一个0终止的位数决定
  //SQLITE_STATIC表示命令执行完后的信息为static类型,不能被改动,而且不需要被free
  sqlite3_bind_text(stmt,1,str,-1,SQLITE_STATIC);
  sqlite3_bind_int(stmt,2,n);
  r = sqlite3_step(stmt);
  if( r!=SQLITE_DONE){
    printf("%s",sqlite3_errmsg(db));
  }
  //清除已经绑定的参数
  sqlite3_reset(stmt);
  //插入第二个数据
  char str2[] = "Jack";
  int n2 = 16;
  sqlite3_bind_text(stmt,1,str2,-1,SQLITE_STATIC);
  sqlite3_bind_int(stmt,2,n2);
  r = sqlite3_step(stmt);
  if( r!=SQLITE_DONE){
    printf("%s",sqlite3_errmsg(db));
  }
  sqlite3_finalize(stmt); //释放stmt所占的内存
  //查询所有数据
  sqlite3_prepare(db,"SELECT ID, name, age FROM players ORDER BY age",-1,&stmt,&zTail);
  r = sqlite3_step(stmt);
  int number;
  int id;
  const unsigned char * name;
  while( r == SQLITE_ROW ){
    id = sqlite3_column_int( stmt, 0 );
    name = sqlite3_column_text( stmt,1 );
    number = sqlite3_column_int( stmt, 2 );
    printf("ID: %d Name: %s Age: %d 
",id,name,number);
    sleep(1);
    r = sqlite3_step(stmt);
  }
  sqlite3_finalize(stmt);
  //关闭数据库
  sqlite3_close(db);
  return 0;
}

编译运行:
$ gcc sqlite.c –o sqlite –lsqlite3
$ ./sqlite

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/110812.html

相关文章

  • C - SQLiteC接口

    摘要:回调函数,若该函数非空,则每次语法的结果都会调用该函数,若为空,则将被忽略。回调函数的第一个参数。存储错误信息,可为返回非零,该函数立即中断查询,并不再执行后续的语句和回调函数返回结束执行。 SQLite之C接口 sqlite之C接口简介 sqlite3_open int sqlite3_open ( const char *filename, /* Database fil...

    xiaotianyi 评论0 收藏0
  • SQLite C/C++接口简介

    摘要:将应用程序的数据与原始中的参数绑定执行到下一个结果或者结束当前结果行所在的列值的析构函数。传递到中的回调函数将用于处理每行结果集。接口用于创建全局的,进程级的配置更改。接口创建新的功能无论是标量还是聚合。 翻译自https://www.sqlite.org/cintro.html,建议阅读原文 摘要 接下来的两个对象和八个方法包含了SQLite接口的基本要素: sql...

    Cympros 评论0 收藏0
  • 一个老鸟发的公司内部整理的 Android 学习路线图

    摘要:一个老鸟发的公司内部整理的学习路线图年月日阅读数发了一篇一个老鸟也发了一份他给公司内部小伙伴整理的路线图。另一份开发学习路线图。看完这本书后,小明对的历史结构代码规范等都有了一个大概的了解,并且,小明已经可以写出一些简单的了。一个老鸟发的公司内部整理的 Android 学习路线图 2017年09月12日 17:13:27 阅读数:20449   jixiaohua发了一篇一个老...

    miya 评论0 收藏0
  • Android Studio 通过一个登录功能介绍SQLite数据库的使用

    摘要:前言简介是一款轻型的数据库,是遵守的关系型数据库管理系统,它包含在一个相对小的库中。第一个版本诞生于年月。数据库,它广泛用于包括浏览器,以及一些便携需求的小型应用系统。接下来,我会通过一个登录功能来介绍一下数据库在实际项目中的使用。前言: SQLite简介:是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领...

    番茄西红柿 评论0 收藏0
  • TaintDroid剖析IPC级污点传播

    摘要:前言在前三篇文章中我们详细分析了对栈帧的修改,以及它是如何在修改之后的栈帧中实现变量级污点跟踪方法级跟踪。总结的污点跟踪粒度是变量粒度的,因此大大提高了污点传播的精准度。下一步继续分析下级污点传播。 前言 在前三篇文章中我们详细分析了TaintDroid对DVM栈帧的修改,以及它是如何在修改之后的栈帧中实现DVM变量级污点跟踪、Native方法级跟踪。本篇文章我们来分析下IPC级污点传...

    baishancloud 评论0 收藏0

发表评论

0条评论

MobService

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<