LCOV - code coverage report
Current view: top level - core/db - SPDbTransaction.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 335 526 63.7 %
Date: 2024-05-12 00:16:13 Functions: 50 67 74.6 %

          Line data    Source code
       1             : /**
       2             : Copyright (c) 2018-2022 Roman Katuntsev <sbkarr@stappler.org>
       3             : Copyright (c) 2023-2024 Stappler LLC <admin@stappler.dev>
       4             : 
       5             : Permission is hereby granted, free of charge, to any person obtaining a copy
       6             : of this software and associated documentation files (the "Software"), to deal
       7             : in the Software without restriction, including without limitation the rights
       8             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       9             : copies of the Software, and to permit persons to whom the Software is
      10             : furnished to do so, subject to the following conditions:
      11             : 
      12             : The above copyright notice and this permission notice shall be included in
      13             : all copies or substantial portions of the Software.
      14             : 
      15             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      16             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      17             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      18             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      19             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      20             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      21             : THE SOFTWARE.
      22             : **/
      23             : 
      24             : #include "SPDbTransaction.h"
      25             : 
      26             : #include "SPDbAdapter.h"
      27             : #include "SPDbScheme.h"
      28             : #include "SPDbWorker.h"
      29             : 
      30             : namespace STAPPLER_VERSIONIZED stappler::db {
      31             : 
      32         200 : Transaction::Op Transaction::getTransactionOp(Action a) {
      33         200 :         switch (a) {
      34          50 :         case Action::Get: return FieldGet; break;
      35          50 :         case Action::Set: return FieldSet; break;
      36         100 :         case Action::Append: return FieldAppend; break;
      37           0 :         case Action::Remove: return FieldClear; break;
      38           0 :         case Action::Count: return FieldCount; break;
      39             :         }
      40           0 :         return None;
      41             : }
      42             : 
      43        8847 : Transaction Transaction::acquire(const Adapter &adapter) {
      44        8847 :         auto pool = pool::acquire();
      45             : 
      46        8845 :         if (auto d = pool::get<Transaction::Data>(pool, adapter.getTransactionKey())) {
      47        3225 :                 auto ret = Transaction(d);
      48        3225 :                 ret.retain();
      49        3225 :                 return ret;
      50             :         } else {
      51        5618 :                 d = new (pool) Transaction::Data{adapter};
      52        5619 :                 d->role = AccessRoleId::System;
      53        5619 :                 pool::store(pool, d, adapter.getTransactionKey());
      54        5614 :                 auto ret = Transaction(d);
      55        5617 :                 ret.retain();
      56             : 
      57        5617 :                 if (adapter.getApplicationInterface()) {
      58        5621 :                         adapter.getApplicationInterface()->initTransaction(ret);
      59             :                 }
      60             : 
      61        5623 :                 return ret;
      62             :         }
      63             : 
      64             :         return Transaction(nullptr);
      65             : }
      66             : 
      67         600 : Transaction Transaction::acquireIfExists() {
      68         600 :         return acquireIfExists(pool::acquire());
      69             : }
      70             : 
      71        3525 : Transaction Transaction::acquireIfExists(stappler::memory::pool_t *pool) {
      72        3525 :         auto stack = pool::get<Stack>(pool, config::STORAGE_TRANSACTION_STACK_KEY);
      73        3525 :         if (!stack) {
      74         475 :                 return Transaction(nullptr);
      75             :         }
      76             : 
      77        3050 :         return stack->stack.empty() ? Transaction(nullptr) : Transaction(stack->stack.back());
      78             : }
      79             : 
      80       38168 : void Transaction::retain() const {
      81       38168 :         auto p = pool::acquire();
      82       38169 :         auto stack = pool::get<Stack>(p, config::STORAGE_TRANSACTION_STACK_KEY);
      83       38173 :         if (!stack) {
      84        5598 :                 stack = new (p) Stack;
      85        5593 :                 pool::store(p, stack, config::STORAGE_TRANSACTION_STACK_KEY);
      86             :         }
      87             : 
      88       38172 :         stack->stack.emplace_back(_data);
      89       38168 : }
      90             : 
      91       33521 : void Transaction::release() const {
      92       33521 :         auto p = pool::acquire();
      93       33520 :         auto stack = pool::get<Stack>(p, config::STORAGE_TRANSACTION_STACK_KEY);
      94       33523 :         if (stack) {
      95       33523 :                 auto it = std::find(stack->stack.rbegin(), stack->stack.rend(), _data);
      96       33522 :                 if (it != stack->stack.rend()) {
      97       33521 :                         stack->stack.erase( std::next(it).base() );
      98             :                 }
      99             :         }
     100       33520 : }
     101             : 
     102        5900 : Transaction::Transaction(nullptr_t) : Transaction((Data *)nullptr) { }
     103             : 
     104       17642 : Transaction::Transaction(Data *d) : _data(d) { }
     105             : 
     106       18575 : void Transaction::setRole(AccessRoleId id) const {
     107       18575 :         _data->role = id;
     108       18575 : }
     109        8700 : AccessRoleId Transaction::getRole() const {
     110        8700 :         return _data->role;
     111             : }
     112             : 
     113           0 : const Value & Transaction::setValue(const StringView &key, Value &&val) {
     114           0 :         return _data->data.emplace(key.str<Interface>(), std::move(val)).first->second;
     115             : }
     116             : 
     117           0 : const Value &Transaction::getValue(const StringView &key) const {
     118           0 :         auto it = _data->data.find(key);
     119           0 :         if (it != _data->data.end()) {
     120           0 :                 return it->second;
     121             :         }
     122           0 :         return Value::Null;
     123             : }
     124             : 
     125        1925 : Value Transaction::setObject(int64_t id, Value &&val) const {
     126        1925 :         Value ret;
     127        1925 :         pool::push(_data->objects.get_allocator());
     128             :         do {
     129        1925 :                 ret = _data->objects.emplace(id, std::move(val)).first->second;
     130             :         } while (0);
     131        1925 :         pool::pop();
     132        1925 :         return ret;
     133           0 : }
     134             : 
     135          75 : Value Transaction::getObject(int64_t id) const {
     136          75 :         auto it = _data->objects.find(id);
     137          75 :         if (it != _data->objects.end()) {
     138           0 :                 return it->second;
     139             :         }
     140          75 :         return Value::Null;
     141             : }
     142             : 
     143           0 : void Transaction::setStatus(int value) { _data->status = value; }
     144           0 : int Transaction::getStatus() const { return _data->status; }
     145             : 
     146           0 : void Transaction::setAdapter(const Adapter &a) {
     147           0 :         _data->adapter = a;
     148           0 : }
     149        6425 : const Adapter &Transaction::getAdapter() const {
     150        6425 :         return _data->adapter;
     151             : }
     152             : 
     153       15500 : bool Transaction::isInTransaction() const {
     154       15500 :         return _data->adapter.isInTransaction();
     155             : }
     156             : 
     157           0 : TransactionStatus Transaction::getTransactionStatus() const {
     158           0 :         return _data->adapter.getTransactionStatus();
     159             : }
     160             : 
     161             : struct DataHolder {
     162        1300 :         DataHolder(Transaction::Data *data, Worker &w) : _data(data) {
     163        1300 :                 _tmpRole = data->role;
     164        1300 :                 if (w.isSystem()) {
     165          50 :                         data->role = AccessRoleId::System;
     166             :                 }
     167        1300 :         }
     168             : 
     169        1300 :         ~DataHolder() {
     170        1300 :                 _data->role = _tmpRole;
     171        1300 :         }
     172             : 
     173             :         Transaction::Data *_data = nullptr;
     174             :         AccessRoleId _tmpRole = AccessRoleId::Nobody;
     175             : };
     176             : 
     177           0 : bool Transaction::foreach(Worker &w, const Query &query, const Callback<bool(Value &)> &cb) const {
     178           0 :         if (!w.scheme().hasAccessControl()) {
     179           0 :                 return _data->adapter.foreach(w, query, cb);
     180             :         }
     181             : 
     182           0 :         DataHolder h(_data, w);
     183             : 
     184           0 :         if (!isOpAllowed(w.scheme(), Select)) {
     185           0 :                 return false;
     186             :         }
     187             : 
     188           0 :         auto r = w.scheme().getAccessRole(_data->role);
     189           0 :         auto d = w.scheme().getAccessRole(AccessRoleId::Default);
     190             : 
     191           0 :         if ((d && d->onSelect && !d->onSelect(w, query)) || (r && r->onSelect && !r->onSelect(w, query))) {
     192           0 :                 return false;
     193             :         }
     194             : 
     195           0 :         return _data->adapter.foreach(w, query, [&, this] (Value &val) -> bool {
     196           0 :                 if (processReturnObject(w.scheme(), val)) {
     197           0 :                         return cb(val);
     198             :                 }
     199           0 :                 return true;
     200           0 :         });
     201           0 : }
     202             : 
     203        6274 : Value Transaction::select(Worker &w, const Query &query) const {
     204        6274 :         if (!w.scheme().hasAccessControl()) {
     205        5799 :                 auto val = _data->adapter.select(w, query);
     206        5800 :                 if (val.empty()) {
     207         300 :                         return Value();
     208             :                 }
     209        5500 :                 return val;
     210        5800 :         }
     211             : 
     212         475 :         DataHolder h(_data, w);
     213             : 
     214         475 :         if (!isOpAllowed(w.scheme(), Select)) {
     215          25 :                 return Value();
     216             :         }
     217             : 
     218         450 :         auto r = w.scheme().getAccessRole(_data->role);
     219         450 :         auto d = w.scheme().getAccessRole(AccessRoleId::Default);
     220             : 
     221         450 :         if ((d && d->onSelect && !d->onSelect(w, query)) || (r && r->onSelect && !r->onSelect(w, query))) {
     222           0 :                 return Value();
     223             :         }
     224             : 
     225         450 :         auto val = _data->adapter.select(w, query);
     226             : 
     227         450 :         auto &arr = val.asArray();
     228         450 :         auto it = arr.begin();
     229         925 :         while (it != arr.end()) {
     230         475 :                 if (processReturnObject(w.scheme(), *it)) {
     231         425 :                         ++ it;
     232             :                 } else {
     233          50 :                         it = arr.erase(it);
     234             :                 }
     235             :         }
     236             : 
     237         450 :         if (val.empty()) {
     238          75 :                 return Value();
     239             :         }
     240         375 :         return val;
     241         475 : }
     242             : 
     243         375 : size_t Transaction::count(Worker &w, const Query &q) const {
     244         375 :         if (!w.scheme().hasAccessControl()) {
     245         375 :                 return _data->adapter.count(w, q);
     246             :         }
     247             : 
     248           0 :         DataHolder h(_data, w);
     249             : 
     250           0 :         if (!isOpAllowed(w.scheme(), Count)) {
     251           0 :                 return 0;
     252             :         }
     253             : 
     254           0 :         auto r = w.scheme().getAccessRole(_data->role);
     255           0 :         auto d = w.scheme().getAccessRole(AccessRoleId::Default);
     256             : 
     257           0 :         if ((d && d->onCount && !d->onCount(w, q)) || (r && r->onCount && !r->onCount(w, q))) {
     258           0 :                 return 0;
     259             :         }
     260             : 
     261           0 :         return _data->adapter.count(w, q);
     262           0 : }
     263             : 
     264         275 : bool Transaction::remove(Worker &w, uint64_t oid) const {
     265         275 :         if (!w.scheme().hasAccessControl()) {
     266         275 :                 return _data->adapter.remove(w, oid);
     267             :         }
     268             : 
     269           0 :         DataHolder h(_data, w);
     270             : 
     271           0 :         if (!isOpAllowed(w.scheme(), Remove)) {
     272           0 :                 return false;
     273             :         }
     274             : 
     275           0 :         auto r = w.scheme().getAccessRole(_data->role);
     276           0 :         auto d = w.scheme().getAccessRole(AccessRoleId::Default);
     277             : 
     278           0 :         bool hasR = (r && r->onRemove);
     279           0 :         bool hasD = (d && d->onRemove);
     280             : 
     281           0 :         if (hasR || hasD) {
     282           0 :                 if (auto obj = acquireObject(w.scheme(), oid)) {
     283           0 :                         if ((!hasD || d->onRemove(w, obj)) && (!hasR || r->onRemove(w, obj))) {
     284           0 :                                 return _data->adapter.remove(w, oid);
     285             :                         }
     286           0 :                 }
     287           0 :                 return false;
     288             :         }
     289             : 
     290           0 :         return _data->adapter.remove(w, oid);
     291           0 : }
     292             : 
     293        3650 : Value Transaction::create(Worker &w, Value &data) const {
     294        3650 :         if (!w.scheme().hasAccessControl()) {
     295        3350 :                 return _data->adapter.create(w, data);
     296             :         }
     297             : 
     298         300 :         DataHolder h(_data, w);
     299             : 
     300         300 :         if (!isOpAllowed(w.scheme(), Create)) {
     301           0 :                 return Value();
     302             :         }
     303             : 
     304         300 :         Value ret;
     305         300 :         if (perform([&, this] {
     306         300 :                 auto r = w.scheme().getAccessRole(_data->role);
     307         300 :                 auto d = w.scheme().getAccessRole(AccessRoleId::Default);
     308             : 
     309         300 :                 if (data.isArray()) {
     310           0 :                         auto &arr = data.asArray();
     311           0 :                         auto it = arr.begin();
     312           0 :                         while (it != arr.end()) {
     313           0 :                                 if ((d && d->onCreate && !d->onCreate(w, *it)) || (r && r->onCreate && !r->onCreate(w, *it))) {
     314           0 :                                         it = arr.erase(it);
     315             :                                 } else {
     316           0 :                                         ++ it;
     317             :                                 }
     318             :                         }
     319             : 
     320           0 :                         if (auto val = _data->adapter.create(w, data)) {
     321           0 :                                 auto &arr = val.asArray();
     322           0 :                                 auto it = arr.begin();
     323             : 
     324           0 :                                 while (it != arr.end()) {
     325           0 :                                         if ((d && d->onCreate && !d->onCreate(w, *it)) || (r && r->onCreate && !r->onCreate(w, *it))) {
     326           0 :                                                 it = arr.erase(it);
     327             :                                         } else {
     328           0 :                                                 ++ it;
     329             :                                         }
     330             :                                 }
     331             : 
     332           0 :                                 ret =  !arr.empty() ? std::move(val) : Value(true);
     333           0 :                                 return true; // if user can not see result - return success but with no object
     334           0 :                         }
     335             :                 } else {
     336         300 :                         if ((d && d->onCreate && !d->onCreate(w, data)) || (r && r->onCreate && !r->onCreate(w, data))) {
     337           0 :                                 return false;
     338             :                         }
     339             : 
     340         300 :                         if (auto val = _data->adapter.create(w, data)) {
     341         300 :                                 ret =  processReturnObject(w.scheme(), val) ? std::move(val) : Value(true);
     342         300 :                                 return true; // if user can not see result - return success but with no object
     343         300 :                         }
     344             :                 }
     345           0 :                 return false;
     346             :         })) {
     347         300 :                 return ret;
     348             :         }
     349           0 :         return Value();
     350         300 : }
     351             : 
     352        1850 : Value Transaction::save(Worker &w, uint64_t oid, Value &obj, Value &patch, Set<const Field *> &fields) const {
     353        1850 :         if (!w.scheme().hasAccessControl()) {
     354        1825 :                 return _data->adapter.save(w, oid, obj, patch, fields);
     355             :         }
     356             : 
     357          25 :         DataHolder h(_data, w);
     358             : 
     359          25 :         if (!isOpAllowed(w.scheme(), Save)) {
     360           0 :                 return Value();
     361             :         }
     362             : 
     363          25 :         Value ret;
     364          25 :         if (perform([&, this] {
     365          25 :                 auto r = w.scheme().getAccessRole(_data->role);
     366          25 :                 auto d = w.scheme().getAccessRole(AccessRoleId::Default);
     367             : 
     368          25 :                 bool hasR = (r && r->onSave);
     369          25 :                 bool hasD = (d && d->onSave);
     370             : 
     371          25 :                 if (hasR || hasD) {
     372           0 :                         if ((hasD && !d->onSave(w, obj, patch, fields)) || (hasR && !r->onSave(w, obj, patch, fields))) {
     373           0 :                                 return false;
     374             :                         }
     375             : 
     376           0 :                         if (auto val = _data->adapter.save(w, oid, obj, patch, fields)) {
     377           0 :                                 ret = processReturnObject(w.scheme(), val) ? std::move(val) : Value(true);
     378           0 :                                 return true;
     379           0 :                         }
     380           0 :                         return false;
     381             :                 }
     382             : 
     383          25 :                 if (auto val = _data->adapter.save(w, oid, obj, patch, fields)) {
     384          25 :                         ret = processReturnObject(w.scheme(), val) ? std::move(val) : Value(true);
     385          25 :                         return true;
     386          25 :                 }
     387           0 :                 return false;
     388             :         })) {
     389          25 :                 return ret;
     390             :         }
     391           0 :         return Value();
     392          25 : }
     393             : 
     394         725 : Value Transaction::patch(Worker &w, uint64_t oid, Value &data) const {
     395         725 :         Value tmp;
     396         725 :         if (!w.scheme().hasAccessControl()) {
     397         850 :                 return _data->adapter.save(w, oid, tmp, data, Set<const Field *>());
     398             :         }
     399             : 
     400         300 :         DataHolder h(_data, w);
     401             : 
     402         300 :         if (!isOpAllowed(w.scheme(), Patch)) {
     403           0 :                 return Value();
     404             :         }
     405             : 
     406         300 :         Value ret;
     407         300 :         if (perform([&, this] {
     408         300 :                 auto r = w.scheme().getAccessRole(_data->role);
     409         300 :                 auto d = w.scheme().getAccessRole(AccessRoleId::Default);
     410         300 :                 if ((d && d->onPatch && !d->onPatch(w, oid, data)) || (r && r->onPatch && !r->onPatch(w, oid, data))) {
     411           0 :                         return false;
     412             :                 }
     413             : 
     414         600 :                 if (auto val = _data->adapter.save(w, oid, tmp, data, Set<const Field *>())) {
     415         300 :                         ret = processReturnObject(w.scheme(), val) ? std::move(val) : Value(true);
     416         300 :                         return true;
     417         300 :                 }
     418           0 :                 return false;
     419             :         })) {
     420         300 :                 return ret;
     421             :         }
     422           0 :         return Value();
     423         725 : }
     424             : 
     425         350 : Value Transaction::field(Action a, Worker &w, uint64_t oid, const Field &f, Value &&patch) const {
     426         350 :         if (!w.scheme().hasAccessControl()) {
     427         200 :                 return _data->adapter.field(a, w, oid, f, std::move(patch));
     428             :         }
     429             : 
     430         150 :         DataHolder h(_data, w);
     431             : 
     432         150 :         if (!isOpAllowed(w.scheme(), getTransactionOp(a), &f)) {
     433           0 :                 return Value();
     434             :         }
     435             : 
     436         150 :         auto r = w.scheme().getAccessRole(_data->role);
     437         150 :         auto d = w.scheme().getAccessRole(AccessRoleId::Default);
     438             : 
     439         150 :         if ((r && r->onField) || (d && d->onField) || f.getSlot()->readFilterFn) {
     440           0 :                 if (auto obj = acquireObject(w.scheme(), oid)) {
     441           0 :                         return field(a, w, obj, f, std::move(patch));
     442           0 :                 }
     443           0 :                 return Value();
     444             :         }
     445             : 
     446         150 :         Value ret;
     447         150 :         if (perform([&, this] {
     448         150 :                 ret = _data->adapter.field(a, w, oid, f, std::move(patch));
     449         150 :                 return true;
     450             :         })) {
     451         150 :                 if (a != Action::Remove) {
     452         150 :                         if (processReturnField(w.scheme(), Value(oid), f, ret)) {
     453         150 :                                 return ret;
     454             :                         }
     455             :                 } else {
     456           0 :                         return ret;
     457             :                 }
     458             :         }
     459           0 :         return Value();
     460         150 : }
     461         425 : Value Transaction::field(Action a, Worker &w, const Value &obj, const Field &f, Value &&patch) const {
     462         425 :         if (!w.scheme().hasAccessControl()) {
     463         375 :                 return _data->adapter.field(a, w, obj, f, std::move(patch));
     464             :         }
     465             : 
     466          50 :         DataHolder h(_data, w);
     467             : 
     468          50 :         if (!isOpAllowed(w.scheme(), getTransactionOp(a), &f)) {
     469           0 :                 return Value();
     470             :         }
     471             : 
     472          50 :         Value ret;
     473          50 :         if (perform([&, this] {
     474          50 :                 auto r = w.scheme().getAccessRole(_data->role);
     475          50 :                 auto d = w.scheme().getAccessRole(AccessRoleId::Default);
     476             : 
     477          50 :                 if ((d && d->onField && !d->onField(a, w, obj, f, patch)) || (r && r->onField && !r->onField(a, w, obj, f, patch))) {
     478           0 :                         return false;
     479             :                 }
     480             : 
     481          50 :                 ret = _data->adapter.field(a, w, obj, f, std::move(patch));
     482          50 :                 return true;
     483             :         })) {
     484          50 :                 if (a != Action::Remove) {
     485          50 :                         if (processReturnField(w.scheme(), obj, f, ret)) {
     486          50 :                                 return ret;
     487             :                         }
     488             :                 } else {
     489           0 :                         return ret;
     490             :                 }
     491             :         }
     492           0 :         return Value();
     493          50 : }
     494             : 
     495         200 : bool Transaction::removeFromView(const Scheme &scheme, const FieldView &field, uint64_t oid, const Value &obj) const {
     496         200 :         if (!isOpAllowed(scheme, RemoveFromView)) {
     497           0 :                 return false;
     498             :         }
     499             : 
     500         200 :         return _data->adapter.removeFromView(field, &scheme, oid);
     501             : }
     502             : 
     503         125 : bool Transaction::addToView(const Scheme &scheme, const FieldView &field, uint64_t oid, const Value &obj, const Value &viewObj) const {
     504         125 :         if (!isOpAllowed(scheme, AddToView)) {
     505           0 :                 return false;
     506             :         }
     507             : 
     508         125 :         return _data->adapter.addToView(field, &scheme, oid, viewObj);
     509             : }
     510             : 
     511        1025 : int64_t Transaction::getDeltaValue(const Scheme &scheme) {
     512        1025 :         if (!isOpAllowed(scheme, Delta)) {
     513           0 :                 return false;
     514             :         }
     515             : 
     516        1025 :         return _data->adapter.getDeltaValue(scheme);
     517             : }
     518             : 
     519          50 : int64_t Transaction::getDeltaValue(const Scheme &scheme, const FieldView &f, uint64_t id) {
     520          50 :         if (!isOpAllowed(scheme, DeltaView)) {
     521           0 :                 return false;
     522             :         }
     523             : 
     524          50 :         return _data->adapter.getDeltaValue(scheme, f, id);
     525             : }
     526             : 
     527         775 : Vector<int64_t> Transaction::performQueryListForIds(const QueryList &list, size_t count) const {
     528        1875 :         for (auto &it : list.getItems()) {
     529        1100 :                 if (!isOpAllowed(*it.scheme, Id)) {
     530           0 :                         return Vector<int64_t>();
     531             :                 }
     532             :         }
     533             : 
     534         775 :         return _data->adapter.performQueryListForIds(list, count);
     535             : }
     536             : 
     537        1500 : Value Transaction::performQueryList(const QueryList &list, size_t count, bool forUpdate) const {
     538        1500 :         count = (count == stappler::maxOf<size_t>()) ? list.size() : count;
     539        3150 :         for (auto &it : list.getItems()) {
     540        1650 :                 if (!isOpAllowed(*it.scheme, Id)) {
     541           0 :                         return Value();
     542             :                 }
     543             :         }
     544             : 
     545        1500 :         if (!isOpAllowed(*list.getScheme(), Select)) {
     546           0 :                 return Value();
     547             :         }
     548             : 
     549        1500 :         Value vals;
     550        1500 :         auto &t = list.getContinueToken();
     551        1500 :         if (t && count == list.size()) {
     552         375 :                 if (count > 1) {
     553           0 :                         auto &item = list.getItems().at(list.size() - 2);
     554           0 :                         return performQueryListField(list, *item.field);
     555             :                 } else {
     556         375 :                         auto q = list.getItems().back().query;
     557         750 :                         vals = t.perform(*list.getItems().back().scheme, *this, q,
     558         750 :                                         t.hasFlag(ContinueToken::Inverted) ? Ordering::Descending : Ordering::Ascending);
     559         375 :                 }
     560             :         } else {
     561        1125 :                 vals = _data->adapter.performQueryList(list, count, forUpdate);
     562             :         }
     563             : 
     564        1500 :         if (vals) {
     565        1500 :                 auto &arr = vals.asArray();
     566        1500 :                 auto it = arr.begin();
     567        7000 :                 while (it != arr.end()) {
     568        5500 :                         if (processReturnObject(*list.getScheme(), *it)) {
     569        5500 :                                 ++ it;
     570             :                         } else {
     571           0 :                                 it = arr.erase(it);
     572             :                         }
     573             :                 }
     574             :         }
     575        1500 :         return vals;
     576        1500 : }
     577             : 
     578         125 : Value Transaction::performQueryListField(const QueryList &list, const Field &f) const {
     579         125 :         auto count = list.size();
     580         125 :         if (f.getType() == Type::View || f.getType() == Type::Set) {
     581          75 :                 count -= 1;
     582             :         }
     583             : 
     584         325 :         for (auto &it : list.getItems()) {
     585         200 :                 if (!isOpAllowed(*it.scheme, Id)) {
     586           0 :                         return Value();
     587             :                 }
     588             :         }
     589             : 
     590         125 :         if (!isOpAllowed(*list.getScheme(), FieldGet, &f)) {
     591           0 :                 return Value();
     592             :         }
     593             : 
     594         125 :         auto ids = performQueryListForIds(list, count);
     595         125 :         if (ids.size() == 1) {
     596         100 :                 auto id = ids.front();
     597         100 :                 auto scheme = list.getItems().at(count - 1).scheme;
     598         100 :                 if (f.getType() == Type::View || f.getType() == Type::Set) {
     599          50 :                         db::Query q = db::Query::field(id, f.getName(), list.getItems().back().query);
     600          50 :                         Worker w(*scheme, *this);
     601             : 
     602          50 :                         Value obj(id);
     603          50 :                         auto r = scheme->getAccessRole(_data->role);
     604          50 :                         auto d = scheme->getAccessRole(AccessRoleId::Default);
     605             : 
     606          50 :                         if ((r && r->onField) || (d && d->onField) || f.getSlot()->readFilterFn) {
     607           0 :                                 if ((obj = acquireObject(w.scheme(), id))) {
     608           0 :                                         Value tmp;
     609           0 :                                         if (!d->onField(Action::Get, w, obj, f, tmp) || !r->onField(Action::Get, w, obj, f, tmp)) {
     610           0 :                                                 return Value();
     611             :                                         }
     612           0 :                                 }
     613             :                         }
     614             : 
     615          50 :                         Value val;
     616          50 :                         if (auto &t = list.getContinueToken()) {
     617           0 :                                 val = t.perform(*scheme, *this, q, t.hasFlag(ContinueToken::Inverted) ? Ordering::Descending : Ordering::Ascending);
     618             :                         } else {
     619          50 :                                 val = scheme->select(*this, q);
     620             :                         }
     621          50 :                         if (val) {
     622          25 :                                 if (!processReturnField(*scheme, obj, f, val)) {
     623           0 :                                         return Value();
     624             :                                 }
     625             :                         }
     626          50 :                         return val;
     627          50 :                 } else {
     628          50 :                         if (auto obj = acquireObject(*scheme, id)) {
     629          50 :                                 return scheme->getProperty(*this, obj, f, list.getItems().at(list.size() - 1).getQueryFields());
     630          50 :                         }
     631             :                 }
     632             :         }
     633             : 
     634          25 :         return Value();
     635         125 : }
     636             : 
     637        2550 : void Transaction::scheduleAutoField(const Scheme &scheme, const Field &field, uint64_t id) const {
     638        2550 :         _data->adapter.scheduleAutoField(scheme, field, id);
     639        2550 : }
     640             : 
     641        2625 : bool Transaction::beginTransaction() const {
     642        2625 :         return _data->adapter.beginTransaction();
     643             : }
     644        2625 : bool Transaction::endTransaction() const {
     645        2625 :         if (_data->adapter.endTransaction()) {
     646        2625 :                 if (!_data->adapter.isInTransaction()) {
     647        2625 :                         clearObjectStorage();
     648             :                 }
     649        2625 :                 return true;
     650             :         }
     651           0 :         return false;
     652             : }
     653           0 : void Transaction::cancelTransaction() const {
     654           0 :         _data->adapter.cancelTransaction();
     655           0 : }
     656             : 
     657        2625 : void Transaction::clearObjectStorage() const {
     658        2625 :         _data->objects.clear();
     659        2625 : }
     660             : 
     661        1650 : static bool Transaction_processFields(const Scheme &scheme, const Value &val, Value &obj, const Map<String, Field> &vec) {
     662        1650 :         if (obj.isDictionary()) {
     663        1650 :                 auto &dict = obj.asDict();
     664        1650 :                 auto it = dict.begin();
     665        7900 :                 while (it != dict.end()) {
     666        6250 :                         auto f_it = vec.find(it->first);
     667        6250 :                         if (f_it != vec.end()) {
     668        4950 :                                 auto slot = f_it->second.getSlot();
     669        4950 :                                 if (slot->readFilterFn) {
     670           0 :                                         if (!slot->readFilterFn(scheme, val, it->second)) {
     671           0 :                                                 it = dict.erase(it);
     672           0 :                                                 continue;
     673             :                                         }
     674             :                                 }
     675             : 
     676        4950 :                                 if (slot->type == Type::Extra) {
     677         350 :                                         auto extraSlot = static_cast<const FieldExtra *>(slot);
     678         350 :                                         if (!Transaction_processFields(scheme, val, it->second, extraSlot->fields)) {
     679           0 :                                                 it = dict.erase(it);
     680           0 :                                                 continue;
     681             :                                         }
     682             :                                 }
     683             :                         }
     684        6250 :                         ++ it;
     685             :                 }
     686             :         }
     687             : 
     688        1650 :         return true;
     689             : }
     690             : 
     691        6600 : bool Transaction::processReturnObject(const Scheme &scheme, Value &val) const {
     692        6600 :         if (!scheme.hasAccessControl()) {
     693        5225 :                 return true;
     694             :         }
     695             : 
     696        1375 :         auto r = scheme.getAccessRole(_data->role);
     697        1375 :         auto d = scheme.getAccessRole(AccessRoleId::Default);
     698             : 
     699           0 :         if ((d && d->onReturn && !d->onReturn(scheme, val))
     700        1375 :                         || (r && r->onReturn && !r->onReturn(scheme, val))) {
     701          75 :                 return false;
     702             :         }
     703             : 
     704        1300 :         return Transaction_processFields(scheme, val, val, scheme.getFields());
     705             : }
     706             : 
     707         225 : bool Transaction::processReturnField(const Scheme &scheme, const Value &obj, const Field &field, Value &val) const {
     708         225 :         if (!scheme.hasAccessControl()) {
     709          25 :                 return true;
     710             :         }
     711             : 
     712         200 :         auto slot = field.getSlot();
     713         200 :         if (slot->readFilterFn) {
     714           0 :                 if (obj.isInteger()) {
     715           0 :                         if (auto tmpObj = acquireObject(scheme, obj.getInteger())) {
     716           0 :                                 if (!slot->readFilterFn(scheme, tmpObj, val)) {
     717           0 :                                         return false;
     718             :                                 }
     719             :                         } else {
     720           0 :                                 return false;
     721           0 :                         }
     722             :                 } else {
     723           0 :                         if (!slot->readFilterFn(scheme, obj, val)) {
     724           0 :                                 return false;
     725             :                         }
     726             :                 }
     727             :         }
     728             : 
     729         200 :         auto r = scheme.getAccessRole(_data->role);
     730         200 :         auto d = scheme.getAccessRole(AccessRoleId::Default);
     731             : 
     732           0 :         if ((d && d->onReturnField && !d->onReturnField(scheme, field, val))
     733         200 :                         || (r && r->onReturnField && !r->onReturnField(scheme, field, val))) {
     734           0 :                 return false;
     735             :         }
     736             : 
     737         200 :         if (field.getType() == Type::Object || field.getType() == Type::Set || field.getType() == Type::View) {
     738           0 :                 if (auto nextScheme = field.getForeignScheme()) {
     739           0 :                         if (val.isDictionary()) {
     740           0 :                                 if (!processReturnObject(*nextScheme, val)) {
     741           0 :                                         return false;
     742             :                                 }
     743           0 :                         } else if (val.isArray()) {
     744           0 :                                 auto &arr = val.asArray();
     745           0 :                                 auto it = arr.begin();
     746           0 :                                 while (it != arr.end()) {
     747           0 :                                         if (processReturnObject(*nextScheme, *it)) {
     748           0 :                                                 ++ it;
     749             :                                         } else {
     750           0 :                                                 it = arr.erase(it);
     751             :                                         }
     752             :                                 }
     753             :                         }
     754             :                 }
     755             :         }
     756         200 :         return true;
     757             : }
     758             : 
     759        7275 : bool Transaction::isOpAllowed(const Scheme &scheme, Op op, const Field *f) const {
     760        7275 :         if (!scheme.hasAccessControl()) {
     761        4575 :                 return true;
     762             :         }
     763             : 
     764        2700 :         if (auto r = scheme.getAccessRole(_data->role)) {
     765        2475 :                 return r->operations.test(stappler::toInt(op));
     766             :         }
     767         225 :         switch (op) {
     768           0 :         case Op::None:
     769             :         case Op::Max:
     770           0 :                 return false;
     771             :                 break;
     772         100 :         case Op::Id:
     773             :         case Op::Select:
     774             :         case Op::Count:
     775             :         case Op::Delta:
     776             :         case Op::DeltaView:
     777             :         case Op::FieldGet:
     778         100 :                 return true;
     779             :                 break;
     780         125 :         case Op::Remove:
     781             :         case Op::Create:
     782             :         case Op::Save:
     783             :         case Op::Patch:
     784             :         case Op::FieldSet:
     785             :         case Op::FieldAppend:
     786             :         case Op::FieldClear:
     787             :         case Op::FieldCount:
     788             :         case Op::RemoveFromView:
     789             :         case Op::AddToView:
     790         125 :                 return _data->role == AccessRoleId::Admin || _data->role == AccessRoleId::System;
     791             :                 break;
     792             :         }
     793           0 :         return false;
     794             : }
     795             : 
     796        5618 : Transaction::Data::Data(const Adapter &adapter, memory::pool_t *p) : adapter(adapter), pool(p) { }
     797             : 
     798             : 
     799          50 : Value Transaction::acquireObject(const Scheme &scheme, uint64_t oid) const {
     800          50 :         Value ret;
     801          50 :         pool::push(_data->objects.get_allocator());
     802             :         do {
     803          50 :                 auto it = _data->objects.find(oid);
     804          50 :                 if (it == _data->objects.end()) {
     805         100 :                         if (auto obj = Worker(scheme, *this).asSystem().get(oid)) {
     806          50 :                                 ret = _data->objects.emplace(oid, std::move(obj)).first->second;
     807          50 :                         }
     808             :                 } else {
     809           0 :                         ret = it->second;
     810             :                 }
     811             :         } while (0);
     812          50 :         pool::pop();
     813          50 :         return ret;
     814           0 : }
     815             : 
     816           0 : AccessRole &AccessRole::define(Transaction::Op op) {
     817           0 :         operations.set(op);
     818           0 :         return *this;
     819             : }
     820         175 : AccessRole &AccessRole::define(AccessRoleId id) {
     821         175 :         users.set(stappler::toInt(id));
     822         175 :         return *this;
     823             : }
     824           0 : AccessRole &AccessRole::define() {
     825           0 :         return *this;
     826             : }
     827          25 : AccessRole &AccessRole::define(OnSelect &&val) {
     828          25 :         if (val.get()) {
     829          25 :                 operations.set(Transaction::Select);
     830             :         }
     831          25 :         onSelect = std::move(val.get());
     832          25 :         return *this;
     833             : }
     834           0 : AccessRole &AccessRole::define(OnCount &&val) {
     835           0 :         if (val.get()) {
     836           0 :                 operations.set(Transaction::Count);
     837             :         }
     838           0 :         onCount = std::move(val.get());
     839           0 :         return *this;
     840             : }
     841          75 : AccessRole &AccessRole::define(OnCreate &&val) {
     842          75 :         if (val.get()) {
     843          75 :                 operations.set(Transaction::Create);
     844             :         }
     845          75 :         onCreate = std::move(val.get());
     846          75 :         return *this;
     847             : }
     848           0 : AccessRole &AccessRole::define(OnPatch &&val) {
     849           0 :         if (val.get()) {
     850           0 :                 operations.set(Transaction::Patch);
     851             :         }
     852           0 :         onPatch = std::move(val.get());
     853           0 :         return *this;
     854             : }
     855           0 : AccessRole &AccessRole::define(OnSave &&val) {
     856           0 :         if (val.get()) {
     857           0 :                 operations.set(Transaction::Save);
     858             :         }
     859           0 :         onSave = std::move(val.get());
     860           0 :         return *this;
     861             : }
     862           0 : AccessRole &AccessRole::define(OnRemove &&val) {
     863           0 :         if (val.get()) {
     864           0 :                 operations.set(Transaction::Remove);
     865             :         }
     866           0 :         onRemove = std::move(val.get());
     867           0 :         return *this;
     868             : }
     869           0 : AccessRole &AccessRole::define(OnField &&val) {
     870           0 :         onField = std::move(val.get());
     871           0 :         return *this;
     872             : }
     873          50 : AccessRole &AccessRole::define(OnReturn &&val) {
     874          50 :         onReturn = std::move(val.get());
     875          50 :         return *this;
     876             : }
     877           0 : AccessRole &AccessRole::define(OnReturnField &&val) {
     878           0 :         onReturnField = std::move(val.get());
     879           0 :         return *this;
     880             : }
     881             : 
     882             : }

Generated by: LCOV version 1.14