PageRenderTime 46ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/src/query.c

https://bitbucket.org/drjokepu/libpx
C | 302 lines | 256 code | 39 blank | 7 comment | 52 complexity | 4b65a19d59a16a8980d0dd500ee02d8a MD5 | raw file
  1. //
  2. // query.c
  3. // libpx
  4. //
  5. // Created by Tamas Czinege on 08/05/2012.
  6. // Copyright (c) 2012 Tamas Czinege. All rights reserved.
  7. //
  8. #include <stdbool.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include "query.h"
  12. #include "connection.h"
  13. #include "error.h"
  14. #include "message.h"
  15. #include "parameter.h"
  16. #include "response.h"
  17. #include "result.h"
  18. #include "utility.h"
  19. static bool px_query_can_use_simple_query(const px_query *restrict query) __attribute__((pure));
  20. static px_result_list *px_query_execute_sync_simple(const px_query *restrict query);
  21. static px_result_list *px_query_execute_sync_extended(const px_query *restrict query);
  22. static bool px_query_parse(const px_query *restrict query);
  23. static bool px_query_bind(const px_query *restrict query);
  24. static bool px_query_describe_portal(const px_query *restrict query);
  25. static bool px_query_execute_portal(const px_query *restrict query);
  26. static bool px_query_close_portal(const px_query *restrict query);
  27. static bool px_query_close_statement(const px_query *restrict query);
  28. px_query *px_query_new(const char *restrict command_text, px_connection *restrict connection)
  29. {
  30. px_query *query = calloc(1, sizeof(px_query));
  31. query->command_text = px_copy_string(command_text);
  32. query->connection = connection;
  33. return query;
  34. }
  35. void px_query_delete(px_query *query)
  36. {
  37. if (query == NULL) return;
  38. if (query->command_text != NULL) free(query->command_text);
  39. if (query->parameters.values != NULL)
  40. {
  41. for (unsigned int i = 0; i < query->parameters.count; i++)
  42. {
  43. px_parameter_delete_members(query->parameters.values + i);
  44. }
  45. free(query->parameters.values);
  46. }
  47. free(query);
  48. }
  49. void px_query_add_parameter(px_query *restrict query, const px_parameter *restrict parameter)
  50. {
  51. if (query->parameters.values == NULL || query->parameters.capacity == 0)
  52. {
  53. query->parameters.capacity = 4;
  54. query->parameters.values = calloc(query->parameters.capacity, sizeof(px_parameter));
  55. }
  56. else if (query->parameters.capacity >= query->parameters.count + 1)
  57. {
  58. query->parameters.capacity *= 2;
  59. query->parameters.values = realloc(query->parameters.values, query->parameters.capacity * sizeof(px_parameter));
  60. }
  61. px_parameter *new_parameter = query->parameters.values + (query->parameters.count++);
  62. px_parameter_clear(new_parameter);
  63. px_parameter_copy_to(new_parameter, parameter);
  64. }
  65. static bool px_query_can_use_simple_query(const px_query *restrict query)
  66. {
  67. return query->parameters.count == 0;
  68. }
  69. px_result_list *px_query_execute(const px_query *restrict query)
  70. {
  71. if (px_query_can_use_simple_query(query))
  72. {
  73. return px_query_execute_sync_simple(query);
  74. }
  75. else
  76. {
  77. return px_query_execute_sync_extended(query);
  78. }
  79. }
  80. static px_result_list *px_query_execute_sync_simple(const px_query *restrict query)
  81. {
  82. px_message *query_message = px_message_new("QTs", query->command_text);
  83. px_message_send(query_message, query->connection->socket_number);
  84. px_message_delete(query_message);
  85. px_result_list *result_list = calloc(1, sizeof(px_result_list));
  86. result_list->capacity = 1;
  87. result_list->results = calloc(result_list->capacity, sizeof(px_result*));
  88. px_result *result = px_result_new();
  89. bool ready_for_query = false;
  90. while (!ready_for_query)
  91. {
  92. px_response *response = px_response_read(query->connection);
  93. if (response == NULL) return NULL;
  94. switch (response->message_type)
  95. {
  96. case px_message_type_ready_for_query:
  97. ready_for_query = true;
  98. break;
  99. case px_message_type_row_description:
  100. if (result == NULL) break;
  101. px_result_add_headers(result,
  102. response->response_data.row_description.column_count,
  103. response->response_data.row_description.columns);
  104. break;
  105. case px_message_type_data_row:
  106. if (result == NULL) break;
  107. px_result_add_data_row(result,
  108. response->response_data.data_row.cell_count,
  109. response->response_data.data_row.cells);
  110. break;
  111. case px_message_type_error:
  112. if (result == NULL) break;
  113. px_result_delete(result);
  114. result = NULL;
  115. break;
  116. case px_message_type_command_complete:
  117. if (result == NULL) break;
  118. px_result_parse_command_tag(result, response->response_data.command_complete.command_tag);
  119. if (result_list->capacity >= result_list->count + 1)
  120. {
  121. result_list->capacity *= 2;
  122. result_list->results = realloc(result_list->results, result_list->capacity + sizeof(px_result*));
  123. }
  124. result_list->results[result_list->count++] = result;
  125. result = px_result_new();
  126. break;
  127. default:
  128. fprintf(stderr, "unhandled message type: %c %i\n", (char)response->message_class, response->message_type);
  129. break;
  130. }
  131. px_response_delete(response);
  132. }
  133. if (result != NULL) px_result_delete(result);
  134. return result_list;
  135. }
  136. static px_result_list *px_query_execute_sync_extended(const px_query *restrict query)
  137. {
  138. if (!px_query_parse(query)) return NULL;
  139. if (!px_query_bind(query)) return NULL;
  140. if (!px_query_describe_portal(query)) return NULL;
  141. if (!px_query_execute_portal(query)) return NULL;
  142. if (!px_query_close_portal(query)) return NULL;
  143. if (!px_query_close_statement(query)) return NULL;
  144. if (!px_connection_sync(query->connection, false)) return NULL;
  145. px_result *result = px_result_new();
  146. bool ready_for_query = false;
  147. while (!ready_for_query || px_connection_has_incoming_data(query->connection))
  148. {
  149. px_response *response = px_response_read(query->connection);
  150. if (response == NULL) return NULL;
  151. switch (response->message_type)
  152. {
  153. case px_message_type_ready_for_query:
  154. ready_for_query = true;
  155. break;
  156. case px_message_type_row_description:
  157. if (result == NULL) break;
  158. px_result_add_headers(result,
  159. response->response_data.row_description.column_count,
  160. response->response_data.row_description.columns);
  161. break;
  162. case px_message_type_data_row:
  163. if (result == NULL) break;
  164. px_result_add_data_row(result,
  165. response->response_data.data_row.cell_count,
  166. response->response_data.data_row.cells);
  167. break;
  168. case px_message_type_error:
  169. if (result == NULL) break;
  170. px_result_delete(result);
  171. result = NULL;
  172. break;
  173. case px_message_type_command_complete:
  174. px_result_parse_command_tag(result, response->response_data.command_complete.command_tag);
  175. break;
  176. case px_message_type_parse_complete:
  177. case px_message_type_bind_complete:
  178. case px_message_type_close_complete:
  179. break;
  180. default:
  181. fprintf(stderr, "unhandled message type: %c %i\n", (char)response->message_class, response->message_type);
  182. break;
  183. }
  184. }
  185. if (result == NULL)
  186. {
  187. return NULL;
  188. }
  189. else
  190. {
  191. px_result_list *result_list = calloc(1, sizeof(px_result_list));
  192. result_list->capacity = 1;
  193. result_list->count = 1;
  194. result_list->results = calloc(1, sizeof(px_result*));
  195. result_list->results[0] = result;
  196. return result_list;
  197. }
  198. }
  199. static bool px_query_parse(const px_query *restrict query)
  200. {
  201. void **parameters = malloc((query->parameters.count + 4) * sizeof(void*));
  202. parameters[0] = "";
  203. parameters[1] = query->command_text;
  204. parameters[2] = (void*)query->parameters.count;
  205. parameters[3] = (void*)query->parameters.count;
  206. for (unsigned int i = 0; i < query->parameters.count; i++)
  207. {
  208. parameters[i + 4] = (void*)(query->parameters.values[i].type);
  209. }
  210. px_message *message = px_message_new_with_array("PTssw(i)", parameters);
  211. free(parameters);
  212. px_message_send(message, query->connection->socket_number);
  213. px_message_delete(message);
  214. return true;
  215. }
  216. static bool px_query_bind(const px_query *restrict query)
  217. {
  218. void **parameters = malloc(((query->parameters.count * 2) + 5 + 1) * sizeof(void*));
  219. parameters[0] = "";
  220. parameters[1] = "";
  221. parameters[2] = 0;
  222. parameters[3] = (void*)query->parameters.count;
  223. parameters[4] = (void*)query->parameters.count;
  224. for (unsigned int i = 0; i < query->parameters.count; i++)
  225. {
  226. parameters[(i * 2 + 0) + 5] = (void*)(query->parameters.values[i].length);
  227. parameters[(i * 2 + 1) + 5] = (void*)(query->parameters.values[i].value);
  228. }
  229. parameters[(query->parameters.count * 2) + 5] = (void*)0;
  230. px_message *message = px_message_new_with_array("BTssww(iS)w", parameters);
  231. free(parameters);
  232. px_message_send(message, query->connection->socket_number);
  233. px_message_delete(message);
  234. return true;
  235. }
  236. static bool px_query_describe_portal(const px_query *restrict query)
  237. {
  238. px_message *message = px_message_new("DTcs", 'P', "");
  239. const bool success = px_message_send(message, query->connection->socket_number);
  240. px_message_delete(message);
  241. return success;
  242. }
  243. static bool px_query_execute_portal(const px_query *restrict query)
  244. {
  245. px_message *message = px_message_new("ETsi", "", 0);
  246. const bool success = px_message_send(message, query->connection->socket_number);
  247. px_message_delete(message);
  248. return success;
  249. }
  250. static bool px_query_close_portal(const px_query *restrict query)
  251. {
  252. px_message *message = px_message_new("CTcs", 'P', "");
  253. const bool success = px_message_send(message, query->connection->socket_number);
  254. px_message_delete(message);
  255. return success;
  256. }
  257. static bool px_query_close_statement(const px_query *restrict query)
  258. {
  259. px_message *message = px_message_new("CTcs", 'S', "");
  260. const bool success = px_message_send(message, query->connection->socket_number);
  261. px_message_delete(message);
  262. return success;
  263. }