0

I am getting segfault on this function.

/**
 * Excutes the passed query and returs the the first row as an array of 
 * strings. You must free this array by calling g_strfreev()
 */
static gchar** mysql_single_row(MYSQL *mysql_handle, char* query){
    my_ulonglong num_rows=0;
    MYSQL_RES *result = NULL;
    gchar ** single_row = NULL;
    GString *q = g_string_new(query);
    MYSQL_ROW row={0};
    int query_status = mysql_real_query(mysql_handle, q->str, q->len);

    if(query_status!=0){
        g_string_free(q, TRUE);
        return NULL;
    }
    fprintf(stderr, "Storing mysql result!\n");
    result = mysql_store_result(mysql_handle);

    if(result==NULL){
            /// it was not a query that returns statemnet (e.g. INSERT, DELETE)
            g_string_free(q, TRUE);
            return NULL;
    }

    num_rows = mysql_num_rows(result);

    fprintf(stderr, "Total rows = %Ld\n", num_rows);

    if(num_rows>0){
            /// We only fetch the first row
            row = mysql_fetch_row(result);
            fprintf(stderr, "Copy single rows\n");
            single_row =  g_strdupv(row); // <------------- SIGSEGV happens here
            fprintf(stderr, "Copy single rows done\n");
    }else{
            mysql_free_result(result);
            g_string_free(q, TRUE);
            return NULL;
    }

    /// clean up
    g_string_free(q, TRUE);
    mysql_free_result(result);
    return single_row;
}

basically what I want to do is to execute some 'SELECT' query and return the first row as an array of strings. According to the manual g_strdupv should copy the returned char ** and makes a new one. I return this. Later I clean this up using g_strfreev which is the recommended method.

But why I am getting segfaults here. I ran it with valgrind. Output and the corresponding code can be found here

Shiplu Mokaddim
  • 56,364
  • 17
  • 141
  • 187

1 Answers1

3

g_strdupv() copies a NULL-terminated array of C strings (which each must be NUL-terminated). The MySQL documentation on the C API Data Structures states that MYSQL_ROW is an array of byte strings which are not necessarily NUL-terminated "if field values may contain binary data". Thus a MYSQL_ROW is neither guaranteed to be a NULL-terminated array nor an array of C strings.

The segfault is likely occurring because g_strdupv() keeps looking for the NULL terminator, but doesn't find one until it attempts a read of non-process memory.

Daniel Trebbien
  • 38,421
  • 18
  • 121
  • 193
  • My query is `SELECT id from users where nick='shiplu'`. It returns only `22`. are you saying 22 is not null-terminated? – Shiplu Mokaddim Jul 16 '12 at 13:32
  • @shiplu.mokadd.im: The one byte string may be NUL-terminated, but the string array `row` is not `NULL`-terminated. – Daniel Trebbien Jul 16 '12 at 13:36
  • I think I have to use [mysql_fetch_lengths](http://dev.mysql.com/doc/refman/5.1/en/mysql-fetch-lengths.html) then. – Shiplu Mokaddim Jul 16 '12 at 13:39
  • @shiplu.mokadd.im: Yes. `g_strdupv()` cannot be used because it relies on two assumptions that are not true for `MYSQL_ROW`s. You could, however, create your own function similar to `g_strdupv()`, but that uses [`mysql_field_count()`](http://dev.mysql.com/doc/refman/5.1/en/mysql-field-count.html) to figure out the number of fields and `mysql_fetch_lengths()` to copy each byte string. – Daniel Trebbien Jul 16 '12 at 16:54