PageRenderTime 48ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/cmyth/libcmyth/mysql_query.c

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