Line data Source code
1 : /**
2 : Copyright (c) 2016-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 "SPDbScheme.h"
25 :
26 : #include "SPDbAdapter.h"
27 : #include "SPDbFile.h"
28 : #include "SPDbObject.h"
29 : #include "SPDbTransaction.h"
30 : #include "SPDbWorker.h"
31 :
32 : #if MODULE_STAPPLER_BITMAP
33 : #include "SPBitmap.h"
34 : #endif
35 :
36 : namespace STAPPLER_VERSIONIZED stappler::db {
37 :
38 : SPUNUSED static void prepareGetQuery(Query &query, uint64_t oid, bool forUpdate);
39 :
40 325 : static void Scheme_setOwner(const Scheme *scheme, const Map<String, Field> &map) {
41 1100 : for (auto &it : map) {
42 775 : const_cast<Field::Slot *>(it.second.getSlot())->owner = scheme;
43 775 : if (it.second.getType() == Type::Extra) {
44 0 : auto slot = static_cast<const FieldExtra *>(it.second.getSlot());
45 0 : Scheme_setOwner(scheme, slot->fields);
46 : }
47 : }
48 325 : }
49 :
50 322 : bool Scheme::initSchemes(const Map<StringView, const Scheme *> &schemes) {
51 1847 : for (auto &it : schemes) {
52 1525 : const_cast<Scheme *>(it.second)->init();
53 : }
54 325 : return true;
55 : }
56 :
57 849 : Scheme::Scheme(const StringView &ns, bool delta) : Scheme(ns, delta ? Options::WithDelta : Options::None) { }
58 :
59 1049 : Scheme::Scheme(const StringView &ns, Options f)
60 1049 : : name(ns.str<Interface>()), flags(f), oidField(Field::Integer("__oid", Flags::Indexed | Flags::ForceInclude)) {
61 1048 : const_cast<Field::Slot *>(oidField.getSlot())->owner = this;
62 17767 : for (size_t i = 0; i < roles.size(); ++ i) {
63 16722 : roles[i] = nullptr;
64 : }
65 1044 : }
66 :
67 75 : Scheme::Scheme(const StringView &name, std::initializer_list<Field> il, bool delta) : Scheme(name, delta ? Options::WithDelta : Options::None) {
68 625 : for (auto &it : il) {
69 550 : auto fname = it.getName();
70 550 : fields.emplace(fname.str<Interface>(), std::move(const_cast<Field &>(it)));
71 : }
72 :
73 75 : updateLimits();
74 75 : }
75 :
76 0 : Scheme::Scheme(const StringView &name, std::initializer_list<Field> il, Options f) : Scheme(name, f) {
77 0 : for (auto &it : il) {
78 0 : auto fname = it.getName();
79 0 : fields.emplace(fname.str<Interface>(), std::move(const_cast<Field &>(it)));
80 : }
81 :
82 0 : updateLimits();
83 0 : }
84 :
85 7950 : bool Scheme::hasDelta() const {
86 7950 : return (flags & Options::WithDelta) != Options::None;
87 : }
88 :
89 925 : bool Scheme::isDetouched() const {
90 925 : return (flags & Options::Detouched) != Options::None;
91 : }
92 :
93 0 : bool Scheme::isCompressed() const {
94 0 : return (flags & Options::Compressed) != Options::None;
95 : }
96 :
97 0 : bool Scheme::hasFullText() const {
98 0 : return !fullTextFields.empty();
99 : }
100 :
101 575 : const Scheme & Scheme::define(std::initializer_list<Field> il) {
102 4300 : for (auto &it : il) {
103 3725 : auto fname = it.getName();
104 3725 : if (it.getType() == Type::Image) {
105 50 : auto image = static_cast<const FieldImage *>(it.getSlot());
106 50 : auto &thumbnails = image->thumbnails;
107 300 : for (auto & thumb : thumbnails) {
108 250 : auto new_f = fields.emplace(thumb.name, Field::Image(String(thumb.name), MaxImageSize(thumb.width, thumb.height))).first;
109 250 : ((FieldImage *)(new_f->second.getSlot()))->primary = false;
110 : }
111 : }
112 3725 : if (it.hasFlag(Flags::ForceExclude)) {
113 0 : _hasForceExclude = true;
114 : }
115 3725 : if (it.getType() == Type::Virtual) {
116 50 : _hasVirtuals = true;
117 : }
118 3725 : if (it.isFile()) {
119 125 : _hasFiles = true;
120 : }
121 3725 : fields.emplace(fname.str<Interface>(), std::move(const_cast<Field &>(it)));
122 : }
123 :
124 575 : updateLimits();
125 575 : return *this;
126 : }
127 :
128 225 : const Scheme & Scheme::define(Vector<Field> &&il) {
129 1475 : for (auto &it : il) {
130 1250 : auto fname = it.getName();
131 1250 : if (it.getType() == Type::Image) {
132 50 : auto image = static_cast<const FieldImage *>(it.getSlot());
133 50 : auto &thumbnails = image->thumbnails;
134 100 : for (auto & thumb : thumbnails) {
135 50 : auto new_f = fields.emplace(thumb.name, Field::Image(String(thumb.name), MaxImageSize(thumb.width, thumb.height))).first;
136 50 : ((FieldImage *)(new_f->second.getSlot()))->primary = false;
137 : }
138 : }
139 1250 : if (it.hasFlag(Flags::ForceExclude)) {
140 0 : _hasForceExclude = true;
141 : }
142 1250 : if (it.getType() == Type::Virtual) {
143 0 : _hasVirtuals = true;
144 : }
145 1250 : if (it.isFile()) {
146 100 : _hasFiles = true;
147 : }
148 1250 : fields.emplace(fname.str<Interface>(), std::move(const_cast<Field &>(it)));
149 : }
150 :
151 225 : updateLimits();
152 225 : return *this;
153 : }
154 :
155 175 : const Scheme & Scheme::define(AccessRole &&role) {
156 175 : if (role.users.count() == 1) {
157 625 : for (size_t i = 0; i < role.users.size(); ++ i) {
158 625 : if (role.users.test(i)) {
159 175 : setAccessRole(AccessRoleId(i), std::move(role));
160 175 : break;
161 : }
162 : }
163 : } else {
164 0 : for (size_t i = 0; i < role.users.size(); ++ i) {
165 0 : if (role.users.test(i)) {
166 0 : setAccessRole(AccessRoleId(i), AccessRole(role));
167 : }
168 : }
169 : }
170 175 : return *this;
171 : }
172 :
173 0 : const Scheme & Scheme::define(UniqueConstraintDef &&def) {
174 0 : Vector<const Field *> fields; fields.reserve(def.fields.size());
175 0 : for (auto &it : def.fields) {
176 0 : if (auto f = getField(it)) {
177 0 : auto iit = std::lower_bound(fields.begin(), fields.end(), f);
178 0 : if (iit == fields.end()) {
179 0 : fields.emplace_back(f);
180 0 : } else if (*iit != f) {
181 0 : fields.emplace(iit, f);
182 : }
183 : } else {
184 0 : log::error("Scheme", "Field for unique constraint not found", data::EncodeFormat::Pretty, Value(it));
185 : }
186 : }
187 :
188 0 : unique.emplace_back(StringView(
189 0 : toString(name, "_", string::tolower<Interface>(def.name), "_unique")).pdup(unique.get_allocator()), std::move(fields));
190 0 : return *this;
191 0 : }
192 :
193 0 : const Scheme & Scheme::define(Bytes &&dict) {
194 0 : _compressDict = std::move(dict);
195 0 : return *this;
196 : }
197 :
198 1525 : bool Scheme::init() {
199 1525 : if (_init) {
200 750 : return true;
201 : }
202 775 : memory::pool::push(fields.get_allocator());
203 : // init non-linked object fields as StrongReferences
204 6325 : for (auto &fit : fields) {
205 5550 : const_cast<Field::Slot *>(fit.second.getSlot())->owner = this;
206 5550 : switch (fit.second.getType()) {
207 625 : case Type::Object:
208 : case Type::Set:
209 625 : if (auto slot = fit.second.getSlot<FieldObject>()) {
210 625 : if (slot->linkage == Linkage::Auto && slot->onRemove == RemovePolicy::Null && !slot->hasFlag(Flags::Reference)) {
211 225 : if (!getForeignLink(slot)) {
212 : // assume strong reference
213 50 : auto mutSlot = const_cast<FieldObject *>(slot);
214 50 : mutSlot->onRemove = RemovePolicy::StrongReference;
215 50 : mutSlot->flags |= Flags::Reference;
216 : }
217 : }
218 : }
219 625 : break;
220 100 : case Type::FullTextView: {
221 100 : auto slot = static_cast<const FieldFullTextView *>(fit.second.getSlot());
222 300 : for (auto &req_it : slot->requireFields) {
223 200 : if (auto f = getField(req_it)) {
224 200 : fullTextFields.emplace(f);
225 : }
226 : }
227 100 : break;
228 : }
229 300 : case Type::Extra: {
230 300 : auto slot = static_cast<const FieldExtra *>(fit.second.getSlot());
231 300 : Scheme_setOwner(this, slot->fields);
232 300 : break;
233 : }
234 100 : case Type::Array: {
235 100 : auto slot = static_cast<const FieldArray *>(fit.second.getSlot());
236 100 : auto arraySlot = slot->tfield.getSlot();
237 100 : const_cast<Field::Slot *>(arraySlot)->owner = this;
238 100 : if (arraySlot->type == Type::Extra) {
239 25 : auto extraSlot = static_cast<const FieldExtra *>(arraySlot);
240 25 : Scheme_setOwner(this, extraSlot->fields);
241 : }
242 100 : break;
243 : }
244 100 : case Type::View: {
245 100 : auto slot = static_cast<const FieldView *>(fit.second.getSlot());
246 100 : if (slot->scheme) {
247 100 : const_cast<Scheme *>(slot->scheme)->addView(this, &fit.second);
248 : }
249 100 : break;
250 : }
251 4325 : default:
252 4325 : break;
253 : }
254 5550 : if (fit.second.getSlot()->autoField.defaultFn) {
255 75 : auto &autoF = fit.second.getSlot()->autoField;
256 150 : for (auto &a_it : autoF.schemes) {
257 75 : const_cast<Scheme &>(a_it.scheme).addAutoField(this, &fit.second, a_it);
258 : }
259 : }
260 5550 : if (fit.second.hasFlag(Flags::Composed) && (fit.second.getType() == Type::Object || fit.second.getType() == Type::Set)) {
261 50 : auto slot = static_cast<const FieldObject *>(fit.second.getSlot());
262 50 : if (slot->scheme) {
263 50 : const_cast<Scheme *>(slot->scheme)->addParent(this, &fit.second);
264 : }
265 : }
266 : }
267 775 : memory::pool::pop();
268 775 : _init = true;
269 775 : return true;
270 : }
271 :
272 0 : void Scheme::addFlags(Options opts) {
273 0 : flags |= opts;
274 0 : }
275 :
276 0 : void Scheme::cloneFrom(Scheme *source) {
277 0 : for (auto &it : source->fields) {
278 0 : fields.emplace(it.first, it.second);
279 : }
280 0 : }
281 :
282 52899 : StringView Scheme::getName() const {
283 52899 : return name;
284 : }
285 50 : bool Scheme::hasAliases() const {
286 300 : for (auto &it : fields) {
287 300 : if (it.second.getType() == Type::Text && it.second.getTransform() == Transform::Alias) {
288 50 : return true;
289 : }
290 : }
291 0 : return false;
292 : }
293 :
294 0 : bool Scheme::isProtected(const StringView &key) const {
295 0 : auto it = fields.find(key);
296 0 : if (it != fields.end()) {
297 0 : return it->second.isProtected();
298 : }
299 0 : return false;
300 : }
301 :
302 14925 : const Set<const Field *> & Scheme::getForceInclude() const {
303 14925 : return forceInclude;
304 : }
305 :
306 44550 : const Map<String, Field> & Scheme::getFields() const {
307 44550 : return fields;
308 : }
309 :
310 163850 : const Field *Scheme::getField(const StringView &key) const {
311 163850 : auto it = fields.find(key);
312 163850 : if (it != fields.end()) {
313 157425 : return &it->second;
314 : }
315 6425 : if (key == "__oid") {
316 5700 : return &oidField;
317 : }
318 725 : return nullptr;
319 : }
320 :
321 775 : const Vector<Scheme::UniqueConstraint> &Scheme::getUnique() const {
322 775 : return unique;
323 : }
324 :
325 0 : BytesView Scheme::getCompressDict() const {
326 0 : return _compressDict;
327 : }
328 :
329 750 : const Field *Scheme::getForeignLink(const FieldObject *f) const {
330 750 : if (!f || f->onRemove == RemovePolicy::Reference || f->onRemove == RemovePolicy::StrongReference) {
331 225 : return nullptr;
332 : }
333 525 : auto &link = f->link;
334 525 : auto nextScheme = f->scheme;
335 525 : if (f->linkage == Linkage::Auto) {
336 525 : auto &nextFields = nextScheme->getFields();
337 2025 : for (auto &it : nextFields) {
338 1825 : auto &nextField = it.second;
339 1825 : if (nextField.getType() == Type::Object
340 1825 : || (nextField.getType() == Type::Set && f->getType() == Type::Object)) {
341 500 : auto nextSlot = static_cast<const FieldObject *>(nextField.getSlot());
342 500 : if (nextSlot->scheme == this) {
343 325 : return &nextField;
344 : }
345 : }
346 : }
347 0 : } else if (f->linkage == Linkage::Manual) {
348 0 : auto nextField = nextScheme->getField(link);
349 0 : if (nextField && (nextField->getType() == Type::Object
350 0 : || (nextField->getType() == Type::Set && f->getType() == Type::Object))) {
351 0 : auto nextSlot = static_cast<const FieldObject *>(nextField->getSlot());
352 0 : if (nextSlot->scheme == this) {
353 0 : return nextField;
354 : }
355 : }
356 : }
357 200 : return nullptr;
358 : }
359 :
360 575 : const Field *Scheme::getForeignLink(const Field &f) const {
361 575 : if (f.getType() == Type::Set || f.getType() == Type::Object) {
362 475 : auto slot = static_cast<const FieldObject *>(f.getSlot());
363 475 : return getForeignLink(slot);
364 : }
365 100 : return nullptr;
366 : }
367 0 : const Field *Scheme::getForeignLink(const StringView &fname) const {
368 0 : auto f = getField(fname);
369 0 : if (f) {
370 0 : return getForeignLink(*f);
371 : }
372 0 : return nullptr;
373 : }
374 :
375 2575 : bool Scheme::isAtomicPatch(const Value &val) const {
376 2575 : if (val.isDictionary()) {
377 5725 : for (auto &it : val.asDict()) {
378 5000 : auto f = getField(it.first);
379 :
380 13275 : if (f && (
381 : // extra field should use select-update
382 5000 : f->getType() == Type::Extra
383 :
384 : // virtual field should use select-update
385 3275 : || f->getType() == Type::Virtual
386 :
387 : // force-includes used to update views, so, we need select-update
388 3250 : || forceInclude.find(f) != forceInclude.end()
389 :
390 : // for full-text views update
391 3250 : || fullTextFields.find(f) != fullTextFields.end()
392 :
393 : // auto fields requires select-update
394 8150 : || autoFieldReq.find(f) != autoFieldReq.end()
395 :
396 : // select-update required for replace filters
397 3150 : || f->getSlot()->replaceFilterFn)) {
398 1850 : return false;
399 : }
400 : }
401 725 : return true;
402 : }
403 0 : return false;
404 : }
405 :
406 0 : uint64_t Scheme::hash(ValidationLevel l) const {
407 0 : StringStream stream;
408 0 : for (auto &it : fields) {
409 0 : it.second.hash(stream, l);
410 : }
411 0 : return std::hash<String>{}(stream.weak());
412 0 : }
413 :
414 0 : const Vector<Scheme::ViewScheme *> &Scheme::getViews() const {
415 0 : return views;
416 : }
417 :
418 0 : Vector<const Field *> Scheme::getPatchFields(const Value &patch) const {
419 0 : Vector<const Field *> ret; ret.reserve(patch.size());
420 0 : for (auto &it : patch.asDict()) {
421 0 : if (auto f = getField(it.first)) {
422 0 : ret.emplace_back(f);
423 : }
424 : }
425 0 : return ret;
426 0 : }
427 :
428 0 : const Scheme::AccessTable &Scheme::getAccessTable() const {
429 0 : return roles;
430 : }
431 :
432 10100 : const AccessRole *Scheme::getAccessRole(AccessRoleId id) const {
433 10100 : return roles[stappler::toInt(id)];
434 : }
435 :
436 175 : void Scheme::setAccessRole(AccessRoleId id, AccessRole &&r) {
437 175 : if (stappler::toInt(id) < stappler::toInt(AccessRoleId::Max)) {
438 175 : roles[stappler::toInt(id)] = new AccessRole(std::move(r));
439 175 : _hasAccessControl = true;
440 : }
441 175 : }
442 :
443 0 : bool Scheme::save(const Transaction &t, Object *obj) const {
444 0 : Worker w(*this, t);
445 0 : Set<const Field *> fields;
446 :
447 0 : Value tmp(obj->_data);
448 0 : return t.save(w, obj->getObjectId(), tmp, obj->_data, fields) ? true : false;
449 0 : }
450 :
451 0 : bool Scheme::hasFiles() const {
452 0 : return _hasFiles;
453 : }
454 :
455 3150 : bool Scheme::hasForceExclude() const {
456 3150 : return _hasForceExclude;
457 : }
458 :
459 28024 : bool Scheme::hasAccessControl() const {
460 28024 : return _hasAccessControl;
461 : }
462 :
463 0 : bool Scheme::hasVirtuals() const {
464 0 : return _hasVirtuals;
465 : }
466 :
467 3650 : Value Scheme::createWithWorker(Worker &w, const Value &data, bool isProtected) const {
468 3650 : if (!data.isDictionary() && !data.isArray()) {
469 0 : w.getApplicationInterface()->error("Storage", "Invalid data for object");
470 0 : return Value();
471 : }
472 :
473 100 : auto checkRequired = [&] (StringView f, const Value &changeSet) {
474 100 : auto &val = changeSet.getValue(f);
475 100 : if (val.isNull()) {
476 0 : w.getApplicationInterface()->error("Storage", "No value for required field",
477 0 : Value({ std::make_pair("field", Value(f)) }));
478 0 : return false;
479 : }
480 100 : return true;
481 3650 : };
482 :
483 3650 : Value changeSet = data;
484 3650 : if (data.isDictionary()) {
485 3625 : transform(changeSet, isProtected?TransformAction::ProtectedCreate:TransformAction::Create);
486 : } else {
487 75 : for (auto &it : changeSet.asArray()) {
488 50 : if (it) {
489 50 : transform(it, isProtected?TransformAction::ProtectedCreate:TransformAction::Create);
490 : }
491 : }
492 : }
493 :
494 3650 : bool stop = false;
495 42475 : for (auto &it : fields) {
496 38825 : if (it.second.hasFlag(Flags::Required)) {
497 100 : if (changeSet.isDictionary()) {
498 100 : if (!checkRequired(it.first, changeSet)) { stop = true; }
499 : } else {
500 0 : for (auto &iit : changeSet.asArray()) {
501 0 : if (!checkRequired(it.first, iit)) { iit = Value(); }
502 : }
503 : }
504 : }
505 : }
506 :
507 3650 : if (stop) {
508 0 : return Value();
509 : }
510 :
511 3650 : Value retVal;
512 3650 : if (w.perform([&, this] (const Transaction &t) -> bool {
513 3650 : Value patch(createFilePatch(t, data, changeSet));
514 3650 : if (auto ret = t.create(w, changeSet)) {
515 3650 : touchParents(t, ret);
516 6225 : for (auto &it : views) {
517 2575 : updateView(t, ret, it, Vector<uint64_t>());
518 : }
519 3650 : retVal = std::move(ret);
520 3650 : return true;
521 : } else {
522 0 : if (patch.isDictionary() || patch.isArray()) {
523 0 : purgeFilePatch(t, patch);
524 : }
525 3650 : }
526 0 : return false;
527 3650 : })) {
528 3650 : return retVal;
529 : }
530 :
531 0 : return Value();
532 3650 : }
533 :
534 475 : Value Scheme::updateWithWorker(Worker &w, uint64_t oid, const Value &data, bool isProtected) const {
535 475 : bool success = false;
536 475 : Value changeSet;
537 :
538 475 : std::tie(success, changeSet) = prepareUpdate(data, isProtected);
539 475 : if (!success) {
540 0 : return Value();
541 : }
542 :
543 475 : Value ret;
544 475 : w.perform([&, this] (const Transaction &t) -> bool {
545 475 : Value filePatch(createFilePatch(t, data, changeSet));
546 475 : if (changeSet.empty()) {
547 0 : w.getApplicationInterface()->error("Storage", "Empty changeset for id", Value({ std::make_pair("oid", Value((int64_t)oid)) }));
548 0 : return false;
549 : }
550 :
551 475 : ret = patchOrUpdate(w, oid, changeSet);
552 475 : if (ret.isNull()) {
553 0 : if (filePatch.isDictionary()) {
554 0 : purgeFilePatch(t, filePatch);
555 : }
556 0 : w.getApplicationInterface()->error("Storage", "Fail to update object for id", Value({ std::make_pair("oid", Value((int64_t)oid)) }));
557 0 : return false;
558 : }
559 475 : return true;
560 475 : });
561 :
562 475 : return ret;
563 475 : }
564 :
565 1750 : Value Scheme::updateWithWorker(Worker &w, const Value & obj, const Value &data, bool isProtected) const {
566 1750 : uint64_t oid = obj.getInteger("__oid");
567 1750 : if (!oid) {
568 0 : w.getApplicationInterface()->error("Storage", "Invalid data for object");
569 0 : return Value();
570 : }
571 :
572 1750 : bool success = false;
573 1750 : Value changeSet;
574 :
575 1750 : std::tie(success, changeSet) = prepareUpdate(data, isProtected);
576 1750 : if (!success) {
577 0 : return Value();
578 : }
579 :
580 1750 : Value ret;
581 1750 : w.perform([&, this] (const Transaction &t) -> bool {
582 1750 : Value filePatch(createFilePatch(t, data, changeSet));
583 1750 : if (changeSet.empty()) {
584 0 : w.getApplicationInterface()->error("Storage", "Empty changeset for id", Value({ std::make_pair("oid", Value((int64_t)oid)) }));
585 0 : return false;
586 : }
587 :
588 1750 : Value tmp(obj);
589 1750 : ret = patchOrUpdate(w, tmp, changeSet);
590 1750 : if (ret.isNull()) {
591 0 : if (filePatch.isDictionary()) {
592 0 : purgeFilePatch(t, filePatch);
593 : }
594 0 : w.getApplicationInterface()->error("Storage", "No object for id to update", Value({ std::make_pair("oid", Value((int64_t)oid)) }));
595 0 : return false;
596 : }
597 1750 : return true;
598 1750 : });
599 :
600 1750 : return ret;
601 1750 : }
602 :
603 2225 : stappler::Pair<bool, Value> Scheme::prepareUpdate(const Value &data, bool isProtected) const {
604 2225 : if (!data.isDictionary()) {
605 0 : log::error("Storage", "Invalid changeset data for object");
606 0 : return stappler::pair(false, Value());
607 : }
608 :
609 2225 : Value changeSet = data;
610 2225 : transform(changeSet, isProtected?TransformAction::ProtectedUpdate:TransformAction::Update);
611 :
612 2225 : bool stop = false;
613 30450 : for (auto &it : fields) {
614 28225 : if (changeSet.hasValue(it.first)) {
615 5125 : auto &val = changeSet.getValue(it.first);
616 5125 : if (val.isNull() && it.second.hasFlag(Flags::Required)) {
617 0 : log::error("Storage", "Value for required field can not be removed", data::EncodeFormat::Pretty,
618 0 : Value({ std::make_pair("field", Value(it.first)) }));
619 0 : stop = true;
620 : }
621 : }
622 : }
623 :
624 2225 : if (stop) {
625 0 : return stappler::pair(false, Value());
626 : }
627 :
628 4450 : return stappler::pair(true, changeSet);
629 2225 : }
630 :
631 4450 : void Scheme::touchParents(const Transaction &t, const Value &obj) const {
632 4450 : t.performAsSystem([&, this] () -> bool {
633 4450 : if (!parents.empty()) {
634 0 : Map<int64_t, const Scheme *> parentsToUpdate;
635 0 : extractParents(parentsToUpdate, t, obj, false);
636 0 : for (auto &it : parentsToUpdate) {
637 0 : Worker(*it.second, t).touch(it.first);
638 : }
639 0 : }
640 4450 : return true;
641 : });
642 4450 : }
643 :
644 0 : void Scheme::extractParents(Map<int64_t, const Scheme *> &parentsToUpdate, const Transaction &t, const Value &obj, bool isChangeSet) const {
645 0 : auto id = obj.getInteger("__oid");
646 0 : for (auto &it : parents) {
647 0 : if (it->backReference) {
648 0 : if (auto value = obj.getInteger(it->backReference->getName())) {
649 0 : parentsToUpdate.emplace(value, it->scheme);
650 : }
651 0 : } else if (!isChangeSet && id) {
652 0 : auto vec = t.getAdapter().getReferenceParents(*this, id, it->scheme, it->pointerField);
653 0 : for (auto &value : vec) {
654 0 : parentsToUpdate.emplace(value, it->scheme);
655 : }
656 0 : }
657 : }
658 0 : }
659 :
660 1850 : Value Scheme::updateObject(Worker &w, Value & obj, Value &changeSet) const {
661 1850 : Set<const Field *> fieldsToUpdate;
662 :
663 1850 : Vector<stappler::Pair<const ViewScheme *, Vector<uint64_t>>> viewsToUpdate; viewsToUpdate.reserve(views.size());
664 1850 : Map<int64_t, const Scheme *> parentsToUpdate;
665 :
666 1850 : Value replacements;
667 :
668 1850 : if (!parents.empty()) {
669 0 : extractParents(parentsToUpdate, w.transaction(), obj);
670 0 : extractParents(parentsToUpdate, w.transaction(), changeSet, true);
671 : }
672 :
673 : // find what fields and views should be updated
674 6350 : for (auto &it : changeSet.asDict()) {
675 4500 : auto &fieldName = it.first;
676 4500 : if (auto f = getField(fieldName)) {
677 4500 : auto slot = f->getSlot();
678 4500 : auto &val = obj.getValue(it.first);
679 4500 : if (!slot->replaceFilterFn || slot->replaceFilterFn(*this, obj, val, it.second)) {
680 4500 : fieldsToUpdate.emplace(f);
681 :
682 4500 : if (forceInclude.find(f) != forceInclude.end() || autoFieldReq.find(f) != autoFieldReq.end()) {
683 400 : for (auto &it : views) {
684 200 : if (it->fields.find(f) != it->fields.end()) {
685 200 : auto lb = std::lower_bound(viewsToUpdate.begin(), viewsToUpdate.end(), it,
686 100 : [] (stappler::Pair<const ViewScheme *, Vector<uint64_t>> &l, const ViewScheme *r) -> bool {
687 100 : return l.first < r;
688 200 : });
689 200 : if (lb == viewsToUpdate.end() && lb->first != it) {
690 100 : viewsToUpdate.emplace(lb, stappler::pair(it, Vector<uint64_t>()));
691 : }
692 : }
693 : }
694 : }
695 : }
696 : }
697 : }
698 :
699 : // acquire current views state
700 1950 : for (auto &it : viewsToUpdate) {
701 100 : it.second = getLinkageForView(obj, *it.first);
702 : }
703 :
704 1850 : if (!viewsToUpdate.empty() || !parentsToUpdate.empty()) {
705 100 : if (w.perform([&, this] (const Transaction &t) {
706 100 : if (t.save(w, obj.getInteger("__oid"), obj, changeSet, fieldsToUpdate)) {
707 100 : t.performAsSystem([&] () -> bool {
708 200 : for (auto &it : parentsToUpdate) {
709 0 : Worker(*it.second, t).touch(it.first);
710 : }
711 100 : return true;
712 : });
713 200 : for (auto &it : viewsToUpdate) {
714 100 : updateView(t, obj, it.first, it.second);
715 : }
716 100 : return true;
717 : }
718 0 : return false;
719 : })) {
720 100 : return obj;
721 : }
722 1750 : } else if (auto ret = w.transaction().save(w, obj.getInteger("__oid"), obj, changeSet, fieldsToUpdate)) {
723 1750 : return ret;
724 1750 : }
725 :
726 0 : return Value();
727 1850 : }
728 :
729 325 : void Scheme::touchWithWorker(Worker &w, uint64_t id) const {
730 325 : Value patch;
731 325 : transform(patch, TransformAction::Touch);
732 325 : w.includeNone();
733 325 : patchOrUpdate(w, id, patch);
734 325 : }
735 :
736 25 : void Scheme::touchWithWorker(Worker &w, const Value & obj) const {
737 25 : Value tmp(obj);
738 25 : Value patch;
739 25 : transform(patch, TransformAction::Touch);
740 25 : w.includeNone();
741 25 : patchOrUpdate(w, tmp, patch);
742 25 : }
743 :
744 350 : Value Scheme::fieldWithWorker(Action a, Worker &w, uint64_t oid, const Field &f, Value &&patch) const {
745 350 : switch (a) {
746 0 : case Action::Get:
747 : case Action::Count:
748 0 : return w.transaction().field(a, w, oid, f, std::move(patch));
749 : break;
750 75 : case Action::Set:
751 75 : if (f.transform(*this, oid, patch)) {
752 75 : Value ret;
753 75 : w.perform([&] (const Transaction &t) -> bool {
754 75 : ret = t.field(a, w, oid, f, std::move(patch));
755 75 : return !ret.isNull();
756 : });
757 75 : return ret;
758 75 : }
759 0 : break;
760 75 : case Action::Remove:
761 150 : return Value(w.perform([&] (const Transaction &t) -> bool {
762 75 : return t.field(a, w, oid, f, std::move(patch)) ? true : false;
763 75 : }));
764 : break;
765 200 : case Action::Append:
766 200 : if (f.transform(*this, oid, patch)) {
767 200 : Value ret;
768 200 : w.perform([&] (const Transaction &t) -> bool {
769 200 : ret = t.field(a, w, oid, f, std::move(patch));
770 200 : return !ret.isNull();
771 : });
772 200 : return ret;
773 200 : }
774 0 : break;
775 : }
776 0 : return Value();
777 : }
778 :
779 425 : Value Scheme::fieldWithWorker(Action a, Worker &w, const Value &obj, const Field &f, Value &&patch) const {
780 425 : switch (a) {
781 425 : case Action::Get:
782 : case Action::Count:
783 425 : return w.transaction().field(a, w, obj, f, std::move(patch));
784 : break;
785 0 : case Action::Set:
786 0 : if (f.transform(*this, obj, patch)) {
787 0 : Value ret;
788 0 : w.perform([&] (const Transaction &t) -> bool {
789 0 : ret = t.field(a, w, obj, f, std::move(patch));
790 0 : return !ret.isNull();
791 : });
792 0 : return ret;
793 0 : }
794 0 : break;
795 0 : case Action::Remove:
796 0 : return Value(w.perform([&] (const Transaction &t) -> bool {
797 0 : return t.field(a, w, obj, f, std::move(patch)).asBool();
798 0 : }));
799 : break;
800 0 : case Action::Append:
801 0 : if (f.transform(*this, obj, patch)) {
802 0 : Value ret;
803 0 : w.perform([&] (const Transaction &t) -> bool {
804 0 : ret = t.field(a, w, obj, f, std::move(patch));
805 0 : return !ret.isNull();
806 : });
807 0 : return ret;
808 0 : }
809 0 : break;
810 : }
811 0 : return Value();
812 : }
813 :
814 0 : Value Scheme::setFileWithWorker(Worker &w, uint64_t oid, const Field &f, InputFile &file) const {
815 0 : Value ret;
816 0 : w.perform([&, this] (const Transaction &t) -> bool {
817 0 : Value patch;
818 0 : transform(patch, TransformAction::Update);
819 0 : auto d = createFile(t, f, file);
820 0 : if (d.isInteger()) {
821 0 : patch.setValue(d, f.getName().str<Interface>());
822 : } else {
823 0 : patch.setValue(std::move(d));
824 : }
825 0 : if (patchOrUpdate(w, oid, patch)) {
826 : // resolve files
827 0 : ret = File::getData(t, patch.getInteger(f.getName()));
828 0 : return true;
829 : } else {
830 0 : purgeFilePatch(t, patch);
831 0 : return false;
832 : }
833 0 : });
834 0 : return ret;
835 0 : }
836 :
837 725 : Value Scheme::doPatch(Worker &w, const Transaction &t, uint64_t id, Value & patch) const {
838 725 : if (auto ret = t.patch(w, id, patch)) {
839 725 : touchParents(t, ret);
840 725 : return ret;
841 725 : }
842 0 : return Value();
843 : }
844 :
845 800 : Value Scheme::patchOrUpdate(Worker &w, uint64_t id, Value & patch) const {
846 800 : if (!patch.empty()) {
847 800 : Value ret;
848 800 : w.perform([&, this] (const Transaction &t) {
849 800 : auto r = getAccessRole(t.getRole());
850 800 : auto d = getAccessRole(AccessRoleId::Default);
851 : // if we have save callback - we unable to do patches
852 800 : if (!isAtomicPatch(patch) || (r && r->onSave && !r->onPatch) || (d && d->onSave && !d->onPatch)) {
853 200 : if (auto obj = makeObjectForPatch(t, id, Value(), patch)) {
854 100 : t.setObject(id, Value(obj));
855 100 : if ((ret = updateObject(w, obj, patch))) {
856 100 : return true;
857 : }
858 100 : }
859 : } else {
860 700 : ret = doPatch(w, w.transaction(), id, patch);
861 700 : if (ret) {
862 700 : return true;
863 : }
864 : }
865 0 : return false;
866 : });
867 800 : return ret;
868 800 : }
869 0 : return Value();
870 : }
871 :
872 1775 : Value Scheme::patchOrUpdate(Worker &w, Value & obj, Value & patch) const {
873 1750 : auto isObjectValid = [&, this] (const Value &obj) -> bool {
874 1750 : for (auto &it : patch.asDict()) {
875 1750 : if (!obj.hasValue(it.first)) {
876 1750 : return false;
877 : }
878 : }
879 0 : for (auto &it : forceInclude) {
880 0 : if (!obj.hasValue(it->getName())) {
881 0 : return false;
882 : }
883 : }
884 0 : return true;
885 1775 : };
886 :
887 1775 : if (!patch.empty()) {
888 1775 : Value ret;
889 1775 : w.perform([&, this] (const Transaction &t) {
890 1775 : if (isAtomicPatch(patch)) {
891 25 : ret = doPatch(w, t, obj.getInteger("__oid"), patch);
892 25 : if (ret) {
893 25 : return true;
894 : }
895 : } else {
896 1750 : auto id = obj.getInteger("__oid");
897 1750 : if (isObjectValid(obj)) {
898 0 : if ((ret = updateObject(w, obj, patch))) {
899 0 : return true;
900 : }
901 1750 : } else if (auto patchObj = makeObjectForPatch(t, id, obj, patch)) {
902 1750 : t.setObject(id, Value(patchObj));
903 1750 : if ((ret = updateObject(w, patchObj, patch))) {
904 1750 : return true;
905 : }
906 1750 : }
907 : }
908 0 : return false;
909 : });
910 1775 : return ret;
911 1775 : }
912 0 : return Value();
913 : }
914 :
915 275 : bool Scheme::removeWithWorker(Worker &w, uint64_t oid) const {
916 275 : bool hasAuto = false;
917 325 : for (auto &it : views) {
918 125 : if (it->autoField) {
919 75 : hasAuto = true;
920 75 : break;
921 : }
922 : }
923 :
924 275 : if (!parents.empty() || hasAuto) {
925 150 : return w.perform([&, this] (const Transaction &t) {
926 75 : Query query;
927 75 : prepareGetQuery(query, oid, true);
928 75 : for (auto &it : parents) {
929 0 : if (it->backReference) {
930 0 : query.include(it->backReference->getName());
931 : }
932 : }
933 150 : if (auto obj = Worker(*this, t).asSystem().reduceGetQuery(query, true)) {
934 75 : touchParents(t, obj); // if transaction fails - all changes will be rolled back
935 :
936 150 : for (auto &it : views) {
937 75 : if (it->autoField) {
938 75 : Vector<uint64_t> ids = getLinkageForView(obj, *it);
939 150 : for (auto &id : ids) {
940 75 : t.scheduleAutoField(*it->scheme, *it->viewField, id);
941 : }
942 75 : }
943 : }
944 :
945 75 : return t.remove(w, oid);
946 75 : }
947 0 : return false;
948 150 : });
949 : } else {
950 400 : return w.perform([&] (const Transaction &t) {
951 200 : return t.remove(w, oid);
952 200 : });
953 : }
954 : }
955 :
956 0 : bool Scheme::foreachWithWorker(Worker &w, const Query &q, const Callback<bool(Value &)> &cb) const {
957 0 : return w.transaction().foreach(w, q, cb);
958 : }
959 :
960 6274 : Value Scheme::selectWithWorker(Worker &w, const Query &q) const {
961 6274 : return w.transaction().select(w, q);
962 : }
963 :
964 375 : size_t Scheme::countWithWorker(Worker &w, const Query &q) const {
965 375 : return w.transaction().count(w, q);
966 : }
967 :
968 6350 : Value &Scheme::transform(Value &d, TransformAction a) const {
969 : // drop readonly and not existed fields
970 6350 : auto &dict = d.asDict();
971 6350 : auto it = dict.begin();
972 37150 : while (it != dict.end()) {
973 30800 : auto &fname = it->first;
974 30800 : auto f_it = fields.find(fname);
975 30800 : if (f_it == fields.end()
976 30700 : || f_it->second.getType() == Type::FullTextView
977 :
978 : // we can write into readonly field only in protected mode
979 30700 : || (f_it->second.hasFlag(Flags::ReadOnly) && a != TransformAction::ProtectedCreate && a != TransformAction::ProtectedUpdate)
980 :
981 : // we can drop files in all modes...
982 61625 : || (f_it->second.isFile() && !it->second.isNull()
983 : // but we can write files as ints only in protected mode
984 125 : && ((a != TransformAction::ProtectedCreate && a != TransformAction::ProtectedUpdate) || !it->second.isInteger()))) {
985 225 : it = dict.erase(it);
986 : } else {
987 30575 : it ++;
988 : }
989 : }
990 :
991 : // write defaults
992 79200 : for (auto &it : fields) {
993 72850 : auto &field = it.second;
994 72850 : if (a == TransformAction::Create || a == TransformAction::ProtectedCreate) {
995 39475 : if (field.hasFlag(Flags::AutoMTime) && !d.hasValue(it.first)) {
996 3000 : d.setInteger(stappler::Time::now().toMicroseconds(), it.first);
997 36475 : } else if (field.hasFlag(Flags::AutoCTime) && !d.hasValue(it.first)) {
998 250 : d.setInteger(stappler::Time::now().toMicroseconds(), it.first);
999 36225 : } else if (field.hasDefault() && !d.hasValue(it.first)) {
1000 250 : if (auto def = field.getDefault(d)) {
1001 250 : d.setValue(std::move(def), it.first);
1002 250 : }
1003 : }
1004 66725 : } else if ((a == TransformAction::Update || a == TransformAction::ProtectedUpdate || a == TransformAction::Touch)
1005 60625 : && field.hasFlag(Flags::AutoMTime)) {
1006 2550 : if ((!d.empty() && !d.hasValue(it.first)) || a == TransformAction::Touch) {
1007 2475 : d.setInteger(stappler::Time::now().toMicroseconds(), it.first);
1008 : }
1009 : }
1010 : }
1011 :
1012 6350 : if (!d.empty()) {
1013 6275 : auto &dict = d.asDict();
1014 6275 : auto it = dict.begin();
1015 42825 : while (it != dict.end()) {
1016 36550 : auto &field = fields.at(it->first);
1017 36550 : if (it->second.isNull() && (a == TransformAction::Update || a == TransformAction::ProtectedUpdate || a == TransformAction::Touch)) {
1018 100 : it ++;
1019 36450 : } else if (!field.transform(*this, d, it->second, (a == TransformAction::Create || a == TransformAction::ProtectedCreate))) {
1020 2450 : it = dict.erase(it);
1021 : } else {
1022 34000 : it ++;
1023 : }
1024 : }
1025 : }
1026 :
1027 6350 : return d;
1028 : }
1029 :
1030 100 : Value Scheme::createFile(const Transaction &t, const Field &field, InputFile &file) const {
1031 : //check if content type is valid
1032 : #if MODULE_STAPPLER_BITMAP
1033 100 : if (field.getType() == Type::Image) {
1034 25 : if (file.type == "application/octet-stream" || file.type.empty()) {
1035 0 : file.type = bitmap::getMimeType(bitmap::detectFormat(file.file).first).str<Interface>();
1036 : }
1037 : }
1038 : #endif
1039 :
1040 100 : if (!File::validateFileField(t.getAdapter().getApplicationInterface(), field, file)) {
1041 0 : return Value();
1042 : }
1043 :
1044 100 : if (field.getType() == Type::File) {
1045 75 : return File::createFile(t, field, file);
1046 25 : } else if (field.getType() == Type::Image) {
1047 25 : return File::createImage(t, field, file);
1048 : }
1049 0 : return Value();
1050 : }
1051 :
1052 0 : Value Scheme::createFile(const Transaction &t, const Field &field, const BytesView &data, const StringView &itype, int64_t mtime) const {
1053 : //check if content type is valid
1054 0 : String type(itype.str<Interface>());
1055 : #if MODULE_STAPPLER_BITMAP
1056 0 : if (field.getType() == Type::Image) {
1057 0 : if (type == "application/octet-stream" || type.empty()) {
1058 0 : stappler::CoderSource source(data);
1059 0 : type = bitmap::getMimeType(bitmap::detectFormat(source).first).str<Interface>();
1060 : }
1061 : }
1062 : #endif
1063 :
1064 0 : if (!File::validateFileField(t.getAdapter().getApplicationInterface(), field, type, data)) {
1065 0 : return Value();
1066 : }
1067 :
1068 0 : if (field.getType() == Type::File) {
1069 0 : return File::createFile(t, type, data, mtime);
1070 0 : } else if (field.getType() == Type::Image) {
1071 0 : return File::createImage(t, field, type, data, mtime);
1072 : }
1073 0 : return Value();
1074 0 : }
1075 :
1076 1850 : Value Scheme::makeObjectForPatch(const Transaction &t, uint64_t oid, const Value &obj, const Value &patch) const {
1077 1850 : Set<const Field *> includeFields;
1078 :
1079 1850 : Query query;
1080 1850 : prepareGetQuery(query, oid, true);
1081 :
1082 6350 : for (auto &it : patch.asDict()) {
1083 4500 : if (auto f = getField(it.first)) {
1084 4500 : if (!obj.hasValue(it.first)) {
1085 4325 : includeFields.emplace(f);
1086 : }
1087 : }
1088 : }
1089 :
1090 1850 : for (auto &it : forceInclude) {
1091 0 : if (!obj.hasValue(it->getName())) {
1092 0 : includeFields.emplace(it);
1093 : }
1094 : }
1095 :
1096 25700 : for (auto &it : fields) {
1097 23850 : if (it.second.getType() == Type::FullTextView) {
1098 1800 : auto slot = it.second.getSlot<FieldFullTextView>();
1099 6200 : for (auto &p_it : patch.asDict()) {
1100 4400 : auto req_it = std::find(slot->requireFields.begin(), slot->requireFields.end(), p_it.first);
1101 4400 : if (req_it != slot->requireFields.end()) {
1102 600 : for (auto &it : slot->requireFields) {
1103 400 : if (auto f = getField(it)) {
1104 400 : includeFields.emplace(f);
1105 : }
1106 : }
1107 : }
1108 : }
1109 : }
1110 : }
1111 :
1112 6225 : for (auto &it : includeFields) {
1113 4375 : query.include(Query::Field(it->getName()));
1114 : }
1115 :
1116 1850 : auto ret = Worker(*this, t).asSystem().reduceGetQuery(query, false);
1117 1850 : if (!obj) {
1118 100 : return ret;
1119 : } else {
1120 7075 : for (auto &it : obj.asDict()) {
1121 5325 : if (!ret.hasValue(it.first)) {
1122 3525 : ret.setValue(it.second, it.first);
1123 : }
1124 : }
1125 1750 : return ret;
1126 : }
1127 1850 : }
1128 :
1129 0 : Value Scheme::removeField(const Transaction &t, Value &obj, const Field &f, const Value &value) {
1130 0 : if (f.isFile()) {
1131 0 : auto scheme = t.getAdapter().getApplicationInterface()->getFileScheme();
1132 0 : int64_t id = 0;
1133 0 : if (value.isInteger()) {
1134 0 : id = value.asInteger();
1135 0 : } else if (value.isInteger("__oid")) {
1136 0 : id = value.getInteger("__oid");
1137 : }
1138 :
1139 0 : if (id) {
1140 0 : if (Worker(*scheme, t).remove(id)) {
1141 0 : return Value(id);
1142 : }
1143 : }
1144 0 : return Value();
1145 : }
1146 0 : return Value(true);
1147 : }
1148 0 : void Scheme::finalizeField(const Transaction &t, const Field &f, const Value &value) {
1149 0 : if (f.isFile()) {
1150 0 : File::removeFile(t.getAdapter().getApplicationInterface(), value);
1151 : }
1152 0 : }
1153 :
1154 875 : void Scheme::updateLimits() {
1155 875 : config.updateLimits(fields);
1156 875 : }
1157 :
1158 0 : bool Scheme::validateHint(uint64_t oid, const Value &hint) {
1159 0 : if (!hint.isDictionary()) {
1160 0 : return false;
1161 : }
1162 0 : auto hoid = hint.getInteger("__oid");
1163 0 : if (hoid > 0 && (uint64_t)hoid == oid) {
1164 0 : return validateHint(hint);
1165 : }
1166 0 : return false;
1167 : }
1168 :
1169 0 : bool Scheme::validateHint(const String &alias, const Value &hint) {
1170 0 : if (!hint.isDictionary()) {
1171 0 : return false;
1172 : }
1173 0 : for (auto &it : fields) {
1174 0 : if (it.second.getType() == Type::Text && it.second.getTransform() == Transform::Alias) {
1175 0 : if (hint.getString(it.first) == alias) {
1176 0 : return validateHint(hint);
1177 : }
1178 : }
1179 : }
1180 0 : return false;
1181 : }
1182 :
1183 0 : bool Scheme::validateHint(const Value &hint) {
1184 0 : if (hint.size() > 1) {
1185 : // all required fields should exists
1186 0 : for (auto &it : fields) {
1187 0 : if (it.second.hasFlag(Flags::Required)) {
1188 0 : if (!hint.hasValue(it.first)) {
1189 0 : return false;
1190 : }
1191 : }
1192 : }
1193 :
1194 : // no fields other then in schemes fields
1195 0 : for (auto &it : hint.asDict()) {
1196 0 : if (it.first != "__oid" && fields.find(it.first) == fields.end()) {
1197 0 : return false;
1198 : }
1199 : }
1200 :
1201 0 : return true;
1202 : }
1203 0 : return false;
1204 : }
1205 :
1206 5875 : Value Scheme::createFilePatch(const Transaction &t, const Value &ival, Value &iChangeSet) const {
1207 36450 : auto createPatch = [&, this] (const Value &val, Value &changeSet) {
1208 5900 : Value patch;
1209 36350 : for (auto &it : val.asDict()) {
1210 30450 : auto f = getField(it.first);
1211 30450 : if (f && (f->getType() == Type::File || (f->getType() == Type::Image && static_cast<const FieldImage *>(f->getSlot())->primary))) {
1212 125 : if (it.second.isInteger() && it.second.getInteger() < 0) {
1213 100 : auto file = t.getAdapter().getApplicationInterface()->getFileFromContext(it.second.getInteger());
1214 100 : if (file && file->isOpen()) {
1215 100 : auto d = createFile(t, *f, *file);
1216 100 : if (d.isInteger()) {
1217 75 : patch.setValue(d, f->getName().str<Interface>());
1218 25 : } else if (d.isDictionary()) {
1219 175 : for (auto & it : d.asDict()) {
1220 150 : patch.setValue(std::move(it.second), it.first);
1221 : }
1222 : }
1223 100 : }
1224 25 : } else if (it.second.isDictionary()) {
1225 0 : if ((it.second.isBytes("content") || it.second.isString("content")) && it.second.isString("type")) {
1226 0 : auto &c = it.second.getValue("content");
1227 0 : Value d;
1228 0 : if (c.isBytes()) {
1229 0 : d = createFile(t, *f, c.getBytes(), it.second.getString("type"), it.second.getInteger("mtime"));
1230 : } else {
1231 0 : auto &str = it.second.getString("content");
1232 0 : d = createFile(t, *f, BytesView((const uint8_t *)str.data(), str.size()), it.second.getString("type"), it.second.getInteger("mtime"));
1233 : }
1234 0 : if (d.isInteger()) {
1235 0 : patch.setValue(d, f->getName().str<Interface>());
1236 0 : } else if (d.isDictionary()) {
1237 0 : for (auto & it : d.asDict()) {
1238 0 : patch.setValue(std::move(it.second), it.first);
1239 : }
1240 : }
1241 0 : }
1242 : }
1243 : }
1244 : }
1245 5900 : if (patch.isDictionary()) {
1246 325 : for (auto &it : patch.asDict()) {
1247 225 : changeSet.setValue(it.second, it.first);
1248 : }
1249 : }
1250 5900 : return patch;
1251 0 : };
1252 :
1253 5875 : if (ival.isDictionary()) {
1254 5850 : return createPatch(ival, iChangeSet);
1255 : } else {
1256 25 : size_t i = 0;
1257 25 : Value ret;
1258 75 : for (auto &it : ival.asArray()) {
1259 50 : auto &changeSet = iChangeSet.getValue(i);
1260 50 : if (!changeSet.isNull()) {
1261 50 : if (auto vl = createPatch(it, changeSet)) {
1262 0 : ret.addValue(std::move(vl));
1263 50 : }
1264 : }
1265 50 : ++ i;
1266 : }
1267 25 : return ret;
1268 25 : }
1269 : }
1270 :
1271 0 : void Scheme::purgeFilePatch(const Transaction &t, const Value &patch) const {
1272 0 : if (patch.isDictionary()) {
1273 0 : for (auto &it : patch.asDict()) {
1274 0 : if (getField(it.first)) {
1275 0 : File::purgeFile(t, it.second);
1276 : }
1277 : }
1278 0 : } else if (patch.isArray()) {
1279 0 : for (auto &v : patch.asArray()) {
1280 0 : for (auto &it : v.asDict()) {
1281 0 : if (getField(it.first)) {
1282 0 : File::purgeFile(t, it.second);
1283 : }
1284 : }
1285 : }
1286 : }
1287 0 : }
1288 :
1289 100 : void Scheme::addView(const Scheme *s, const Field *f) {
1290 100 : memory::pool::push(views.get_allocator());
1291 100 : if (auto view = static_cast<const FieldView *>(f->getSlot())) {
1292 100 : views.emplace_back(new ViewScheme{s, f, *view});
1293 100 : auto viewScheme = views.back();
1294 :
1295 100 : bool linked = false;
1296 150 : for (auto &it : view->requireFields) {
1297 50 : auto fit = fields.find(it);
1298 50 : if (fit != fields.end()) {
1299 50 : if (fit->second.getType() == Type::Object && !view->linkage && !linked) {
1300 : // try to autolink from required field
1301 0 : auto nextSlot = static_cast<const FieldObject *>(fit->second.getSlot());
1302 0 : if (nextSlot->scheme == s) {
1303 0 : viewScheme->autoLink = &fit->second;
1304 0 : linked = true;
1305 : }
1306 : }
1307 50 : viewScheme->fields.emplace(&fit->second);
1308 50 : forceInclude.emplace(&fit->second);
1309 : } else {
1310 0 : log::error("Scheme", "Field for view not foumd", data::EncodeFormat::Pretty, Value({
1311 0 : stappler::pair("view", Value(toString(s->getName(), ".", f->getName()))),
1312 0 : stappler::pair("field", Value(toString(getName(), ".", it)))
1313 0 : }));
1314 : }
1315 : }
1316 100 : if (!view->linkage && !linked) {
1317 : // try to autolink from other fields
1318 825 : for (auto &it : fields) {
1319 825 : auto &field = it.second;
1320 825 : if (field.getType() == Type::Object) {
1321 100 : auto nextSlot = static_cast<const FieldObject *>(field.getSlot());
1322 100 : if (nextSlot->scheme == s) {
1323 100 : viewScheme->autoLink = &field;
1324 100 : viewScheme->fields.emplace(&field);
1325 100 : forceInclude.emplace(&field);
1326 100 : linked = true;
1327 100 : break;
1328 : }
1329 : }
1330 : }
1331 : }
1332 100 : if (view->linkage) {
1333 0 : linked = true;
1334 : }
1335 100 : if (!linked) {
1336 0 : log::error("Scheme", "Failed to autolink view field", data::EncodeFormat::Pretty, Value({
1337 0 : stappler::pair("view", Value(toString(s->getName(), ".", f->getName()))),
1338 0 : }));
1339 : }
1340 : }
1341 100 : memory::pool::pop();
1342 100 : }
1343 :
1344 75 : void Scheme::addAutoField(const Scheme *s, const Field *f, const AutoFieldScheme &a) {
1345 75 : views.emplace_back(new ViewScheme{s, f, a});
1346 75 : auto viewScheme = views.back();
1347 :
1348 75 : if (this == s && !a.linkage) {
1349 225 : for (auto &it : a.requiresForAuto) {
1350 150 : if (auto f = getField(it)) {
1351 150 : viewScheme->fields.emplace(f);
1352 150 : autoFieldReq.emplace(f);
1353 : } else {
1354 0 : log::error("Scheme", "Field for view not foumd", data::EncodeFormat::Pretty, Value({
1355 0 : stappler::pair("view", Value(toString(s->getName(), ".", f->getName()))),
1356 0 : stappler::pair("field", Value(toString(getName(), ".", it)))
1357 0 : }));
1358 : }
1359 : }
1360 : } else {
1361 0 : bool linked = false;
1362 0 : for (auto &it : a.requiresForLinking) {
1363 0 : if (auto f = getField(it)) {
1364 0 : if (f->getType() == Type::Object && !a.linkage && !linked) {
1365 : // try to autolink from required field
1366 0 : auto nextSlot = static_cast<const FieldObject *>(f->getSlot());
1367 0 : if (nextSlot->scheme == s) {
1368 0 : viewScheme->autoLink = f;
1369 0 : linked = true;
1370 : }
1371 : }
1372 0 : viewScheme->fields.emplace(f);
1373 0 : forceInclude.emplace(f);
1374 : } else {
1375 0 : log::error("Scheme", "Field for view not foumd", data::EncodeFormat::Pretty, Value({
1376 0 : stappler::pair("view", Value(toString(s->getName(), ".", f->getName()))),
1377 0 : stappler::pair("field", Value(toString(getName(), ".", it)))
1378 0 : }));
1379 : }
1380 : }
1381 0 : for (auto &it : a.requiresForAuto) {
1382 0 : if (auto f = getField(it)) {
1383 0 : viewScheme->fields.emplace(f);
1384 0 : autoFieldReq.emplace(f);
1385 : } else {
1386 0 : log::error("Scheme", "Field for view not foumd", data::EncodeFormat::Pretty, Value({
1387 0 : stappler::pair("view", Value(toString(s->getName(), ".", f->getName()))),
1388 0 : stappler::pair("field", Value(toString(getName(), ".", it)))
1389 0 : }));
1390 : }
1391 : }
1392 0 : if (!a.linkage && !linked) {
1393 : // try to autolink from other fields
1394 0 : for (auto &it : fields) {
1395 0 : auto &field = it.second;
1396 0 : if (field.getType() == Type::Object) {
1397 0 : auto nextSlot = static_cast<const FieldObject *>(field.getSlot());
1398 0 : if (nextSlot->scheme == s) {
1399 0 : viewScheme->autoLink = &field;
1400 0 : viewScheme->fields.emplace(&field);
1401 0 : forceInclude.emplace(&field);
1402 0 : linked = true;
1403 0 : break;
1404 : }
1405 : }
1406 : }
1407 : }
1408 0 : if (a.linkage) {
1409 0 : linked = true;
1410 : }
1411 0 : if (!linked) {
1412 0 : log::error("Scheme", "Failed to autolink view field", data::EncodeFormat::Pretty, Value({
1413 0 : stappler::pair("view", Value(toString(s->getName(), ".", f->getName()))),
1414 0 : }));
1415 : }
1416 : }
1417 75 : }
1418 :
1419 50 : void Scheme::addParent(const Scheme *s, const Field *f) {
1420 50 : parents.emplace_back(new ParentScheme(s, f));
1421 50 : auto &p = parents.back();
1422 :
1423 50 : auto slot = static_cast<const FieldObject *>(f->getSlot());
1424 50 : if (f->getType() == Type::Set) {
1425 50 : auto link = s->getForeignLink(slot);
1426 50 : if (link) {
1427 0 : p->backReference = link;
1428 0 : forceInclude.emplace(p->backReference);
1429 : }
1430 : }
1431 50 : }
1432 :
1433 2850 : Vector<uint64_t> Scheme::getLinkageForView(const Value &obj, const ViewScheme &s) const {
1434 2850 : Vector<uint64_t> ids; ids.reserve(1);
1435 2850 : if (s.autoLink && s.autoLink->getSlot()) {
1436 200 : if (auto id = obj.getInteger(s.autoLink->getName())) {
1437 175 : ids.push_back(id);
1438 : }
1439 2650 : } else if (s.autoField) {
1440 2650 : if (s.autoField->linkage) {
1441 0 : ids = s.autoField->linkage(*s.scheme, *this, obj);
1442 2650 : } else if (&s.autoField->scheme == this) {
1443 2650 : ids.push_back(obj.getInteger("__oid"));
1444 : }
1445 : } else {
1446 0 : auto view = s.viewField->getSlot<FieldView>();
1447 0 : if (!view->viewFn) {
1448 0 : return Vector<uint64_t>();
1449 : }
1450 :
1451 0 : if (view->linkage) {
1452 0 : ids = view->linkage(*s.scheme, *this, obj);
1453 : }
1454 : }
1455 2850 : return ids;
1456 2850 : }
1457 :
1458 2675 : void Scheme::updateView(const Transaction &t, const Value & obj, const ViewScheme *scheme, const Vector<uint64_t> &orig) const {
1459 2675 : const FieldView *view = nullptr;
1460 2675 : if (scheme->viewField->getType() == Type::View) {
1461 200 : view = static_cast<const FieldView *>(scheme->viewField->getSlot());
1462 : }
1463 2675 : if ((!view || !view->viewFn) && !scheme->autoField) {
1464 0 : return;
1465 : }
1466 :
1467 2675 : auto objId = obj.getInteger("__oid");
1468 :
1469 : // list of objects, that view fields should contain this object
1470 2675 : Vector<uint64_t> ids = getLinkageForView(obj, *scheme);
1471 :
1472 2675 : if (scheme->autoField) {
1473 2575 : for (auto &it : orig) {
1474 100 : auto ids_it = std::find(ids.begin(), ids.begin(), it);
1475 100 : if (ids_it != ids.end()) {
1476 100 : ids.erase(ids_it);
1477 : }
1478 100 : t.scheduleAutoField(*scheme->scheme, *scheme->viewField, it);
1479 : }
1480 :
1481 4850 : for (auto &it : ids) {
1482 2375 : t.scheduleAutoField(*scheme->scheme, *scheme->viewField, it);
1483 : }
1484 : } else {
1485 200 : t.performAsSystem([&, this] () -> bool {
1486 200 : t.removeFromView(*scheme->scheme, *view, objId, obj);
1487 :
1488 200 : if (!ids.empty()) {
1489 175 : if (view->viewFn(*this, obj)) {
1490 250 : for (auto &id : ids) {
1491 125 : Value it;
1492 125 : it.setInteger(objId, toString(getName(), "_id"));
1493 125 : if (scheme->scheme) {
1494 125 : it.setInteger(id, toString(scheme->scheme->getName(), "_id"));
1495 : }
1496 125 : t.addToView(*scheme->scheme, *view, id, obj, it);
1497 125 : }
1498 : }
1499 : }
1500 200 : return true;
1501 : });
1502 : }
1503 2675 : }
1504 :
1505 : }
|