minimum-viable MySQL connector

This commit is contained in:
Udo 2021-12-01 01:38:19 +00:00
parent a5d63ce3f0
commit 740dc28b11
5 changed files with 212 additions and 31 deletions

Binary file not shown.

View File

@ -49,6 +49,7 @@ struct DTree {
string _string; string _string;
f64 _float; f64 _float;
s64 _array_index;
bool _bool; bool _bool;
void* _ptr; void* _ptr;
std::map<string, DTree> _map; std::map<string, DTree> _map;
@ -88,7 +89,7 @@ struct DTree {
return(_bool ? "(true)" : "(false)"); return(_bool ? "(true)" : "(false)");
break; break;
case('M'): case('M'):
return("(array)"); return("");
break; break;
case('P'): case('P'):
return(std::to_string((u64)_ptr)); 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) void set(string s)
{ {
if(type == 'M') _map.clear(); set_type('S');
type = 'S';
_string = s; _string = s;
} }
void set(void* p) void set(void* p)
{ {
if(type == 'M') _map.clear(); set_type('P');
type = 'P';
_ptr = p; _ptr = p;
} }
void set(f64 f) void set(f64 f)
{ {
if(type == 'M') _map.clear(); set_type('F');
type = 'F';
_float = f; _float = f;
} }
void set_bool(bool b) void set_bool(bool b)
{ {
if(type == 'M') _map.clear(); set_type('B');
type = 'B';
_bool = b; _bool = b;
} }
void set(DTree source) void set(DTree source)
{ {
type = source.type; set_type(source.type);
switch(type) switch(type)
{ {
case('S'): case('S'):
@ -193,8 +205,7 @@ struct DTree {
void set(StringMap source) void set(StringMap source)
{ {
type = 'M'; set_type('M');
_map.clear();
for (auto it = source.begin(); it != source.end(); ++it) for (auto it = source.begin(); it != source.end(); ++it)
{ {
(*this)[it->first] = it->second; (*this)[it->first] = it->second;
@ -203,12 +214,12 @@ struct DTree {
DTree* key(string s) DTree* key(string s)
{ {
type = 'M'; set_type('M');
return(&_map[s]); return(&_map[s]);
} }
DTree& operator [] (string s) { DTree& operator [] (string s) {
type = 'M'; set_type('M');
return(_map[s]); return(_map[s]);
} }
@ -218,15 +229,31 @@ struct DTree {
void operator = (DTree v) { set(v); } void operator = (DTree v) { set(v); }
void operator = (StringMap 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) void remove(string s)
{ {
type = 'M'; set_type('M');
_map.erase(s); _map.erase(s);
} }
void clear() void clear()
{ {
type = 'M'; set_type('M');
_map.clear(); _map.clear();
} }

View File

@ -67,19 +67,118 @@ string MySQL::escape(string raw, char quote_char)
return(result); 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()); DTree result;
return(true); 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( DTree result_data;
(MYSQL*)connection, // based on: https://dev.mysql.com/doc/c-api/5.7/en/mysql-field-count.html
parse_query_parameters(query, params).c_str() MYSQL_RES *result;
); result = mysql_store_result((MYSQL*)connection);
return(true); 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) string MySQL::parse_query_parameters(string query, StringMap map)
@ -144,6 +243,31 @@ void MySQL::disconnect()
string MySQL::error() 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); const char* res = mysql_error((MYSQL*)connection);
if(res) if(res)
{ {

View File

@ -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 { 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<MySQLFieldInfo> field_info;
bool connect(string host = "localhost", string username = "root", string password = ""); bool connect(string host = "localhost", string username = "root", string password = "");
void disconnect(); void disconnect();
string error(); string error();
string escape(string raw, char quote_char = '\''); string escape(string raw, char quote_char = '\'');
string parse_query_parameters(string query, StringMap m); string parse_query_parameters(string query, StringMap m);
bool query(string query); DTree query(string q);
bool query(string query, StringMap params); DTree query(string q, StringMap params);
DTree get_pending_result();
}; };

View File

@ -19,14 +19,25 @@ RENDER()
params["val"] = "Dubious\\Value'Name\nwith;breaks"; params["val"] = "Dubious\\Value'Name\nwith;breaks";
MySQL con; MySQL con;
if(con.connect("localhost", "root", "q831b197f")) if(con.connect("localhost", "root", ""))
{ {
echo("connection established\n"); echo("connection established\n");
echo("error: "+con.error()+"\n"); echo("error: "+con.error()+"\n");
echo("quotation test: "+con.escape("\"' or 1=1;.")+"\n"); echo("quotation test: "+con.escape("\"' or 1=1;.")+"\n");
echo("query parse: "+con.parse_query_parameters(query, params)+"\n"); echo("query parse: "+con.parse_query_parameters(query, params)+"\n");
echo(var_dump(con.query("SHOW DATABASES")));
con.query("USE mysql");
?></pre>
SELECT * FROM user table
<pre><?
echo(var_dump(con.query("SELECT * FROM user")));
} }
?></pre> ?></pre>
<label>CGI Params</label> <label>CGI Params</label>