LCOV - code coverage report
Current view: top level - core/db - SPDbScheme.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 606 965 62.8 %
Date: 2024-05-12 00:16:13 Functions: 68 99 68.7 %

          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             : }

Generated by: LCOV version 1.14