Я провожу некоторые эксперименты с SQLite с использованием простого тестового набора, используя одну таблицу, которая содержит ключ строки символов и одно целочисленное значение. Ниже приведены фрагменты источника из экспериментального жгута, который я использую. Я вытащил эти фрагменты, чтобы показать создание таблицы вместе с функцией, которую я использую для создания набора записей из оператора SQL выбора, используя функциональность обратного вызова SQLite. В разных местах есть операторы printf()
и операторы fprintf()
, поэтому я могу видеть результаты действий, поскольку это простое приложение консольного типа для тестового жгута.
Обратите внимание, что бывают случаи, когда вам не нужен аргумент обратного вызова, поэтому SQLite позволяет вам указывать NULL-указатель, указывающий не беспокоить обратный вызов.
И когда вы читаете источник, просто помните, что это экспериментальный взлом!
Функция для создания таблицы выглядит так:
int CreateSetupTable (sqlite3 *db)
{
char *zErrMsg = 0;
int rc;
char *aszSqlCreate = "create table tbl1(one varchar(10), two smallint)";
char *aszSqlCreateIndex01 = "create unique index index1 on tbl1 (one)";
do {
rc = sqlite3_exec(db, aszSqlCreate, 0, 0, &zErrMsg);
if( rc!=SQLITE_OK ){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
break;
}
rc = sqlite3_exec(db, aszSqlCreateIndex01, 0, 0, &zErrMsg);
if( rc!=SQLITE_OK ){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
break;
}
} while (0); // loop only once to allow breaks on errors
return rc;
}
Я вставляю некоторые записи в эту таблицу, а затем вызываю функцию, которая вызывает одну или несколько записей из таблицы с помощью оператора select SQL. Функция select извлекает записи и использует обратный вызов для преобразования каждой записи, возвращенной в структуру C. Структура C выглядит следующим образом:
typedef struct {
char cKey[20];
int iValue;
} Tbl1Record;
Для обратного вызова, используемого для набора записей, используется структура, содержащая данные управления выбором записей. Под этим я подразумеваю, что обратный вызов принимает в качестве первого аргумента указатель на структуру, которая, в свою очередь, указывает на место для размещения преобразованных данных вместе с некоторой информацией о размере области памяти. Поскольку выборка может возвращать более одной записи в зависимости от предложения where, функция обратного вызова использует структуру обратного вызова, чтобы узнать, сколько преобразованных записей она может поместить в область памяти, а также индекс, чтобы при размещении записей, он может индексировать область памяти, чтобы возвращать несколько преобразованных записей.
Структура управления обратным вызовом выглядит следующим образом:
typedef struct _RecordProcessor {
void *pRecordSet;
int nRecordSetMax;
int nRecordSetActual;
} RecordProcessor;
Функция выбора выглядит так:
int SelectRecord (sqlite3 *db, char *cSelect, char *cKey)
{
char *zErrMsg = 0;
int rc;
char aszSqlSelect[128];
Tbl1Record myRec[20];
RecordProcessor myProcessor;
myProcessor.pRecordSet = myRec;
myProcessor.nRecordSetActual = 0;
myProcessor.nRecordSetMax = 20;
if (cKey) {
sprintf (aszSqlSelect, "select %s from tbl1 where one='%s'", cSelect, cKey);
} else {
sprintf (aszSqlSelect, "select %s from tbl1", cSelect);
}
rc = sqlite3_exec(db, aszSqlSelect, MyRecordProcessor, &myProcessor, &zErrMsg);
if( rc!=SQLITE_OK ){
fprintf(stderr, "SQL error SelectRecord: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
int i;
for (i = 0; i < myProcessor.nRecordSetActual; i++) {
printf ("Rec #%d cKey = %s iValue = %d\n", i+1, myRec[i].cKey, myRec[i].iValue);
}
}
return rc;
}
Обратный вызов, который обрабатывает каждую запись, возвращаемую select, выглядит следующим образом:
static int MyRecordProcessor (void *callBackArg, int argc, char **argv, char **azColName)
{
int iRetStatus = 0;
char *colNameTable[] = {
"one",
"two"
};
Tbl1Record *pTbl1Record = (Tbl1Record *)((RecordProcessor *)callBackArg)->pRecordSet;
if (((RecordProcessor *)callBackArg)->nRecordSetActual < ((RecordProcessor *)callBackArg)->nRecordSetMax) {
int i, j;
int iIndex = ((RecordProcessor *)callBackArg)->nRecordSetActual;
memset (pTbl1Record + iIndex, 0, sizeof(Tbl1Record));
((RecordProcessor *)callBackArg)->nRecordSetActual++;
for (i = 0; i < argc; i++){
int j;
for (j = 0; j < sizeof (colNameTable)/sizeof(colNameTable[0]); j++) {
if (strcmp (azColName[i], colNameTable[j]) == 0) {
switch (j) {
case 0:
strncpy (pTbl1Record[iIndex].cKey, (argv[i] ? argv[i] : "NULL"), 19);
break;
case 1:
pTbl1Record[iIndex].iValue = atoi (argv[i] ? argv[i] : "0");
break;
default:
break;
}
break;
}
}
}
}
return iRetStatus;
}