sq_mysql.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890
  1. #ifdef WITH_MYSQL
  2. #include "squirrel.h"
  3. #include "mysql.h"
  4. #include <string.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include "sqstdblobimpl.h"
  8. SQ_OPT_STRING_STRLEN();
  9. #include "dynamic_library.h"
  10. /*SquiLu
  11. local mysql_functions = [
  12. ["MYSQL *", "mysql_init", "MYSQL *mysql"],
  13. ["MYSQL *", "mysql_real_connect", @"MYSQL *mysql, const char *host,
  14. const char *user,
  15. const char *passwd,
  16. const char *db,
  17. unsigned int port,
  18. const char *unix_socket,
  19. unsigned long clientflag"],
  20. ["void", "mysql_close", "MYSQL *sock"],
  21. ["const char *", "mysql_error", "MYSQL *mysql"],
  22. ["int", "mysql_ping", "MYSQL *mysql"],
  23. ["unsigned long", "mysql_real_escape_string", @"MYSQL *mysql,
  24. char *to,const char *from,
  25. unsigned long length"],
  26. ["unsigned long", "mysql_get_server_version", "MYSQL *mysql"],
  27. ["unsigned long", "mysql_insert_id", "MYSQL *mysql"],
  28. ["void", "mysql_data_seek", "MYSQL_RES *result, my_ulonglong offset"],
  29. ["MYSQL_RES *", "mysql_store_result", "MYSQL *mysql"],
  30. ["int", "mysql_real_query", @"MYSQL *mysql, const char *q,
  31. unsigned long length"],
  32. ["my_ulonglong", "mysql_num_rows", "MYSQL_RES *res"],
  33. ["my_ulonglong", "mysql_affected_rows", "MYSQL *mysql"],
  34. ["unsigned int", "mysql_num_fields", "MYSQL_RES *res"],
  35. ["MYSQL_FIELD *", "mysql_fetch_fields", "MYSQL_RES *res"],
  36. ["MYSQL_FIELD *", "mysql_fetch_field_direct", "MYSQL_RES *res, unsigned int fieldnr"],
  37. ["MYSQL_ROW", "mysql_fetch_row", "MYSQL_RES *res"],
  38. ["unsigned long *", "mysql_fetch_lengths", "MYSQL_RES *res"],
  39. ["void", "mysql_free_result", "MYSQL_RES *result"],
  40. ["my_bool", "mysql_eof", "MYSQL_RES *res"],
  41. //prepared statements
  42. ["MYSQL_STMT *", "mysql_stmt_init", "MYSQL *mysql"],
  43. ["int", "mysql_stmt_prepare", @"MYSQL_STMT *stmt, const char *query,
  44. unsigned long length"],
  45. ["int", "mysql_stmt_execute", "MYSQL_STMT *stmt"],
  46. ["unsigned long", "mysql_stmt_param_count", "MYSQL_STMT * stmt"],
  47. ["my_bool", "mysql_stmt_bind_param", "MYSQL_STMT * stmt, MYSQL_BIND * bnd"],
  48. ["my_bool", "mysql_stmt_reset", "MYSQL_STMT * stmt"],
  49. ["my_bool", "mysql_stmt_free_result", "MYSQL_STMT * stmt"],
  50. ["my_bool", "mysql_stmt_close", "MYSQL_STMT * stmt"],
  51. ["const char *", "mysql_stmt_error", "MYSQL_STMT * stmt"],
  52. ];
  53. function write_mysql_functions_declaration(){
  54. foreach(k,v in mysql_functions) {
  55. putsnl("typedef " + v[0] + " (*" + v[1] + "_t)(" + v[2] + ");");
  56. putsnl("static " + v[1] + "_t dl" + v[1] + " = 0;");
  57. }
  58. }
  59. function write_mysql_functions_load(){
  60. foreach(k,v in mysql_functions){
  61. putsnl("dl" + v[1] + " = (" + v[1] + "_t) libmysqlclient.dlsym(\"" + v[1] + "\");");
  62. putsnl("if(!dl" + v[1] + ") return false;");
  63. }
  64. }
  65. SquiLu*/
  66. static DynamicLibrary libmysqlclient;
  67. //@write_mysql_functions_declaration();
  68. // generated-code:begin
  69. typedef MYSQL * (*mysql_init_t)(MYSQL *mysql);
  70. static mysql_init_t dlmysql_init = 0;
  71. typedef MYSQL * (*mysql_real_connect_t)(MYSQL *mysql, const char *host,
  72. const char *user,
  73. const char *passwd,
  74. const char *db,
  75. unsigned int port,
  76. const char *unix_socket,
  77. unsigned long clientflag);
  78. static mysql_real_connect_t dlmysql_real_connect = 0;
  79. typedef void (*mysql_close_t)(MYSQL *sock);
  80. static mysql_close_t dlmysql_close = 0;
  81. typedef const char * (*mysql_error_t)(MYSQL *mysql);
  82. static mysql_error_t dlmysql_error = 0;
  83. typedef int (*mysql_ping_t)(MYSQL *mysql);
  84. static mysql_ping_t dlmysql_ping = 0;
  85. typedef unsigned long (*mysql_real_escape_string_t)(MYSQL *mysql,
  86. char *to,const char *from,
  87. unsigned long length);
  88. static mysql_real_escape_string_t dlmysql_real_escape_string = 0;
  89. typedef unsigned long (*mysql_get_server_version_t)(MYSQL *mysql);
  90. static mysql_get_server_version_t dlmysql_get_server_version = 0;
  91. typedef unsigned long (*mysql_insert_id_t)(MYSQL *mysql);
  92. static mysql_insert_id_t dlmysql_insert_id = 0;
  93. typedef void (*mysql_data_seek_t)(MYSQL_RES *result, my_ulonglong offset);
  94. static mysql_data_seek_t dlmysql_data_seek = 0;
  95. typedef MYSQL_RES * (*mysql_store_result_t)(MYSQL *mysql);
  96. static mysql_store_result_t dlmysql_store_result = 0;
  97. typedef int (*mysql_real_query_t)(MYSQL *mysql, const char *q,
  98. unsigned long length);
  99. static mysql_real_query_t dlmysql_real_query = 0;
  100. typedef my_ulonglong (*mysql_num_rows_t)(MYSQL_RES *res);
  101. static mysql_num_rows_t dlmysql_num_rows = 0;
  102. typedef my_ulonglong (*mysql_affected_rows_t)(MYSQL *mysql);
  103. static mysql_affected_rows_t dlmysql_affected_rows = 0;
  104. typedef unsigned int (*mysql_num_fields_t)(MYSQL_RES *res);
  105. static mysql_num_fields_t dlmysql_num_fields = 0;
  106. typedef MYSQL_FIELD * (*mysql_fetch_fields_t)(MYSQL_RES *res);
  107. static mysql_fetch_fields_t dlmysql_fetch_fields = 0;
  108. typedef MYSQL_FIELD * (*mysql_fetch_field_direct_t)(MYSQL_RES *res, unsigned int fieldnr);
  109. static mysql_fetch_field_direct_t dlmysql_fetch_field_direct = 0;
  110. typedef MYSQL_ROW (*mysql_fetch_row_t)(MYSQL_RES *res);
  111. static mysql_fetch_row_t dlmysql_fetch_row = 0;
  112. typedef unsigned long * (*mysql_fetch_lengths_t)(MYSQL_RES *res);
  113. static mysql_fetch_lengths_t dlmysql_fetch_lengths = 0;
  114. typedef void (*mysql_free_result_t)(MYSQL_RES *result);
  115. static mysql_free_result_t dlmysql_free_result = 0;
  116. typedef my_bool (*mysql_eof_t)(MYSQL_RES *res);
  117. static mysql_eof_t dlmysql_eof = 0;
  118. typedef MYSQL_STMT * (*mysql_stmt_init_t)(MYSQL *mysql);
  119. static mysql_stmt_init_t dlmysql_stmt_init = 0;
  120. typedef int (*mysql_stmt_prepare_t)(MYSQL_STMT *stmt, const char *query,
  121. unsigned long length);
  122. static mysql_stmt_prepare_t dlmysql_stmt_prepare = 0;
  123. typedef int (*mysql_stmt_execute_t)(MYSQL_STMT *stmt);
  124. static mysql_stmt_execute_t dlmysql_stmt_execute = 0;
  125. typedef unsigned long (*mysql_stmt_param_count_t)(MYSQL_STMT * stmt);
  126. static mysql_stmt_param_count_t dlmysql_stmt_param_count = 0;
  127. typedef my_bool (*mysql_stmt_bind_param_t)(MYSQL_STMT * stmt, MYSQL_BIND * bnd);
  128. static mysql_stmt_bind_param_t dlmysql_stmt_bind_param = 0;
  129. typedef my_bool (*mysql_stmt_reset_t)(MYSQL_STMT * stmt);
  130. static mysql_stmt_reset_t dlmysql_stmt_reset = 0;
  131. typedef my_bool (*mysql_stmt_free_result_t)(MYSQL_STMT * stmt);
  132. static mysql_stmt_free_result_t dlmysql_stmt_free_result = 0;
  133. typedef my_bool (*mysql_stmt_close_t)(MYSQL_STMT * stmt);
  134. static mysql_stmt_close_t dlmysql_stmt_close = 0;
  135. typedef const char * (*mysql_stmt_error_t)(MYSQL_STMT * stmt);
  136. static mysql_stmt_error_t dlmysql_stmt_error = 0;
  137. // generated-code:end
  138. static const char *dynamicLibName = DYNLIB_FOR_OS(libmysqlclient);
  139. static bool load_libmysqlclient()
  140. {
  141. if(dlmysql_init) return true;
  142. if(libmysqlclient.open(dynamicLibName))
  143. {
  144. //@write_mysql_functions_load();
  145. // generated-code:begin
  146. dlmysql_init = (mysql_init_t) libmysqlclient.dlsym("mysql_init");
  147. if(!dlmysql_init) return false;
  148. dlmysql_real_connect = (mysql_real_connect_t) libmysqlclient.dlsym("mysql_real_connect");
  149. if(!dlmysql_real_connect) return false;
  150. dlmysql_close = (mysql_close_t) libmysqlclient.dlsym("mysql_close");
  151. if(!dlmysql_close) return false;
  152. dlmysql_error = (mysql_error_t) libmysqlclient.dlsym("mysql_error");
  153. if(!dlmysql_error) return false;
  154. dlmysql_ping = (mysql_ping_t) libmysqlclient.dlsym("mysql_ping");
  155. if(!dlmysql_ping) return false;
  156. dlmysql_real_escape_string = (mysql_real_escape_string_t) libmysqlclient.dlsym("mysql_real_escape_string");
  157. if(!dlmysql_real_escape_string) return false;
  158. dlmysql_get_server_version = (mysql_get_server_version_t) libmysqlclient.dlsym("mysql_get_server_version");
  159. if(!dlmysql_get_server_version) return false;
  160. dlmysql_insert_id = (mysql_insert_id_t) libmysqlclient.dlsym("mysql_insert_id");
  161. if(!dlmysql_insert_id) return false;
  162. dlmysql_data_seek = (mysql_data_seek_t) libmysqlclient.dlsym("mysql_data_seek");
  163. if(!dlmysql_data_seek) return false;
  164. dlmysql_store_result = (mysql_store_result_t) libmysqlclient.dlsym("mysql_store_result");
  165. if(!dlmysql_store_result) return false;
  166. dlmysql_real_query = (mysql_real_query_t) libmysqlclient.dlsym("mysql_real_query");
  167. if(!dlmysql_real_query) return false;
  168. dlmysql_num_rows = (mysql_num_rows_t) libmysqlclient.dlsym("mysql_num_rows");
  169. if(!dlmysql_num_rows) return false;
  170. dlmysql_affected_rows = (mysql_affected_rows_t) libmysqlclient.dlsym("mysql_affected_rows");
  171. if(!dlmysql_affected_rows) return false;
  172. dlmysql_num_fields = (mysql_num_fields_t) libmysqlclient.dlsym("mysql_num_fields");
  173. if(!dlmysql_num_fields) return false;
  174. dlmysql_fetch_fields = (mysql_fetch_fields_t) libmysqlclient.dlsym("mysql_fetch_fields");
  175. if(!dlmysql_fetch_fields) return false;
  176. dlmysql_fetch_field_direct = (mysql_fetch_field_direct_t) libmysqlclient.dlsym("mysql_fetch_field_direct");
  177. if(!dlmysql_fetch_field_direct) return false;
  178. dlmysql_fetch_row = (mysql_fetch_row_t) libmysqlclient.dlsym("mysql_fetch_row");
  179. if(!dlmysql_fetch_row) return false;
  180. dlmysql_fetch_lengths = (mysql_fetch_lengths_t) libmysqlclient.dlsym("mysql_fetch_lengths");
  181. if(!dlmysql_fetch_lengths) return false;
  182. dlmysql_free_result = (mysql_free_result_t) libmysqlclient.dlsym("mysql_free_result");
  183. if(!dlmysql_free_result) return false;
  184. dlmysql_eof = (mysql_eof_t) libmysqlclient.dlsym("mysql_eof");
  185. if(!dlmysql_eof) return false;
  186. dlmysql_stmt_init = (mysql_stmt_init_t) libmysqlclient.dlsym("mysql_stmt_init");
  187. if(!dlmysql_stmt_init) return false;
  188. dlmysql_stmt_prepare = (mysql_stmt_prepare_t) libmysqlclient.dlsym("mysql_stmt_prepare");
  189. if(!dlmysql_stmt_prepare) return false;
  190. dlmysql_stmt_execute = (mysql_stmt_execute_t) libmysqlclient.dlsym("mysql_stmt_execute");
  191. if(!dlmysql_stmt_execute) return false;
  192. dlmysql_stmt_param_count = (mysql_stmt_param_count_t) libmysqlclient.dlsym("mysql_stmt_param_count");
  193. if(!dlmysql_stmt_param_count) return false;
  194. dlmysql_stmt_bind_param = (mysql_stmt_bind_param_t) libmysqlclient.dlsym("mysql_stmt_bind_param");
  195. if(!dlmysql_stmt_bind_param) return false;
  196. dlmysql_stmt_reset = (mysql_stmt_reset_t) libmysqlclient.dlsym("mysql_stmt_reset");
  197. if(!dlmysql_stmt_reset) return false;
  198. dlmysql_stmt_free_result = (mysql_stmt_free_result_t) libmysqlclient.dlsym("mysql_stmt_free_result");
  199. if(!dlmysql_stmt_free_result) return false;
  200. dlmysql_stmt_close = (mysql_stmt_close_t) libmysqlclient.dlsym("mysql_stmt_close");
  201. if(!dlmysql_stmt_close) return false;
  202. dlmysql_stmt_error = (mysql_stmt_error_t) libmysqlclient.dlsym("mysql_stmt_error");
  203. if(!dlmysql_stmt_error) return false;
  204. // generated-code:end
  205. return true;
  206. }
  207. return false;
  208. }
  209. ////////////////////////////////////////////////////////////////////////////////
  210. static const SQChar *MySQL_TAG = _SC("MySQL");
  211. static SQRESULT get_mysql_instance(HSQUIRRELVM v, SQInteger idx, MYSQL **self){
  212. SQRESULT _rc_;
  213. if((_rc_ = sq_getinstanceup(v,idx,(SQUserPointer*)self,(void*)MySQL_TAG)) < 0) return _rc_;
  214. if(!*self) return sq_throwerror(v, _SC("database is closed"));
  215. return _rc_;
  216. }
  217. #define GET_mysql_INSTANCE_AT(idx) \
  218. MYSQL *self=NULL; \
  219. if((_rc_ = get_mysql_instance(v,idx,&self)) < 0) return _rc_;
  220. #define GET_mysql_INSTANCE() GET_mysql_INSTANCE_AT(1)
  221. static const SQChar *MySQL_Result_TAG = _SC("MySQL_Result");
  222. static const SQChar *_curr_row_key = _SC("_curr_row");
  223. static SQRESULT get_mysql_result_instance(HSQUIRRELVM v, SQInteger idx, MYSQL_RES **self){
  224. SQRESULT _rc_;
  225. if((_rc_ = sq_getinstanceup(v,idx,(SQUserPointer*)self,(void*)MySQL_Result_TAG)) < 0) return _rc_;
  226. if(!*self) return sq_throwerror(v, _SC("MYSQL_RES is closed"));
  227. return _rc_;
  228. }
  229. #define GET_mysql_result_INSTANCE_AT(idx) \
  230. MYSQL_RES *self=NULL; \
  231. if((_rc_ = get_mysql_result_instance(v,idx,&self)) < 0) return _rc_;
  232. #define GET_mysql_result_INSTANCE() GET_mysql_result_INSTANCE_AT(1)
  233. static SQRESULT sq_mysql_result_releasehook(SQUserPointer p, SQInteger size, void */*ep*/)
  234. {
  235. MYSQL_RES *self = ((MYSQL_RES *)p);
  236. if (self) dlmysql_free_result(self);
  237. return 0;
  238. }
  239. static SQRESULT sq_mysql_result_close(HSQUIRRELVM v){
  240. SQ_FUNC_VARS_NO_TOP(v);
  241. GET_mysql_result_INSTANCE();
  242. dlmysql_free_result(self);
  243. sq_setinstanceup(v, 1, 0); //next calls will fail with "Pgresult is closed"
  244. return 0;
  245. }
  246. static SQRESULT sq_mysql_result_col_count(HSQUIRRELVM v){
  247. SQ_FUNC_VARS_NO_TOP(v);
  248. GET_mysql_result_INSTANCE();
  249. sq_pushinteger(v, (SQInteger)dlmysql_num_fields(self));
  250. return 1;
  251. }
  252. static SQRESULT sq_mysql_result_row_count(HSQUIRRELVM v){
  253. SQ_FUNC_VARS_NO_TOP(v);
  254. GET_mysql_result_INSTANCE();
  255. sq_pushinteger(v, (SQInteger)dlmysql_num_rows(self));
  256. return 1;
  257. }
  258. static SQRESULT sq_mysql_result_col_name(HSQUIRRELVM v){
  259. SQ_FUNC_VARS_NO_TOP(v);
  260. GET_mysql_result_INSTANCE();
  261. SQ_GET_INTEGER(v, 2, col);
  262. if(col < 0 || col > dlmysql_num_fields(self)-1)
  263. return sq_throwerror(v, _SC("invalid col number (%d)"), col);
  264. MYSQL_FIELD *fields = dlmysql_fetch_fields(self);
  265. sq_pushstring(v, fields[col].name, -1);
  266. return 1;
  267. }
  268. static int get_field_idx(MYSQL_RES *self, const SQChar *name){
  269. MYSQL_FIELD *fields = dlmysql_fetch_fields(self);
  270. unsigned int count = dlmysql_num_fields(self);
  271. for(unsigned int i=0; i<count; ++i){
  272. if(scstrcmp(name, fields[i].name) == 0){
  273. return i;
  274. }
  275. }
  276. return -1;
  277. }
  278. static SQRESULT sq_mysql_result_col_index(HSQUIRRELVM v){
  279. SQ_FUNC_VARS_NO_TOP(v);
  280. GET_mysql_result_INSTANCE();
  281. SQ_GET_STRING(v, 2, name);
  282. sq_pushinteger(v, get_field_idx(self, name));
  283. return 1;
  284. }
  285. static SQRESULT sq_mysql_result_eof(HSQUIRRELVM v){
  286. SQ_FUNC_VARS_NO_TOP(v);
  287. GET_mysql_result_INSTANCE();
  288. sq_pushstring(v, _curr_row_key, -1);
  289. if(sq_get(v, 1) == SQ_OK){
  290. SQ_GET_INTEGER(v, -1, curr_row);
  291. sq_pushbool(v, curr_row < dlmysql_num_rows(self));
  292. }
  293. else sq_pushbool(v, SQTrue);
  294. return 1;
  295. }
  296. static SQRESULT sq_mysql_result_next_row(HSQUIRRELVM v){
  297. SQ_FUNC_VARS_NO_TOP(v);
  298. GET_mysql_result_INSTANCE();
  299. sq_pushstring(v, _curr_row_key, -1);
  300. sq_push(v, -1); //make a copy
  301. if(sq_get(v, 1) == SQ_OK){
  302. SQ_GET_INTEGER(v, -1, curr_row);
  303. if(++curr_row < dlmysql_num_rows(self)){
  304. sq_poptop(v);
  305. sq_pushinteger(v, curr_row);
  306. sq_set(v, 1);
  307. sq_pushbool(v, SQTrue);
  308. return 1;
  309. }
  310. }
  311. sq_pushbool(v, SQFalse);
  312. return 1;
  313. }
  314. static SQRESULT sq_mysql_result_col_value(HSQUIRRELVM v){
  315. SQ_FUNC_VARS_NO_TOP(v);
  316. GET_mysql_result_INSTANCE();
  317. SQObjectType ptype = sq_gettype(v, 2);
  318. int col = -1;
  319. if(ptype == OT_STRING){
  320. SQ_GET_STRING(v, 2, col_name);
  321. col = get_field_idx(self, col_name);
  322. }
  323. else
  324. {
  325. SQ_GET_INTEGER(v, 2, idx);
  326. col = idx;
  327. }
  328. if(col < 0) return sq_throwerror(v, _SC("invalid col index/name"));
  329. sq_pushstring(v, _curr_row_key, -1);
  330. if(sq_get(v, 1) == SQ_OK){
  331. SQ_GET_INTEGER(v, -1, curr_row);
  332. if(curr_row < dlmysql_num_rows(self)){
  333. dlmysql_data_seek(self, curr_row);
  334. const MYSQL_ROW res_row = dlmysql_fetch_row(self);
  335. unsigned long *lengths = dlmysql_fetch_lengths(self);
  336. sq_pushstring(v, (const SQChar*)res_row[col], lengths[col]);
  337. return 1;
  338. }
  339. }
  340. return SQ_ERROR;
  341. }
  342. static SQRESULT sq_mysql_result_row_as_array(HSQUIRRELVM v){
  343. SQ_FUNC_VARS(v);
  344. GET_mysql_result_INSTANCE();
  345. SQ_OPT_INTEGER(v, 2, row, -1);
  346. if(row < 0){
  347. sq_pushstring(v, _curr_row_key, -1);
  348. if(sq_get(v, 1) == SQ_OK){
  349. sq_getinteger(v, -1, &row);
  350. }
  351. }
  352. int row_count = dlmysql_num_rows(self);
  353. if(row < 0 || row >= row_count) return sq_throwerror(v, _SC("invalid row (%d)"), row);
  354. int col_count = dlmysql_num_fields(self);
  355. sq_newarray(v, col_count);
  356. dlmysql_data_seek(self, row);
  357. const MYSQL_ROW res_row = dlmysql_fetch_row(self);
  358. unsigned long *lengths = dlmysql_fetch_lengths(self);
  359. for(int i=0; i < col_count; ++i){
  360. sq_pushinteger(v, i);
  361. sq_pushstring(v, (const SQChar*)res_row[i], lengths[i]);
  362. sq_rawset(v, -3);
  363. }
  364. return 1;
  365. }
  366. static SQRESULT sq_mysql_result_row_as_table(HSQUIRRELVM v){
  367. SQ_FUNC_VARS(v);
  368. GET_mysql_result_INSTANCE();
  369. SQ_OPT_INTEGER(v, 2, row, -1);
  370. if(row < 0){
  371. sq_pushstring(v, _curr_row_key, -1);
  372. if(sq_get(v, 1) == SQ_OK){
  373. sq_getinteger(v, -1, &row);
  374. }
  375. }
  376. int row_count = dlmysql_num_rows(self);
  377. if(row < 0 || row >= row_count) return sq_throwerror(v, _SC("invalid row (%d)"), row);
  378. int col_count = dlmysql_num_fields(self);
  379. sq_newtableex(v, col_count);
  380. dlmysql_data_seek(self, row);
  381. const MYSQL_ROW res_row = dlmysql_fetch_row(self);
  382. unsigned long *lengths = dlmysql_fetch_lengths(self);
  383. MYSQL_FIELD *fields = dlmysql_fetch_fields(self);
  384. for(int i=0; i < col_count; ++i){
  385. sq_pushstring(v, fields[i].name, -1);
  386. sq_pushstring(v, (const SQChar*)res_row[i], lengths[i]);
  387. sq_rawset(v, -3);
  388. }
  389. return 1;
  390. }
  391. #define _DECL_FUNC(name,nparams,tycheck) {_SC(#name), sq_mysql_result_##name,nparams,tycheck}
  392. static SQRegFunction sq_mysql_result_methods[] =
  393. {
  394. _DECL_FUNC(close, 1, _SC("x")),
  395. _DECL_FUNC(eof, 1, _SC("x")),
  396. _DECL_FUNC(next_row, 1, _SC("x")),
  397. _DECL_FUNC(col_count, 1, _SC("x")),
  398. _DECL_FUNC(row_count, 1, _SC("x")),
  399. _DECL_FUNC(col_name, 2, _SC("xi")),
  400. _DECL_FUNC(col_index, 2, _SC("xs")),
  401. _DECL_FUNC(col_value, 2, _SC("x i|s")),
  402. _DECL_FUNC(row_as_array, -1, _SC("xi")),
  403. _DECL_FUNC(row_as_table, -1, _SC("xi")),
  404. {0,0}
  405. };
  406. #undef _DECL_FUNC
  407. struct SQ_MysqlStatement
  408. {
  409. MYSQL_STMT *stmt;
  410. size_t bind_data_buffer_size;
  411. unsigned long param_count;
  412. MYSQL_BIND *bind_params_info;
  413. void *bind_params_data;
  414. char bind_data_buffer[1];
  415. };
  416. static const SQChar *MySQL_Statement_TAG = _SC("MySQL_Statement");
  417. static SQRESULT get_mysql_statement_instance(HSQUIRRELVM v, SQInteger idx, SQ_MysqlStatement **self){
  418. SQRESULT _rc_;
  419. if((_rc_ = sq_getinstanceup(v,idx,(SQUserPointer*)self,(void*)MySQL_Statement_TAG)) < 0) return _rc_;
  420. if(!*self) return sq_throwerror(v, _SC("MySql statement is closed"));
  421. return _rc_;
  422. }
  423. #define GET_mysql_statement_INSTANCE_AT(idx) \
  424. SQ_MysqlStatement *self=NULL; \
  425. if((_rc_ = get_mysql_statement_instance(v,idx,&self)) < 0) return _rc_;
  426. #define GET_mysql_statement_INSTANCE() GET_mysql_statement_INSTANCE_AT(1)
  427. static SQRESULT sq_mysql_statement_releasehook(SQUserPointer p, SQInteger size, void */*ep*/)
  428. {
  429. SQ_MysqlStatement *self = ((SQ_MysqlStatement *)p);
  430. if (self && self->stmt)
  431. {
  432. dlmysql_stmt_close(self->stmt);
  433. sq_free(self, 0);
  434. }
  435. return 0;
  436. }
  437. static SQRESULT sq_mysql_statement_close(HSQUIRRELVM v){
  438. SQ_FUNC_VARS_NO_TOP(v);
  439. GET_mysql_statement_INSTANCE();
  440. sq_mysql_statement_releasehook(self->stmt, 0, v);
  441. sq_setinstanceup(v, 1, 0); //next calls will fail with "Pgstatement is closed"
  442. return 0;
  443. }
  444. static SQRESULT sq_mysql_statement_bind_base(HSQUIRRELVM v, SQInteger top, SQ_MysqlStatement *self){
  445. SQRESULT _rc_;
  446. if((top - 1) != self->param_count){
  447. return sq_throwerror(v, "Expect %d params but got %d !", self->param_count, top-1);
  448. }
  449. memset(self->bind_data_buffer, 0, self->bind_data_buffer_size);
  450. int offset = 0;
  451. for (int argn = 2; argn <= top; ++argn) {
  452. SQObjectType ptype = sq_gettype(v, argn);
  453. int i = argn - 2;
  454. MYSQL_BIND &bind_info = self->bind_params_info[i];
  455. SQInteger *boolean_or_int;
  456. switch(ptype) {
  457. case OT_NULL:{
  458. bind_info.buffer_type = MYSQL_TYPE_NULL;
  459. bind_info.is_null = (my_bool*)1;
  460. }
  461. break;
  462. case OT_BOOL:{
  463. SQ_GET_BOOL(v, argn, param_bool);
  464. boolean_or_int = (SQInteger *)(((char*)self->bind_params_data) + offset);
  465. offset += sizeof(SQInteger);
  466. *boolean_or_int = (SQInteger)param_bool;
  467. bind_info.buffer_type = MYSQL_TYPE_LONG;
  468. bind_info.is_null = (my_bool*)0;
  469. bind_info.buffer = (char *)boolean_or_int;
  470. bind_info.length = 0;
  471. }
  472. break;
  473. case OT_INTEGER:{
  474. SQ_GET_INTEGER(v, argn, param_int);
  475. boolean_or_int = (SQInteger *)(((char*)self->bind_params_data) + offset);
  476. offset += sizeof(SQInteger);
  477. *boolean_or_int = param_int;
  478. bind_info.buffer_type = ((sizeof(SQInteger) == sizeof(int64_t)) ? MYSQL_TYPE_LONGLONG : MYSQL_TYPE_LONG);
  479. bind_info.is_null = (my_bool*)0;
  480. bind_info.buffer = (char *)boolean_or_int;
  481. bind_info.length = 0;
  482. }
  483. break;
  484. case OT_FLOAT:{
  485. /*
  486. * num needs to be it's own
  487. * memory here
  488. */
  489. SQFloat *num = (SQFloat *)(((char*)self->bind_params_data) + offset);
  490. offset += sizeof(SQFloat);
  491. SQ_GET_FLOAT(v, argn, param_float);
  492. *num = param_float;
  493. bind_info.buffer_type = ((sizeof(SQFloat) == sizeof(float)) ? MYSQL_TYPE_FLOAT : MYSQL_TYPE_DOUBLE);
  494. bind_info.is_null = (my_bool*)0;
  495. bind_info.buffer = (char *)num;
  496. bind_info.length = 0;
  497. }
  498. break;
  499. case OT_STRING:{
  500. SQ_GET_STRING(v, argn, param_string);
  501. offset += sizeof(char*);
  502. bind_info.buffer_type = MYSQL_TYPE_STRING;
  503. bind_info.is_null = (my_bool*)0;
  504. bind_info.buffer = (char *)param_string;
  505. bind_info.length_value = param_string_size;
  506. bind_info.length = &bind_info.length_value;
  507. }
  508. break;
  509. default:{
  510. return sq_throwerror(v, "Unsupported bind parameter type %d", i);
  511. }
  512. }
  513. }
  514. return dlmysql_stmt_bind_param(self->stmt, self->bind_params_info);
  515. }
  516. static SQRESULT sq_mysql_statement_bind(HSQUIRRELVM v){
  517. SQ_FUNC_VARS(v);
  518. GET_mysql_statement_INSTANCE();
  519. SQRESULT rc = sq_mysql_statement_bind_base(v, _top_, self);
  520. if(rc == SQ_ERROR) return rc;
  521. sq_pushbool(v, rc == 0);
  522. return 1;
  523. }
  524. static SQRESULT sq_mysql_statement_execute(HSQUIRRELVM v){
  525. SQ_FUNC_VARS_NO_TOP(v);
  526. GET_mysql_statement_INSTANCE();
  527. if (dlmysql_stmt_execute(self->stmt)) {
  528. return sq_throwerror(v, _SC("error executing prepared statement. MySQL: %s"), dlmysql_stmt_error(self->stmt));
  529. }
  530. return 0;
  531. }
  532. static SQRESULT sq_mysql_statement_reset(HSQUIRRELVM v){
  533. SQ_FUNC_VARS_NO_TOP(v);
  534. GET_mysql_statement_INSTANCE();
  535. if (dlmysql_stmt_reset(self->stmt)) {
  536. return sq_throwerror(v, _SC("error reseting prepared statement. MySQL: %s"), dlmysql_stmt_error(self->stmt));
  537. }
  538. return 0;
  539. }
  540. static SQRESULT sq_mysql_statement_bind_exec(HSQUIRRELVM v){
  541. SQ_FUNC_VARS(v);
  542. GET_mysql_statement_INSTANCE();
  543. SQRESULT rc = sq_mysql_statement_bind_base(v, _top_, self);
  544. if(rc == SQ_ERROR) return rc;
  545. if(rc > 0) return sq_throwerror(v, _SC("error binding prepared statement. MySQL: %s"), dlmysql_stmt_error(self->stmt));
  546. if (dlmysql_stmt_execute(self->stmt)) {
  547. return sq_throwerror(v, _SC("error executing prepared statement. MySQL: %s"), dlmysql_stmt_error(self->stmt));
  548. }
  549. /*
  550. if (dlmysql_stmt_reset(self->stmt)) {
  551. return sq_throwerror(v, _SC("error reseting prepared statement. MySQL: %s"), dlmysql_stmt_error(self->stmt));
  552. }*/
  553. return 0;
  554. }
  555. #define _DECL_FUNC(name,nparams,tycheck) {_SC(#name), sq_mysql_statement_##name,nparams,tycheck}
  556. static SQRegFunction sq_mysql_statement_methods[] =
  557. {
  558. _DECL_FUNC(close, 1, _SC("x")),
  559. _DECL_FUNC(bind, -2, _SC("x.")),
  560. _DECL_FUNC(execute, 1, _SC("x")),
  561. _DECL_FUNC(reset, 1, _SC("x")),
  562. _DECL_FUNC(bind_exec, -2, _SC("x.")),
  563. {0,0}
  564. };
  565. #undef _DECL_FUNC
  566. static SQRESULT sq_mysql_releasehook(SQUserPointer p, SQInteger size, void */*ep*/)
  567. {
  568. MYSQL *self = ((MYSQL *)p);
  569. if (self) dlmysql_close(self);
  570. return 0;
  571. }
  572. static SQRESULT sq_mysql_constructor(HSQUIRRELVM v)
  573. {
  574. SQ_FUNC_VARS(v);
  575. SQ_GET_STRING(v, 2, host);
  576. SQ_GET_STRING(v, 3, username);
  577. SQ_GET_STRING(v, 4, password);
  578. SQ_GET_STRING(v, 5, sourcename);
  579. SQ_OPT_INTEGER(v, 6, port, 3306);
  580. SQ_OPT_STRING(v, 7, sockname, NULL);
  581. SQ_OPT_INTEGER(v, 8, optflags, 0);
  582. MYSQL *self=0;
  583. if(load_libmysqlclient())
  584. {
  585. self = dlmysql_init(NULL);
  586. if (!self) return sq_throwerror(v, _SC("error connecting: Out of memory."));
  587. if (!dlmysql_real_connect(self, host, username, password, sourcename, port, sockname, optflags))
  588. {
  589. SQRESULT res = sq_throwerror(v, _SC("error connecting to database. MySQL: %s"), dlmysql_error(self));
  590. dlmysql_close (self); /* Close conn if connect failed */
  591. return res;
  592. }
  593. }
  594. else return sq_throwerror(v, _SC("Failed to load libmysqlclient !"));
  595. sq_setinstanceup(v, 1, self);
  596. sq_setreleasehook(v,1, sq_mysql_releasehook);
  597. //save a weakref to allow statement return it's db
  598. sq_pushuserpointer(v, self);
  599. sq_weakref(v, 1);
  600. sq_setonregistrytable(v);
  601. return 1;
  602. }
  603. static SQRESULT sq_mysql_close(HSQUIRRELVM v){
  604. SQ_FUNC_VARS_NO_TOP(v);
  605. GET_mysql_INSTANCE();
  606. dlmysql_close(self);
  607. sq_setinstanceup(v, 1, 0); //next calls will fail with "database is closed"
  608. return 0;
  609. }
  610. static SQRESULT sq_mysql_exec_dml(HSQUIRRELVM v){
  611. SQ_FUNC_VARS_NO_TOP(v);
  612. GET_mysql_INSTANCE();
  613. SQ_GET_STRING(v, 2, szSQL);
  614. if (dlmysql_real_query(self, szSQL, szSQL_size))
  615. /* error executing query */
  616. return sq_throwerror(v, _SC("error executing query. MySQL: %s"), dlmysql_error(self));
  617. sq_pushinteger(v, (SQInteger)dlmysql_affected_rows(self));
  618. return 1;
  619. }
  620. static SQRESULT sq_mysql_exec_scalar(HSQUIRRELVM v){
  621. SQ_FUNC_VARS_NO_TOP(v);
  622. GET_mysql_INSTANCE();
  623. SQ_GET_STRING(v, 2, szSQL);
  624. int result = 0;
  625. if (dlmysql_real_query(self, szSQL, szSQL_size))
  626. /* error executing query */
  627. return sq_throwerror(v, _SC("error executing query. MySQL: %s"), dlmysql_error(self));
  628. MYSQL_RES *qres = dlmysql_store_result(self);
  629. bool is_ok = (dlmysql_num_rows(qres) == 1) && (dlmysql_num_fields(qres) > 0);
  630. if (is_ok) {
  631. MYSQL_ROW row = dlmysql_fetch_row(qres);
  632. unsigned long *lengths = dlmysql_fetch_lengths(qres);
  633. result = lengths[0] && row[0] ? atoi(row[0]) : 0;
  634. }
  635. dlmysql_free_result(qres);
  636. if (!is_ok) return sq_throwerror(v, _SC("invalid scalar query (%s)"), szSQL);
  637. sq_pushinteger(v, result);
  638. return 1;
  639. }
  640. static SQRESULT sq_mysql_exec_query(HSQUIRRELVM v){
  641. SQ_FUNC_VARS_NO_TOP(v);
  642. GET_mysql_INSTANCE();
  643. SQ_GET_STRING(v, 2, szSQL);
  644. if (dlmysql_real_query(self, szSQL, szSQL_size))
  645. /* error executing query */
  646. return sq_throwerror(v, _SC("error executing query. MySQL: %s"), dlmysql_error(self));
  647. MYSQL_RES *qres = dlmysql_store_result(self);
  648. sq_pushroottable(v);
  649. sq_pushstring(v, MySQL_Result_TAG, -1);
  650. if(sq_get(v, -2) == SQ_OK){
  651. if(sq_createinstance(v, -1) == SQ_OK){
  652. sq_setinstanceup(v, -1, qres);
  653. sq_setreleasehook(v, -1, sq_mysql_result_releasehook);
  654. sq_pushstring(v, _curr_row_key, -1);
  655. sq_pushinteger(v, -1);
  656. sq_set(v, -3);
  657. return 1;
  658. }
  659. }
  660. return SQ_ERROR;
  661. }
  662. static SQRESULT sq_mysql_prepare(HSQUIRRELVM v){
  663. SQ_FUNC_VARS_NO_TOP(v);
  664. GET_mysql_INSTANCE();
  665. SQ_GET_STRING(v, 2, szSQL);
  666. MYSQL_STMT *stmt = dlmysql_stmt_init(self);
  667. if (!stmt) return sq_throwerror(v, _SC(" mysql_stmt_init(), out of memory\n"));
  668. if (dlmysql_stmt_prepare(stmt, szSQL, szSQL_size)) {
  669. SQRESULT res = sq_throwerror(v, dlmysql_stmt_error(stmt));
  670. dlmysql_stmt_close(stmt);
  671. return res;
  672. }
  673. unsigned long expected_params = dlmysql_stmt_param_count(stmt);
  674. size_t bind_params_info_size = sizeof(MYSQL_BIND) * expected_params;
  675. size_t bind_params_data_size = sizeof(double) * expected_params;
  676. size_t bind_data_buffer_size = bind_params_info_size + bind_params_data_size;
  677. size_t st_mysq_stmt_size = sizeof(SQ_MysqlStatement) + bind_data_buffer_size;
  678. SQ_MysqlStatement *sq_stmt = (SQ_MysqlStatement*)sq_malloc(st_mysq_stmt_size);
  679. sq_stmt->stmt = stmt;
  680. sq_stmt->bind_data_buffer_size = bind_data_buffer_size;
  681. sq_stmt->param_count = expected_params;
  682. sq_stmt->bind_params_info = (MYSQL_BIND*)sq_stmt->bind_data_buffer;
  683. sq_stmt->bind_params_data = ((char*)sq_stmt->bind_params_info) + bind_params_info_size;
  684. sq_pushroottable(v);
  685. sq_pushstring(v, MySQL_Statement_TAG, -1);
  686. if(sq_get(v, -2) == SQ_OK){
  687. if(sq_createinstance(v, -1) == SQ_OK){
  688. sq_setinstanceup(v, -1, sq_stmt);
  689. sq_setreleasehook(v, -1, sq_mysql_statement_releasehook);
  690. return 1;
  691. }
  692. }
  693. return SQ_ERROR;
  694. }
  695. static SQRESULT sq_mysql_error_message(HSQUIRRELVM v){
  696. SQ_FUNC_VARS_NO_TOP(v);
  697. GET_mysql_INSTANCE();
  698. sq_pushstring(v, dlmysql_error(self), -1);
  699. return 1;
  700. }
  701. static SQRESULT sq_mysql_version(HSQUIRRELVM v){
  702. SQ_FUNC_VARS_NO_TOP(v);
  703. GET_mysql_INSTANCE();
  704. sq_pushinteger(v, (SQInteger)dlmysql_get_server_version(self));
  705. return 1;
  706. }
  707. static SQRESULT sq_mysql_last_insert_id(HSQUIRRELVM v){
  708. SQ_FUNC_VARS_NO_TOP(v);
  709. GET_mysql_INSTANCE();
  710. sq_pushinteger(v, (SQInteger)dlmysql_insert_id(self));
  711. return 1;
  712. }
  713. static SQRESULT sq_mysql_ping(HSQUIRRELVM v){
  714. SQ_FUNC_VARS_NO_TOP(v);
  715. GET_mysql_INSTANCE();
  716. sq_pushinteger(v, (SQInteger)dlmysql_ping(self));
  717. return 1;
  718. }
  719. static SQRESULT sq_mysql_escape_string(HSQUIRRELVM v){
  720. SQ_FUNC_VARS_NO_TOP(v);
  721. GET_mysql_INSTANCE();
  722. SQ_GET_STRING(v, 2, str);
  723. SQInteger to_size = (str_size*2+1) * sizeof(SQChar);
  724. SQChar *to = sq_getscratchpad(v, to_size);
  725. if(to) {
  726. SQInteger new_size = (SQInteger)dlmysql_real_escape_string(self, to, str, str_size);
  727. sq_pushstring(v, to, new_size);
  728. return 1;
  729. }
  730. return sq_throwerror(v, _SC("could not allocate escaped string"));
  731. }
  732. #define _DECL_FUNC(name,nparams,tycheck) {_SC(#name), sq_mysql_##name,nparams,tycheck}
  733. static SQRegFunction sq_mysql_methods[] =
  734. {
  735. _DECL_FUNC(constructor, -5, _SC("xssssisi")),
  736. _DECL_FUNC(close, 1, _SC("x")),
  737. _DECL_FUNC(ping, 1, _SC("x")),
  738. _DECL_FUNC(exec_dml, 2, _SC("xs")),
  739. _DECL_FUNC(exec_scalar, 2, _SC("xs")),
  740. _DECL_FUNC(exec_query, 2, _SC("xs")),
  741. _DECL_FUNC(prepare, 2, _SC("xs")),
  742. _DECL_FUNC(error_message, 1, _SC("x")),
  743. _DECL_FUNC(version, 1, _SC("x")),
  744. _DECL_FUNC(last_insert_id, 1, _SC("x")),
  745. _DECL_FUNC(escape_string, 2, _SC("xs")),
  746. {0,0}
  747. };
  748. #undef _DECL_FUNC
  749. #ifdef __cplusplus
  750. extern "C" {
  751. #endif
  752. SQRESULT sqext_register_MySQL(HSQUIRRELVM v)
  753. {
  754. sq_pushstring(v,MySQL_TAG,-1);
  755. sq_newclass(v,SQFalse);
  756. sq_settypetag(v,-1,(void*)MySQL_TAG);
  757. sq_insert_reg_funcs(v, sq_mysql_methods);
  758. sq_newslot(v,-3,SQTrue);
  759. sq_pushstring(v,MySQL_Statement_TAG,-1);
  760. sq_newclass(v,SQFalse);
  761. sq_settypetag(v,-1,(void*)MySQL_Statement_TAG);
  762. sq_insert_reg_funcs(v, sq_mysql_statement_methods);
  763. sq_newslot(v,-3,SQTrue);
  764. sq_pushstring(v,MySQL_Result_TAG,-1);
  765. sq_newclass(v,SQFalse);
  766. sq_settypetag(v,-1,(void*)MySQL_Result_TAG);
  767. sq_insert_reg_funcs(v, sq_mysql_result_methods);
  768. sq_pushstring(v, _curr_row_key, -1);
  769. sq_pushnull(v);
  770. sq_newslot(v, -3, SQFalse);
  771. sq_newslot(v,-3,SQTrue);
  772. return 0;
  773. }
  774. #ifdef __cplusplus
  775. }
  776. #endif
  777. #endif