PageRenderTime 30ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/cmyth/libcmyth/mysql_query.c

https://github.com/billfngn/xbmc
C | 370 lines | 262 code | 26 blank | 82 comment | 45 complexity | ce75fd0bdd5314deb6917a9e112d6720 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, 0BSD, GPL-3.0, GPL-2.0, LGPL-2.1, LGPL-3.0
  1. /*
  2. * Copyright (C) 2006-2009, Simon Hyde
  3. * http://www.mvpmc.org/
  4. *
  5. * This library is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU Lesser General Public
  7. * License as published by the Free Software Foundation; either
  8. * version 2.1 of the License, or (at your option) any later version.
  9. *
  10. * This library is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * Lesser General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Lesser General Public
  16. * License along with this library; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18. */
  19. #include <sys/types.h>
  20. #include <string.h>
  21. #include <stdio.h>
  22. #include <cmyth_local.h>
  23. #define CMYTH_ULONG_STRLEN ((sizeof(long)*3)+1)
  24. #define CMYTH_LONG_STRLEN (CMYTH_ULONG_STRLEN+1)
  25. /**
  26. * Hold in-progress query
  27. */
  28. struct cmyth_mysql_query_s
  29. {
  30. char * buf;
  31. const char * source;
  32. const char * source_pos;
  33. int buf_size, buf_used, source_len;
  34. cmyth_database_t db;
  35. };
  36. /**
  37. * Internal only! Registered as callback to de-allocate actual buffer if
  38. * the query is de-allocated
  39. * \param p pointer to the query data structure
  40. */
  41. static void
  42. query_destroy(void *p)
  43. {
  44. cmyth_mysql_query_t * query = (cmyth_mysql_query_t *)p;
  45. if(query->buf != NULL)
  46. {
  47. ref_release(query->buf);
  48. query->buf = NULL;
  49. query->buf_size = 0;
  50. }
  51. if(query->db != NULL)
  52. {
  53. ref_release(query->db);
  54. query->db = NULL;
  55. }
  56. }
  57. /**
  58. * Allocate a dynamic query string to have parameters added to it
  59. * \param db database connection object
  60. * \param query_string Query string with ? placemarks for all dynamic
  61. * parameters, this is NOT copied and must therefore
  62. * remain valid for the life of the query.
  63. */
  64. cmyth_mysql_query_t *
  65. cmyth_mysql_query_create(cmyth_database_t db, const char * query_string)
  66. {
  67. cmyth_mysql_query_t * out;
  68. out = ref_alloc(sizeof(*out));
  69. if(out != NULL)
  70. {
  71. ref_set_destroy(out,query_destroy);
  72. out->source = out->source_pos = query_string;
  73. out->source_len = strlen(out->source);
  74. out->buf_size = out->source_len *2;
  75. out->buf_used = 0;
  76. out->db = ref_hold(db);
  77. out->buf = ref_alloc(out->buf_size);
  78. if(out->buf == NULL)
  79. {
  80. ref_release(out);
  81. out = NULL;
  82. }
  83. else
  84. {
  85. out->buf[0] = '\0';
  86. }
  87. }
  88. return out;
  89. }
  90. void
  91. cmyth_mysql_query_reset(cmyth_mysql_query_t *query)
  92. {
  93. query->buf_used = 0;
  94. query->source_pos = query->source;
  95. }
  96. static int
  97. query_buffer_check_len(cmyth_mysql_query_t *query, int len)
  98. {
  99. if(len + query->buf_used >= query->buf_size)
  100. {
  101. /* Increase buffer size by len or out->source_len, whichever
  102. * is bigger
  103. */
  104. if(query->source_len > len)
  105. query->buf_size += query->source_len;
  106. else
  107. query->buf_size += len;
  108. query->buf = ref_realloc(query->buf,query->buf_size);
  109. if(query->buf == NULL)
  110. {
  111. cmyth_mysql_query_reset(query);
  112. return -1;
  113. }
  114. }
  115. return 0;
  116. }
  117. static int
  118. query_buffer_add(cmyth_mysql_query_t *query, const char *buf,int len)
  119. {
  120. int ret = query_buffer_check_len(query,len);
  121. if(ret < 0)
  122. return ret;
  123. memcpy(query->buf + query->buf_used,buf,len);
  124. query->buf_used +=len;
  125. query->buf[query->buf_used] = '\0';
  126. return len;
  127. }
  128. static inline int
  129. query_buffer_add_str(cmyth_mysql_query_t *query, const char *str)
  130. {
  131. return query_buffer_add(query,str,strlen(str));
  132. }
  133. static int
  134. query_buffer_add_escape_str(cmyth_mysql_query_t *query, const char *str)
  135. {
  136. int ret;
  137. int srclen = strlen(str);
  138. MYSQL * mysql;
  139. unsigned long destlen;
  140. /*According to the mysql C API refrence, there must be sourcelen*2 +1
  141. * characters of space in the destination buffer
  142. */
  143. ret = query_buffer_check_len(query,srclen*2 +1);
  144. if(ret < 0)
  145. return ret;
  146. mysql = cmyth_db_get_connection(query->db);
  147. if(mysql == NULL)
  148. return -1;
  149. destlen = mysql_real_escape_string(mysql, query->buf + query->buf_used,
  150. str, srclen);
  151. query->buf_used += destlen;
  152. /* MySQL claims it null terminates, but do so anyway just in case we've
  153. * done something stupid
  154. */
  155. query->buf[query->buf_used] = '\0';
  156. return destlen;
  157. }
  158. static int
  159. query_begin_next_param(cmyth_mysql_query_t *query)
  160. {
  161. int len,ret;
  162. const char * endpos = strchr(query->source_pos,(int)'?');
  163. /*No more parameter insertion points left!*/
  164. if(endpos == NULL)
  165. return -1;
  166. len = endpos - query->source_pos;
  167. ret = query_buffer_add(query,query->source_pos,len);
  168. query->source_pos = endpos + 1;
  169. return ret;
  170. }
  171. static inline int
  172. query_buffer_add_long(cmyth_mysql_query_t * query, long param)
  173. {
  174. char buf[CMYTH_LONG_STRLEN];
  175. sprintf(buf,"%ld",param);
  176. return query_buffer_add_str(query,buf);
  177. }
  178. static inline int
  179. query_buffer_add_ulong(cmyth_mysql_query_t * query, long param)
  180. {
  181. char buf[CMYTH_ULONG_STRLEN];
  182. sprintf(buf,"%lu",param);
  183. return query_buffer_add_str(query,buf);
  184. }
  185. /**
  186. * Add a long integer parameter
  187. * \param query the query object
  188. * \param param the integer to add
  189. */
  190. int
  191. cmyth_mysql_query_param_long(cmyth_mysql_query_t * query,long param)
  192. {
  193. int ret;
  194. ret = query_begin_next_param(query);
  195. if(ret < 0)
  196. return ret;
  197. return query_buffer_add_long(query,param);
  198. }
  199. /**
  200. * Add an unsigned long integer parameter
  201. * \param query the query object
  202. * \param param the integer to add
  203. */
  204. int
  205. cmyth_mysql_query_param_ulong(cmyth_mysql_query_t * query,unsigned long param)
  206. {
  207. int ret;
  208. ret = query_begin_next_param(query);
  209. if(ret < 0)
  210. return ret;
  211. return query_buffer_add_ulong(query,param);
  212. }
  213. /**
  214. * Add an integer parameter
  215. * \param query the query object
  216. * \param param the integer to add
  217. */
  218. int
  219. cmyth_mysql_query_param_int(cmyth_mysql_query_t * query,int param)
  220. {
  221. return cmyth_mysql_query_param_long(query,(long)param);
  222. }
  223. /**
  224. * Add an unsigned integer parameter
  225. * \param query the query object
  226. * \param param the integer to add
  227. */
  228. int
  229. cmyth_mysql_query_param_uint(cmyth_mysql_query_t * query,int param)
  230. {
  231. return cmyth_mysql_query_param_ulong(query,(unsigned long)param);
  232. }
  233. /**
  234. * Add, and convert a unixtime to mysql date/timestamp
  235. * \param query the query object
  236. * \param param the time to add
  237. */
  238. int
  239. cmyth_mysql_query_param_unixtime(cmyth_mysql_query_t * query, time_t param)
  240. {
  241. int ret;
  242. ret = query_begin_next_param(query);
  243. if(ret < 0)
  244. return ret;
  245. ret = query_buffer_add_str(query,"FROM_UNIXTIME(");
  246. if(ret < 0)
  247. return ret;
  248. ret = query_buffer_add_long(query,(long)param);
  249. if(ret < 0)
  250. return ret;
  251. return query_buffer_add_str(query,")");
  252. }
  253. /**
  254. * Add (including adding quotes), and escape a string parameter.
  255. * \param query the query object
  256. * \param param the string to add
  257. */
  258. int
  259. cmyth_mysql_query_param_str(cmyth_mysql_query_t * query, const char *param)
  260. {
  261. int ret;
  262. ret = query_begin_next_param(query);
  263. if(ret < 0)
  264. return ret;
  265. if(param == NULL)
  266. return query_buffer_add_str(query,"NULL");
  267. ret = query_buffer_add_str(query,"'");
  268. if(ret < 0)
  269. return ret;
  270. ret = query_buffer_add_escape_str(query,param);
  271. if(ret < 0)
  272. return ret;
  273. return query_buffer_add_str(query,"'");
  274. }
  275. /**
  276. * Get the completed query string
  277. * \return If all fields haven't been filled, or there is some other failure
  278. * this will return NULL, otherwise a string is returned. The returned
  279. * string must be released by the caller using ref_release().
  280. */
  281. char *
  282. cmyth_mysql_query_string(cmyth_mysql_query_t * query)
  283. {
  284. if(strchr(query->source_pos, (int)'?') != NULL)
  285. {
  286. return NULL;/*Still more parameters to be added*/
  287. }
  288. if(query_buffer_add_str(query,query->source_pos) < 0)
  289. return NULL;
  290. /*Point source_pos to the '\0' at the end of the string so this can
  291. * be called multiple times
  292. */
  293. query->source_pos = query->source + query->source_len;
  294. return ref_hold(query->buf);
  295. }
  296. MYSQL_RES *
  297. cmyth_mysql_query_result(cmyth_mysql_query_t * query)
  298. {
  299. MYSQL_RES * retval = NULL;
  300. int ret;
  301. char * query_str;
  302. MYSQL *mysql = cmyth_db_get_connection(query->db);
  303. if(mysql == NULL)
  304. return NULL;
  305. query_str = cmyth_mysql_query_string(query);
  306. if(query_str == NULL)
  307. return NULL;
  308. ret = mysql_query(mysql,query_str);
  309. ref_release(query_str);
  310. if(ret != 0)
  311. {
  312. cmyth_dbg(CMYTH_DBG_ERROR, "%s: mysql_query(%s) Failed: %s\n",
  313. __FUNCTION__, query_str, mysql_error(mysql));
  314. return NULL;
  315. }
  316. retval = mysql_store_result(mysql);
  317. if(retval == NULL)
  318. {
  319. cmyth_dbg(CMYTH_DBG_ERROR, "%s: mysql_use_result Failed: %s\n",
  320. __FUNCTION__, query_str, mysql_error(mysql));
  321. }
  322. return retval;
  323. }
  324. int
  325. cmyth_mysql_query(cmyth_mysql_query_t * query)
  326. {
  327. int ret;
  328. char * query_str;
  329. MYSQL *mysql = cmyth_db_get_connection(query->db);
  330. if(mysql == NULL)
  331. return -1;
  332. query_str = cmyth_mysql_query_string(query);
  333. if(query_str == NULL)
  334. return -1;
  335. ret = mysql_query(mysql,query_str);
  336. ref_release(query_str);
  337. if(ret != 0)
  338. {
  339. cmyth_dbg(CMYTH_DBG_ERROR, "%s: mysql_query(%s) Failed: %s\n",
  340. __FUNCTION__, query_str, mysql_error(mysql));
  341. return -1;
  342. }
  343. return 0;
  344. }