-
2022-04-11 14:35:39
int sqlite3_exec( sqlite3* ppDb, /* 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 */ );//函数原型
1、操作成功
SQLITE_OK = 0; 返回成功
2、操作失败
SQLITE_ERROR = 1; SQL错误或错误的数据库
SQLITE_INTERNAL = 2; An internal logic error in SQLite
SQLITE_PERM = 3; 拒绝访问
SQLITE_ABORT = 4; 回调函数请求中断
SQLITE_BUSY = 5; 数据库文件被锁
SQLITE_LOCKED = 6; 数据库中的一个表被锁
SQLITE_NOMEM = 7; 内存分配失败
SQLITE_READONLY = 8; 试图对一个只读数据库进行写操作
SQLITE_INTERRUPT = 9; 由sqlite_interrupt()结束操作
SQLITE_IOERR = 10; 磁盘I/O发生错误
SQLITE_CORRUPT = 11; 数据库磁盘镜像畸形
SQLITE_NOTFOUND = 12; (Internal Only)表或记录不存在
SQLITE_FULL = 13; 数据库满插入失败
SQLITE_CANTOPEN = 14; 不能打开数据库文件
SQLITE_PROTOCOL = 15; 数据库锁定协议错误
SQLITE_EMPTY = 16; (Internal Only)数据库表为空
SQLITE_SCHEMA = 17; 数据库模式改变
SQLITE_TOOBIG = 18; 对一个表数据行过多
SQLITE_CONSTRAINT = 19; 由于约束冲突而中止(unique)
SQLITE_MISMATCH = 20; 数据类型不匹配
SQLITE_MISUSE = 21; 数据库错误使用
SQLITE_NOLFS = 22; 使用主机操作系统不支持的特性
SQLITE_AUTH = 23; 非法授权
SQLITE_FORMAT = 24; 辅助数据库格式错误
SQLITE_RANGE = 25; 2nd parameter to sqlite_bind out of range
SQLITE_NOTADB = 26; 打开的不是一个数据库文件
SQLITE_ROW = 100; sqlite_step() has another row ready
SQLITE_DONE = 101; sqlite_step() has finished executing
更多相关内容 -
Sqlite3插入速度比较(sqlite3_exec/sqlite3_prepare)
2021-10-20 15:24:37最近做到的项目涉及一个大数据量缓存重传,其中要用到的sqlite技术,把自己的学习心得整理了一下。 SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。同时能够跟很多程序...最近做到的项目涉及一个大数据量缓存重传,其中要用到的sqlite技术,把自己的学习心得整理了一下。
SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。同时能够跟很多程序语言相结合,比如 Tcl、C#、PHP、Java等,还有ODBC接口,同样比起Mysql、PostgreSQL这两款开源的世界著名数据库管理系统来讲,它的处理速度比他们都快。SQLite数据库由于其简单、灵活、轻量、开源,已经被越来越多的被应用到中小型应用中。因此在许多软件中例如(QQ,微信)等许多软件中都有广泛应用。
sqlite应用蛮广泛的,小到app应用,大到服务器缓存。不同的插入方法有不同的优劣,在实际开发过程中,不要一味的追求快,而忽视了安全性。下面我就介绍几种我在这段学习过程中所了解的插入方式。
慢插入-暴力插入
调用sqlite3_exec()函数,会隐式地开启了一个事务,其次,sqlite3_exec() 是sqlite3_perpare(),sqlite3_step(), sqlite3_finalize()的一个结合,每调用一次这个函数,就会重复的执行这三条语句,对于相同的语句,其中sqlite3_perpare相当于编译sql语句,如果语句相同且重复操作,就会增加很多重复操作。如果插入一条数据,就调该函数一次,事务就会被反复地开启、关闭,会增大IO量。所以当大批量数据插入时,此方法简直无法忍受。
事务插入-显示的开启事务
在这里插入图片描述所谓”事务“就是指一组SQL命令,这些命令要么一起执行,要么都不被执行。如果在插入数据前显式开启事务,插入后再一起提交,
则会大大提高IO效率,进而加数据快插入速度
同步关闭模式-synchronous = OFF
当synchronous设置为FULL, SQLite数据库引擎在紧急时刻会暂停以确定数据已经写入磁盘。这使系统崩溃或电源出问题时能确保数据库在重起后不会损坏。FULL synchronous很安全但很慢。
当synchronous设置为NORMAL, SQLite数据库引擎在大部分紧急时刻会暂停,但不像FULL模式下那么频繁。 NORMAL模式下有很小的几率(但不是不存在)发生电源故障导致数据库损坏的情况。但实际上,在这种情况 下很可能你的硬盘已经不能使用,或者发生了其他的不可恢复的硬件错误。
当设置为synchronous OFF时,SQLite在传递数据给系统以后直接继续而不暂停。若运行SQLite的应用程序崩溃, 数据不会损伤,但在系统崩溃或写入数据时意外断电的情况下数据库可能会损坏。另一方面,在synchronous OFF时 一些操作可能会快50倍甚至更多。在SQLite 2中,缺省值为NORMAL.而在3中修改为FULL。
执行前准备-sqlite3_prepare_v2
此方法就是“执行准备”(类似于存储过程)操作,即先将SQL语句编译好,然后再一步一步(或一行一行)地执行。如果采用前者的话,就算开起了事务,SQLite仍然要对循环中每一句SQL语句进行“词法分析”和“语法分析”,这对于同时插入大量数据的操作来说,简直就是浪费时间。因此,要进一步提高插入效率的话,就应该使用此方法
测试结果展示
extern "C" { #include "sqlite3.h" }; #include<sstream> #include <string> #include <iostream> #include <stdlib.h> #include <ctime> #include<windows.h> #define MAX_TEST_COUNT 200 using namespace std; int main() { char cmdCreatTable[256] = "create table SqliteTest (id integer , x integer , y integer, weight real)" ; sqlite3* db = NULL; char * errorMessage = NULL; int iResult = sqlite3_open("SqliteTest.db", &db); do { if (SQLITE_OK != iResult) { cout<<"创建InsertTest.db文件失败"<<endl; break; } sqlite3_exec(db,"drop table if exists SqliteTest",0,0,0); iResult = sqlite3_exec(db, cmdCreatTable, NULL, NULL, &errorMessage); if (SQLITE_OK != iResult) { cout<<"创建表SqliteTest失败"<<endl; break; } DWORD timeStart; DWORD timeStop; timeStart = GetTickCount(); for (int i = 0; i< MAX_TEST_COUNT; ++i) { stringstream ssm; ssm<<"insert into SqliteTest values("<<i<<","<<i*2<<","<<i/2<<","<<i*i<<")"; iResult = sqlite3_exec(db,ssm.str().c_str(),0,0,0); } timeStop = GetTickCount(); cout<< "直接Insert"<<MAX_TEST_COUNT<<"条数据操作执行时间" << timeStart<<"结束时间:"<<timeStop<<"共耗时:"<<timeStop-timeStart<<"ms"<<endl; timeStart = GetTickCount(); sqlite3_exec(db,"PRAGMA synchronous = OFF; ",0,0,0); for(int i = MAX_TEST_COUNT; i < MAX_TEST_COUNT*2; ++i) { stringstream ssm; ssm<<"insert into SqliteTest values("<<i<<","<<i*2<<","<<i/2<<","<<i*i<<")"; sqlite3_exec(db,ssm.str().c_str(),0,0,0); } timeStop = GetTickCount(); cout<< "同步写关闭+直接Insert"<<MAX_TEST_COUNT<<"条数据操作执行时间" << timeStart<<"结束时间:"<<timeStop<<"共耗时:"<<timeStop-timeStart<<"ms"<<endl; timeStart = GetTickCount(); sqlite3_exec(db,"PRAGMA synchronous = FULL; ",0,0,0); sqlite3_exec(db,"begin;",0,0,0); for(int i= MAX_TEST_COUNT*2; i< MAX_TEST_COUNT*3; ++i) { stringstream ssm; ssm<<"insert into SqliteTest values("<<i<<","<<i*2<<","<<i/2<<","<<i*i<<")"; sqlite3_exec(db,ssm.str().c_str(),0,0,0); } sqlite3_exec(db,"commit;",0,0,0); timeStop = GetTickCount(); cout<< "事务Insert"<<MAX_TEST_COUNT<<"条数据操作执行时间"<< timeStart<<"结束时间:"<<timeStop<<"共耗时:"<<timeStop-timeStart<<"ms"<<endl; timeStart = GetTickCount(); sqlite3_exec(db,"PRAGMA synchronous = OFF; ",0,0,0); sqlite3_exec(db,"begin;",0,0,0); for(int i = MAX_TEST_COUNT*3; i < MAX_TEST_COUNT*4; ++i) { stringstream ssm; ssm<<"insert into SqliteTest values("<<i<<","<<i*2<<","<<i/2<<","<<i*i<<")"; sqlite3_exec(db,ssm.str().c_str(),0,0,0); } sqlite3_exec(db,"commit;",0,0,0); timeStop = GetTickCount(); cout<< "事务+同步写关闭Insert"<<MAX_TEST_COUNT<<"条数据操作执行时间" << timeStart<<"结束时间:"<<timeStop<<"共耗时:"<<timeStop-timeStart<<"ms"<<endl; timeStart = GetTickCount(); //sqlite3_exec(db,"PRAGMA synchronous = FULL; ",0,0,0); sqlite3_exec(db,"begin;",0,0,0); sqlite3_stmt *stmt; const char* sql = "insert into SqliteTest values(?,?,?,?)"; sqlite3_prepare(db,sql,strlen(sql),&stmt,0); for(int i = MAX_TEST_COUNT*4; i<MAX_TEST_COUNT*5; ++i) { sqlite3_reset(stmt); sqlite3_bind_int(stmt,1,i); sqlite3_bind_int(stmt,2,i*2); sqlite3_bind_int(stmt,3,i/2); sqlite3_bind_double(stmt,4,i*i); sqlite3_step(stmt); } sqlite3_finalize(stmt); sqlite3_exec(db,"commit;",0,0,0); timeStop = GetTickCount(); cout<< "事务+执行准备+同步写关闭Insert"<<MAX_TEST_COUNT<<"条数据操作执行时间:"<< timeStart<<"结束时间:"<<timeStop<<"共耗时:"<<timeStop-timeStart<<"ms"<<endl; }while(0); cout<<"插入测试结束"<<endl; Sleep(2000); sqlite3_close(db); system("pause"); }
-
PC微信逆向--调用sqlite3_exec执行SQL
2022-04-22 20:57:11文章目录写在前面回顾sqlite3_exec回调函数函数指针编写代码注入的DLL注入程序输出结果写在后面 写在前面 上一篇文章,实现了定位保存微信数据库句柄的容器和微信内部的sqlite3_exec函数地址,这一篇文章,尝试使用...写在前面
上一篇文章,实现了定位保存微信数据库句柄的容器和微信内部的sqlite3_exec函数地址,这一篇文章,尝试使用得到的数据库句柄和sqlite3_exec,来查询数据库中的内容。
回顾
首先回顾一下上篇文章:PC微信逆向–定位sqlite3_exec和数据库句柄得到的结果,IDA中sqlite3_exec的地址是
0x11356570
,对应的偏移是0x11356570 - 0x10000000 = 0x1356570
,对应微信中的地址:WeChatWin.dll + 0x1356570
保存数据库句柄的容器首地址:[[WeChatWin.dll + 0x222F3FC] + 0x1888]
保存数据库句柄的容器尾地址:[[WeChatWin.dll + 0x222F3FC] + 0x188C]
sqlite3_exec
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打开的数据库句柄 const char *sql:要执行的sql语句 int (*callback)(void*,int,char**,char**):执行sql语句时对应的回调函数 void *:回调函数的参数 char **errmsg:存放错误信息
上一篇文章只关注了参数个数,以及默认调用约定,这一次需要理解每个参数的作用,才能在调用的时候不出错。
第一个参数是数据库句柄,已经拿到了。
第二个参数是要执行的SQL语句,为了必定执行成功,以下面这句作为测试:select * from sqlite_master where type='table';
第三个参数是回调函数,每查询出一条数据,都会调用一次回调函数,毫无疑问,这个参数是最重要的,等下单独讲解。
第四个参数是传递给回调函数的参数,这是一个void*参数,必须在回调函数里面解引用,如果不需要,可以写0。
第五个参数是错误信息,这个对我们来说不重要,可以写0。
最后还要说一下返回值,如果SQL执行成功,sqlite3_exec返回0,如果失败,返回非0值。回调函数
来看一个官方的例子:
static int callback(void *NotUsed, int argc, char **argv, char **azColName){ int i; for(i=0; i<argc; i++){ printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL"); } printf("\n"); return 0; }
第一个参数是为sqlite3_exec设置的第四个参数。
第二个参数是本次查询得到的字段个数。
第三个参数是char**
,可以将其理解为保存char*
的容器,所有成员都是char*
类型,实际上保存的是字段的值。
第四个参数和第三个参数类似,并且成员一一对应,保存的是字段名。函数指针
这个就很简单了,用typedef声明一个函数指针,并且解引用微信中的sqlite3_exec,就在不引入sqlite3库的情况下得到了sqlite3_exec函数:
typedef int(__cdecl* Sqlite3_exec)( DWORD, /* The database on which the SQL executes */ const char *, /* The SQL to be executed */ sqlite3_callback, /* Invoke this callback routine */ void *, /* First argument to xCallback() */ char ** /* Write error messages here */ );
这段代码是我抄来的,可能有人会问,第一个参数,应该是
sqlite3*
类型,这里改成DOWRD
,会不会有问题。在32位汇编中,指针(不管是void*
还是其他什么*
或者**
)和DWORD
都占用4个字节,对于目标函数来说,都是地址而已,只要结构正确,就不会有问题。(如果理解有误望大佬指正)。
此外,还需要sqlite3_callback
函数指针:typedef int(*sqlite3_callback)( void*, int, char**, char** );
编写代码
终于到了万众瞩目的写代码环节,要实现两个功能,一个是DLL,进入微信内部干活,另外一个是注入程序,帮助DLL进入微信内部干活。
注入的DLL
相信这些代码你一看就能懂:
sqlite_exec.h
#pragma once #include<windows.h> #include<iostream> #include<vector> using namespace std; void __stdcall execute();
sqlite_exec.cpp
#include "pch.h" #include "sqlite_exec.h" #define sqlite3_exec_offset 0x1356570 #define db_handles_base_offset 0x222F3FC // 回调函数指针 typedef int(*sqlite3_callback)( void*, int, char**, char** ); // sqlite3_exec函数指针 typedef int(__cdecl* Sqlite3_exec)( DWORD, /* The database on which the SQL executes */ const char*, /* The SQL to be executed */ sqlite3_callback, /* Invoke this callback routine */ void*, /* First argument to xCallback() */ char** /* Write error messages here */ ); // 保存数据库句柄和数据库名的结构体 struct dbStruct { DWORD dbhandle; wchar_t* dbname; }; // 保存数据库信息的容器 vector<dbStruct> dbhandles; // 创建一个Console窗口,方便观察输出结果 BOOL CreateConsole(void) { if (AllocConsole()) { AttachConsole(GetCurrentProcessId()); FILE* retStream; freopen_s(&retStream, "CONOUT$", "w", stdout); if (!retStream) throw std::runtime_error("Stdout redirection failed."); freopen_s(&retStream, "CONOUT$", "w", stderr); if (!retStream) throw std::runtime_error("Stderr redirection failed."); return 0; } return 1; } // 获取WeChatWin.dll的基址 DWORD GetWeChatWinBase() { DWORD WeChatWinBase = (DWORD)GetModuleHandle(L"WeChatWin.dll"); return WeChatWinBase; } // 获取数据库句柄和数据库名并存入容器 void GetHandles() { DWORD WeChatWinBase = GetWeChatWinBase(); DWORD SqlHandleBaseAddr = *(DWORD*)(WeChatWinBase + db_handles_base_offset); DWORD SqlHandleBeginAddr = *(DWORD*)(SqlHandleBaseAddr + 0x1888); DWORD SqlHandleEndAddr = *(DWORD*)(SqlHandleBaseAddr + 0x188C); wstring dbnames = L""; while (SqlHandleBeginAddr < SqlHandleEndAddr) { DWORD dwHandle = *(DWORD*)SqlHandleBeginAddr; SqlHandleBeginAddr += 0x4; // 做一下简单的去重 if (dbnames.find((wchar_t*)(*(DWORD*)(dwHandle + 0x78)), 0) != wstring::npos) continue; dbStruct db = { 0 }; db.dbname = (wchar_t*)(*(DWORD*)(dwHandle + 0x78)); dbnames += (wchar_t*)(*(DWORD*)(dwHandle + 0x78)); db.dbhandle = *(DWORD*)(dwHandle + 0x64); dbhandles.push_back(db); } } // 回调函数的实现,官方例子未做改动 static int callback(void* NotUsed, int argc, char** argv, char** azColName) { int i; for (i = 0; i < argc; i++) { printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL"); } printf("\n"); return 0; } // 由dllmain调用的入口函数 void __stdcall execute() { CreateConsole(); const char* sql = "select * from sqlite_master where type=\"table\";"; DWORD WeChatWinBase = GetWeChatWinBase(); Sqlite3_exec p_Sqlite3_exec = (Sqlite3_exec)(WeChatWinBase + sqlite3_exec_offset); GetHandles(); for (unsigned int i = 0; i < dbhandles.size(); i++) { printf("dbname: %ws\n", dbhandles[i].dbname); p_Sqlite3_exec(dbhandles[i].dbhandle, sql, (sqlite3_callback)callback, 0, 0); } }
dllmain.cpp
// dllmain.cpp : 定义 DLL 应用程序的入口点。 #include "pch.h" #include "sqlite_exec.h" BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { // 程序注入后,会执行到此处 execute(); } case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
注入程序
本来想发个工具,但还要弄网盘啥的,还是抄一点代码吧,轻松加愉快。
#include <iostream> #include <windows.h> using namespace std; // 注入DLL到微信的进程空间 bool InjectDll(DWORD dwId, WCHAR* szPath)//参数1:目标进程PID 参数2:DLL路径 { HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwId); /* 【1.2 在目标进程的内存里开辟空间】 */ LPVOID pRemoteAddress = VirtualAllocEx(hProcess, NULL, 1, MEM_COMMIT, PAGE_READWRITE); //二、 把dll的路径写入到目标进程的内存空间中 DWORD dwWriteSize = 0; /* 【写一段数据到刚才给指定进程所开辟的内存空间里】 */ if (pRemoteAddress) { WriteProcessMemory(hProcess, pRemoteAddress, szPath, wcslen(szPath) * 2 + 2, &dwWriteSize); } else { printf("写入失败!\n"); return 1; } //三、 创建一个远程线程,让目标进程调用LoadLibrary HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibrary, pRemoteAddress, NULL, NULL); if (hThread) { WaitForSingleObject(hThread, -1); } else { printf("调用失败!\n"); return 1; } CloseHandle(hThread); VirtualFreeEx(hProcess, pRemoteAddress, 0, MEM_RELEASE); CloseHandle(hProcess); return 0; } // 目标进程卸载DLL,释放Console窗口 BOOL RemoveDll(DWORD dwId,wchar_t* dllname) { HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwId); LPVOID pRemoteAddress = VirtualAllocEx(hProcess, NULL, 1, MEM_COMMIT, PAGE_READWRITE); DWORD dwWriteSize = 0; HANDLE hThread = NULL; DWORD dwHandle, dwID; LPVOID pFunc = NULL; if (pRemoteAddress) WriteProcessMemory(hProcess, pRemoteAddress, dllname, wcslen(dllname) * 2 + 2, &dwWriteSize); else { printf("写入失败!\n"); return 1; } hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)GetModuleHandleW, pRemoteAddress, 0, &dwID); if (hThread) { WaitForSingleObject(hThread, INFINITE); GetExitCodeThread(hThread, &dwHandle); } else { printf("GetModuleHandleW调用失败!\n"); return 1; } CloseHandle(hThread); // 释放console窗口,不然关闭console的同时微信也会退出 hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)FreeConsole, NULL, 0, &dwID); if (hThread) { WaitForSingleObject(hThread, INFINITE); } else { printf("FreeConsole调用失败!\n"); return 1; } CloseHandle(hThread); // 使目标进程调用FreeLibrary,卸载DLL hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)FreeLibrary, (LPVOID)dwHandle, 0, &dwID); if (hThread) { WaitForSingleObject(hThread, INFINITE); } else { printf("FreeLibrary调用失败!\n"); return 1; } CloseHandle(hThread); VirtualFreeEx(hProcess, pRemoteAddress, 0, MEM_RELEASE); CloseHandle(hProcess); return 0; } // 获取当前工作目录,拼接DLL绝对路径 wchar_t* GetDllPath(wchar_t* dllname) { wchar_t* dllpath = new wchar_t[MAX_PATH]; wchar_t workPath[MAX_PATH]; wchar_t* pworkPath = _wgetcwd(workPath, MAX_PATH); swprintf_s(dllpath, MAX_PATH, L"%ws%ws%ws", pworkPath, L"\\", dllname); return dllpath; } // 主函数 int main(int nargv, WCHAR* argvs[]) { HWND hCalc = FindWindow(NULL, L"微信"); DWORD dwPid = 0; DWORD dwRub = GetWindowThreadProcessId(hCalc, &dwPid); if (!dwPid) { printf("%s\n","请先启动目标进程!"); return 1; } wchar_t* dllname = (wchar_t*)L"DbTest.dll"; wchar_t* dllpath = GetDllPath(dllname); InjectDll(dwPid, dllpath); delete[] dllpath; dllpath = NULL; system("pause"); RemoveDll(dwPid, dllname); return 0; }
平台配置为win32(x86),笔者编译环境为VS 2019社区版。
输出结果
写在后面
本篇文章使用上一篇文章:PC微信逆向–定位sqlite3_exec和数据库句柄找到的sqlite3_exec函数地址和数据库句柄,成功绕过加密执行了SQL,下一篇文章,介绍如何寻找微信内部的sqlite3 API,为在线备份做铺垫。
-
sqlite3_exec()函数归纳
2020-08-26 14:44:52一、sqlite3_exec函数介绍 sqlite3_exec是sqlite3的接口API函数 源码API接口截图 1.参数分析: int sqlite3_exec( sqlite3*, /* An open database */ const char *sql, /* SQL to be evaluated */ int (*callba一、sqlite3_exec函数介绍
sqlite3_exec是sqlite3的接口API函数
源码API接口截图
1.参数分析: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*:打开的数据库 const char *sql:要执行的sql语句 int (*callback)(void*,int,char**,char**):执行sql语句时对应的回调函数 void *:回调函数的参数 char **errmsg:存放错误信息
2.回调函数分析:
int (*callback)(void*,int,char**,char**);
||int callback(void *arg, int column_size, char *column_value[], char *column_name[])
参数分析:void *arg:是sqlite3_exec函数的第四个参数 column_size:数据库的字段数 column_value[]:列的值 column_name:字段名字
源码解释:
3.使用demon#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sqlite3.h> int callback(void *arg, int column_size, char *column_value[], char *column_name[]){ static int count = 0; int i; printf("%s\n", (char *)arg); if(0 == count){ for(i = 0; i < column_size; i++){ printf("%-8s\t", column_name[i]); } printf("\n"); } for(i = 0; i < column_size; i++){ printf("%-8s\t", column_value[i]); } printf("\n"); printf("size is %d \n",column_size); count++; printf("%d\n",count); return 0; } int main(int argc, const char **argv) { sqlite3 *db = NULL; int ret; char sql[1024] = "\0"; char *errmsg = NULL; if(argc < 5){ fprintf(stderr, "please input:%s name id score sex\n", argv[0]); exit(EXIT_FAILURE);//异常退出 } //step 1: 创建数据库 ret = sqlite3_open("./student.db", &db);//存在打开,不存在创建 if(SQLITE_OK != ret){ fprintf(stderr, "sqlite3_open:%s\n", sqlite3_errmsg(db)); exit(EXIT_FAILURE);//异常退出 } //step 2: 建表 sprintf(sql, "create table if not exists student (name text, id integer, score real ,sex text)"); ret = sqlite3_exec(db, sql, NULL, NULL, &errmsg); if(SQLITE_OK != ret){ fprintf(stderr, "create table:%s\n", sqlite3_errmsg(db)); fprintf(stderr, "create table:%s\n", errmsg); exit(EXIT_FAILURE);//异常退出 } //step3: 插入数据 sprintf(sql, "insert into student (name, id, score, sex) values ('%s', '%s', '%s','%s')", argv[1], argv[2], argv[3], argv[4]); ret = sqlite3_exec(db, sql, NULL, NULL, &errmsg); if(SQLITE_OK != ret){ fprintf(stderr, "insert into:%s\n", sqlite3_errmsg(db)); fprintf(stderr, "insert into:%s\n", errmsg); exit(EXIT_FAILURE);//异常退出 } //step4: 查询 sprintf(sql, "select * from student "); ret = sqlite3_exec(db, sql, callback, "callback msg", &errmsg); if(SQLITE_OK != ret){ fprintf(stderr, "select:%s\n", sqlite3_errmsg(db)); fprintf(stderr, "select:%s\n", errmsg); exit(EXIT_FAILURE);//异常退出 } //last step :关闭数据库 sqlite3_close(db); return 0; }
运行结果:
-
sqlite3_exec及其回调函数
2021-10-31 16:16:58一)sqlite3_exec 1.头文件和函数原型 #include "sqlite3.h" int sqlite3_exec(sqlite3*, const char *sql, int (*callback)(void*para,int argc,char**argv,char**argv_name), void *para,char **errmsg) 2.说明 ... -
深入理解SQLite3之sqlite3_exec及回调函数
2021-01-25 14:22:21sqlite3的C/C++接口API主要有3个重要函数,分别为 1、sqlite3_open(const char* filename, sqlite3 **ppDb)...2、int sqlite3_exec( sqlite3*, /* An open database */ const char *sql, /* SQL to be evaluated */ -
数据库(SQLITE3函数总结): sqlite3_open, sqlite3_exec, slite3_close,sqlite3_prepare_v2,sqlite3_column_...
2019-01-23 19:04:11Sqlite3 的确非常好用。小巧、速度快。近期研究它,有一些收获,这里把我对 sqlite3 的研究列出来,以备忘记。 导入SQLLite library并引入头文件. libsqlite3.dylib本身是个链接,在这里它指向libsqlite3.0... -
数据库sqlite3之 sqlite3_exec()第三个参数回调函数的使用
2020-05-17 00:02:00关于sqlite3_exec的回调函数的知识,我也是在做一个项目中学习到的,看了一些博客吧,很多博客,都表达的不是很清楚,所以我想写这篇博客,记录自己的学习过程。大家先了解一下sqlite3_exec()函数吧。 1、sqlite3_... -
PC微信逆向--定位sqlite3_exec和数据库句柄
2022-04-20 15:32:26目标 需要分步实现下面的目标: 定位sqlite3_exec函数 获取微信打开的数据库句柄 调用微信内部的sqlite3_exec执行SQL 定位sqlite3_open、sqlite3_backup_init等函数 完成数据库在线备份 定位数据库密码的保存位置 ... -
sqlite3_step()与sqlite3_exec()区别:
2021-04-13 10:24:47sqlite_step()在执行select时使用,因为查询到的结果可能会有多条,所以只要返回值是SQLITE_ROW,就要一直循环去获取...而sqlite3_exec()则是对insert、update,delete等执行后只影响数据库中的内容的操作时使用。 ... -
【sqlite的C语言访问接口】执行SQL语句的接口------sqlite3_exec回调函数的使用
2022-01-17 10:19:39sqlite3_exec的函数原型: int sqlite3_exec( sqlite3 *pDb, const char *sql, int (*callback)(void *arg, int col, char **str, char **name), void *arg, char **errmsg ) 参数说明: pDb:打开数据库的句柄 sql... -
sqlite3入门基础:sqlite3_open,sqlite3_exec,sqlite3_close
2018-07-27 13:39:55打开数据库链接sqlite3_open用法 原型: int sqlite3_open( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb /* OUT: SQLite db handle */ ); 用这个函数开始数据库操作。需要传入两个... -
sqlite3_exec返回SQLITE_MISUSE(21)
2020-07-10 18:00:09sqlite3版本3.29.0 sqlite3打开数据的路径中有中文时,sqlite3_exec会返回这个错误,将中文转utf8就好了 -
数据库函数:sqlite3_exec() SQL语句
2017-12-19 09:41:00函数:sqlite3_exec(),称为便捷函数,封装了好多任务。 函数声明: int sqlite3_exec( sqlite * , const char * sql , sqlite_callback , void *data , char ** errmmsg) ; 其中 sqlite* 表示打开的数据库; ... -
SQLite3:报错:sqlite3_exec error :query aborted
2019-05-31 23:24:35使用sqlite3_exec函数时,没有添加返回值,导致出现如上述错误: sqlite3_exec error :query aborted 继续测试发现,如果回调函数返回1,依然出现上述错误。 回调函数返回0,则不会出现。 总结: 应该是... -
sqlite3_exec
2014-12-11 14:01:54函数:sqlite3_exec(),称为便捷函数,封装了好多任务。 函数声明: int sqlite3_exec( sqlite * , const char * sql , sqlite_callback , void *data , char ** errmmsg) ; 其中 sqlite* 表示打开的... -
sqlite3函数sqlite3_exec
2016-04-20 22:01:54函数:sqlite3_exec(),称为便捷函数,封装了好多任务。 函数声明: int sqlite3_exec( sqlite * , const char * sql , sqlite_callback , void *data , char ** errmmsg) ; 其中 sqlite* 表示打开的... -
iOS 12 sqlite3_exec insert时崩溃
2018-09-20 17:25:50今天老大突然告诉我,他的手机升了... int result = sqlite3_exec(db, [sqlite UTF8String], nil, nil, &error); 原因暂时不清楚,如果有清楚的麻烦告诉我一下,感谢! 解决方法: 替换为下面的方法:... -
数据库操作 ——sqlite3_exec
2017-08-06 23:17:50#include #include int LoadMyInfo( void * para, int n_column, ...//para是你在 sqlite3_exec 里传入的 void * 参数 //通过para参数,你可以传入一些特殊的指针(比如类指针、结构指针),然后在这里面强 -
sqlite3_exec使用inner join查询失败
2015-10-23 01:57:50sqlite3_exec使用inner join的话查询返回结果是sqlite_ok的,但是拿不到结果,并且连回调函数都没有进入,起初以为是我sql语句写错了,但是在sqlitestudio中使用的话,是能查询到结果的,而且和预期一样,语句都是用... -
sqlite3 调用sqlite3_exec查询数据时出现"callback requested query abort
2017-05-16 14:29:33不知道有没有人跟我一样,在调用sqlite3的API中sqlite3_exec()函数时出现abort问题,今天我就遇到了,如下图。 函数基本原型为: int sqlite3_exec(sqlite3*,const char *sql,sqlite_callback,void *param,... -
sqlite入门基础(一):sqlite3_open,sqlite3_exec,slite3_close
2015-08-27 10:06:28打开数据库链接sqlite3_open用法 原型: int sqlite3_open( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb /* OUT: SQLite db handle */ ); 用这个函数开始数据库操作。需要... -
笔记sqlite3_exec / callback /get_table
2016-11-23 20:15:42笔记 -
sqlite3_exec函数的使用
2013-11-06 20:55:00int sqlite3_exec(sqlite3 *db, const char *sql, sqlite3_callback callback, void *,char **errmsg); 功能:执行sql语句 参数: db:数据库句柄 sql:sql语句 callback:回调函数,每成功执行一次sql语句... -
使用sqlite3_exec回掉函数输出查询内容
2016-10-31 13:09:20ret = sqlite3_exec(db,sql,my_callback,NULL,&errmsg); if(ret == SQLITE_OK) { printf("select error : %s\n",errmsg); } printf("input any character\n"); getchar(); } -
对比sqlite3_exec 和sqlite3_bind 插入100万行数据的速度 with BEGIN TRANSACTION using C++ and SQLite
2012-03-26 11:06:08使用sqlite3_exec 插入100万行数据...主要是因为采用sqlite3_exec(),相当于每插入一行数据同时用到sqlite3_prepare_v2(), sqlite3_step() 和 sqlite3_finalize(),另外需要把double 强制转换成 string 然后再转换成 -
Apple Mach-O Linker Error "_sqlite3_exec", referenced from: "_sqlite_open", referenced from: "_sqlit
2016-12-03 20:43:26报错:Apple Mach-O Linker Error "_sqlite3_exec", referenced from: "_sqlite_open", referenced from: "_sqlite3_prepare_v2", referenced from "_sqlite3_column_count" referenced From: 点击项目名称 ... -
sql操作sqlite3_exec
2014-05-07 11:13:55int sqlite3_open( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb /* OUT: SQLite db handle */ ); 用这个函数开始数据库操作。需要传入两个参数,一是数据库文件名,比如:E:/test...