2010-11-09 3 views
3

J'ai essayé d'ajouter la prise en charge des entiers 64 bits à ma branche d'encapsuleur Erlang SQLite 3 (puisque c'est le type entier principal utilisé par SQLite 3). Alors que cela semblait très simple, j'ai rencontré un problème.Pourquoi la sortie du pilote Erlang est-elle différente de celle attendue?

C'est le code du côté C du conducteur avec les instructions de débogage (voir https://github.com/alexeyr/erlang-sqlite3/tree/64bit pour d'autres fichiers, mais je pense que le problème est contenu ici):

#include "sqlite3_drv.h" 

// Callback Array 
static ErlDrvEntry basic_driver_entry = { 
    NULL,        /* init */ 
    start,       /* startup (defined below) */ 
    stop,        /* shutdown (defined below) */ 
    NULL,        /* output */ 
    NULL,        /* ready_input */ 
    NULL,        /* ready_output */ 
    "sqlite3_drv",     /* the name of the driver */ 
    NULL,        /* finish */ 
    NULL,        /* handle */ 
    control,       /* control */ 
    NULL,        /* timeout */ 
    NULL,        /* outputv (defined below) */ 
    ready_async,      /* ready_async */ 
    NULL,        /* flush */ 
    NULL,        /* call */ 
    NULL,        /* event */ 
    ERL_DRV_EXTENDED_MARKER,   /* ERL_DRV_EXTENDED_MARKER */ 
    ERL_DRV_EXTENDED_MAJOR_VERSION, /* ERL_DRV_EXTENDED_MAJOR_VERSION */ 
    ERL_DRV_EXTENDED_MAJOR_VERSION, /* ERL_DRV_EXTENDED_MINOR_VERSION */ 
    ERL_DRV_FLAG_USE_PORT_LOCKING  /* ERL_DRV_FLAGs */ 
}; 

DRIVER_INIT(basic_driver) { 
    return &basic_driver_entry; 
} 

// Driver Start 
static ErlDrvData start(ErlDrvPort port, char* cmd) { 
    sqlite3_drv_t* retval = (sqlite3_drv_t*) driver_alloc(sizeof(sqlite3_drv_t)); 
    struct sqlite3 *db = 0; 
    int status = 0; 

    retval->log = fopen ("/tmp/erlang-sqlite3-drv.log", "a+"); 
    if (!retval->log) { 
    fprintf (stderr, "Can't create log file\n"); 
    } 

    fprintf (retval->log, "--- Start erlang-sqlite3 driver\nCommand line: [%s]\n", cmd); 


    const char *db_name = strstr (cmd, " "); 
    if (!db_name) { 
    fprintf (retval->log, "ERROR: DB name should be passed at command line\n"); 
    db_name = DB_PATH; 
    } else { 
    ++db_name; 
    } 

    // Create and open the database 
    sqlite3_open(db_name, &db); 
    status = sqlite3_errcode(db); 

    if(status != SQLITE_OK) { 
    fprintf(retval->log, "ERROR: Unabled to open file: %s because %s\n\n", DB_PATH, sqlite3_errmsg(db)); 
    } else { 
    fprintf(retval->log, "Opened file %s\n", db_name); 
    } 

    // Set the state for the driver 
    retval->port = port; 
    retval->db = db; 
    retval->key = 42; //FIXME: Just a magic number, make real key 

#define STR_(ARG) #ARG 
#define STR(ARG) STR_(ARG) 
    retval->atom_error = driver_mk_atom ("error"); 
    retval->atom_columns = driver_mk_atom ("columns"); 
    retval->atom_rows = driver_mk_atom ("rows"); 
    retval->atom_null = driver_mk_atom (STR (NULL_ATOM)); 
    retval->atom_id = driver_mk_atom ("id"); 
    retval->atom_ok = driver_mk_atom ("ok"); 
    retval->atom_unknown_cmd = driver_mk_atom ("uknown_command"); 

    fflush (retval->log); 
    return (ErlDrvData) retval; 
} 


// Driver Stop 
static void stop(ErlDrvData handle) { 
    sqlite3_drv_t* driver_data = (sqlite3_drv_t*) handle; 

    sqlite3_close(driver_data->db); 
    fclose (driver_data->log); 
    driver_data->log = 0; 

    driver_free(driver_data); 
} 

// Handle input from Erlang VM 
static int control(ErlDrvData drv_data, unsigned int command, char *buf, 
        int len, char **rbuf, int rlen) { 
    sqlite3_drv_t* driver_data = (sqlite3_drv_t*) drv_data; 

    switch(command) { 
    case CMD_SQL_EXEC: 
     sql_exec(driver_data, buf, len); 
     break; 
    default: 
     unknown(driver_data, buf, len); 
    } 
    return 0; 
} 


static inline int return_error(sqlite3_drv_t *drv, const char *error, ErlDrvTermData **spec, int *terms_count) { 
    *spec = (ErlDrvTermData *)calloc(7, sizeof(ErlDrvTermData)); 
    (*spec)[0] = ERL_DRV_ATOM; 
    (*spec)[1] = drv->atom_error; 
    (*spec)[2] = ERL_DRV_STRING; 
    (*spec)[3] = (ErlDrvTermData)error; 
    (*spec)[4] = strlen(error); 
    (*spec)[5] = ERL_DRV_TUPLE; 
    (*spec)[6] = 2; 
    *terms_count = 7; 
    return 0; 
} 

static int sql_exec(sqlite3_drv_t *drv, char *command, int command_size) { 

    int result, next_row; 
    char *rest = NULL; 
    sqlite3_stmt *statement; 

    // fprintf(drv->log, "Preexec: %.*s\n", command_size, command); 
    // fflush (drv->log); 
    result = sqlite3_prepare_v2(drv->db, command, command_size, &statement, (const char **)&rest); 
    if(result != SQLITE_OK) { 
    ErlDrvTermData *dataset; 
    int term_count; 
    return_error(drv, sqlite3_errmsg(drv->db), &dataset, &term_count); 
    driver_output_term(drv->port, dataset, term_count); 
    return 0; 
    } 

    async_sqlite3_command *async_command = (async_sqlite3_command *)calloc(1, sizeof(async_sqlite3_command)); 
    async_command->driver_data = drv; 
    async_command->statement = statement; 

    // fprintf(drv->log, "Driver async: %d %p\n", SQLITE_VERSION_NUMBER, async_command->statement); 
    // fflush (drv->log); 

    if (sqlite3_threadsafe()) { 
    drv->async_handle = driver_async(drv->port, &drv->key, sql_exec_async, async_command, sql_free_async); 
    } else { 
    sql_exec_async(async_command); 
    ready_async((ErlDrvData)drv, (ErlDrvThreadData)async_command); 
    } 
    return 0; 
} 

static void sql_free_async(void *_async_command) 
{ 
    int i; 
    async_sqlite3_command *async_command = (async_sqlite3_command *)_async_command; 
    free(async_command->dataset); 

    async_command->driver_data->async_handle = 0; 

    if (async_command->int64s) { 
    free(async_command->int64s); 
    } 
    if (async_command->floats) { 
    free(async_command->floats); 
    } 
    for (i = 0; i < async_command->binaries_count; i++) { 
    driver_free_binary(async_command->binaries[i]); 
    } 
    if(async_command->binaries) { 
    free(async_command->binaries); 
    } 
    if (async_command->statement) { 
    sqlite3_finalize(async_command->statement); 
    } 
    free(async_command); 
} 


static void sql_exec_async(void *_async_command) { 
    async_sqlite3_command *async_command = (async_sqlite3_command *)_async_command; 
    ErlDrvTermData *dataset = async_command->dataset; 
    int term_count = async_command->term_count; 
    int row_count = async_command->row_count; 
    sqlite3_drv_t *drv = async_command->driver_data; 

    int result, next_row, column_count; 
    char *error = NULL; 
    char *rest = NULL; 
    sqlite3_stmt *statement = async_command->statement; 

    sqlite3_int64 *int64s = NULL; 
    int int64_count = 0; 

    double *floats = NULL; 
    int float_count = 0; 

    ErlDrvBinary **binaries = NULL; 
    int binaries_count = 0; 
    int i; 


    column_count = sqlite3_column_count(statement); 
    dataset = NULL; 

    term_count += 2; 
    dataset = realloc(dataset, sizeof(*dataset) * term_count); 
    dataset[term_count - 2] = ERL_DRV_PORT; 
    dataset[term_count - 1] = driver_mk_port(drv->port); 

    if (column_count > 0) { 
    int base = term_count; 
    term_count += 2 + column_count*3 + 1 + 2 + 2 + 2; 
    dataset = realloc(dataset, sizeof(*dataset) * term_count); 
    dataset[base] = ERL_DRV_ATOM; 
    dataset[base + 1] = drv->atom_columns; 
    for (i = 0; i < column_count; i++) { 
     char *column_name = (char *)sqlite3_column_name(statement, i); 
     // fprintf(drv->log, "Column: %s\n", column_name); 
     // fflush (drv->log); 

     dataset[base + 2 + (i*3)] = ERL_DRV_STRING; 
     dataset[base + 2 + (i*3) + 1] = (ErlDrvTermData) column_name; 
     dataset[base + 2 + (i*3) + 2] = strlen (column_name); 
    } 
    dataset[base + 2 + column_count*3 + 0] = ERL_DRV_NIL; 
    dataset[base + 2 + column_count*3 + 1] = ERL_DRV_LIST; 
    dataset[base + 2 + column_count*3 + 2] = column_count + 1; 
    dataset[base + 2 + column_count*3 + 3] = ERL_DRV_TUPLE; 
    dataset[base + 2 + column_count*3 + 4] = 2; 

    dataset[base + 2 + column_count*3 + 5] = ERL_DRV_ATOM; 
    dataset[base + 2 + column_count*3 + 6] = drv->atom_rows; 
    } 

    // fprintf(drv->log, "Exec: %s\n", sqlite3_sql(statement)); 
    // fflush (drv->log); 

    while ((next_row = sqlite3_step(statement)) == SQLITE_ROW) { 

    for (i = 0; i < column_count; i++) { 
     // fprintf(drv->log, "Column %d type: %d\n", i, sqlite3_column_type(statement, i)); 
     // fflush (drv->log); 
     switch (sqlite3_column_type(statement, i)) { 
     case SQLITE_INTEGER: { 
      int64_count++; 
      int64s = realloc(int64s, sizeof(sqlite3_int64) * int64_count); 
      int64s[int64_count - 1] = sqlite3_column_int64(statement, i); 

      term_count += 2; 
      dataset = realloc(dataset, sizeof(*dataset) * term_count); 
      dataset[term_count - 2] = ERL_DRV_INT64; 
      dataset[term_count - 1] = (ErlDrvTermData)&int64s[int64_count - 1]; 
      printf("Dataset element: %lld\n", *((sqlite3_int64 *) dataset[term_count - 1])); 
      break; 
     } 
     case SQLITE_FLOAT: { 
      float_count++; 
      floats = realloc(floats, sizeof(double) * float_count); 
      floats[float_count - 1] = sqlite3_column_double(statement, i); 

      term_count += 2; 
      dataset = realloc(dataset, sizeof(*dataset) * term_count); 
      dataset[term_count - 2] = ERL_DRV_FLOAT; 
      dataset[term_count - 1] = (ErlDrvTermData)&floats[float_count - 1]; 
      break; 
     } 
     case SQLITE_BLOB: 
     case SQLITE_TEXT: { 
      int bytes = sqlite3_column_bytes(statement, i); 
      binaries_count++; 
      binaries = realloc(binaries, sizeof(*binaries) * binaries_count); 
      binaries[binaries_count - 1] = driver_alloc_binary(bytes); 
      binaries[binaries_count - 1]->orig_size = bytes; 
      memcpy(binaries[binaries_count - 1]->orig_bytes, sqlite3_column_blob(statement, i), bytes); 

      term_count += 4; 
      dataset = realloc(dataset, sizeof(*dataset) * term_count); 
      dataset[term_count - 4] = ERL_DRV_BINARY; 
      dataset[term_count - 3] = (ErlDrvTermData)binaries[binaries_count - 1]; 
      dataset[term_count - 2] = bytes; 
      dataset[term_count - 1] = 0; 
      break; 
     } 
     case SQLITE_NULL: { 
      term_count += 2; 
      dataset = realloc (dataset, sizeof (*dataset) * term_count); 
      dataset[term_count - 2] = ERL_DRV_ATOM; 
      dataset[term_count - 1] = drv->atom_null; 
      break; 
     } 
     } 
    } 
    term_count += 2; 
    dataset = realloc(dataset, sizeof(*dataset) * term_count); 
    dataset[term_count - 2] = ERL_DRV_TUPLE; 
    dataset[term_count - 1] = column_count; 

    row_count++; 
    } 
    async_command->row_count = row_count; 
    async_command->int64s = int64s; 
    async_command->floats = floats; 
    async_command->binaries = binaries; 
    async_command->binaries_count = binaries_count; 


    if (next_row == SQLITE_BUSY) { 
    return_error(drv, "SQLite3 database is busy", &async_command->dataset, &async_command->term_count); 
    return; 
    } 
    if (next_row != SQLITE_DONE) { 
    return_error(drv, sqlite3_errmsg(drv->db), &async_command->dataset, &async_command->term_count); 
    return; 
    } 


    if (column_count > 0) { 
    term_count += 3; 
    dataset = realloc(dataset, sizeof(*dataset) * term_count); 
    dataset[term_count - 3] = ERL_DRV_NIL; 
    dataset[term_count - 2] = ERL_DRV_LIST; 
    dataset[term_count - 1] = row_count + 1; 

    term_count += 2; 
    dataset = realloc(dataset, sizeof(*dataset) * term_count); 
    dataset[term_count - 2] = ERL_DRV_TUPLE; 
    dataset[term_count - 1] = 2; 


    term_count += 3; 
    dataset = realloc(dataset, sizeof(*dataset) * term_count); 
    dataset[term_count - 3] = ERL_DRV_NIL; 
    dataset[term_count - 2] = ERL_DRV_LIST; 
    dataset[term_count - 1] = 3; 

    } else if (strcasestr(sqlite3_sql(statement), "INSERT")) { 

    sqlite3_int64 rowid = sqlite3_last_insert_rowid(drv->db); 
    term_count += 6; 
    dataset = realloc(dataset, sizeof(*dataset) * term_count); 
    dataset[term_count - 6] = ERL_DRV_ATOM; 
    dataset[term_count - 5] = drv->atom_id; 
    dataset[term_count - 4] = ERL_DRV_INT; 
    dataset[term_count - 3] = rowid; 
    dataset[term_count - 2] = ERL_DRV_TUPLE; 
    dataset[term_count - 1] = 2; 
    } else { 
    term_count += 6; 
    dataset = realloc(dataset, sizeof(*dataset) * term_count); 
    dataset[term_count - 6] = ERL_DRV_ATOM; 
    dataset[term_count - 5] = drv->atom_ok; 
    dataset[term_count - 4] = ERL_DRV_INT; 
    dataset[term_count - 3] = next_row; 
    dataset[term_count - 2] = ERL_DRV_TUPLE; 
    dataset[term_count - 1] = 2; 
    } 

    term_count += 2; 
    dataset = realloc(dataset, sizeof(*dataset) * term_count); 
    dataset[term_count - 2] = ERL_DRV_TUPLE; 
    dataset[term_count - 1] = 2; 




    async_command->dataset = dataset; 
    async_command->term_count = term_count; 
    // fprintf(drv->log, "Total term count: %p %d, rows count: %dx%d\n", statement, term_count, column_count, row_count); 
    // fflush (drv->log); 
} 

static void ready_async(ErlDrvData drv_data, ErlDrvThreadData thread_data) 
{ 
    async_sqlite3_command *async_command = (async_sqlite3_command *)thread_data; 
    sqlite3_drv_t *drv = async_command->driver_data; 

    int res = driver_output_term(drv->port, async_command->dataset, async_command->term_count); 
    // fprintf(drv->log, "Total term count: %p %d, rows count: %d (%d)\n", async_command->statement, async_command->term_count, async_command->row_count, res); 
    // fflush (drv->log); 
    sql_free_async(async_command); 
} 


// Unkown Command 
static int unknown(sqlite3_drv_t *drv, char *command, int command_size) { 
    // Return {error, unkown_command} 
    ErlDrvTermData spec[] = {ERL_DRV_ATOM, drv->atom_error, 
       ERL_DRV_ATOM, drv->atom_unknown_cmd, 
       ERL_DRV_TUPLE, 2}; 
    return driver_output_term(drv->port, spec, sizeof(spec)/sizeof(spec[0])); 
} 

La sortie teste ceci:

module 'sqlite3_test' 
    sqlite3_test: basic_functionality_test...Dataset element: 1 
Dataset element: 20 
Dataset element: 2000 
Dataset element: 2 
Dataset element: 30 
Dataset element: 2000 
*failed* 
::error:{assertEqual_failed, 
      [{module,sqlite3_test}, 
      {line,58}, 
      {expression,"sqlite3 : sql_exec (ct , \"select * from user;\")"}, 
      {expected, 
       [{columns,["id","name","age","wage"]}, 
       {rows,[{1,<<"abby">>,20,2000},{2,<<"marge">>,30,2000}]}]}, 
      {value, 
       [{columns,["id","name","age","wage"]}, 
       {rows,[{0,<<"abby">>,20,2000},{2,<<"marge">>,30,2000}]}]}]} 
    in function sqlite3_test:'-basic_functionality_test/0-fun-5-'/1 
    in call from sqlite3_test:basic_functionality_test/0 


    sqlite3_test: large_number_test...Dataset element: 4294967298 
Dataset element: 4294967298 
*failed* 
::error:{assertEqual_failed, 
      [{module,sqlite3_test}, 
      {line,141}, 
      {expression,"rows (sqlite3 : sql_exec (ct , Query1))"}, 
      {expected,[{4294967298,4294967298}]}, 
      {value,[{4294967296,4294967298}]}]} 
    in function sqlite3_test:'-large_number_test/0-fun-0-'/2 

Ainsi, lorsque la première colonne est un nombre entier, la valeur correcte est placé en jeu de données, mais Erlang reçoit une valeur différente du conducteur. Pourquoi est-ce possible?

Répondre

0

Le problème est survenu en raison de la réaffectation des baies. Lorsque j'ai imprimé l'adresse de int64 s ainsi que leur valeur, je me suis sortie similaire à

While filling row: (0x8a9c010:4294967298) 
dataset (22 terms): 
int64: 0x8a9c010:4294967298 
While filling row: (0x8a9bfb8:4294967298 0x8a9bfc0:4294967298) 
dataset (24 terms): 
int64: 0x8a9c010:4294967296 
int64: 0x8a9bfc0:4294967298 
While filling row: (0x8a9bfb8:4294967298 0x8a9bfc0:4294967298 0x8a9bfc8:4294967298) 
dataset (26 terms): 
int64: 0x8a9c010:4294967296 
int64: 0x8a9bfc0:4294967298 
int64: 0x8a9bfc8:4294967298 

(Le tableau int64s est imprimé entre parenthèses après « tout en remplissant ligne », les valeurs ci-dessous sont extraites de l'ensemble de données). Ainsi, lorsque le tableau est réalloué, le pointeur répertorié dans l'ensemble de données pointe vers l'ancien tableau.

Corrigé en basculant vers une liste chaînée.

+0

Comme cela semble répondre à la question, vous pouvez accepter cette réponse. – ndim

+0

@ndim: Je pensais que je ne pouvais pas accepter mes propres réponses si elles n'étaient pas votées par quelqu'un. –

Questions connexes