diff --git a/bin/uce_fastcgi.debug.linux.bin b/bin/uce_fastcgi.debug.linux.bin index 18a0da9..2d83a0e 100755 Binary files a/bin/uce_fastcgi.debug.linux.bin and b/bin/uce_fastcgi.debug.linux.bin differ diff --git a/src/lib/dtree.h b/src/lib/dtree.h index baa435a..c879ca6 100644 --- a/src/lib/dtree.h +++ b/src/lib/dtree.h @@ -49,6 +49,7 @@ struct DTree { string _string; f64 _float; + s64 _array_index; bool _bool; void* _ptr; std::map _map; @@ -88,7 +89,7 @@ struct DTree { return(_bool ? "(true)" : "(false)"); break; case('M'): - return("(array)"); + return(""); break; case('P'): return(std::to_string((u64)_ptr)); @@ -140,37 +141,48 @@ struct DTree { } } + void set_type(char t) + { + if(type != t) + { + type = t; + switch(type) + { + case('M'): + _map.clear(); + _array_index = 0; + break; + } + } + } + void set(string s) { - if(type == 'M') _map.clear(); - type = 'S'; + set_type('S'); _string = s; } void set(void* p) { - if(type == 'M') _map.clear(); - type = 'P'; + set_type('P'); _ptr = p; } void set(f64 f) { - if(type == 'M') _map.clear(); - type = 'F'; + set_type('F'); _float = f; } void set_bool(bool b) { - if(type == 'M') _map.clear(); - type = 'B'; + set_type('B'); _bool = b; } void set(DTree source) { - type = source.type; + set_type(source.type); switch(type) { case('S'): @@ -193,8 +205,7 @@ struct DTree { void set(StringMap source) { - type = 'M'; - _map.clear(); + set_type('M'); for (auto it = source.begin(); it != source.end(); ++it) { (*this)[it->first] = it->second; @@ -203,12 +214,12 @@ struct DTree { DTree* key(string s) { - type = 'M'; + set_type('M'); return(&_map[s]); } DTree& operator [] (string s) { - type = 'M'; + set_type('M'); return(_map[s]); } @@ -218,15 +229,31 @@ struct DTree { void operator = (DTree v) { set(v); } void operator = (StringMap v) { set(v); } + void push(DTree& child) + { + set_type('M'); + _map[std::to_string(_array_index)] = child; + _array_index += 1; + } + + DTree pop() + { + set_type('M'); + auto last = _map.rbegin(); + DTree result = last->second; + _map.erase(last->first); + return(result); + } + void remove(string s) { - type = 'M'; + set_type('M'); _map.erase(s); } void clear() { - type = 'M'; + set_type('M'); _map.clear(); } diff --git a/src/lib/mysql-connector.cpp b/src/lib/mysql-connector.cpp index 04c022f..75d67dc 100644 --- a/src/lib/mysql-connector.cpp +++ b/src/lib/mysql-connector.cpp @@ -67,19 +67,118 @@ string MySQL::escape(string raw, char quote_char) return(result); } -bool MySQL::query(string query) +DTree field_to_dtree_node(char* data_ptr, MySQLFieldInfo field_info, u32 len) { - auto res = mysql_query((MYSQL*)connection, query.c_str()); - return(true); + DTree result; + switch(field_info.type) + { + case(MYSQL_TYPE_TINY): + result.set(atoll(data_ptr)); + break; + case(MYSQL_TYPE_SHORT): + result.set(atoll(data_ptr)); + break; + case(MYSQL_TYPE_LONG): + result.set(atoll(data_ptr)); + break; + case(MYSQL_TYPE_INT24): + result.set(atoll(data_ptr)); + break; + case(MYSQL_TYPE_LONGLONG): + result.set(atoll(data_ptr)); + break; + case(MYSQL_TYPE_FLOAT): + result.set(atof(data_ptr)); + break; + case(MYSQL_TYPE_DOUBLE): + result.set(atof(data_ptr)); + break; + case(MYSQL_TYPE_NULL): + break; + default: + string s; + s.assign(data_ptr); + result.set(s); + break; + } + return(result); } -bool MySQL::query(string query, StringMap params) +DTree MySQL::get_pending_result() { - auto res = mysql_query( - (MYSQL*)connection, - parse_query_parameters(query, params).c_str() - ); - return(true); + DTree result_data; + // based on: https://dev.mysql.com/doc/c-api/5.7/en/mysql-field-count.html + MYSQL_RES *result; + result = mysql_store_result((MYSQL*)connection); + insert_id = mysql_insert_id((MYSQL*)connection); + //statement_info.assign(mysql_info((MYSQL*)connection)); + if (result) // there are rows + { + field_count = mysql_num_fields(result); + row_count = mysql_num_rows(result); + + field_info.clear(); + unsigned int i; + MYSQL_FIELD *fields; + fields = mysql_fetch_fields(result); + for(i = 0; i < field_count; i++) + { + MySQLFieldInfo fi; + if(fields[i].name) fi.name.assign(fields[i].name); + if(fields[i].table) fi.table.assign(fields[i].table); + if(fields[i].db) fi.db.assign(fields[i].db); + fi.length = (fields[i].length); + if(fields[i].def) fi.def.assign(fields[i].def); + fi.max_length = (fields[i].max_length); + fi.flags = (fields[i].flags); + fi.type = (fields[i].type); + field_info.push_back(fi); + } + + MYSQL_ROW row; + while ((row = mysql_fetch_row(result))) + { + DTree row_data; + auto lengths = mysql_fetch_lengths(result); + for(i = 0; i < field_count; i++) + { + row_data[field_info[i].name] = field_to_dtree_node(row[i], field_info[i], lengths[i]); + } + result_data.push(row_data); + } + + mysql_free_result(result); + } + else // mysql_store_result() returned nothing; should it have? + { + if(mysql_field_count((MYSQL*)connection) == 0) + { + // query does not return data + // (it was not a SELECT) + affected_rows = mysql_affected_rows((MYSQL*)connection); + } + else // mysql_store_result() should have returned data + { + // error + } + } + return(result_data); +} + +DTree MySQL::query(string q) +{ + _preload_next_error_code = mysql_query((MYSQL*)connection, q.c_str()); + DTree result; + if(_preload_next_error_code == 0) + result = get_pending_result(); + return(result); +} + +DTree MySQL::query(string q, StringMap params) +{ + return(query( + parse_query_parameters(q, params).c_str() + )); } string MySQL::parse_query_parameters(string query, StringMap map) @@ -144,6 +243,31 @@ void MySQL::disconnect() string MySQL::error() { + if(_preload_next_error_code) + { + string p = "Unknown error"; + switch(_preload_next_error_code) + { + case(CR_COMMANDS_OUT_OF_SYNC): + p = "Commands out of sync"; + break; + case(CR_SERVER_GONE_ERROR): + p = "Server connection error"; + break; + case(CR_SERVER_LOST): + p = "Server hung up"; + break; + case(CR_OUT_OF_MEMORY): + p = "Out of memory"; + break; + default: + case(CR_UNKNOWN_ERROR): + p = "Unknown server error"; + break; + } + _preload_next_error_code = 0; + return(p); + } const char* res = mysql_error((MYSQL*)connection); if(res) { diff --git a/src/lib/mysql-connector.h b/src/lib/mysql-connector.h index 347b231..84ef2cc 100644 --- a/src/lib/mysql-connector.h +++ b/src/lib/mysql-connector.h @@ -1,14 +1,33 @@ +struct MySQLFieldInfo { + string name; + string table; + string db; + u64 length; + string def; + u64 max_length; + u64 flags; + u32 type; +}; struct MySQL { - void* connection; + void* connection = 0; + u32 _preload_next_error_code = 0; + u32 affected_rows = 0; + u32 field_count = 0; + u32 row_count = 0; + u64 insert_id = 0; + string statement_info = ""; + + std::vector field_info; bool connect(string host = "localhost", string username = "root", string password = ""); void disconnect(); string error(); string escape(string raw, char quote_char = '\''); string parse_query_parameters(string query, StringMap m); - bool query(string query); - bool query(string query, StringMap params); - + DTree query(string q); + DTree query(string q, StringMap params); + DTree get_pending_result(); + }; diff --git a/test/mysql.uce b/test/mysql.uce index 82cc874..bd53a18 100644 --- a/test/mysql.uce +++ b/test/mysql.uce @@ -19,14 +19,25 @@ RENDER() params["val"] = "Dubious\\Value'Name\nwith;breaks"; MySQL con; - if(con.connect("localhost", "root", "q831b197f")) + if(con.connect("localhost", "root", "")) { echo("connection established\n"); echo("error: "+con.error()+"\n"); echo("quotation test: "+con.escape("‘\"' or 1=1;–.")+"\n"); echo("query parse: "+con.parse_query_parameters(query, params)+"\n"); + echo(var_dump(con.query("SHOW DATABASES"))); + con.query("USE mysql"); + + ?> + + SELECT * FROM user table + +