/** * This is the main command execution function. The function contains * all the necessary logic to detect reset or disconnected database * connections and uploads commands to the server if necessary. * @param cmd Command to be executed * @return 0 if OK, <0 on MySQL failure, >0 on DB API failure */ static int exec_cmd_safe(db_cmd_t* cmd) { int i, err; db_con_t* con; struct my_cmd* mcmd; struct my_con* mcon; /* First things first: retrieve connection info * from the currently active connection and also * mysql payload from the database command */ mcmd = DB_GET_PAYLOAD(cmd); con = cmd->ctx->con[db_payload_idx]; mcon = DB_GET_PAYLOAD(con); for(i = 0; i <= my_retries; i++) { if ((mcon->flags & MY_CONNECTED) == 0) { /* The connection is disconnected, try to reconnect */ if (my_con_connect(con)) { INFO("mysql: exec_cmd_safe failed to re-connect\n"); continue; } } /* Next check the number of resets in the database connection, if this * number is higher than the number we keep in my_cmd structure in * last_reset variable then the connection was reset and we need to * upload the command again to the server before executing it, because * the server recycles all server side information upon disconnect. */ if (mcon->resets > mcmd->last_reset) { INFO("mysql: Connection reset detected, uploading command to server\n"); err = upload_cmd(cmd); if (err < 0) { INFO("mysql: Error while uploading command\n"); continue; } else if (err > 0) { /* DB API error, this is a serious problem such as memory * allocation failure, bail out */ return 1; } } set_mysql_params(cmd); err = mysql_stmt_execute(mcmd->st); if (err == 0) { /* The command was executed successfully, now fetch all data to * the client if it was requested by the user */ if (mcmd->flags & MY_FETCH_ALL) { err = mysql_stmt_store_result(mcmd->st); if (err) { INFO("mysql: Error while fetching data to client.\n"); goto error; } } return 0; } error: /* Command execution failed, log a message and try to reconnect */ INFO("mysql: libmysql: %d, %s\n", mysql_stmt_errno(mcmd->st), mysql_stmt_error(mcmd->st)); INFO("mysql: Error while executing command on server, trying to reconnect\n"); my_con_disconnect(con); if (my_con_connect(con)) { INFO("mysql: Failed to reconnect server\n"); } else { INFO("mysql: Successfully reconnected server\n"); } } INFO("mysql: Failed to execute command, giving up\n"); return -1; }
int pg_cmd(db_cmd_t* cmd) { struct pg_cmd* pcmd; pcmd = (struct pg_cmd*)pkg_malloc(sizeof(struct pg_cmd)); if (pcmd == NULL) { ERR("postgres: No memory left\n"); goto error; } memset(pcmd, '\0', sizeof(struct pg_cmd)); if (db_drv_init(&pcmd->gen, pg_cmd_free) < 0) goto error; switch(cmd->type) { case DB_PUT: if (build_insert_sql(&pcmd->sql_cmd, cmd) < 0) goto error; break; case DB_DEL: if (build_delete_sql(&pcmd->sql_cmd, cmd) < 0) goto error; break; case DB_GET: if (build_select_sql(&pcmd->sql_cmd, cmd) < 0) goto error; break; case DB_UPD: if (build_update_sql(&pcmd->sql_cmd, cmd) < 0) goto error; break; case DB_SQL: pcmd->sql_cmd.s = (char*)pkg_malloc(cmd->table.len + 1); if (pcmd->sql_cmd.s == NULL) { ERR("postgres: Out of private memory\n"); goto error; } memcpy(pcmd->sql_cmd.s,cmd->table.s, cmd->table.len); pcmd->sql_cmd.s[cmd->table.len] = '\0'; pcmd->sql_cmd.len = cmd->table.len; break; } DB_SET_PAYLOAD(cmd, pcmd); /* Create parameter arrays for PostgreSQL API functions */ if (create_pg_params(cmd) < 0) goto error; /* Generate a unique name for the command on the server */ if (gen_cmd_name(cmd) != 0) goto error; /* Upload the command to the server */ if (upload_cmd(cmd) != 0) goto error; /* Obtain the description of the uploaded command, this includes * information about result and parameter fields */ if (get_types(cmd) != 0) goto error; /* Update fields based on the information retrieved from the */ if (pg_resolve_param_oids(cmd->vals, cmd->match, cmd->vals_count, cmd->match_count, pcmd->types)) goto error; if (pg_resolve_result_oids(cmd->result, cmd->result_count, pcmd->types)) goto error; if (check_types(cmd)) goto error; return 0; error: if (pcmd) { DB_SET_PAYLOAD(cmd, NULL); free_pg_params(&pcmd->params); if (pcmd->types) PQclear(pcmd->types); if (pcmd->name) pkg_free(pcmd->name); if (pcmd->sql_cmd.s) pkg_free(pcmd->sql_cmd.s); db_drv_free(&pcmd->gen); pkg_free(pcmd); } return -1; }
int my_cmd(db_cmd_t* cmd) { struct my_cmd* res; res = (struct my_cmd*)pkg_malloc(sizeof(struct my_cmd)); if (res == NULL) { ERR("mysql: No memory left\n"); goto error; } memset(res, '\0', sizeof(struct my_cmd)); /* Fetch all data to client at once by default */ res->flags |= MY_FETCH_ALL; if (db_drv_init(&res->gen, my_cmd_free) < 0) goto error; switch(cmd->type) { case DB_PUT: if (DB_FLD_EMPTY(cmd->vals)) { BUG("mysql: No parameters provided for DB_PUT in context '%.*s'\n", cmd->ctx->id.len, ZSW(cmd->ctx->id.s)); goto error; } if (build_replace_cmd(&res->sql_cmd, cmd) < 0) goto error; break; case DB_DEL: if (build_delete_cmd(&res->sql_cmd, cmd) < 0) goto error; break; case DB_GET: if (build_select_cmd(&res->sql_cmd, cmd) < 0) goto error; break; case DB_UPD: if (build_update_cmd(&res->sql_cmd, cmd) < 0) goto error; break; case DB_SQL: res->sql_cmd.s = (char*)pkg_malloc(cmd->table.len); if (res->sql_cmd.s == NULL) { ERR("mysql: Out of private memory\n"); goto error; } memcpy(res->sql_cmd.s,cmd->table.s, cmd->table.len); res->sql_cmd.len = cmd->table.len; break; } DB_SET_PAYLOAD(cmd, res); /* In order to check all the parameters and results, we need to upload * the command to the server. We need to do that here before we report * back that the command was created successfully. Hence, this * function requires the corresponding connection be established. We * would not be able to check parameters if we don't do that there and * that could result in repeated execution failures at runtime. */ if (upload_cmd(cmd)) goto error; return 0; error: if (res) { DB_SET_PAYLOAD(cmd, NULL); db_drv_free(&res->gen); if (res->sql_cmd.s) pkg_free(res->sql_cmd.s); pkg_free(res); } return -1; }