Question

Building a SQL in is painful work. It takes a lot of time to build such thing. I am new to . It helps on string manipulation. But I dont find any to shorten query building code. See a sample here.

GString *acc_protocol = g_string_new(acc->prpl->name);
GString *acc_handle = g_string_new(acc->user);
GString *acc_password = g_string_new(acc->pass);
GString *acc_tag = g_string_new(acc->tag);
g_string_printf(q, "INSERT INTO accounts (user, protocol, handle, password, autoconnect, tag) values (%ld, ", user_id);
g_string_append(q,"'");
append_mysql_escaped_param(q, buf, acc_protocol);
g_string_append(q,"', '");
append_mysql_escaped_param(q, buf, acc_handle);
g_string_append(q,"', '");
append_mysql_escaped_param(q, buf, acc_password);
g_string_append(q,"', '");
g_string_append(q, atoi(acc->auto_connect));
g_string_append(q,"', '");
append_mysql_escaped_param(q, buf, acc_tag);
g_string_append(q,"') on duplicate key UPDATE password='");
append_mysql_escaped_param(q, buf, acc_password);
g_string_append(q,"', autoconnect='");
g_string_append(q, atoi(acc->auto_connect));
g_string_append(q,"', tag='");
append_mysql_escaped_param(q, buf, acc_tag);
g_string_append(q,"'");

g_string_free(acc_handle);
g_string_free(acc_password);
g_string_free(acc_protocol);
g_string_free(acc_tag);

mysql_real_query(mysql);
    num_rows =  mysql_affected_rows(mysql);
    ....
/// .... mysql processing here ...

For your convenience here is function append_mysql_escaped_param

static void append_mysql_escaped_param(GString *query, GString *buffer, GString *param){
    g_string_set_size(buffer, param->len*2+1);
    mysql_real_escape_string(mysql, buffer->str, param->str, param->len);
    g_string_append(query, buffer->str);
}

How can I make it smaller? There are too many redundant code here which is enough for endless headache. Any idea to improve it?

I know I can use prepared statement. But when I bind the values on prepared statement I need to write such bulk code too. I just want to get rid of redundant code which are error prone. This is true specially for C.

Was it helpful?

Solution

I would recommend g_strdup_printf. This allows you to get rid of buffer related quirks, and enables printf like usage for strings and such. All you have to take care of would be the de-allocation of spawned buffers.

For proper escaping I would recommend to take a look at g_strcanon which allows seek&replace for characters.

OTHER TIPS

I will only answer the last question you have ("Any idea to improve it?").

What you are doing in your code is only good enough for some one-time DB access, or an extremely infrequent access. Every time a new request text comes to DB server that doesn't match any cached string the server would have to spend lots of time preparing the statement for execution. Quite often the preparation takes 50-90% of the total execution time of a query.

Now, what I would suggest you do for improvement is to split your DB access into three phases: prepare, bind, execute:

  • Prepare is where you send a constant text string to DB server with placeholders for where actual values will be bound into
  • Bind is where you assign values to those placeholders, binding in and out parameters. When you run your query next time all you need to do is bind new values to it and skip the first step entirely (!)
  • Execute at this point could be called more than once (in case of a recoverable error) without repeating the previous two steps

For more info search Google for "sql prepare bind execute" for your target DB

What I would suggest to you is using custom placeholders:

char querystring[]="INSERT INTO accounts (user, protocol, handle, password, autoconnect, tag) values ({param_user_id}, {param_protocol}, {param_handle}, {param_password}, {param_autoconnect}, {param_tag});"
parameterizeQuery(querystring, "user_id", user_id);
parameterizeQuery(querystring, "protocol", acc_protocol);
//do this for all remaining fields
g_string_printf(q, querystring);

while parameterizeQuery could look like this:

void parameterizeQuery(char stringofquery[], char parameterstring[], char parametervalue[])
{
    //PSEUDO-CODE: stringofquery.str_replace("{param_"+parameterstring+"}", g_string_mysql_escape_param(parametervalue));
}

This would be a bit shorter, but I am not familiar with glib.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top