#include "dbaccess.h" /* $Id: dbaccess.c,v 1.14 2008/03/06 01:50:58 derek Exp $ */ db_handle_t * db_handle_reattach(db_handle_t *handle,const char *dbserver_name) { if(handle->dblookup == NULL) { int ct = handle->server_offset; for(ct = handle->server_offset ; ct < handle->server_count; ct++) { if(handle->db) mysql_close(handle->db); handle->db = mysql_init(NULL); mysql_options(handle->db,MYSQL_READ_DEFAULT_FILE,handle->config); mysql_options(handle->db,MYSQL_READ_DEFAULT_GROUP,handle->server[ct]); if(mysql_real_connect(handle->db,NULL,handle->user,handle->pass,NULL,0,NULL,CLIENT_MULTI_STATEMENTS) != NULL){ handle->server_offset = ct; return handle; } } for(ct = 0; ct < handle->server_offset; ct++) { if(handle->db) mysql_close(handle->db); handle->db = mysql_init(NULL); mysql_options(handle->db,MYSQL_READ_DEFAULT_FILE,handle->config); mysql_options(handle->db,MYSQL_READ_DEFAULT_GROUP,handle->server[ct]); if(mysql_real_connect(handle->db,NULL,handle->user,handle->pass,NULL,0,NULL,CLIENT_MULTI_STATEMENTS) != NULL){ handle->server_offset = ct; return handle; } } } else { MYSQL *db = json_skip_get(handle->dblookup,(void*) dbserver_name); if(db) { mysql_close(db); } db = mysql_init(NULL); mysql_options(db,MYSQL_READ_DEFAULT_FILE,handle->config); mysql_options(db,MYSQL_READ_DEFAULT_GROUP,dbserver_name); json_skip_replace(handle->dblookup,(void*)dbserver_name,db); if(mysql_real_connect(db,NULL,NULL,NULL,NULL,0,NULL,CLIENT_MULTI_STATEMENTS) != NULL){ return handle; } } return NULL; } db_handle_t * db_handle_init(const char *_user, const char *_pass, const char *_server, const char *_config, void *userarg,int multidb){ db_handle_t *dbhandle= malloc(sizeof(db_handle_t)); //should then be defined in the config if the user/pass is NULL dbhandle->user = _user ? strdup(_user) : NULL; dbhandle->pass = _pass ? strdup(_pass) : NULL; dbhandle->config = strdup(_config); dbhandle->db = NULL; dbhandle->dblookup = NULL; dbhandle->mpool = NULL; //chop up the server string int i = 0; char *server = strdup(_server); char *e = server; dbhandle->server_count = 1; while((e = strchr(e,':'))!=NULL) {dbhandle->server_count++; e++;} char *b = e = server; dbhandle->server = malloc(sizeof(char*) * dbhandle->server_count); while((e = strchr(e,':'))!=NULL) { *e = '\0'; dbhandle->server[i++] = strdup(b); e++; b = e; } dbhandle->server[i++] = strdup(b); unsigned int seed = *((unsigned int*)userarg); free(server); dbhandle->server_offset = seed % dbhandle->server_count; if(multidb == 0) { db_handle_reattach(dbhandle,""); } else { apr_pool_create(&(dbhandle->mpool),NULL); dbhandle->dblookup = json_skip_create(dbhandle->mpool,4,(json_skip_cmp_t)strcmp); for(i=0;i < dbhandle->server_count; i++) { db_handle_reattach(dbhandle,dbhandle->server[i]); } } return dbhandle; } void db_handle_destroy(db_handle_t *dbhandle) { int i; if(dbhandle->dblookup == NULL) { mysql_close(dbhandle->db); } else { for(i = 0; i < dbhandle->server_count; i++) { mysql_close((MYSQL*)json_skip_get(dbhandle->dblookup,dbhandle->server[i])); } apr_pool_destroy(dbhandle->mpool); } mysql_thread_end(); for(i = 0; i < dbhandle->server_count; i++) { free(dbhandle->server[i]); } free(dbhandle->server); free(dbhandle->config); free(dbhandle->user); free(dbhandle->pass); free(dbhandle); } json_value * dbresult2json(MYSQL_RES * myresult,apr_pool_t *mpool) { json_value *result = json_object_create(mpool); unsigned int num_fields; unsigned int i; MYSQL_FIELD *fields; num_fields = mysql_num_fields(myresult); fields = mysql_fetch_fields(myresult); json_value *header = json_array_create(mpool,num_fields); json_object_add(result,"HEADER",header); json_value *coltypes = json_array_create(mpool,num_fields); json_object_add(result,"TYPES",coltypes); for(i = 0; i < num_fields; i++) { json_array_append(header,json_string_create(mpool,fields[i].name)); switch(fields[i].type) { case MYSQL_TYPE_TINY: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_TINY")); break; case MYSQL_TYPE_SHORT: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_SHORT")); break; case MYSQL_TYPE_LONG: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_LONG")); break; case MYSQL_TYPE_INT24: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_INT24")); break; case MYSQL_TYPE_DECIMAL: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_DECIMAL")); break; case MYSQL_TYPE_NEWDECIMAL: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_NEWDECIMAL")); break; case MYSQL_TYPE_DOUBLE: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_DOUBLE")); break; case MYSQL_TYPE_FLOAT: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_FLOAT")); break; case MYSQL_TYPE_LONGLONG: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_LONGLONG")); break; case MYSQL_TYPE_BIT: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_BIT")); break; case MYSQL_TYPE_TIMESTAMP: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_TIMESTAMP")); break; case MYSQL_TYPE_DATE: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_DATE")); break; case MYSQL_TYPE_TIME: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_TIME")); break; case MYSQL_TYPE_DATETIME: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_DATETIME")); break; case MYSQL_TYPE_YEAR: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_YEAR")); break; case MYSQL_TYPE_STRING: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_STRING")); break; case MYSQL_TYPE_VAR_STRING: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_VAR_STRING")); break; case MYSQL_TYPE_NEWDATE: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_NEWDATE")); break; case MYSQL_TYPE_VARCHAR: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_VARCHAR")); break; case MYSQL_TYPE_BLOB: //DON'T ASK - it is not 63 it is TEXT //http://www.mysql.org/doc/refman/5.1/en/c-api-datatypes.html if (fields[i].charsetnr == 63) { /**BINARY - NEEDS TO B64 **/ json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_TEXT")); } else { json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_BLOB")); } break; case MYSQL_TYPE_TINY_BLOB: if (fields[i].charsetnr == 63) { json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_TEXT")); } else { json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_TINY_BLOB")); } break; case MYSQL_TYPE_MEDIUM_BLOB: if (fields[i].charsetnr == 63) { json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_TEXT")); } else { json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_MEDIUM_BLOB")); } break; case MYSQL_TYPE_LONG_BLOB: if (fields[i].charsetnr == 63) { json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_TEXT")); } else { json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_LONG_BLOB")); } break; case MYSQL_TYPE_SET: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_SET")); break; case MYSQL_TYPE_ENUM: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_ENUM")); break; case MYSQL_TYPE_GEOMETRY: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_GEOMETRY")); break; case MYSQL_TYPE_NULL: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_NULL")); break; default: json_array_append(coltypes,json_string_create(mpool,"MYSQL_TYPE_UNKNOWN")); break; } } json_value *rows = json_array_create(mpool,1000); json_object_add(result,"ROWS",rows); MYSQL_ROW myrow; while( (myrow = mysql_fetch_row(myresult)) !=NULL) { json_value *orow = json_array_create(mpool,num_fields); json_array_append(rows,orow); //unsigned long *lengths = mysql_fetch_lengths(myresult); for(i = 0; i < num_fields; i++) { switch(fields[i].type) { case MYSQL_TYPE_TINY: case MYSQL_TYPE_SHORT: case MYSQL_TYPE_LONG: case MYSQL_TYPE_INT24: json_array_append(orow,myrow[i] ? json_long_create(mpool,atol(myrow[i] )) :json_null_create(mpool)); break; case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: case MYSQL_TYPE_DOUBLE: case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_LONGLONG: json_array_append(orow,myrow[i] ? json_double_create(mpool,atof(myrow[i])) :json_null_create(mpool)); break; case MYSQL_TYPE_BIT: case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_DATE: case MYSQL_TYPE_TIME: case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_YEAR: case MYSQL_TYPE_STRING: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_NEWDATE: case MYSQL_TYPE_VARCHAR: json_array_append(orow, myrow[i] ? json_string_create(mpool,myrow[i]) : json_null_create(mpool) ); break; case MYSQL_TYPE_BLOB: case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: //DON'T ASK - it is not 63 it is TEXT //http://www.mysql.org/doc/refman/5.1/en/c-api-datatypes.html if (fields[i].charsetnr == 63) { /**BINARY - NEEDS TO B64 **/ json_array_append(orow,json_null_create(mpool)); } else { json_array_append(orow, myrow[i] ? json_string_create(mpool,myrow[i]) : json_null_create(mpool) ); } break; case MYSQL_TYPE_SET: case MYSQL_TYPE_ENUM: case MYSQL_TYPE_GEOMETRY: case MYSQL_TYPE_NULL: json_array_append(orow,json_null_create(mpool)); break; } } } return result; } json_value * dbexecute(db_handle_t *dbhandle, json_value *injson, apr_pool_t *mpool) { json_value *out = json_object_create(mpool); json_value *sql = NULL; MYSQL *db = NULL; //dbhandle->db; const char *dbserver_name = NULL;//dbhandle->server[xdbhandle->server_offset]; json_value *request_server = NULL; // CHECK FOR SERVER in the incoming JSON - and pick out flag - if(injson->type == JSON_OBJECT && (request_server = (json_value*)json_skip_get(injson->value.object,"SERVER")) !=NULL && request_server->type == JSON_STRING) { if(dbhandle->dblookup == NULL) { json_object_add(out,"ERROR",json_string_create(mpool,"Provided a SERVER argument but dbslayer is not configure for named server access - perhaps you want the -m config option")); return out; } dbserver_name = request_server->value.string; db = (MYSQL*) json_skip_get(dbhandle->dblookup,(void*)dbserver_name); if(db == NULL) { json_object_add(out,"ERROR",json_string_create(mpool,"Couldn't find the database handle for SERVER requested")); json_object_add(out,"SERVER",request_server); return out; } } else if(dbhandle->dblookup !=NULL) { json_object_add(out,"ERROR",json_string_create(mpool,"dbslayer is configured to take a SERVER argument - perhaps you want the -s config option")); return out; } else { db = dbhandle->db; dbserver_name = dbhandle->server[dbhandle->server_offset]; } if(injson->type == JSON_OBJECT && (sql = (json_value*)json_skip_get(injson->value.object,"SQL")) !=NULL && sql->type == JSON_STRING) { if(mysql_query(db,sql->value.string)) { /** NEED TO CHECK FOR BETTER ERROR TO MAKE SURE IT IS A CONNECTION ISSUE **/ if(db_handle_reattach(dbhandle,dbserver_name) == NULL || mysql_query((db = dbhandle->dblookup == NULL ? dbhandle->db : json_skip_get(dbhandle->dblookup,(void*)dbserver_name)) ,sql->value.string)){ dbserver_name = dbhandle->dblookup == NULL ? dbhandle->server[dbhandle->server_offset] : dbserver_name; json_object_add(out,"MYSQL_ERROR",json_string_create(mpool,mysql_error(db))); json_object_add(out,"MYSQL_ERRNO",json_long_create(mpool,mysql_errno(db))); json_object_add(out,"SERVER" , json_string_create(mpool,dbserver_name)); /** issue a rollback if caller asked for it **/ if ((injson->type == JSON_OBJECT) && ((sql = (json_value*)json_skip_get(injson->value.object, "ROLLBACK_ON_ERROR" )) != NULL) && (sql->type == JSON_BOOLEAN) && sql->value.boolean) { json_object_add(out, "ROLLBACK_ON_ERROR", json_boolean_create(mpool, 1)); json_object_add(out, "ROLLBACK_ON_ERROR_SUCCESS", json_boolean_create(mpool, !mysql_rollback(db))); } return out; } } json_value *all_result = NULL; json_value *sql_result = NULL; int status = 0; do { MYSQL_RES *myresult = mysql_store_result(db); if(myresult == NULL ) { /** ERROR OCCURED ***/ if(mysql_errno(db)) { json_object_add(out,"SUCCESS",json_boolean_create(mpool,0)); json_object_add(out,"MYSQL_ERROR",json_string_create(mpool,mysql_error(db))); json_object_add(out,"MYSQL_ERRNO",json_long_create(mpool,mysql_errno(db))); json_object_add(out,"SERVER" , json_string_create(mpool,dbserver_name)); /** issue a rollback if caller asked for it **/ if ((injson->type == JSON_OBJECT) && ((sql = (json_value*)json_skip_get(injson->value.object, "ROLLBACK_ON_ERROR")) != NULL) && (sql->type == JSON_BOOLEAN) && sql->value.boolean) { json_object_add(out, "ROLLBACK_ON_ERROR", json_boolean_create(mpool, 1)); json_object_add(out, "ROLLBACK_ON_ERROR_SUCCESS", json_boolean_create(mpool, !mysql_rollback(db))); } } else { /** SUCCESS NO RESULT RETURNED ie UPDATE | DELETE | INSERT ***/ ; if(sql_result && all_result ) { sql_result = json_object_create(mpool); json_object_add(sql_result,"SUCCESS",json_boolean_create(mpool,1)); json_array_append(all_result,sql_result); } else if (sql_result && !all_result) { all_result = json_array_create(mpool,5); json_array_append(all_result,sql_result); sql_result = json_object_create(mpool); json_object_add(sql_result,"SUCCESS",json_boolean_create(mpool,1)); json_array_append(all_result,sql_result); json_object_add(out,"RESULT",all_result); } else { sql_result = json_object_create(mpool); json_object_add(sql_result,"SUCCESS",json_boolean_create(mpool,1)); } } } else { if(sql_result && all_result ) { json_array_append(all_result,dbresult2json(myresult,mpool)); } else if (sql_result && !all_result) { all_result = json_array_create(mpool,5); json_array_append(all_result,sql_result); json_array_append(all_result,dbresult2json(myresult,mpool)); json_object_add(out,"RESULT",all_result); } else { sql_result = dbresult2json(myresult,mpool); } mysql_free_result(myresult); } if ((status = mysql_next_result(db)) > 0) printf("Could not execute statement\n"); }while(status ==0); if(sql_result && !all_result) { json_object_add(out,"RESULT",sql_result); } json_object_add(out,"SERVER" , json_string_create(mpool,dbserver_name)); } if(injson->type == JSON_OBJECT && (sql = (json_value*)json_skip_get(injson->value.object,"STAT")) !=NULL && sql->type == JSON_BOOLEAN && sql->value.boolean) { json_object_add(out,"STAT",json_string_create(mpool,mysql_stat(db))); } if(injson->type == JSON_OBJECT && (sql = (json_value*)json_skip_get(injson->value.object,"CLIENT_INFO")) !=NULL && sql->type == JSON_BOOLEAN && sql->value.boolean) { json_object_add(out,"CLIENT_INFO",json_string_create(mpool,mysql_get_client_info())); } if(injson->type == JSON_OBJECT && (sql = (json_value*)json_skip_get(injson->value.object,"HOST_INFO")) !=NULL && sql->type == JSON_BOOLEAN && sql->value.boolean) { json_object_add(out,"HOST_INFO",json_string_create(mpool,mysql_get_host_info(db))); } if(injson->type == JSON_OBJECT && (sql = (json_value*)json_skip_get(injson->value.object,"SERVER_VERSION")) !=NULL && sql->type == JSON_BOOLEAN && sql->value.boolean) { json_object_add(out,"SERVER_VERSION",json_long_create(mpool,mysql_get_server_version(db))); } if(injson->type == JSON_OBJECT && (sql = (json_value*)json_skip_get(injson->value.object,"CLIENT_VERSION")) !=NULL && sql->type == JSON_BOOLEAN && sql->value.boolean) { json_object_add(out,"CLIENT_VERSION",json_long_create(mpool,mysql_get_client_version())); } if(injson->type == JSON_OBJECT && (sql = (json_value*)json_skip_get(injson->value.object,"SLAYER_DEBUG_RETURN_INPUT")) !=NULL && sql->type == JSON_BOOLEAN && sql->value.boolean) { json_object_add(out,"SLAYER_DEBUG_RETURN_INPUT",injson); } if(injson->type == JSON_OBJECT && (sql = (json_value*)json_skip_get(injson->value.object,"SERVER_NAME")) !=NULL && sql->type == JSON_BOOLEAN && sql->value.boolean) { json_object_add(out,"SERVER_NAME",json_string_create(mpool,dbserver_name)); } if(injson->type == JSON_OBJECT && (sql = (json_value*)json_skip_get(injson->value.object,"SLAYER_HELP")) !=NULL && sql->type == JSON_BOOLEAN && sql->value.boolean) { json_value *commands = json_object_create(mpool); json_object_add(out,"SLAYER_HELP",commands); json_object_add(commands,"SQL",json_string_create(mpool,"in(string)[sql statement to execute] : out()eturns results in RESULTS node")); json_object_add(commands,"STAT",json_string_create(mpool,"in(boolean) : out(string) result of mysql_stat()")); json_object_add(commands,"CLIENT_INFO",json_string_create(mpool,"in(boolean) : out(string) result of mysql_get_client_info()")); json_object_add(commands,"HOST_INFO",json_string_create(mpool,"in(boolean) : out(string) result of mysql_get_host_info()")); json_object_add(commands,"SERVER_VERSION",json_string_create(mpool,"in(boolean) : out(long) result of mysql_server_version()")); json_object_add(commands,"CLIENT_VERSION",json_string_create(mpool,"in(boolean) : out(long) result of mysql_client_version()")); json_object_add(commands,"SLAYER_HELP",json_string_create(mpool,"in(boolean) : true ? returns this result")); json_object_add(commands,"SLAYER_DEBUG_RETURN_INPUT",json_string_create(mpool,"in(boolean) : true ? returns this result")); json_object_add(commands,"SERVER_NAME",json_string_create(mpool,"in(boolean) : true ? returns stanza from the -s option that was used")); } if(out->type == JSON_OBJECT && out->value.object->node_count == 0) { json_object_add(out,"ERROR",json_string_create(mpool,"TRY {\"SLAYER_HELP\":true } ")); } return out; }