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.
 
 
 

542 lines
16 KiB

  1. #include <cpphibernate/driver/mariadb/impl/filter.inl>
  2. #include <cpphibernate/driver/mariadb/context/read_context.inl>
  3. #include <cpphibernate/driver/mariadb/classes/tables/table.inl>
  4. using namespace ::cpphibernate;
  5. using namespace ::cpphibernate::mariadb;
  6. struct foreign_many_tuple_t
  7. {
  8. const field_t& field;
  9. read_context_ptr_u context;
  10. std::string owner;
  11. };
  12. struct foreign_many_list_t :
  13. public std::list<foreign_many_tuple_t>
  14. { };
  15. struct data_extractor_t
  16. {
  17. const table_t& _table;
  18. const read_context& _context;
  19. const ::cppmariadb::row& _row;
  20. foreign_many_list_t& _foreign_many_list;
  21. const filter_t& _filter;
  22. mutable size_t _index;
  23. data_extractor_t(
  24. const table_t& p_table,
  25. const read_context& p_context,
  26. const ::cppmariadb::row& p_row,
  27. foreign_many_list_t& p_foreign_many_list)
  28. : _table (p_table)
  29. , _context (p_context)
  30. , _row (p_row)
  31. , _foreign_many_list(p_foreign_many_list)
  32. , _filter (p_context.filter)
  33. , _index (0)
  34. { }
  35. inline bool has_value() const
  36. { return !_row.at(_index).is_null(); }
  37. inline value_t get_value() const
  38. {
  39. value_t ret;
  40. auto f = _row.at(_index);
  41. if (!f.is_null())
  42. ret = f.get<std::string>();
  43. return ret;
  44. }
  45. inline void next_field() const
  46. { ++_index; }
  47. inline void read_field(const field_t& field, const read_context& context, bool skip = false) const
  48. {
  49. auto value = get_value();
  50. ++_index;
  51. if (!skip)
  52. field.set(context, value);
  53. }
  54. inline bool read_table(const table_t& table, const read_context& context, bool read_base, bool read_derived, bool skip = false) const
  55. {
  56. /* read the base table */
  57. if (read_base && table.base_table)
  58. {
  59. skip = read_table(*table.base_table, context, true, false, skip);
  60. }
  61. /* create a dynamic dataset depending on the derived table */
  62. else if ( read_base
  63. && context.is_dynamic
  64. && !table.derived_tables.empty())
  65. {
  66. auto value = get_value();
  67. next_field();
  68. if (static_cast<bool>(value) && !skip)
  69. {
  70. auto type = cppcore::from_string<uint>(*value);
  71. auto derived = _table.get_derived_by_table_id(type);
  72. if (!derived)
  73. throw exception(std::string("unable to find dereived table for id ") + std::to_string(type));
  74. derived->emplace(context);
  75. }
  76. else
  77. {
  78. skip = true;
  79. }
  80. }
  81. /* create a static dataset */
  82. else if (has_value() && !skip)
  83. {
  84. if (read_base)
  85. {
  86. context.emplace();
  87. }
  88. }
  89. /* no data -> skip */
  90. else
  91. {
  92. skip = true;
  93. }
  94. if (_context.filter.is_excluded(table))
  95. return skip;
  96. /* primary key */
  97. assert(table.primary_key_field);
  98. read_field(*table.primary_key_field, context, skip);
  99. /* data fields */
  100. for (auto& ptr : table.data_fields)
  101. {
  102. assert(ptr);
  103. auto& field = *ptr;
  104. if (!_context.filter.is_excluded(field))
  105. read_field(field, context, skip);
  106. }
  107. /* foreign table one */
  108. for (auto& ptr : table.foreign_table_one_fields)
  109. {
  110. assert(ptr);
  111. assert(ptr->referenced_table);
  112. auto& field = *ptr;
  113. auto& ref_table = *field.referenced_table;
  114. if ( _filter.is_excluded(field)
  115. || _filter.is_excluded(ref_table))
  116. continue;
  117. auto next_context = field.foreign_read(context, skip);
  118. assert(static_cast<bool>(next_context));
  119. read_table(ref_table, *next_context, true, true, skip);
  120. next_context->finish();
  121. }
  122. /* foreign table many */
  123. if (!skip)
  124. {
  125. for (auto& ptr : table.foreign_table_many_fields)
  126. {
  127. assert(ptr);
  128. assert(ptr->referenced_table);
  129. auto& field = *ptr;
  130. auto& ref_table = *field.referenced_table;
  131. if ( _filter.is_excluded(field)
  132. || _filter.is_excluded(ref_table))
  133. continue;
  134. _foreign_many_list.emplace_back(
  135. foreign_many_tuple_t {
  136. field,
  137. field.foreign_read(context, false),
  138. *table.primary_key_field->get(context),
  139. });
  140. }
  141. }
  142. /* derived tables */
  143. if (read_derived && context.is_dynamic)
  144. {
  145. for (auto& ptr : table.derived_tables)
  146. {
  147. assert(ptr);
  148. auto& derived_table = *ptr;
  149. read_table(derived_table, context, false, true, skip);
  150. }
  151. }
  152. return skip;
  153. }
  154. inline void operator()() const
  155. {
  156. _index = 0;
  157. read_table(_table, _context, true, true, false);
  158. if (_index != _row.size())
  159. throw exception("result was not completely read!");
  160. }
  161. };
  162. /* select_query_builder_t */
  163. struct select_query_builder_t
  164. {
  165. struct local_context
  166. {
  167. const table_t& table;
  168. std::string alias;
  169. bool add_base;
  170. bool add_derived;
  171. bool is_dynamic;
  172. };
  173. const table_t& _table;
  174. const filter_t& _filter;
  175. bool _is_dynamic;
  176. size_t alias_id { 0 };
  177. size_t index { 0 };
  178. std::ostringstream os;
  179. std::list<std::string> joins;
  180. select_query_builder_t(
  181. const table_t& p_table,
  182. const filter_t& p_filter,
  183. bool p_is_dynamic)
  184. : _table (p_table)
  185. , _filter (p_filter)
  186. , _is_dynamic(p_is_dynamic)
  187. { }
  188. inline std::string make_alias()
  189. { return std::string("T") + std::to_string(alias_id++); }
  190. inline void add_field(const field_t& field, const std::string& alias)
  191. {
  192. if (index++) os << ", ";
  193. os << field.convert_from_open
  194. << "`"
  195. << alias
  196. << "`.`"
  197. << field.name
  198. << "`"
  199. << field.convert_from_close;
  200. }
  201. inline bool add_table(const local_context& ctx)
  202. {
  203. bool ret = false;
  204. auto has_alias = !ctx.alias.empty();
  205. auto real_alias = has_alias
  206. ? ctx.alias
  207. : ctx.table.name;
  208. if (ctx.table.base_table && ctx.add_base)
  209. {
  210. assert(ctx.table.base_table->primary_key_field);
  211. auto& base_table = *ctx.table.base_table;
  212. auto& base_key = *base_table.primary_key_field;
  213. auto base_alias = has_alias ? make_alias() : std::string();
  214. auto real_base_alias = has_alias ? base_alias : base_table.name;
  215. std::ostringstream ss;
  216. ss << " JOIN `"
  217. << base_table.name;
  218. if (has_alias)
  219. {
  220. ss << "` AS `"
  221. << base_alias;
  222. }
  223. ss << "` ON `"
  224. << real_alias
  225. << "`.`"
  226. << base_key.name
  227. << "`=`"
  228. << real_base_alias
  229. << "`.`"
  230. << base_key.name
  231. << "`";
  232. auto it = joins.insert(joins.end(), ss.str());
  233. if (add_table({
  234. base_table,
  235. base_alias,
  236. true,
  237. false,
  238. ctx.is_dynamic
  239. }))
  240. {
  241. ret = true;
  242. }
  243. else
  244. {
  245. joins.erase(it);
  246. }
  247. }
  248. /* __type */
  249. if ( ctx.is_dynamic
  250. && !ctx.table.base_table
  251. && !ctx.table.derived_tables.empty())
  252. {
  253. if (index++) os << ", ";
  254. os << "`"
  255. << ctx.table.name
  256. << "`.`__type` AS `__type`";
  257. ret = true;
  258. }
  259. if (_filter.is_excluded(ctx.table))
  260. return ret;
  261. /* primary key */
  262. assert(ctx.table.primary_key_field);
  263. add_field(*ctx.table.primary_key_field, real_alias);
  264. ret = true;
  265. /* data fields */
  266. for (auto& ptr : ctx.table.data_fields)
  267. {
  268. assert(ptr);
  269. auto& field = *ptr;
  270. if (!_filter.is_excluded(field))
  271. {
  272. add_field(field, real_alias);
  273. }
  274. }
  275. /* foreign table one */
  276. for (auto& ptr : ctx.table.foreign_table_one_fields)
  277. {
  278. assert(ptr);
  279. assert(ptr->table.primary_key_field);
  280. assert(ptr->referenced_table);
  281. assert(ptr->referenced_table->primary_key_field);
  282. auto& field = *ptr;
  283. auto& table = field.table;
  284. auto& own_key = *table.primary_key_field;
  285. auto& ref_table = *field.referenced_table;
  286. auto& ref_key = *ref_table.primary_key_field;
  287. if ( _filter.is_excluded(field)
  288. || _filter.is_excluded(ref_table))
  289. continue;
  290. auto new_alias = make_alias();
  291. std::ostringstream ss;
  292. if (ref_table.is_used_in_container)
  293. {
  294. ss << " LEFT JOIN `"
  295. << ref_table.name
  296. << "` AS `"
  297. << new_alias
  298. << "` ON `"
  299. << real_alias
  300. << "`.`"
  301. << own_key.name
  302. << "`=`"
  303. << new_alias
  304. << "`.`"
  305. << table.name
  306. << "_id_"
  307. << field.name
  308. << "`";
  309. }
  310. else
  311. {
  312. ss << " LEFT JOIN `"
  313. << ref_table.name
  314. << "` AS `"
  315. << new_alias
  316. << "` ON `"
  317. << real_alias
  318. << "`.`"
  319. << ref_key.table.name
  320. << "_id_"
  321. << field.name
  322. << "`=`"
  323. << new_alias
  324. << "`.`"
  325. << ref_key.name
  326. << "`";
  327. }
  328. auto it = joins.insert(joins.end(), ss.str());
  329. if (!add_table({
  330. ref_table,
  331. new_alias,
  332. true,
  333. true,
  334. field.value_is_pointer
  335. }))
  336. {
  337. joins.erase(it);
  338. }
  339. }
  340. /* derived tables */
  341. if (ctx.add_derived && ctx.is_dynamic)
  342. {
  343. for (auto& ptr : ctx.table.derived_tables)
  344. {
  345. assert(ptr);
  346. assert(ptr->primary_key_field);
  347. auto& derived_table = *ptr;
  348. auto& primary_key = *ctx.table.primary_key_field;
  349. auto derived_alias = has_alias ? make_alias() : std::string();
  350. auto real_derived_alias = has_alias ? derived_alias : derived_table.name;
  351. std::ostringstream ss;
  352. ss << " LEFT JOIN `"
  353. << derived_table.name;
  354. if (has_alias)
  355. {
  356. ss << "` AS `"
  357. << derived_alias;
  358. }
  359. ss << "` ON `"
  360. << real_alias
  361. << "`.`"
  362. << primary_key.name
  363. << "`=`"
  364. << real_derived_alias
  365. << "`.`"
  366. << primary_key.name
  367. << "`";
  368. auto it = joins.insert(joins.end(), ss.str());
  369. if (!add_table({
  370. derived_table,
  371. derived_alias,
  372. false,
  373. true,
  374. ctx.is_dynamic,
  375. }))
  376. {
  377. joins.erase(it);
  378. }
  379. }
  380. }
  381. return ret;
  382. }
  383. inline std::string operator()()
  384. {
  385. os << "SELECT ";
  386. add_table({
  387. _table,
  388. "",
  389. true,
  390. true,
  391. _is_dynamic,
  392. });
  393. os << " FROM `"
  394. << _table.name
  395. << "`";
  396. for (auto& join : joins)
  397. os << join;
  398. os << " ?where! ?order! ?limit!";
  399. return os.str();
  400. }
  401. };
  402. void table_t::read(const read_context& context) const
  403. {
  404. auto& statement = get_statement_select(context.filter, context.is_dynamic);
  405. auto& connection = context.connection;
  406. statement.set(0, context.where);
  407. statement.set(1, context.order_by);
  408. statement.set(2, context.limit);
  409. cpphibernate_log_debug("execute SELECT query: " << std::endl << statement.query(connection) << std::endl);
  410. auto result = connection.execute_used(statement);
  411. if (!result)
  412. throw exception("Unable to fetching data from database!");
  413. ::cppmariadb::row * row;
  414. foreign_many_list_t foreign_many_list;
  415. while ((row = result->next()))
  416. {
  417. data_extractor_t(*this, context, *row, foreign_many_list)();
  418. }
  419. context.finish();
  420. for (auto& tuple : foreign_many_list)
  421. {
  422. auto& field = tuple.field;
  423. auto& next_context = *tuple.context;
  424. assert(field.referenced_table);
  425. assert(field.referenced_table->primary_key_field);
  426. auto& ref_table = *field.referenced_table;
  427. auto& ref_field = *ref_table.primary_key_field;
  428. {
  429. std::ostringstream ss;
  430. ss << "WHERE (`"
  431. << ref_table.name
  432. << "`.`"
  433. << field.table.name
  434. << "_id_"
  435. << field.name
  436. << "`="
  437. << ref_field.convert_to_open
  438. << "'"
  439. << context.connection.escape(tuple.owner)
  440. << "'"
  441. << ref_field.convert_to_close
  442. << ")";
  443. next_context.where = ss.str();
  444. }
  445. {
  446. std::ostringstream ss;
  447. ss << "ORDER BY `"
  448. << ref_table.name
  449. << "`.`"
  450. << field.table.name
  451. << "_index_"
  452. << field.name
  453. << "` ASC";
  454. next_context.order_by = ss.str();
  455. }
  456. ref_table.read(next_context);
  457. }
  458. }
  459. void table_t::emplace(const read_context& context) const
  460. { throw exception(std::string("'") + name + "' does not implement the emplace() method!"); }
  461. ::cppmariadb::statement& table_t::get_statement_select(const filter_t& filter, bool dynamic) const
  462. {
  463. auto& map = dynamic
  464. ? _statement_select_dynamic
  465. : _statement_select_static;
  466. auto key = std::make_tuple(filter.cache_id, static_cast<const field_t*>(nullptr));
  467. auto it = map.find(key);
  468. if (it == map.end())
  469. {
  470. auto query = select_query_builder_t(*this, filter, dynamic)();
  471. it = map.emplace(key, ::cppmariadb::statement(query)).first;
  472. }
  473. return it->second;
  474. }