Объедините строки ответа sqlite_exe c в одну строку из обратного вызова SQLite в C - PullRequest
0 голосов
/ 01 августа 2020

Я пытаюсь распечатать таблицу sqlite, возвращенную вызовом SELECT, но не могу заставить указатели работать правильно.

Пока что у меня есть следующее:

char *full_response = "";
sqlite3_exec(db, select_query, callback, (void *)full_response, &zErrMsg);
printf(full_response);  // This is where I'd like the entire table

И следующая функция обратного вызова:

static int callback(void *full_response, int argc, char **argv, char **azColName){
   int i;
   int response_header_length = 0;
   int response_body_length = 0;
   
   // This is to get the length of the header and body strings.
   for (i=0;i<argc;i++){
    response_header_length+=strlen(azColName[i])+1;
    response_body_length+=strlen(argv[i])+1;
   }
   //This is to turn the headers and field values of the returned table into a single string
   char response_header[response_header_length-1];strcpy(response_header,azColName[0]);
   char response_body[response_body_length-1];strcpy(response_body,argv[0]);
   for (i=1;i<argc;i++){
       strcat(response_header,"|"); strcat(response_header,azColName[i]);
       strcat(response_body,"|"); strcat(response_body,argv[i]);
   }


   if (strlen((char *)full_response)==0){ // full_response is empty so we need the header
    char temp_response[strlen(response_header) + strlen(response_body)+1];
    strcpy(temp_response, response_header);
    strcat(temp_response, "\n");
    strcat(temp_response, response_body);
    full_response = &temp_response;
   }
   else{ // full_response has stuff in it so we just need the field values for this row
   char temp_response[strlen((char *)full_response) + strlen(response_body)+1];
   strcpy(temp_response, (char *)full_response);
   strcat(temp_response, "\n");
   strcat(temp_response, response_body);
   full_response = &temp_response;
   }
   return 0;
}

(я знаю, что это не очень элегантно, но это в основном для доказательства концепции чего-то еще).

Я подумал, что строка

full_response = &temp_response

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

-

В качестве альтернативы, если есть функция pretty-print или фрагмент кода или git репо в C, которое возвращает мне всю возвращенную таблицу в строка в основном теле (то есть там, где называется sqlite_exec), это было бы великолепно.

Ответы [ 2 ]

1 голос
/ 01 августа 2020

Это один из немногих случаев, когда использование sqlite3_exec() вместо обычного подготовленного оператора рабочего процесса для использования результатов запроса имеет смысл. Однако вместо того, чтобы создавать собственную расширяемую строку, используйте sqlite3 sqlite3_str API:

static int callback(void *data, int ncols, char **cols, char **headers) {
    sqlite3_str *str = data;
    for (int n = 0; n < ncols; n++) {
      sqlite3_str_apendall(str, cols[n]);
      if (n + 1 < ncols) 
        sqlite3_str_appendchar(str, 1, '|');
    }
    sqlite3_str_appendchar(str, 1, '\n');
    return 0;
}

//...

    sqlite3_str *str = sqlite3_str_new();
    sqlite3_exec(db, select_query, callback, str, &zErrMsg);
    char *s = sqlite3_str_finish(str); // Get the string you just built
    fputs(s, stdout); // Or do whatever with the string
    sqlite3_free(s); // Just remember to free it when done.

1 голос
/ 01 августа 2020

Вам необходимо динамически распределить вашу строку (ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ - непроверено):

struct Buffer {
    size_t n;
    // todo: may want to add capacity and over-allocate to avoid worst-case quadratic performance.
    char *p;
};
void bufcat(struct Buffer *buf, const char *s) {
    size_t m = strlen(s), n = buf->n+m;
    char *p = realloc(buf->p, n+1);
    /* note: realloc may fail */
    memcpy(p+buf->n, s, m+1);
    buf->p = p;
    buf->n = n;
}

static int callback(void *full_response, int argc, char **argv, char **azColName){
    struct Buffer *buf = full_response;
    if(buf->n == 0) {
        for(int i=0;i<argc;i++){
            if(i) bufcat(buf, "|");
            bufcat(buf, azColName[i]);
        }
        bufcat(buf, "\n");
    }
    for(int i=0;i<argc;i++){
        if(i) bufcat(buf, "|");
        bufcat(buf, argv[i]);
    }
    bufcat(buf, "\n");
    return 0;
}

Затем вы можете использовать это следующим образом:

struct Buffer buf = {};
sqlite3_exec(db, select_query, callback, (void *)&buf, &zErrMsg);
puts(buf.p ? buf.p : "");
free(buf.p);

В качестве альтернативы вы можете использовать open_memstream на платформах, где это доступно:

static int callback(void *full_response, int argc, char **argv, char **azColName){
    FILE *file = full_response;
    if(ftell(file) == 0) {
        for(int i=0;i<argc;i++){
            if(i) fputc('|',file);
            fputs(azColName[i],file);
        }
        fputc('\n',file);
    }
    for(int i=0;i<argc;i++){
        if(i) fputc('|',file);
        fputs(argv[i],file);
    }
    fputc('\n',file);
    return 0;
}

Использование:

char *full_response = 0;
size_t n = 0;
FILE *file = open_memstream(&full_response, &n);
sqlite3_exec(db, select_query, callback, (void *)file, &zErrMsg);
fclose(file);
puts(full_response);
free(full_response);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...