Line data Source code
1 : /**
2 : Copyright (c) 2018-2022 Roman Katuntsev <sbkarr@stappler.org>
3 : Copyright (c) 2023-2024 Stappler LLC <admin@stappler.dev>
4 :
5 : Permission is hereby granted, free of charge, to any person obtaining a copy
6 : of this software and associated documentation files (the "Software"), to deal
7 : in the Software without restriction, including without limitation the rights
8 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 : copies of the Software, and to permit persons to whom the Software is
10 : furnished to do so, subject to the following conditions:
11 :
12 : The above copyright notice and this permission notice shall be included in
13 : all copies or substantial portions of the Software.
14 :
15 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 : THE SOFTWARE.
22 : **/
23 :
24 : #include "SPSqlHandle.h"
25 : #include "SPSqlDriver.h"
26 : #include "SPDbFile.h"
27 : #include "SPDbScheme.h"
28 : #include "SPDbUser.h"
29 :
30 : namespace STAPPLER_VERSIONIZED stappler::db::sql {
31 :
32 375 : StringView SqlHandle::getKeyValueSchemeName() {
33 375 : return "__sessions";
34 : }
35 :
36 1550 : String SqlHandle::getNameForDelta(const Scheme &scheme) {
37 3100 : return toString("__delta_", scheme.getName());
38 : }
39 :
40 250 : Value SqlHandle::get(const stappler::CoderSource &key) {
41 250 : Value ret;
42 250 : makeQuery([&, this] (SqlQuery &query) {
43 250 : query.select("data").from(getKeyValueSchemeName()).where("name", Comparation::Equal, key).finalize();
44 500 : selectQuery(query, [&] (Result &res) {
45 250 : if (!res.empty()) {
46 500 : ret = data::read<Interface, BytesView>(res.current().toBytes(0));
47 : }
48 250 : return true;
49 : });
50 250 : }, nullptr);
51 250 : return ret;
52 0 : }
53 :
54 5699 : SqlHandle::SqlHandle(const Driver *driver) : _driver(driver) { }
55 :
56 125 : bool SqlHandle::set(const stappler::CoderSource &key, const Value &data, stappler::TimeInterval maxage) {
57 125 : bool ret = false;
58 125 : makeQuery([&, this] (SqlQuery &query) {
59 125 : query.insert(getKeyValueSchemeName()).fields("name", "mtime", "maxage", "data")
60 125 : .values(key, stappler::Time::now().toSeconds(), maxage.toSeconds(), data::write<Interface>(data, stappler::data::EncodeFormat::Cbor))
61 250 : .onConflict("name").doUpdate().excluded("mtime").excluded("maxage").excluded("data")
62 125 : .finalize();
63 125 : ret = (performQuery(query) != stappler::maxOf<size_t>());
64 125 : }, nullptr);
65 125 : return ret;
66 : }
67 :
68 100 : bool SqlHandle::clear(const stappler::CoderSource &key) {
69 100 : bool ret = false;
70 100 : makeQuery([&, this] (SqlQuery &query) {
71 100 : query.remove("__sessions").where("name", Comparation::Equal, key).finalize();
72 100 : ret = (performQuery(query) == 1); // one row should be affected
73 100 : }, nullptr);
74 100 : return ret;
75 : }
76 :
77 575 : db::User * SqlHandle::authorizeUser(const db::Auth &auth, const StringView &iname, const StringView &password) {
78 575 : auto namePair = auth.getNameField(iname);
79 575 : auto paswodField = auth.getPasswordField();
80 :
81 575 : if (!namePair.first || !paswodField) {
82 0 : _driver->getApplicationInterface()->error("Auth", "Invalid scheme: fields 'name', 'email' and 'password' is not defined");
83 0 : return nullptr;
84 : }
85 :
86 575 : auto minTime = stappler::Time::now() - config::AUTH_MAX_TIME;
87 :
88 575 : bool transactionStarted = false;
89 575 : if (transactionStatus == TransactionStatus::None) {
90 50 : transactionStarted = beginTransaction();
91 : }
92 :
93 575 : db::User *ret = nullptr;
94 575 : makeQuery([&, this] (SqlQuery &query) {
95 575 : query.with("u", [&] (SqlQuery::GenericQuery &q) {
96 575 : q.select().from(auth.getScheme().getName())
97 1150 : .where(namePair.first->getName(), Comparation::Equal, std::move(namePair.second));
98 1150 : }).with("l", [&] (SqlQuery::GenericQuery &q) {
99 1150 : q.select().count("failed_count").from("__login").innerJoinOn("u", [&] (SqlQuery::WhereBegin &w) {
100 575 : w.where(SqlQuery::Field( "__login", "user"), Comparation::Equal, SqlQuery::Field("u", "__oid"))
101 575 : .where(Operator::And, SqlQuery::Field( "__login", "success"), Comparation::Equal, Value(false))
102 1725 : .where(Operator::And, SqlQuery::Field( "__login", "date"), Comparation::GreatherThen, uint64_t((minTime).toSeconds()));
103 575 : });
104 1150 : }).select().from("l", "u").finalize();
105 :
106 575 : size_t count = 0;
107 575 : Value ud;
108 575 : selectQuery(query, [&, this] (Result &res) {
109 575 : count = res.current().toInteger(0);
110 :
111 575 : if (count >= size_t(config::AUTH_MAX_LOGIN_ATTEMPT)) {
112 0 : _driver->getApplicationInterface()->error("Auth", "Autorization blocked", Value{
113 0 : stappler::pair("cooldown", Value((int64_t)config::AUTH_MAX_TIME.toSeconds())),
114 0 : stappler::pair("failedAttempts", Value((int64_t)count)),
115 0 : });
116 0 : return false;
117 : }
118 :
119 575 : auto dv = res.decode(auth.getScheme(), Vector<const Field *>());
120 575 : if (dv.size() == 1) {
121 550 : ud = std::move(dv.getValue(0));
122 : }
123 575 : return true;
124 575 : });
125 :
126 575 : if (!ud) {
127 25 : return;
128 : }
129 :
130 550 : bool success = false;
131 550 : auto req = _driver->getApplicationInterface()->getRequestData();
132 550 : auto passwd = ud.getBytes("password");
133 :
134 550 : auto userId = ud.getInteger("__oid");
135 550 : if (auth.authorizeWithPassword(password, passwd, count)) {
136 550 : ret = new db::User(std::move(ud), auth.getScheme());
137 550 : success = true;
138 : }
139 :
140 550 : query.clear();
141 :
142 550 : query.with("u", [&] (SqlQuery::GenericQuery &q) {
143 550 : q.select().from(auth.getScheme().getName())
144 550 : .where(namePair.first->getName(), Comparation::Equal, std::move(namePair.second));
145 1100 : }).with("l", [&] (SqlQuery::GenericQuery &q) {
146 1100 : q.select().aggregate("MAX", SqlQuery::Field("id").as("maxId")).from("__login").innerJoinOn("u", [&] (SqlQuery::WhereBegin &w) {
147 550 : w.where(SqlQuery::Field( "__login", "user"), Comparation::Equal, SqlQuery::Field("u", "__oid"))
148 550 : .where(Operator::And, SqlQuery::Field( "__login", "success"), Comparation::Equal, Value(true))
149 1100 : .where(Operator::And, SqlQuery::Field( "__login", "date"), Comparation::GreatherThen, uint64_t((minTime).toSeconds()));
150 550 : });
151 1100 : }).select().from("l", "u").finalize();
152 :
153 1100 : selectQuery(query, [&, this] (Result &res) {
154 550 : query.clear();
155 550 : int64_t id = 0;
156 550 : if (!res.empty() && (id = res.readId())) {
157 500 : query.update("__login").set("date", stappler::Time::now().toSeconds())
158 500 : .where("id", Comparation::Equal, Value(id)).finalize();
159 500 : performQuery(query);
160 : } else {
161 50 : auto &f = query.insert("__login")
162 50 : .fields("user", "name", "password", "date", "success", "addr", "host", "path");
163 50 : if (req) {
164 650 : f.values(userId, iname, passwd, stappler::Time::now().toSeconds(), Value(success),
165 50 : SqlQuery::TypeString(req.address, "inet"),
166 100 : req.hostname.str<Interface>(),
167 100 : req.uri.str<Interface>())
168 50 : .finalize();
169 : } else {
170 0 : f.values(userId, iname, passwd, stappler::Time::now().toSeconds(), Value(success),
171 0 : SqlQuery::TypeString("NULL", "inet"), String("NULL"), String("NULL"))
172 0 : .finalize();
173 : }
174 50 : performQuery(query);
175 : }
176 550 : return true;
177 : });
178 575 : }, nullptr);
179 :
180 575 : if (transactionStarted) {
181 50 : endTransaction();
182 : }
183 :
184 575 : return ret;
185 575 : }
186 :
187 0 : void SqlHandle::makeSessionsCleanup() {
188 0 : bool transactionStarted = false;
189 0 : if (transactionStatus == TransactionStatus::None) {
190 0 : beginTransaction();
191 0 : transactionStarted = true;
192 : }
193 :
194 0 : StringStream query;
195 0 : query << "DELETE FROM __sessions WHERE (mtime + maxage + 10) < " << stappler::Time::now().toSeconds() << ";";
196 0 : performSimpleQuery(query.weak());
197 :
198 0 : query.clear();
199 0 : query << "DELETE FROM __removed RETURNING __oid;";
200 0 : performSimpleSelect(query.weak(), [&, this] (Result &res) {
201 0 : if (res.empty()) {
202 0 : return;
203 : }
204 :
205 0 : query.clear();
206 0 : query << "SELECT obj.__oid AS id FROM __files obj WHERE obj.__oid IN (";
207 :
208 0 : bool first = true;
209 0 : for (auto it : res) {
210 0 : if (first) { first = false; } else { query << ","; }
211 0 : query << it.at(0);
212 : }
213 0 : query << ");";
214 0 : performSimpleSelect(query.weak(), [&, this] (Result &res) {
215 0 : if (!res.empty()) {
216 0 : query.clear();
217 0 : query << "DELETE FROM __files WHERE __oid IN (";
218 0 : first = true;
219 : // check for files to remove
220 0 : for (auto it : res) {
221 0 : if (auto fileId = it.toInteger(0)) {
222 0 : db::File::removeFile(_driver->getApplicationInterface(), fileId);
223 0 : if (first) { first = false; } else { query << ","; }
224 0 : query << fileId;
225 : }
226 : }
227 0 : query << ");";
228 0 : performSimpleQuery(query.weak());
229 : }
230 0 : });
231 : });
232 :
233 0 : query.clear();
234 0 : query << "DELETE FROM __broadcasts WHERE date < " << (stappler::Time::now() - stappler::TimeInterval::seconds(10)).toMicroseconds() << ";";
235 0 : performSimpleQuery(query.weak());
236 :
237 0 : if (transactionStarted) {
238 0 : endTransaction();
239 : }
240 0 : }
241 :
242 6273 : void SqlHandle::finalizeBroadcast() {
243 6273 : if (!_bcasts.empty()) {
244 0 : makeQuery([&, this] (SqlQuery &query) {
245 0 : auto vals = query.insert("__broadcasts").fields("date", "msg").values();
246 0 : for (auto &it : _bcasts) {
247 0 : vals.values(it.first, std::move(it.second));
248 : }
249 0 : query.finalize();
250 0 : performQuery(query);
251 0 : _bcasts.clear();
252 0 : }, nullptr);
253 : }
254 6274 : }
255 :
256 0 : int64_t SqlHandle::processBroadcasts(const stappler::Callback<void(BytesView)> &cb, int64_t value) {
257 0 : int64_t maxId = value;
258 0 : makeQuery([&, this] (SqlQuery &query) {
259 0 : if (value <= 0) {
260 0 : query.select("last_value").from("__broadcasts_id_seq").finalize();
261 0 : maxId = selectQueryId(query);
262 : } else {
263 0 : query.select("id", "date", "msg").from("__broadcasts")
264 0 : .where("id", Comparation::GreatherThen, value).finalize();
265 0 : selectQuery(query, [&] (Result &res) {
266 0 : for (auto it : res) {
267 0 : if (it.size() >= 3) {
268 0 : auto msgId = it.toInteger(0);
269 0 : auto msgData = it.toBytes(2);
270 0 : if (!msgData.empty()) {
271 0 : if (msgId > maxId) {
272 0 : maxId = msgId;
273 : }
274 0 : cb(msgData);
275 : }
276 : }
277 : }
278 0 : return true;
279 : });
280 : }
281 0 : }, nullptr);
282 0 : return maxId;
283 : }
284 :
285 50 : void SqlHandle::broadcast(const Bytes &bytes) {
286 50 : if (getTransactionStatus() == db::TransactionStatus::None) {
287 50 : makeQuery([&, this] (SqlQuery &query) {
288 50 : query.insert("__broadcasts").fields("date", "msg").values(stappler::Time::now(), Bytes(bytes)).finalize();
289 50 : performQuery(query);
290 50 : }, nullptr);
291 50 : if (isNotificationsSupported()) {
292 0 : makeQuery([&, this] (SqlQuery &query) {
293 0 : query.getStream() << "NOTIFY " << config::BROADCAST_CHANNEL_NAME << ";";
294 0 : performQuery(query);
295 0 : }, nullptr);
296 : }
297 : } else {
298 0 : _bcasts.emplace_back(stappler::Time::now(), bytes);
299 : }
300 50 : }
301 :
302 100 : static bool Handle_convertViewDelta(Value &it) {
303 100 : auto vid_it = it.asDict().find("__vid");
304 100 : auto d_it = it.asDict().find("__delta");
305 100 : if (vid_it != it.asDict().end() && d_it != it.asDict().end()) {
306 0 : if (vid_it->second.getInteger()) {
307 0 : d_it->second.setString("update", "action");
308 0 : it.asDict().erase(vid_it);
309 : } else {
310 0 : d_it->second.setString("delete", "action");
311 0 : auto dict_it = it.asDict().begin();
312 0 : while (dict_it != it.asDict().end()) {
313 0 : if (dict_it->first != "__oid" && dict_it->first != "__delta") {
314 0 : dict_it = it.asDict().erase(dict_it);
315 : } else {
316 0 : ++ dict_it;
317 : }
318 : }
319 0 : return false;
320 : }
321 : }
322 100 : return true;
323 : }
324 :
325 50 : static void Handle_mergeViews(Value &objs, Value &vals) {
326 150 : for (auto &it : objs.asArray()) {
327 100 : if (!Handle_convertViewDelta(it)) {
328 0 : continue;
329 : }
330 :
331 100 : if (auto oid = it.getInteger("__oid")) {
332 100 : auto v_it = std::lower_bound(vals.asArray().begin(), vals.asArray().end(), oid,
333 200 : [&] (const Value &l, int64_t r) -> bool {
334 200 : return (l.isInteger() ? l.getInteger() : l.getInteger("__oid")) < r;
335 100 : });
336 100 : if (v_it != vals.asArray().end()) {
337 100 : auto objId = v_it->getInteger("__oid");
338 100 : if (objId == oid) {
339 100 : v_it->erase("__oid");
340 100 : if (it.hasValue("__views")) {
341 0 : it.getValue("__views").addValue(std::move(*v_it));
342 : } else {
343 100 : it.emplace("__views").addValue(std::move(*v_it));
344 : }
345 100 : v_it->setInteger(oid);
346 : }
347 : }
348 : }
349 : }
350 50 : }
351 :
352 1025 : int64_t SqlHandle::getDeltaValue(const Scheme &scheme) {
353 1025 : if (scheme.hasDelta()) {
354 275 : int64_t ret = 0;
355 275 : makeQuery([&, this] (SqlQuery &q) {
356 275 : q.select().aggregate("max", SqlQuery::Field("d", "time"))
357 275 : .from(SqlQuery::Field(getNameForDelta(scheme)).as("d")).finalize();
358 550 : selectQuery(q, [&] (Result &res) {
359 275 : if (res) {
360 550 : ret = res.current().toInteger(0);
361 : }
362 275 : return true;
363 : });
364 275 : }, nullptr);
365 275 : return ret;
366 : }
367 750 : return 0;
368 : }
369 :
370 50 : int64_t SqlHandle::getDeltaValue(const Scheme &scheme, const db::FieldView &view, uint64_t tag) {
371 50 : if (view.delta) {
372 50 : int64_t ret = 0;
373 50 : makeQuery([&, this] (SqlQuery &q) {
374 50 : String deltaName = toString(scheme.getName(), "_f_", view.name, "_delta");
375 50 : q.select().aggregate("max", SqlQuery::Field("d", "time"))
376 50 : .from(SqlQuery::Field(deltaName).as("d")).where("tag", Comparation::Equal, tag).finalize();
377 100 : selectQuery(q, [&] (Result &res) {
378 50 : if (res) {
379 100 : ret = res.current().toInteger(0);
380 : }
381 50 : return true;
382 : });
383 50 : }, nullptr);
384 50 : return ret;
385 : }
386 0 : return 0;
387 : }
388 :
389 25 : Value SqlHandle::getHistory(const Scheme &scheme, const stappler::Time &time, bool resolveUsers) {
390 25 : Value ret;
391 25 : if (!scheme.hasDelta()) {
392 0 : return ret;
393 : }
394 :
395 25 : makeQuery([&, this] (SqlQuery &q) {
396 25 : q.select().from(getNameForDelta(scheme)).where("time", Comparation::GreatherThen, time.toMicroseconds())
397 25 : .order(db::Ordering::Descending, "time").finalize();
398 :
399 50 : selectQuery(q, [&, this] (Result &res) {
400 150 : for (auto it : res) {
401 125 : auto &d = ret.emplace();
402 625 : for (size_t i = 0; i < it.size(); ++ i) {
403 500 : auto name = res.name(i);
404 500 : if (name == "action") {
405 125 : switch (DeltaAction(it.toInteger(i))) {
406 25 : case DeltaAction::Create: d.setString("create", "action"); break;
407 100 : case DeltaAction::Update: d.setString("update", "action"); break;
408 0 : case DeltaAction::Delete: d.setString("delete", "action"); break;
409 0 : case DeltaAction::Append: d.setString("append", "action"); break;
410 0 : case DeltaAction::Erase:d.setString("erase", "action"); break;
411 0 : default: break;
412 : }
413 375 : } else if (name == "time") {
414 125 : d.setString(Time::microseconds(it.toInteger(i)).toHttp<Interface>(), "http-date");
415 125 : d.setInteger(it.toInteger(i), "time");
416 275 : } else if (name == "user" && resolveUsers) {
417 125 : if (auto u = db::User::get(db::Adapter(this, _driver->getApplicationInterface()), it.toInteger(i))) {
418 0 : auto &ud = d.emplace("user");
419 0 : ud.setInteger(u->getObjectId(), "id");
420 0 : ud.setString(u->getName(), "name");
421 : } else {
422 125 : d.setInteger(it.toInteger(i), name.str<Interface>());
423 : }
424 125 : } else if (name != "id") {
425 125 : d.setInteger(it.toInteger(i), name.str<Interface>());
426 : }
427 : }
428 : }
429 25 : return true;
430 : });
431 25 : }, nullptr);
432 25 : return ret;
433 0 : }
434 :
435 25 : Value SqlHandle::getHistory(const db::FieldView &view, const Scheme *scheme, uint64_t tag, const stappler::Time &time, bool resolveUsers) {
436 25 : Value ret;
437 25 : if (!view.delta) {
438 0 : return ret;
439 : }
440 :
441 25 : makeQuery([&, this] (SqlQuery &q) {
442 25 : String name = toString(scheme->getName(), "_f_", view.name, "_delta");
443 25 : q.select().from(name).where("time", Comparation::GreatherThen, time.toMicroseconds())
444 25 : .where(Operator::And, "tag", Comparation::Equal, tag)
445 25 : .order(db::Ordering::Descending, "time").finalize();
446 :
447 50 : selectQuery(q, [&, this] (Result &res) {
448 25 : for (auto it : res) {
449 0 : auto &d = ret.emplace();
450 0 : for (size_t i = 0; i < it.size(); ++ i) {
451 0 : auto name = res.name(i);
452 0 : if (name == "tag") {
453 0 : d.setInteger(it.toInteger(i), "tag");
454 0 : } else if (name == "time") {
455 0 : d.setString(Time::microseconds(it.toInteger(i)).toHttp<Interface>(), "http-date");
456 0 : d.setInteger(it.toInteger(i), "time");
457 25 : } else if (name == "user" && resolveUsers) {
458 0 : if (auto u = db::User::get(db::Adapter(this, _driver->getApplicationInterface()), it.toInteger(i))) {
459 0 : auto &ud = d.emplace("user");
460 0 : ud.setInteger(u->getObjectId(), "id");
461 0 : ud.setString(u->getName(), "name");
462 : } else {
463 0 : d.setInteger(it.toInteger(i), name.str<Interface>());
464 : }
465 0 : } else if (name != "id") {
466 0 : d.setInteger(it.toInteger(i), name.str<Interface>());
467 : }
468 : }
469 : }
470 25 : return true;
471 : });
472 25 : }, nullptr);
473 :
474 25 : return ret;
475 0 : }
476 :
477 25 : Value SqlHandle::getDeltaData(const Scheme &scheme, const stappler::Time &time) {
478 25 : Value ret;
479 25 : if (scheme.hasDelta()) {
480 25 : makeQuery([&, this] (SqlQuery &q) {
481 25 : FieldResolver resv(scheme);
482 25 : q.writeQueryDelta(scheme, time, Set<const Field *>(), false);
483 25 : q.finalize();
484 25 : ret = selectValueQuery(scheme, q, resv.getVirtuals());
485 25 : }, nullptr);
486 : }
487 25 : return ret;
488 0 : }
489 :
490 50 : SPUNUSED static void Handle_writeSelectViewDataQuery(SqlQuery &q, const db::Scheme &s, uint64_t oid, const db::FieldView &f, const Value &data) {
491 50 : auto fs = f.scheme;
492 :
493 50 : auto fieldName = toString(fs->getName(), "_id");
494 50 : auto sel = q.select(SqlQuery::Field(fieldName).as("__oid")).field("__vid");
495 :
496 50 : sel.from(toString(s.getName(), "_f_", f.getName(), "_view"))
497 100 : .where(toString(s.getName(), "_id"), db::Comparation::Equal, oid)
498 50 : .parenthesis(db::Operator::And, [&] (SqlQuery::WhereBegin &w) {
499 50 : auto subw = w.where();
500 150 : for (auto &it : data.asArray()) {
501 100 : subw.where(db::Operator::Or, fieldName, db::Comparation::Equal, it.getInteger("__oid"));
502 : }
503 100 : }).order(db::Ordering::Ascending, SqlQuery::Field(fieldName)).finalize();
504 50 : }
505 :
506 25 : Value SqlHandle::getDeltaData(const Scheme &scheme, const db::FieldView &view, const stappler::Time &time, uint64_t tag) {
507 25 : Value ret;
508 25 : if (view.delta) {
509 25 : makeQuery([&, this] (SqlQuery &q) {
510 25 : Field field(&view);
511 25 : QueryList list(_driver->getApplicationInterface(), &scheme);
512 25 : list.selectById(&scheme, tag);
513 25 : list.setField(view.scheme, &field);
514 :
515 25 : FieldResolver resv(scheme);
516 :
517 25 : q.writeQueryViewDelta(list, time, Set<const Field *>(), false);
518 25 : auto r = selectValueQuery(*view.scheme, q, resv.getVirtuals());
519 25 : if (r.isArray() && r.size() > 0) {
520 0 : q.clear();
521 0 : Field f(&view);
522 0 : Handle_writeSelectViewDataQuery(q, scheme, tag, view, r);
523 0 : selectValueQuery(r, view, q);
524 0 : ret = std::move(r);
525 : }
526 25 : }, nullptr);
527 : }
528 25 : return ret;
529 0 : }
530 :
531 3625 : int64_t SqlHandle::selectQueryId(const SqlQuery &query) {
532 3625 : if (getTransactionStatus() == db::TransactionStatus::Rollback) {
533 0 : return 0;
534 : }
535 3625 : int64_t id = 0;
536 3625 : selectQuery(query, [&] (Result &res) {
537 3625 : if (!res.empty()) {
538 3625 : id = res.readId();
539 3625 : return true;
540 : }
541 0 : return false;
542 : });
543 3625 : return id;
544 : }
545 :
546 1850 : size_t SqlHandle::performQuery(const SqlQuery &query) {
547 1850 : if (getTransactionStatus() == db::TransactionStatus::Rollback) {
548 0 : return stappler::maxOf<size_t>();
549 : }
550 1850 : size_t ret = stappler::maxOf<size_t>();
551 1850 : selectQuery(query, [&] (Result &res) {
552 1850 : if (res.success()) {
553 1850 : ret = res.getAffectedRows();
554 1850 : return true;
555 : }
556 0 : return false;
557 : });
558 1850 : return ret;
559 : }
560 :
561 10350 : Value SqlHandle::selectValueQuery(const Scheme &scheme, const SqlQuery &query, const Vector<const Field *> &virtuals) {
562 10350 : Value ret;
563 10350 : selectQuery(query, [&] (Result &result) {
564 10350 : if (result) {
565 10350 : ret = result.decode(scheme, virtuals);
566 10350 : return true;
567 : }
568 0 : return false;
569 : });
570 10350 : return ret;
571 0 : }
572 :
573 50 : Value SqlHandle::selectValueQuery(const Field &field, const SqlQuery &query, const Vector<const Field *> &virtuals) {
574 50 : Value ret;
575 50 : selectQuery(query, [&] (Result &result) {
576 50 : if (result) {
577 50 : ret = result.decode(field, virtuals);
578 50 : return true;
579 : }
580 0 : return false;
581 : });
582 50 : return ret;
583 0 : }
584 :
585 50 : void SqlHandle::selectValueQuery(Value &objs, const FieldView &field, const SqlQuery &query) {
586 50 : selectQuery(query, [&] (Result &result) {
587 50 : if (result) {
588 50 : auto vals = result.decode(field);
589 50 : if (!vals.isArray()) {
590 0 : for (auto &it : objs.asArray()) {
591 0 : Handle_convertViewDelta(it);
592 : }
593 50 : } else if (vals.isArray() && objs.isArray()) {
594 50 : Handle_mergeViews(objs, vals);
595 : }
596 50 : return true;
597 50 : }
598 0 : return false;
599 : });
600 50 : }
601 :
602 : }
|