summaryrefslogtreecommitdiff
path: root/lib/dbStmt.h
blob: 3e98b34940e2cfd6bca1fa91180fd17cf18d2e79 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#ifndef MYGRATE_DBSTMT_H
#define MYGRATE_DBSTMT_H

#include <compileTimeFormatter.h>
#include <dbConn.h>
#include <dbRecordSet.h>
#include <memory>
#include <string_view>
#include <type_traits>

namespace MyGrate {
	class DbConn;
	enum class ParamMode { None, DollarNum, QMark };

	template<AdHoc::support::basic_fixed_string S, ParamMode pm = ParamMode::None> class DbStmt {
	public:
		// This don't account for common table expressions, hopefully won't need those :)
		static constexpr auto isSelect {S.v().starts_with("SELECT") || S.v().starts_with("SHOW")
				|| S.v().find("RETURNING") != std::string_view::npos};

		// These don't account for string literals, which we'd prefer to avoid anyway :)
		static constexpr auto paramCount {[]() -> std::size_t {
			switch (pm) {
				case ParamMode::None:
					return 0LU;
				case ParamMode::DollarNum: {
					const auto pn = [](const char * c, const char * const e) {
						std::size_t n {0};
						while (++c != e && *c >= '0' && *c <= '9') {
							n = (n * 10) + (*c - '0');
						}
						return n;
					};
					return pn(std::max_element(S.v().begin(), S.v().end(),
									  [pn, e = S.v().end()](const char & a, const char & b) {
										  return (a == '$' ? pn(&a, e) : 0) < (b == '$' ? pn(&b, e) : 0);
									  }),
							S.v().end());
				}
				case ParamMode::QMark:
					return std::count_if(S.v().begin(), S.v().end(), [](char c) {
						return c == '?';
					});
			}
		}()};

		using Return = std::conditional_t<isSelect, RecordSetPtr, std::size_t>;

		template<typename... P>
		static Return
		execute(DbConn * c, P &&... p)
		{
			static_assert(sizeof...(P) == paramCount);
			auto stmt {c->prepare(S, sizeof...(P))};
			stmt->execute({std::forward<P...>(p)...});
			if constexpr (isSelect) {
				return stmt->recordSet();
			}
			else {
				return stmt->rows();
			}
		}
	};
}

#endif