minimum-viable MySQL connector
This commit is contained in:
parent
a5d63ce3f0
commit
740dc28b11
Binary file not shown.
@ -49,6 +49,7 @@ struct DTree {
|
||||
|
||||
string _string;
|
||||
f64 _float;
|
||||
s64 _array_index;
|
||||
bool _bool;
|
||||
void* _ptr;
|
||||
std::map<string, DTree> _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();
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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<MySQLFieldInfo> 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();
|
||||
|
||||
};
|
||||
|
||||
@ -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");
|
||||
|
||||
?></pre>
|
||||
|
||||
SELECT * FROM user table
|
||||
|
||||
<pre><?
|
||||
|
||||
echo(var_dump(con.query("SELECT * FROM user")));
|
||||
|
||||
}
|
||||
|
||||
|
||||
?></pre>
|
||||
|
||||
<label>CGI Params</label>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user