2015-06-30 12 views
7

Mam problemy ze zrozumieniem korzystania z funkcji zwrotnej w bazie danych SQLite3.sqlite3_exec() Funkcja oddzwaniania Wyjaśnienie

Rozumiem, że jest używany do przechodzenia instrukcji SELECT z wieloma rekordami. Ale nie rozumiem, jak to robi, ani jak utworzyć własne wywołanie zwrotne. Przeczytałem TutorialsPoint kilka razy, aby spróbować zrozumieć, ale to po prostu nie robię tego dla mnie.

Kiedy używam ich przykładu i debugowania w Visual Studio, aby zobaczyć, w jaki sposób tablice argumentów są zapełniane i przechodzić, gubię się. Również VS pokazuje tylko bieżący slot w tablicy, a nie całą samą tablicę.

Jeśli potrzebujesz wyjaśnienia, daj mi znać, ponieważ jestem tutaj, aby się uczyć!

Proszę, aby ktoś wyjaśnił, w jaki sposób jest używane wywołanie zwrotne. Może kilka przykładów tego, jak inni go użyli. Tylko wyjaśnienie, co to się robi nawet:

static int callback(void *data, int argc, char **argv, char **azColName){ 
    int i; 
    fprintf(stderr, "%s: ", (const char*)data); 
    for(i=0; i<argc; i++){ 
     printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL"); 
    } 
    printf("\n"); 
    return 0; 
} 
+1

Czy możesz wyjaśnić swoje pytanie? To jest strona Q/A. –

+0

Aby uzyskać ogólne informacje dotyczące tematu, powinieneś zapytać się na jednej z list mailingowych (patrz http://www.sqlite.org/support.html). Stosowanie przepełnienia stosu wymaga znacznie bardziej szczegółowego pytania (zazwyczaj z użyciem kodu, który napisałeś i nie możesz wykonać pracy). – mah

+0

Myślę, że gdybyś miał wyjaśnić, co masz na myśli przez "przydatne wywołania zwrotne", może to pomóc komuś odpowiedzieć. Co chcesz zrobić z twoim oddzwonieniem? Co próbujesz osiągnąć? –

Odpowiedz

22

Załóżmy, że masz bardzo prosty stół zwany User który wygląda mniej więcej tak:

 
╔════╦══════════╗ 
║ ID ║ Name  ║ 
╟────╫──────────╢ 
║ 1 ║ Slvrfn ║ 
║ 2 ║ Sean  ║ 
║ 3 ║ Drew  ║ 
║ 4 ║ mah  ║ 
╚════╩══════════╝ 

I ty nazywasz sqlite3_exec tak (argumenty są opisane szczegółowo in the documentation):

/* Error handling omitted for brevity */ 
sqlite3_exec(db, "SELECT * FROM User", my_special_callback, NULL, NULL); 

SQLite wykona przeszedł SQL i dla każdego wyniku wiersz, który znajdzie, zadzwoni pod numer my_special_callback. Tak więc z naszym przykładem tabeli User, my_special_callback zostanie wywołany 4 razy. Więc stwórzmy my_special_callback:

/* 
* Arguments: 
* 
* unused - Ignored in this case, see the documentation for sqlite3_exec 
* count - The number of columns in the result set 
*  data - The row's data 
* columns - The column names 
*/ 
static int my_special_callback(void *unused, int count, char **data, char **columns) 
{ 
    int idx; 

    printf("There are %d column(s)\n", count); 

    for (idx = 0; idx < count; idx++) { 
     printf("The data in column \"%s\" is: %s\n", columns[idx], data[idx]); 
    } 

    printf("\n"); 

    return 0; 
} 

Biorąc pod uwagę nasz przykład tabela danych, wyjście będzie wyglądać następująco:

 
There are 2 column(s) 
The data in column "ID" is: 1 
The data in column "Name" is: Slvrfn 

There are 2 column(s) 
The data in column "ID" is: 2 
The data in column "Name" is: Sean 

There are 2 column(s) 
The data in column "ID" is: 3 
The data in column "Name" is: Drew 

There are 2 column(s) 
The data in column "ID" is: 4 
The data in column "Name" is: mah 

Teraz, jak czyni to użyteczne, to znaczy gdzie 4. argument sqlite3_exec przychodzi Od dokumentacji.

4th argument sqlite3_exec() jest przekazywana przez do 1 argumentu wywołania zwrotnego każdego.

Załóżmy, że chcemy uruchomić nasz SQL i zbudować połączoną listę nazw wszystkich naszych użytkowników.Pierwszą rzeczą, jaką musimy zrobić, to zmienić sposób wzywamy sqlite3_exec:

/* Create my fictional linked list */ 
struct my_linked_list *head = my_linked_list_alloc(); 

/* 
* Pass a pointer to my list as the 4th argument to sqlite3_exec. Error 
* handling omitted for brevity 
*/ 
sqlite3_exec(db, "SELECT * FROM User", my_special_callback, head, NULL); 

/* My list is now built, I can do stuff with it... */ 
my_linked_list_traverse(head, /* ... Stuff ... */); 

i modyfikować my_special_callback go używać

/* 
* Arguments: 
* 
*  list - Pointer to a linked list of names 
* count - The number of columns in the result set 
*  data - The row's data 
* columns - The column names 
*/ 
static int my_special_callback(void *list, int count, char **data, char **columns) 
{ 
    struct my_linked_list *head = list; 

    /* 
    * We know that the value from the Name column is in the second slot 
    * of the data array. 
    */ 
    my_linked_list_append(head, data[1]); 

    return 0; 
} 

Teraz, jeśli było użyć callback uwzględnionych w pytaniu nazwałbyś go tak:

/* 
* Pass the table name as the 4th argument to sqlite3_exec. Error 
* handling omitted for brevity 
*/ 
sqlite3_exec(db, "SELECT * FROM User", callback, "User", NULL); 

wyjście będzie:

 
User: 
ID = 1 
Name = Slvrfn 

User: 
ID = 2 
Name = Sean 

... etc ... 

(Z wyjątkiem User: części będzie wydrukowany na stderr zamiast stdout)

Mam nadzieję, że to pomoże jasne rzeczy dla Ciebie. Daj mi znać, jeśli nadal jest coś, czego nie rozumiesz.

+1

Świetnie! Dziękuję za pomoc! Ostatnim pytaniem w wyjaśnieniu argumentu wywołania zwrotnego jest jednak policzenie liczby _kolumn_ lub _rówek_? – Slvrfn

+0

Jest to kolumna o numerze –

+0

, ale jeśli zadzwonię do sqlite3_exec (db, "SELECT * FROM User", callback, "User", NULL); i przejdź do debugowania, argument licznika oddzwonienia będzie 4. Czy to nie 4 rekordy, a więc 4 wiersze? – Slvrfn

15

Ten samouczek jest okropny, ponieważ nie używa niczego poza sqlite3_exec().

W ogólnym przypadku, tylko użyteczny sposób wykorzystać sqlite3_exec() jest zastąpienie go z sqlite3_prepare_v2()/sqlite3_step()/sqlite3_column_*()/sqlite3_finalize() połączeń, dzięki czemu można odczytać dane w tym samym miejscu, gdzie rzeczywiście trzeba go obsłużyć:

sqlite3_stmt *stmt; 
const char *sql = "SELECT ID, Name FROM User"; 
int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); 
if (rc != SQLITE_OK) { 
    print("error: ", sqlite3_errmsg(db)); 
    return; 
} 
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { 
    int id   = sqlite3_column_int (stmt, 0); 
    const char *name = sqlite3_column_text(stmt, 1); 
    // ... 
} 
if (rc != SQLITE_DONE) { 
    print("error: ", sqlite3_errmsg(db)); 
} 
sqlite3_finalize(stmt); 
+2

Być może możesz opublikować swoją niezatwierdzoną odpowiedź [na moje pytanie] (http://stackoverflow.com/q/35141039/105539), nie tutaj, ponieważ nie odpowiada ona na pytanie autora, ale odpowiada na moje pytanie? BTW, użyłem odmiany powyższego kodu w moim projekcie i to doskonale rozwiązało mój problem. – Volomike

+0

Takie piękne rozwiązanie. Dziękuję, kl – Sipty