SQLite, С++: пример пользовательской функции, возвращающей строку

0000

Имеется вот такой

void foo (sqlite3_context* cx, int cnt, sqlite3_value** v)
{
static char ym[7];
strncpy( ym, s_null_date, 23 );
sqlite3_result_text( cx, ym, 7, 0 );
}
...
sqlite3_create_function(mDB->db_, "foo", 3, SQLITE_UTF8, 0, &foo, 0, 0 );

Код писал не я. Я только правлю. Смушает static char ym[7]; .
Можно как то без него обойтись?
P.S. Пример возвращения числа не нужен - в интернете находится без проблем. А вот строковой - вопрос.

barbos

strncpy( ym, s_null_date, 23 );

А эта строка не смущает?

0000

7 должно быть? Ну это уже моя бага.

barbos

Либо тут <=7, либо строкой выше >=23. Либо надо менять обе строки.
ym может быть и не static. Не static даже лучше, ящетаю.

0000

Небольшой пример: выполняется запрос select foo("123" foo("321"), выводящий 321 321, т.е. использование static в данном случае не работает. Или я опять где накосячил?
P.S. Проверил в Билдере и gcc.
P.P.S. Книжка вроде ниче так, надо будет почитать. Спасибо :)

#include <stdio.h>
#include <stdlib.h>

#include "sqlite3.h"

static int callback(void *notused, int coln, char **rows, char **colnm)
{
int i;
static int b = 1;

if (b)
{
for(i=0; i<coln; i++)
printf("%s\t", colnm[i]);
printf("\n");
b = 0;
}
// разделитель строк
for(i=0; i<coln; i++)
printf("-------\t");
printf("\n");
//печать значений
for(i=0; i<coln; i++)
printf("%s\t|", rows[i]);
printf("\n");
return 0;
} // end callback

void foo (sqlite3_context* cx, int cnt, sqlite3_value** v)
{
static char y[3];
memset(y, 3, 0);
const char* arg0 = (const char*)sqlite3_value_text( v[0] );
strncpy( y, arg0, 3);
sqlite3_result_text( cx, y, 3, 0 );
}


int main
{
int rc, i;
sqlite3 *db; // указатель на открытую базу данных
char * errmsg; // сообщение об ошибке
// SQL инструкций
char *sql[] = {"select foo(\"123\" foo(\"321\")"};

// создадим новую базу данных
rc = sqlite3_open("exam2.db", &db);
if (rc)
{
//если ошибка при открытии БД
errmsg = (char*) sqlite3_errmsg(db);
printf("%s\n", errmsg);
sqlite3_free(errmsg);
sqlite3_close(db);
exit(1);
}
sqlite3_create_function(db, "foo", 1, SQLITE_UTF8, 0, &foo, 0, 0 );


// выполнение SQL инструкций
rc = sqlite3_exec(db, sql[0], callback, NULL, &errmsg);
if (rc != SQLITE_OK)
{
//если ошибка при выполнении запроса
printf("%s\n", errmsg);
sqlite3_free(errmsg);
sqlite3_close(db);
exit(1);
}

//закрытие БД
sqlite3_close(db);
system("pause");
return 1;
}

evgen5555

sqlite3_result_text( cx, y, 3, 0 );
sqlite3_result_text( cx, y, 3, -1 );

barbos


void foo (sqlite3_context* cx, int cnt, sqlite3_value** v)
{
static char y[3];
memset(y, 3, 0);
const char* arg0 = (const char*)sqlite3_value_text( v[0] );
strncpy( y, arg0, 3);
sqlite3_result_text( cx, y, 3, 0 );
}

Тут есть несколько проблем.
1. Никто не гарантирует, что длина строки в v[0] будет равна 2. Вообще, если у строки длина n, она должна занимать n+1 байт ('\0' должен в конце присутствовать).
2. Последний параметр в sqlite3_result_text кое-что значит. Это указатель на функцию, которая чего-нибудь сделает с переданным указателем в конце выполнения. Есть три возможных значения для неё:
а) SQLITE_TRANSIENT --- в этом случае зовется sqlite3_malloc, результат копируется в новый буфер памяти, и эта память в итоге и приходит в callback после выполнения запроса.
б) Указатель на функцию, которая берёт void* и возвращает void*. Эта функция зовется в конце, как деструктор для переданного указателя на результирующую строку.
в) SQLITE_STATIC --- в этом случае предполагается, что данные по переданному указателю никуда не исчезнут static char[100], например, поэтому и копировать их не обязательно. Тогда в callback придет тот самый указатель, который был отправлен в sqlite3_result_text
Твой случай --- в) потому что так оказывается, что SQLITE_STATIC == 0, что ты и передаешь. Поскольку переменная y объявлена как static, массив всё ещё существует на момент вызова callback. А проблема состоит в том, что в запросе было два вызова функции foo. Соответственно, в этом месте памяти лежит результат работы последней функции.

0000

, после сообщения полез смотреть, что же значат параметры.
Насколько я понял надо модифицировать функцию вот так:

void foo (sqlite3_context* cx, int cnt, sqlite3_value** v)
{
char ym[7];
strncpy( ym, s_null_date, 7);
sqlite3_result_text( cx, ym, 7, SQLITE_TRANSIENT);
}

Все верно? Что такое callback в данном случае не совсем понятно.
В чем засада была - понятно :)
3 поставил просто для примера, поскольку строки там заведомо длины 3. Про длину нуль-терминантных строк я в курсе.

barbos

Теперь лучше. Дальше надо, наверное, в callback после обработки результатов сделать sqlite3_free на тех данных, которые придут.

0000

Поискал по инету, как прилепить callback для освобождения ресурсов. С текстовыми вообще что то примеров мало, да и народ особо не парится, напр. тут - http://gnu.ethz.ch/debian/gworkspace/gworkspace-0.8.6/GWMeta...

static void path_moved(sqlite3_context *context, int argc, sqlite3_value **argv)
{
const unsigned char *oldbase = sqlite3_value_text(argv[0]);
int oldblen = strlenconst char *)oldbase);
const unsigned char *newbase = sqlite3_value_text(argv[1]);
int newblen = strlenconst char *)newbase);
const unsigned char *oldpath = sqlite3_value_text(argv[2]);
int oldplen = strlenconst char *)oldpath);
char newpath[PATH_MAX] = "";
int i = newblen;
int j;

strncpy(newpath, (const char *)newbase, newblen);

for (j = oldblen; j < oldplen; j++) {
newpath[i] = oldpath[j];
i++;
}

newpath[i] = '\0';

sqlite3_result_text(context, newpath, strlen(newpath SQLITE_TRANSIENT);
}
...
sqlite3_create_function(db, "pathMoved", 3, SQLITE_UTF8, 0, path_moved, 0, 0);

Так что наверно забью. Определю переменную как static, а для результата поставлю SQLITE_TRANSIENT.

barbos

Да, внутри исходников в sqlite3_exec зовется sqlite3_column_text память за которой вычищать не нужно, так что утечек памяти не будет.
Если многопоточное приложение, то static нужно удалить, если не многопоточное, то разницы никакой нет почти.

0000

Спасибо :)
Приложение однопоточное, так что все гут.
Оставить комментарий
Имя или ник:
Комментарий: