PageRenderTime 745ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/xbmc/lib/libcmyth/mysql_query.c

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