You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

260 lines
8.1 KiB

  1. #include <string>
  2. #include <sstream>
  3. #include <cpputils/misc/enum.h>
  4. #include <cpputils/misc/string.h>
  5. #include <cpputils/misc/indent.h>
  6. #include <cpphibernate/misc.h>
  7. #include <cpphibernate/driver/mariadb/schema/schema.h>
  8. using namespace ::utl;
  9. using namespace ::cpphibernate::driver::mariadb_impl;
  10. void schema_t::update()
  11. {
  12. // clear everything
  13. for (auto& kvp : tables)
  14. {
  15. assert(static_cast<bool>(kvp.second));
  16. auto& table = *kvp.second;
  17. table.primary_key_field = nullptr;
  18. table.derived_tables.clear();
  19. table.foreign_key_fields.clear();
  20. table.foreign_table_fields.clear();
  21. table.foreign_table_one_fields.clear();
  22. table.foreign_table_many_fields.clear();
  23. table.data_fields.clear();
  24. table.is_used_in_container = false;
  25. for (auto& ptr : table.fields)
  26. {
  27. assert(ptr);
  28. auto& field = *ptr;
  29. field.update();
  30. }
  31. }
  32. // update references
  33. for (auto& kvp : tables)
  34. {
  35. assert(static_cast<bool>(kvp.second));
  36. auto& table = *kvp.second;
  37. // base table
  38. auto it = tables.find(table.base_dataset_id);
  39. table.base_table = (it != tables.end()
  40. ? it->second.get()
  41. : nullptr);
  42. // derived tables
  43. for (auto& id : table.derived_dataset_ids)
  44. {
  45. it = tables.find(id);
  46. if (it == tables.end())
  47. throw misc::hibernate_exception(std::string("unable to find derived table for dataset id ") + std::to_string(id));
  48. table.derived_tables.emplace_back(it->second.get());
  49. }
  50. // update fields
  51. for (auto& ptr : table.fields)
  52. {
  53. assert(ptr);
  54. auto& field = *ptr;
  55. // table
  56. if (table.dataset_id != field.dataset_id)
  57. throw misc::hibernate_exception(std::string("dataset id of field '") + field.table_name + '.' + field.field_name + "' does not match!");
  58. field.table = &table;
  59. // referenced table
  60. it = tables.find(field.real_value_id);
  61. auto referenced_table = (it != tables.end()
  62. ? it->second.get()
  63. : nullptr);
  64. field.referenced_table = referenced_table;
  65. // is primary key field
  66. if (field.attributes.count(attribute_t::primary_key))
  67. {
  68. if (static_cast<bool>(table.primary_key_field))
  69. throw misc::hibernate_exception(std::string("Table '") + table.table_name + "' can not have more then one primary key!");
  70. table.primary_key_field = &field;
  71. }
  72. // is foreign table field
  73. else if (static_cast<bool>(referenced_table))
  74. {
  75. table.foreign_table_fields.emplace_back(&field);
  76. if (field.value_is_container)
  77. {
  78. referenced_table->is_used_in_container = true;
  79. }
  80. }
  81. // is data field
  82. else
  83. {
  84. table.data_fields.emplace_back(&field);
  85. }
  86. }
  87. if (!static_cast<bool>(table.primary_key_field))
  88. throw misc::hibernate_exception(std::string("Table '") + table.table_name + "' does not have a primary key!");
  89. }
  90. // update foreign fields (one, many, key)
  91. for (auto& kvp : tables)
  92. {
  93. assert(static_cast<bool>(kvp.second));
  94. auto& table = *kvp.second;
  95. for (auto& ptr : table.foreign_table_fields)
  96. {
  97. assert(ptr);
  98. assert(ptr->referenced_table);
  99. auto& field = *ptr;
  100. auto& referenced_table = *field.referenced_table;
  101. if (field.value_is_container)
  102. {
  103. table.foreign_table_many_fields.emplace_back(&field);
  104. referenced_table.foreign_key_fields.push_back(&field);
  105. }
  106. else
  107. {
  108. table.foreign_table_one_fields.emplace_back(&field);
  109. if (referenced_table.is_used_in_container)
  110. {
  111. referenced_table.foreign_key_fields.push_back(&field);
  112. }
  113. }
  114. }
  115. }
  116. }
  117. void schema_t::print(std::ostream& os) const
  118. {
  119. os << indent << '{'
  120. << incindent
  121. << indent << "\"schema_name\": \"" << schema_name << "\","
  122. << indent << "\"tables\": " << misc::print_container(tables, true, [](auto& s, auto& kvp) {
  123. kvp.second->print(s);
  124. })
  125. << decindent
  126. << indent << '}';
  127. }
  128. const table_t& schema_t::table(size_t dataset_id) const
  129. {
  130. auto it = tables.find(dataset_id);
  131. if (it == tables.end())
  132. throw misc::hibernate_exception(std::string("unable to find table for dataset with id ") + std::to_string(dataset_id));
  133. assert(static_cast<bool>(it->second));
  134. return *it->second;
  135. }
  136. const field_t& schema_t::field(size_t field_id) const
  137. {
  138. for (auto& kvp : tables)
  139. {
  140. assert(kvp.second);
  141. auto& table = *kvp.second;
  142. for (auto& ptr : table.fields)
  143. {
  144. assert(ptr);
  145. auto& field = *ptr;
  146. if (field.id == field_id)
  147. return field;
  148. }
  149. }
  150. throw misc::hibernate_exception(std::string("unable to find field with id ") + std::to_string(field_id));
  151. }
  152. #define exec_query() \
  153. do { \
  154. cpphibernate_debug_log("execute init query: " << ss.str()); \
  155. connection.execute(ss.str()); \
  156. ss.str(std::string()); \
  157. ss.clear(); \
  158. } while(0)
  159. void schema_t::init(const init_context& context) const
  160. {
  161. std::ostringstream ss;
  162. auto& connection = context.connection;
  163. if (context.recreate)
  164. {
  165. ss << "DROP DATABASE IF EXISTS `"
  166. << schema_name
  167. << "`";
  168. exec_query();
  169. }
  170. /* create schema */
  171. ss << "CREATE SCHEMA IF NOT EXISTS `"
  172. << schema_name
  173. << "` DEFAULT CHARACTER SET utf8";
  174. exec_query();
  175. /* use schema */
  176. ss << "USE `"
  177. << schema_name
  178. << "`";
  179. exec_query();
  180. /* UuidToBin */
  181. ss << "CREATE FUNCTION IF NOT EXISTS UuidToBin(_uuid CHAR(36))\n"
  182. " RETURNS BINARY(16)\n"
  183. " LANGUAGE SQL\n"
  184. " DETERMINISTIC\n"
  185. " CONTAINS SQL\n"
  186. " SQL SECURITY INVOKER\n"
  187. "RETURN\n"
  188. " UNHEX(CONCAT(\n"
  189. " SUBSTR(_uuid, 25, 12),\n" // node id
  190. " SUBSTR(_uuid, 20, 4),\n" // clock sequence
  191. " SUBSTR(_uuid, 15, 4),\n" // time high and version
  192. " SUBSTR(_uuid, 10, 4),\n" // time mid
  193. " SUBSTR(_uuid, 1, 8)\n" // time low
  194. " )\n"
  195. ")";
  196. exec_query();
  197. /* BinToUuid */
  198. ss << "CREATE FUNCTION IF NOT EXISTS BinToUuid(_bin BINARY(16))\n"
  199. " RETURNS CHAR(36)\n"
  200. " LANGUAGE SQL\n"
  201. " DETERMINISTIC\n"
  202. " CONTAINS SQL\n"
  203. " SQL SECURITY INVOKER\n"
  204. "RETURN\n"
  205. " IF(\n"
  206. " _bin IS NULL,\n"
  207. " NULL,\n"
  208. " LCASE(CONCAT_WS('-',\n"
  209. " HEX(SUBSTR(_bin, 13, 4)),\n" // time low
  210. " HEX(SUBSTR(_bin, 11, 2)),\n" // time mid
  211. " HEX(SUBSTR(_bin, 9, 2)),\n" // time high and version
  212. " HEX(SUBSTR(_bin, 7, 2)),\n" // clock sequence
  213. " HEX(SUBSTR(_bin, 1, 6))\n" // node id
  214. " )\n"
  215. " )\n"
  216. ")";
  217. exec_query();
  218. /* initialize tables */
  219. for (auto& kvp : tables)
  220. {
  221. assert(kvp.second);
  222. kvp.second->init_stage1(context);
  223. }
  224. for (auto& kvp : tables)
  225. {
  226. assert(kvp.second);
  227. kvp.second->init_stage2(context);
  228. }
  229. }
  230. #undef exec_query