Tuesday, December 24, 2013

Simplistic ORM like thing in C for MySQL

Take a look at extern_mysql.h and extern_mysql.c. It is a simple request to struct mapping done in C for MySQL 5.0 and above for one of the projects. libmysql_r is not the simplest thing to work with, code provided was tested in production, so it works, take a look if you had a bad day and were forced to get data from MySQL in C.

A few words about an interface. To get data from mysql:
1) add request to mb_mysql_stmt:
mb_mysql_stmt_t mb_mysql_stmt[] = {
    ...
    {
        "select * from SCHEDULE",
        NULL, NULL, sizeof(mb_db_schedule_t), bind_r_schedule
    },
    ...
};
where bind_r_schedule is a binding definition:
mb_bind_t   bind_r_schedule[] = {
    {"schedule_id",     offsetof(mb_db_schedule_t, schedule_id),        sizeof(((mb_db_schedule_t*)0)->schedule_id),    0},
    {"name",            offsetof(mb_db_schedule_t, name),               sizeof(((mb_db_schedule_t*)0)->name),           0},
    {"schedule_type",   offsetof(mb_db_schedule_t, schedule_type),      sizeof(((mb_db_schedule_t*)0)->schedule_type),  0},
    {"enabled",         offsetof(mb_db_schedule_t, enabled),            sizeof(((mb_db_schedule_t*)0)->enabled),        0},
    {"last_backup",     offsetof(mb_db_schedule_t, last_backup),        sizeof(((mb_db_schedule_t*)0)->last_backup),    0},
    {NULL, 0, 0, 0}
};
This means that mb_db_exec() will return an array of mb_db_schedule_t, each element of this array will correspond to one row in the result set. To map fields in result set to struct members bindings are used, consult C API Prepared Statement Type Codes to map mysql types to C ones (for MYSQL_TIME mb_time_t is defined). The length of the array will be stored in variable pointed by the count argument of the mb_db_exec() function.
If you want to get single value from db you can use, for instance
mb_bind_t   bind_r_config_value[] = {
    {"value",   0,  256, 0},
    {NULL, 0, 0, 0}
};
mb_mysql_stmt_t mb_mysql_stmt[] = {
    ...
    {
        "select value from CONFIG where `key`='address'",
        NULL, NULL, 256, bind_r_config_value
    },
    ...
};
We do not use struct and map the whole row to buffer of 256 bytes (256 is the max length of the value field in config table), to obtain results call
char *data;
data = mb_db_exec(mb_db_ip_address_stmt, NULL);
if (!data)
    mb_log_err("cannot find ip address");
Note that we do not need count here. Also note that bindings can be reused (bind_r_config_value can be used to obtain various values from config table).
2) add request number to mb_db_stmt_e enum:
typedef enum {
    ...
    mb_db_ip_address_stmt
} mb_db_stmt_e;

No comments:

Post a Comment