- 浏览: 151376 次
- 性别:
- 来自: 北京
文章分类
最新评论
SQlite数据库的C编程接口(三) 预处理语句(Prepared Statements) ——《Using SQlite》读书笔记 .
SQlite数据库的C编程接口(三) 预处理语句(Prepared Statements) by斜风细雨QQ:253786989 2012-02-04
SQlite3数据库连接完成之后,就可以执行SQL命令了。下面将要介绍的prepare和step函数都是用来操作和执行SQL命令的。
典型的函数操作流程(伪代码):
- /*createastatementfromanSQLstring*/
- sqlite3_stmt*stmt=NULL;
- sqlite3_prepare_v2(db,sql_str,sql_str_len,&stmt,NULL);
- /*usethestatementasmanytimesasrequired*/
- while(...)
- {
- /*bindanyparametervalues*/
- sqlite3_bind_xxx(stmt,param_idx,param_value...);
- ...
- /*executestatementandstepovereachrowoftheresultset*/
- while(sqlite3_step(stmt)==SQLITE_ROW)
- {
- /*extractcolumnvaluesfromthecurrentresultrow*/
- col_val=sqlite3_column_xxx(stmt,col_index);
- ...
- }
- /*resetthestatementsoitmaybeusedagain*/
- sqlite3_reset(stmt);
- sqlite3_clear_bindings(stmt);/*optional*/
- }
- /*destroyandreleasethestatement*/
- sqlite3_finalize(stmt);
- stmt=NULL;
/* create a statement from an SQL string */ sqlite3_stmt *stmt = NULL; sqlite3_prepare_v2( db, sql_str, sql_str_len, &stmt, NULL ); /* use the statement as many times as required */ while( ... ) { /* bind any parameter values */ sqlite3_bind_xxx( stmt, param_idx, param_value... ); ... /* execute statement and step over each row of the result set */ while ( sqlite3_step( stmt ) == SQLITE_ROW ) { /* extract column values from the current result row */ col_val = sqlite3_column_xxx( stmt, col_index ); ... } /* reset the statement so it may be used again */ sqlite3_reset( stmt ); sqlite3_clear_bindings( stmt ); /* optional */ } /* destroy and release the statement */ sqlite3_finalize( stmt ); stmt = NULL;
这段程序首先调用sqlite3_prepare_v2函数,将一个SQL命令字符串转换成一条prepared语句,存储在sqlite3_stmt类型结构体中。随后调用sqlite3_bind_xxx函数给这条prepared语句绑定参数。然后不停的调用sqlite3_step函数执行这条prepared语句,获取结果集中的每一行数据,从每一行数据中调用qlite3_column_xxx函数获取有用的列数据,直到结果集中所有的行都被处理完毕。
prepared语句可以被重置(调用sqlite3_reset函数),然后可以重新绑定参数之后重新执行。sqlite3_prepare_v2函数代价昂贵,所以通常尽可能的重用prepared语句。最后,这条prepared语句确实不在使用时,调用sqlite3_finalize函数释放所有的内部资源和sqlite3_stmt数据结构,有效删除prepared语句。
预处理(Prepare)
- intsqlite3_prepare(
- sqlite3*db,/*Databasehandle*/
- constchar*zSql,/*SQLstatement,UTF-8encoded*/
- intnByte,/*MaximumlengthofzSqlinbytes.*/
- sqlite3_stmt**ppStmt,/*OUT:Statementhandle*/
- constchar**pzTail/*OUT:PointertounusedportionofzSql*/
- );
- intsqlite3_prepare_v2(
- sqlite3*db,/*Databasehandle*/
- constchar*zSql,/*SQLstatement,UTF-8encoded*/
- intnByte,/*MaximumlengthofzSqlinbytes.*/
- sqlite3_stmt**ppStmt,/*OUT:Statementhandle*/
- constchar**pzTail/*OUT:PointertounusedportionofzSql*/
- );
- intsqlite3_prepare16(
- sqlite3*db,/*Databasehandle*/
- constvoid*zSql,/*SQLstatement,UTF-16encoded*/
- intnByte,/*MaximumlengthofzSqlinbytes.*/
- sqlite3_stmt**ppStmt,/*OUT:Statementhandle*/
- constvoid**pzTail/*OUT:PointertounusedportionofzSql*/
- );
- intsqlite3_prepare16_v2(
- sqlite3*db,/*Databasehandle*/
- constvoid*zSql,/*SQLstatement,UTF-16encoded*/
- intnByte,/*MaximumlengthofzSqlinbytes.*/
- sqlite3_stmt**ppStmt,/*OUT:Statementhandle*/
- constvoid**pzTail/*OUT:PointertounusedportionofzSql*/
- );
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命令字符串转换为prepared语句。参数db是由sqlite3_open函数返回的指向数据库连接的指针。参数zSql是UTF-8或者UTF-16编码的SQL命令字符串,参数nByte是zSql的字节长度。如果nByte为负值,则prepare函数会自动计算出zSql的字节长度,不过要确保zSql传入的是以NULL结尾的字符串。如果SQL命令字符串中只包含一条SQL语句,那么它没有必要以“;”结尾。参数ppStmt是一个指向指针的指针,用来传回一个指向新建的sqlite3_stmt结构体的指针,sqlite3_stmt结构体里面保存有转换好的SQL语句。如果SQL命令字符串包含多条SQL语句,同时参数pzTail不为NULL,那么它将指向SQL命令字符串中的下一条SQL语句。上面4个函数中的v2版本是加强版,与原始版函数参数相同,不同的是函数内部对于sqlite3_stmt结构体的表现上。细节不去理会,尽量使用v2版本。
在sqlite3_prepare函数转换一条语句完毕之后,可以给这条语句绑定参数。语句参数允许我们插入一个特殊的占位符,然后给这个参数占位符绑定指定的值,然后再执行它。执行完成之后,还可以重置语句,绑定新的参数值,再重新执行该语句。像INSERT操作,每次执行INSERT命令,绑定不同的值,插入不同的数据。参数绑定是一个有点复杂的深入话题,在下一节中学习。
步进(Step)
- intsqlite3_step(sqlite3_stmt*);
int sqlite3_step(sqlite3_stmt*);
sqlite3_prepare函数将SQL命令字符串解析并转换为一系列的命令字节码,这些字节码最终被传送到SQlite3的虚拟数据库引擎(VDBE: Virtual Database Engine)中执行,完成这项工作的是sqlite3_step函数。比如一个SELECT查询操作,sqlite3_step函数的每次调用都会返回结果集中的其中一行,直到再没有有效数据行了。每次调用sqlite3_step函数如果返回SQLITE_ROW,代表获得了有效数据行,可以通过sqlite3_column函数提取某列的值。如果调用sqlite3_step函数返回SQLITE_DONE,则代表prepared语句已经执行到终点了,没有有效数据了。很多命令第一次调用sqlite3_step函数就会返回SQLITE_DONE,因为这些SQL命令不会返回数据。对于INSERT,UPDATE,DELETE命令,会返回它们所修改的行号——一个单行单列的值。
结果列(Result Columns)
- intsqlite3_column_count(sqlite3_stmt*pStmt);
int sqlite3_column_count(sqlite3_stmt *pStmt);
返回结果集的列数。
- constchar*sqlite3_column_name(sqlite3_stmt*,intN);
- constvoid*sqlite3_column_name16(sqlite3_stmt*,intN);
const char *sqlite3_column_name(sqlite3_stmt*, int N); const void *sqlite3_column_name16(sqlite3_stmt*, int N);
返回结果集中指定列的列名,列的序号以0开始。比如一条SQL语句:SELECT pid AS person_id...,那么调用sqlite3_column_name函数返回结果集中第0列的列名就是person_id。返回的字符串指针将一直有效,直到再次调用sqlite3_column_name函数并再次读取该列的列名时失效。
- intsqlite3_column_type(sqlite3_stmt*,intiCol);
int sqlite3_column_type(sqlite3_stmt*, int iCol);
该函数返回结果集中指定列的本地存储类型,如SQLITE_INTEGER,SQLITE_FLOAT,SQLITE_TEXT,SQLITE_BLOB,SQLITE_NULL。为了获取正确的类型,该函数应该在任何试图提取数据的函数调用之前被调用。SQlite3数据库允许不同类型的数据存储在同一列中,所以对于不同行的相同索引的列调用该函数获取的列类型可能会不同。
- constvoid*sqlite3_column_blob(sqlite3_stmt*,intiCol);
const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
返回一个指针,指向给定列的BLOB类型值。
- doublesqlite3_column_double(sqlite3_stmt*,intiCol);
double sqlite3_column_double(sqlite3_stmt*, int iCol);
从给定列返回一个64位浮点值。
- intsqlite3_column_int(sqlite3_stmt*,intiCol);
int sqlite3_column_int(sqlite3_stmt*, int iCol);
从给定列返回一个32位有符号整数,如果该列中包含的整型值无法用32位数值表示,那它将会在没有任何警告的情况下被截断。
- sqlite3_int64sqlite3_column_int64(sqlite3_stmt*,intiCol);
sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);
从给定列返回一个64位有符号整数。
- constunsignedchar*sqlite3_column_text(sqlite3_stmt*,intiCol);
- constvoid*sqlite3_column_text16(sqlite3_stmt*,intiCol);
const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);
返回一个指针,指向给定列的UTF-8或者UTF-16编码的字符串,该字符串以NULL结尾。
- sqlite3_value*sqlite3_column_value(sqlite3_stmt*,intiCol);
sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);
返回一个指针,指向一个无保护的sqlite3_value结构,该结构无法进行安全的数据类型转换,所以无法调用sqlite3_value_xxx函数从这个结构体中提取原始数值。如果想提取原始数值,只能调用其它的sqlite3_column_xxx函数。对于该函数返回的指针,安全的用法是以它为参数调用sqlite3_bind_value函数给一个prepared语句绑定参数,或者以它为参数调用sqlite3_result_value函数得到一个用户自定义的SQL函数的返回值。
对于这些sqlite3_column_xxx函数返回的指针,当再次调用sqlite3_column_xxx函数并操作相同的列的时失效,或者在sqlite3_step、sqlite3_reset、sqlite3_finalize等函数调用之后失效。
如果提取列值时使用的sqlite3_column_xxx函数版本与原始值的本地数据类型不同,SQlite数据库将进行转换。转换原则:
- intsqlite3_value_bytes(sqlite3_value*);
- intsqlite3_value_bytes16(sqlite3_value*);
int sqlite3_value_bytes(sqlite3_value*); int sqlite3_value_bytes16(sqlite3_value*);
对于BLOB和text类型,sqlite3_column_blob和sqlite3_column_text函数将会返回一个buffer指针。通过sqlite3_value_bytes函数可以得到buffer字节长度,对于text类型,这个字节长度将包括一个字符串结尾符。
需要注意的是:假如先调用sqlite3_column_text函数获取一个指向UTF-8编码的字符串指针,之后又调用了sqlite3_column_bytes16在相同的列上获取buffer大小,那么该列的字符串将会从UTF-8编码转换为UTF-16编码,导致之前由sqlite3_column_text函数返回的指针失效。
正确做法是提取值时的函数和获取值buffer大小的函数,以相同类型匹配使用,如:
- /*correctlyextractablob*/
- buf_ptr=sqlite3_column_blob(stmt,n);
- buf_len=sqlite3_column_bytes(stmt,n);
- /*correctlyextractaUTF-8encodedstring*/
- buf_ptr=sqlite3_column_text(stmt,n);
- buf_len=sqlite3_column_bytes(stmt,n);
- /*correctlyextractaUTF-16encodedstring*/
- buf_ptr=sqlite3_column_text16(stmt,n);
- buf_len=sqlite3_column_bytes16(stmt,n);
/* correctly extract a blob */ buf_ptr = sqlite3_column_blob( stmt, n ); buf_len = sqlite3_column_bytes( stmt, n ); /* correctly extract a UTF-8 encoded string */ buf_ptr = sqlite3_column_text( stmt, n ); buf_len = sqlite3_column_bytes( stmt, n ); /* correctly extract a UTF-16 encoded string */ buf_ptr = sqlite3_column_text16( stmt, n ); buf_len = sqlite3_column_bytes16( stmt, n );
重置与完成(Reset and Finalize)
- intsqlite3_reset(sqlite3_stmt*pStmt);
int sqlite3_reset(sqlite3_stmt *pStmt);
当sqlite3_step函数调用返回SQLITE_DONE时,则代表这条语句已经完成执行,这时如果还想重用这条prepared语句,就需要调用sqlite3_reset函数进行重置。或者,比如我们只想提取结果集的前六行数据,那么我们就可以连续调用6次sqlite3_step函数,之后调用sqlite3_reset函数重置prepared语句,以备下一次使用。
- intsqlite3_finalize(sqlite3_stmt*pStmt);
int sqlite3_finalize(sqlite3_stmt *pStmt);
销毁prepared语句,释放资源。在关闭数据库连接之前,对于不再使用的prepared语句,一定要调用sqlite3_finalize函数进行销毁,
语句(状态)转换(Statement Transitions)
一条语句可以处于不同状态,对于一条新的或者刚刚被reset的语句,它们处于“ready”状态,代表它们已经准备好执行,但还没有开始执行。一条语句也有可能处于“running”状态,表明这条语句已经开始执行,但还没有完成。还有一种状态叫做“done”,表明一条语句已经执行完成。
对于有些API函数,只能在某条语句处于特定状态下才可以执行,比如sqlite3_bind_xxx函数,只有在一条语句处于“ready”状态时才可以被调用,否则函数将会返回SQLITE_MISUSE错误码。下图展示了一条语句所处于的不同状态,以及不同状态之间是如何转换的。
示例代码
(1)
- sqlite3_stmt*stmt=NULL;
- /*...opendatabase...*/
sqlite3_stmt *stmt = NULL; /* ... open database ... */
- rc=sqlite3_prepare_v2(db,"CREATETABLEtbl(strTEXT)",-1,&stmt,NULL);
- if(rc!=SQLITE_OK)exit(-1);
rc = sqlite3_prepare_v2( db, "CREATE TABLE tbl ( str TEXT )", -1, &stmt, NULL ); if ( rc != SQLITE_OK) exit( -1 );
- rc=sqlite3_step(stmt);
- if(rc!=SQLITE_DONE)exit(-1);
- sqlite3_finalize(stmt);
- /*...closedatabase...*/
rc = sqlite3_step( stmt ); if ( rc != SQLITE_DONE ) exit ( -1 ); sqlite3_finalize( stmt ); /* ... close database ... */
CREATE TABLE语句没有返回值,调用sqlite3_step函数执行这条语句,最后在关闭数据库之前调用sqlite3_finalize销毁这条语句。
(2)
- constchar*data=NULL;
- sqlite3_stmt*stmt=NULL;
- /*...opendatabase...*/
const char *data = NULL; sqlite3_stmt *stmt = NULL; /* ... open database ... */
- rc=sqlite3_prepare_v2(db,"SELECTstrFROMtblORDERBY1",-1,&stmt,NULL);
- if(rc!=SQLITE_OK)exit(-1);
- while(sqlite3_step(stmt)==SQLITE_ROW){
- data=(constchar*)sqlite3_column_text(stmt,0);
- printf("%s\n",data?data:"[NULL]");
- }
- sqlite3_finalize(stmt);
- /*...closedatabase...*/
rc = sqlite3_prepare_v2( db, "SELECT str FROM tbl ORDER BY 1", -1, &stmt, NULL ); if ( rc != SQLITE_OK) exit( -1 ); while( sqlite3_step( stmt ) == SQLITE_ROW ) { data = (const char*)sqlite3_column_text( stmt, 0 ); printf( "%s\n", data ? data : "[NULL]" ); } sqlite3_finalize( stmt ); /* ... close database ... */
这段代码循环提取表tbl的所有行,并把每一行第0列值打印出来。
SQlite数据库的C编程接口(三) 预处理语句(Prepared Statements) by斜风细雨QQ:253786989 2012-02-04
相关推荐
7. **性能优化**:了解如何使用索引、预编译语句( Prepared Statements)等技巧,可以显著提升SQLite的查询速度和安全性。 8. **版本管理**:随着SQLite版本的更新,可能需要更新SQLite3.pas和SQLiteTable3.pas以...
标题"‘C sqlite3 数据库’"表明我们将探讨的是使用C语言接口来操作SQLite3数据库的相关知识。 1. **SQLite3 API介绍** SQLite3提供了C语言接口,允许开发者通过一组函数调用来创建、查询和管理数据库。这些API...
在电子书中,你可能还会学习到如何使用预编译语句( Prepared Statements)防止SQL注入攻击,以及如何利用SQLite的VFS(Virtual File System)层扩展其功能,比如在内存中创建数据库或实现加密。 总之,SQLite...
易语言SQLite3数据库操作类模块源码是一款专为易语言编程环境设计的数据库访问组件,它使得在易语言中操作SQLite3轻而易举。SQLite3是一种轻量级的、自包含的、开源的SQL数据库引擎,广泛应用于嵌入式系统和桌面应用...
对于性能优化,SQLite支持预编译语句( Prepared Statements)。Objective-C中可能有一个`- (sqlite3_stmt *)prepareStatement:(NSString *)sql`方法,用于创建预编译的SQL语句,可以避免重复编译,提高效率。 4. ...
不过,需要注意的是,虽然SQLite.swift简化了SQL操作,但在处理大量数据或复杂的查询时,可能需要考虑性能优化,比如使用预编译语句(`prepared statements`)和事务。同时,对于更复杂的应用场景,如多用户同步,...
3. **预编译语句(Prepared Statements)**:对于频繁执行的SQL操作,预编译语句可以提高效率。通过`sqlite3_prepare_v2()`创建一个预编译的语句对象,然后使用`sqlite3_bind_*()`函数设置参数,最后用`sqlite3_step...
Ensure that prepared statements automatically reset on extended error codes of SQLITE_BUSY and SQLITE_LOCKED even when compiled using SQLITE_OMIT_AUTORESET. Correct miscounts in the sqlite3_analyzer....
在Python编程中,SQLite是一种轻量级的数据库引擎,它被广泛用于小型项目或作为大型应用程序的本地数据存储。SQLite数据库支持多线程环境,但并发处理不是它的强项,因为其默认的串行化事务模式可能会导致性能瓶颈。...
SQLite3提供了一系列C语言风格的API接口,如`sqlite3_open()`用于打开数据库,`sqlite3_exec()`用于执行SQL语句,`sqlite3_prepare_v2()`用于编译SQL语句,以及`sqlite3_step()`和`sqlite3_column_*()`用于遍历查询...
在实际项目中,你可能还需要使用预编译的SQL语句(prepared statements)以防止SQL注入攻击,并进行更复杂的查询操作,如联接、子查询等。 通过阅读`SQLite3_CC++.pdf`文档,你可以深入了解如何在C++中高效地使用...
例如,可以通过预编译语句(prepared statements)来减少SQL解析的开销,或者调整数据库配置参数以提高I/O性能。 6. 错误处理和调试: SQLite3 API返回一个错误代码,以指示操作是否成功。在开发过程中,通过检查...
2. **API接口**:SQLite3提供了一套C语言API,包含了许多函数,如`sqlite3_open()`用于打开数据库,`sqlite3_exec()`用于执行SQL语句,`sqlite3_prepare_v2()`用于编译SQL语句,以及`sqlite3_step()`和`sqlite3_...
通过阅读`gosqlite-74691fb6f837`的源代码,你可以深入了解Go和SQLite的绑定实现,包括错误处理、内存管理以及与C语言接口的交互等细节。这将有助于你优化性能、解决兼容性问题,甚至为项目贡献代码。
FMDB是一个流行的Objective-C库,它为SQLite数据库提供了一个简洁的接口。这个库使得在iOS应用程序中操作SQLite变得更加容易,无需深入理解SQLite的C语言API。本文将详细介绍FMDB的使用方法以及其在iOS开发中的重要...
2. **预处理语句(Prepared Statements)**:在代码中,我们看到了`prepare()`方法的使用,这是PDO预处理语句的一部分。预处理语句能确保用户输入的数据不会与SQL命令混淆,从而避免SQL注入。在本例中,`$sql`变量...
在C++中,我们可以使用预编译的SQL语句(prepared statements)来防止SQL注入攻击,并提高代码的安全性。同时,使用事务来确保数据的一致性,比如在处理预订时,如果房间已满或预订冲突,可以回滚事务。 在设计酒店...
使用预处理语句(Prepared Statements) 预处理语句是一种将SQL查询与参数分离的技术,可以有效防止SQL注入。例如,在Python的`sqlite3`库中实现预处理语句的方法如下: ```python import sqlite3 conn = sqlite3...
- 使用预处理语句( Prepared Statements)和参数化查询。 - 对用户输入进行验证和清洗,确保输入符合预期的格式。 - 使用数据库管理系统提供的功能来限制和转义输入中的特殊字符。 - 限制数据库错误信息的详细程度...
这可能涉及使用索引、优化查询语句、预编译的SQL语句(如SQLite的prepared statements)以及合理地管理数据库连接池。对于大规模并发访问,了解如何设计数据库架构以支持水平或垂直分区、读写分离等策略也是必要的。...