Line data Source code
1 : /**
2 : * Copyright Soramitsu Co., Ltd. All Rights Reserved.
3 : * SPDX-License-Identifier: Apache-2.0
4 : */
5 :
6 : #include "ametsuchi/impl/storage_impl.hpp"
7 :
8 : #include <soci/postgresql/soci-postgresql.h>
9 : #include <boost/format.hpp>
10 :
11 : #include "ametsuchi/impl/flat_file/flat_file.hpp"
12 : #include "ametsuchi/impl/mutable_storage_impl.hpp"
13 : #include "ametsuchi/impl/peer_query_wsv.hpp"
14 : #include "ametsuchi/impl/postgres_block_index.hpp"
15 : #include "ametsuchi/impl/postgres_block_query.hpp"
16 : #include "ametsuchi/impl/postgres_command_executor.hpp"
17 : #include "ametsuchi/impl/postgres_query_executor.hpp"
18 : #include "ametsuchi/impl/postgres_wsv_query.hpp"
19 : #include "ametsuchi/impl/temporary_wsv_impl.hpp"
20 : #include "backend/protobuf/permissions.hpp"
21 : #include "common/bind.hpp"
22 : #include "common/byteutils.hpp"
23 : #include "converters/protobuf/json_proto_converter.hpp"
24 : #include "postgres_ordering_service_persistent_state.hpp"
25 :
26 : namespace {
27 : void prepareStatements(soci::connection_pool &connections, size_t pool_size) {
28 4774 : for (size_t i = 0; i != pool_size; i++) {
29 4340 : soci::session &session = connections.at(i);
30 4340 : iroha::ametsuchi::PostgresCommandExecutor::prepareStatements(session);
31 4340 : }
32 434 : }
33 :
34 : /**
35 : * Verify whether postgres supports prepared transactions
36 : */
37 : bool preparedTransactionsAvailable(soci::session &sql) {
38 434 : int prepared_txs_count = 0;
39 434 : sql << "SHOW max_prepared_transactions;", soci::into(prepared_txs_count);
40 434 : return prepared_txs_count != 0;
41 0 : }
42 :
43 : } // namespace
44 :
45 : namespace iroha {
46 : namespace ametsuchi {
47 : const char *kCommandExecutorError = "Cannot create CommandExecutorFactory";
48 : const char *kPsqlBroken = "Connection to PostgreSQL broken: %s";
49 : const char *kTmpWsv = "TemporaryWsv";
50 :
51 : ConnectionContext::ConnectionContext(
52 : std::unique_ptr<KeyValueStorage> block_store)
53 434 : : block_store(std::move(block_store)) {}
54 :
55 : StorageImpl::StorageImpl(
56 : std::string block_store_dir,
57 : PostgresOptions postgres_options,
58 : std::unique_ptr<KeyValueStorage> block_store,
59 : std::shared_ptr<soci::connection_pool> connection,
60 : std::shared_ptr<shared_model::interface::CommonObjectsFactory> factory,
61 : std::shared_ptr<shared_model::interface::BlockJsonConverter> converter,
62 : std::shared_ptr<shared_model::interface::PermissionToString>
63 : perm_converter,
64 : size_t pool_size,
65 : bool enable_prepared_blocks)
66 434 : : block_store_dir_(std::move(block_store_dir)),
67 434 : postgres_options_(std::move(postgres_options)),
68 434 : block_store_(std::move(block_store)),
69 434 : connection_(std::move(connection)),
70 434 : factory_(std::move(factory)),
71 434 : converter_(std::move(converter)),
72 434 : perm_converter_(std::move(perm_converter)),
73 434 : log_(logger::log("StorageImpl")),
74 434 : pool_size_(pool_size),
75 434 : prepared_blocks_enabled_(enable_prepared_blocks),
76 434 : block_is_prepared(false) {
77 434 : prepared_block_name_ =
78 434 : "prepared_block" + postgres_options_.dbname().value_or("");
79 434 : soci::session sql(*connection_);
80 : // rollback current prepared transaction
81 : // if there exists any since last session
82 : if (prepared_blocks_enabled_) {
83 434 : rollbackPrepared(sql);
84 434 : }
85 434 : sql << init_;
86 434 : prepareStatements(*connection_, pool_size_);
87 434 : }
88 :
89 : expected::Result<std::unique_ptr<TemporaryWsv>, std::string>
90 : StorageImpl::createTemporaryWsv() {
91 712 : std::shared_lock<std::shared_timed_mutex> lock(drop_mutex);
92 : if (connection_ == nullptr) {
93 0 : return expected::makeError("Connection was closed");
94 : }
95 712 : auto sql = std::make_unique<soci::session>(*connection_);
96 :
97 712 : return expected::makeValue<std::unique_ptr<TemporaryWsv>>(
98 712 : std::make_unique<TemporaryWsvImpl>(
99 712 : std::move(sql), factory_, perm_converter_));
100 712 : }
101 :
102 : expected::Result<std::unique_ptr<MutableStorage>, std::string>
103 : StorageImpl::createMutableStorage() {
104 543 : boost::optional<shared_model::interface::types::HashType> top_hash;
105 :
106 543 : std::shared_lock<std::shared_timed_mutex> lock(drop_mutex);
107 543 : if (connection_ == nullptr) {
108 4 : return expected::makeError("Connection was closed");
109 : }
110 :
111 539 : auto sql = std::make_unique<soci::session>(*connection_);
112 : // if we create mutable storage, then we intend to mutate wsv
113 : // this means that any state prepared before that moment is not needed
114 : // and must be removed to preventy locking
115 539 : if (block_is_prepared) {
116 2 : rollbackPrepared(*sql);
117 2 : }
118 539 : auto block_result = getBlockQuery()->getTopBlock();
119 539 : return expected::makeValue<std::unique_ptr<MutableStorage>>(
120 539 : std::make_unique<MutableStorageImpl>(
121 539 : block_result.match(
122 : [](expected::Value<
123 : std::shared_ptr<shared_model::interface::Block>> &block) {
124 22 : return block.value->hash();
125 : },
126 : [](expected::Error<std::string> &) {
127 517 : return shared_model::interface::types::HashType("");
128 0 : }),
129 539 : std::make_shared<PostgresCommandExecutor>(*sql, perm_converter_),
130 539 : std::move(sql),
131 539 : factory_));
132 543 : }
133 :
134 : boost::optional<std::shared_ptr<PeerQuery>> StorageImpl::createPeerQuery()
135 : const {
136 1919 : auto wsv = getWsvQuery();
137 1919 : if (not wsv) {
138 0 : return boost::none;
139 : }
140 1919 : return boost::make_optional<std::shared_ptr<PeerQuery>>(
141 1919 : std::make_shared<PeerQueryWsv>(wsv));
142 1919 : }
143 :
144 : boost::optional<std::shared_ptr<BlockQuery>> StorageImpl::createBlockQuery()
145 : const {
146 1662 : auto block_query = getBlockQuery();
147 1662 : if (not block_query) {
148 1 : return boost::none;
149 : }
150 1661 : return boost::make_optional(block_query);
151 1662 : }
152 :
153 : boost::optional<std::shared_ptr<OrderingServicePersistentState>>
154 : StorageImpl::createOsPersistentState() const {
155 1205 : log_->info("create ordering service persistent state");
156 1205 : std::shared_lock<std::shared_timed_mutex> lock(drop_mutex);
157 1205 : if (not connection_) {
158 1 : log_->info("connection to database is not initialised");
159 1 : return boost::none;
160 : }
161 1204 : return boost::make_optional<
162 : std::shared_ptr<OrderingServicePersistentState>>(
163 1204 : std::make_shared<PostgresOrderingServicePersistentState>(
164 1204 : std::make_unique<soci::session>(*connection_)));
165 1205 : }
166 :
167 : boost::optional<std::shared_ptr<QueryExecutor>>
168 : StorageImpl::createQueryExecutor(
169 : std::shared_ptr<PendingTransactionStorage> pending_txs_storage,
170 : std::shared_ptr<shared_model::interface::QueryResponseFactory>
171 : response_factory) const {
172 171 : std::shared_lock<std::shared_timed_mutex> lock(drop_mutex);
173 171 : if (not connection_) {
174 0 : log_->info("connection to database is not initialised");
175 0 : return boost::none;
176 : }
177 171 : return boost::make_optional<std::shared_ptr<QueryExecutor>>(
178 171 : std::make_shared<PostgresQueryExecutor>(
179 170 : std::make_unique<soci::session>(*connection_),
180 171 : *block_store_,
181 170 : std::move(pending_txs_storage),
182 170 : converter_,
183 170 : std::move(response_factory),
184 170 : perm_converter_));
185 171 : }
186 :
187 : bool StorageImpl::insertBlock(const shared_model::interface::Block &block) {
188 245 : log_->info("create mutable storage");
189 245 : auto storageResult = createMutableStorage();
190 245 : bool inserted = false;
191 245 : storageResult.match(
192 : [&](expected::Value<std::unique_ptr<ametsuchi::MutableStorage>>
193 : &storage) {
194 245 : inserted = storage.value->apply(block);
195 245 : log_->info("block inserted: {}", inserted);
196 245 : commit(std::move(storage.value));
197 245 : },
198 : [&](expected::Error<std::string> &error) {
199 0 : log_->error(error.error);
200 0 : });
201 :
202 245 : return inserted;
203 245 : }
204 :
205 : bool StorageImpl::insertBlocks(
206 : const std::vector<std::shared_ptr<shared_model::interface::Block>>
207 : &blocks) {
208 246 : log_->info("create mutable storage");
209 246 : bool inserted = true;
210 246 : auto storageResult = createMutableStorage();
211 246 : storageResult.match(
212 : [&](iroha::expected::Value<std::unique_ptr<MutableStorage>>
213 : &mutableStorage) {
214 : std::for_each(blocks.begin(), blocks.end(), [&](auto block) {
215 247 : inserted &= mutableStorage.value->apply(*block);
216 247 : });
217 246 : commit(std::move(mutableStorage.value));
218 246 : },
219 : [&](iroha::expected::Error<std::string> &error) {
220 0 : log_->error(error.error);
221 0 : inserted = false;
222 0 : });
223 :
224 246 : log_->info("insert blocks finished");
225 246 : return inserted;
226 246 : }
227 :
228 : void StorageImpl::reset() {
229 490 : log_->info("drop wsv records from db tables");
230 490 : soci::session sql(*connection_);
231 490 : sql << reset_;
232 :
233 490 : log_->info("drop blocks from disk");
234 490 : block_store_->dropAll();
235 490 : }
236 :
237 : void StorageImpl::dropStorage() {
238 441 : log_->info("drop storage");
239 441 : if (connection_ == nullptr) {
240 9 : log_->warn("Tried to drop storage without active connection");
241 9 : return;
242 : }
243 :
244 432 : if (auto dbname = postgres_options_.dbname()) {
245 432 : auto &db = dbname.value();
246 432 : std::unique_lock<std::shared_timed_mutex> lock(drop_mutex);
247 432 : log_->info("Drop database {}", db);
248 432 : freeConnections();
249 432 : soci::session sql(soci::postgresql,
250 432 : postgres_options_.optionsStringWithoutDbName());
251 : // perform dropping
252 432 : sql << "DROP DATABASE " + db;
253 432 : } else {
254 0 : soci::session(*connection_) << drop_;
255 : }
256 :
257 : // erase blocks
258 432 : log_->info("drop block store");
259 432 : block_store_->dropAll();
260 441 : }
261 :
262 : void StorageImpl::freeConnections() {
263 1111 : if (connection_ == nullptr) {
264 677 : log_->warn("Tried to free connections without active connection");
265 677 : return;
266 : }
267 : // rollback possible prepared transaction
268 434 : if (block_is_prepared) {
269 5 : soci::session sql(*connection_);
270 5 : rollbackPrepared(sql);
271 5 : }
272 434 : std::vector<std::shared_ptr<soci::session>> connections;
273 4774 : for (size_t i = 0; i < pool_size_; i++) {
274 4340 : connections.push_back(std::make_shared<soci::session>(*connection_));
275 4340 : connections[i]->close();
276 4340 : log_->debug("Closed connection {}", i);
277 4340 : }
278 434 : connections.clear();
279 434 : connection_.reset();
280 1111 : }
281 :
282 : expected::Result<bool, std::string> StorageImpl::createDatabaseIfNotExist(
283 : const std::string &dbname,
284 : const std::string &options_str_without_dbname) {
285 : try {
286 435 : soci::session sql(soci::postgresql, options_str_without_dbname);
287 :
288 : int size;
289 434 : std::string name = dbname;
290 :
291 434 : sql << "SELECT count(datname) FROM pg_catalog.pg_database WHERE "
292 434 : "datname = :dbname",
293 434 : soci::into(size), soci::use(name);
294 :
295 434 : if (size == 0) {
296 432 : std::string query = "CREATE DATABASE ";
297 432 : query += dbname;
298 432 : sql << query;
299 432 : return expected::makeValue(true);
300 432 : }
301 2 : return expected::makeValue(false);
302 434 : } catch (std::exception &e) {
303 1 : return expected::makeError<std::string>(
304 1 : std::string("Connection to PostgreSQL broken: ") + e.what());
305 1 : }
306 435 : }
307 :
308 : expected::Result<ConnectionContext, std::string>
309 : StorageImpl::initConnections(std::string block_store_dir) {
310 434 : auto log_ = logger::log("StorageImpl:initConnection");
311 434 : log_->info("Start storage creation");
312 :
313 434 : auto block_store = FlatFile::create(block_store_dir);
314 434 : if (not block_store) {
315 0 : return expected::makeError(
316 0 : (boost::format("Cannot create block store in %s") % block_store_dir)
317 0 : .str());
318 : }
319 434 : log_->info("block store created");
320 :
321 434 : return expected::makeValue(ConnectionContext(std::move(*block_store)));
322 434 : }
323 :
324 : expected::Result<std::shared_ptr<soci::connection_pool>, std::string>
325 : StorageImpl::initPostgresConnection(std::string &options_str,
326 : size_t pool_size) {
327 434 : auto pool = std::make_shared<soci::connection_pool>(pool_size);
328 :
329 4774 : for (size_t i = 0; i != pool_size; i++) {
330 4340 : soci::session &session = pool->at(i);
331 4340 : session.open(soci::postgresql, options_str);
332 4340 : }
333 434 : return expected::makeValue(pool);
334 434 : };
335 :
336 : expected::Result<std::shared_ptr<StorageImpl>, std::string>
337 : StorageImpl::create(
338 : std::string block_store_dir,
339 : std::string postgres_options,
340 : std::shared_ptr<shared_model::interface::CommonObjectsFactory> factory,
341 : std::shared_ptr<shared_model::interface::BlockJsonConverter> converter,
342 : std::shared_ptr<shared_model::interface::PermissionToString>
343 : perm_converter,
344 : size_t pool_size) {
345 435 : boost::optional<std::string> string_res = boost::none;
346 :
347 435 : PostgresOptions options(postgres_options);
348 :
349 : // create database if
350 : options.dbname() | [&options, &string_res](const std::string &dbname) {
351 435 : createDatabaseIfNotExist(dbname, options.optionsStringWithoutDbName())
352 : .match([](expected::Value<bool> &val) {},
353 : [&string_res](expected::Error<std::string> &error) {
354 1 : string_res = error.error;
355 1 : });
356 435 : };
357 :
358 435 : if (string_res) {
359 1 : return expected::makeError(string_res.value());
360 : }
361 :
362 434 : auto ctx_result = initConnections(block_store_dir);
363 434 : auto db_result = initPostgresConnection(postgres_options, pool_size);
364 434 : expected::Result<std::shared_ptr<StorageImpl>, std::string> storage;
365 434 : ctx_result.match(
366 : [&](expected::Value<ConnectionContext> &ctx) {
367 434 : db_result.match(
368 : [&](expected::Value<std::shared_ptr<soci::connection_pool>>
369 : &connection) {
370 434 : soci::session sql(*connection.value);
371 434 : bool enable_prepared_transactions =
372 434 : preparedTransactionsAvailable(sql);
373 434 : storage = expected::makeValue(std::shared_ptr<StorageImpl>(
374 434 : new StorageImpl(block_store_dir,
375 434 : options,
376 434 : std::move(ctx.value.block_store),
377 434 : connection.value,
378 434 : factory,
379 434 : converter,
380 434 : perm_converter,
381 434 : pool_size,
382 434 : enable_prepared_transactions)));
383 434 : },
384 : [&](expected::Error<std::string> &error) { storage = error; });
385 434 : },
386 : [&](expected::Error<std::string> &error) { storage = error; });
387 434 : return storage;
388 435 : }
389 :
390 : void StorageImpl::commit(std::unique_ptr<MutableStorage> mutableStorage) {
391 535 : auto storage_ptr = std::move(mutableStorage); // get ownership of storage
392 535 : auto storage = static_cast<MutableStorageImpl *>(storage_ptr.get());
393 1071 : for (const auto &block : storage->block_store_) {
394 536 : storeBlock(*block.second);
395 : }
396 535 : *(storage->sql_) << "COMMIT";
397 535 : storage->committed = true;
398 535 : }
399 :
400 : bool StorageImpl::commitPrepared(
401 : const shared_model::interface::Block &block) {
402 710 : if (not prepared_blocks_enabled_) {
403 0 : log_->warn("prepared blocks are not enabled");
404 0 : return false;
405 : }
406 :
407 710 : if (not block_is_prepared) {
408 2 : log_->info("there are no prepared blocks");
409 2 : return false;
410 : }
411 708 : log_->info("applying prepared block");
412 :
413 : try {
414 708 : std::shared_lock<std::shared_timed_mutex> lock(drop_mutex);
415 708 : if (not connection_) {
416 3 : log_->info("connection to database is not initialised");
417 3 : return false;
418 : }
419 705 : soci::session sql(*connection_);
420 705 : sql << "COMMIT PREPARED '" + prepared_block_name_ + "';";
421 705 : PostgresBlockIndex block_index(sql);
422 705 : block_index.index(block);
423 705 : block_is_prepared = false;
424 1415 : } catch (const std::exception &e) {
425 0 : log_->warn("failed to apply prepared block {}: {}",
426 0 : block.hash().hex(),
427 0 : e.what());
428 0 : return false;
429 0 : }
430 :
431 705 : return storeBlock(block);
432 710 : }
433 :
434 : std::shared_ptr<WsvQuery> StorageImpl::getWsvQuery() const {
435 2065 : std::shared_lock<std::shared_timed_mutex> lock(drop_mutex);
436 2065 : if (not connection_) {
437 0 : log_->info("connection to database is not initialised");
438 0 : return nullptr;
439 : }
440 2065 : return std::make_shared<PostgresWsvQuery>(
441 2065 : std::make_unique<soci::session>(*connection_), factory_);
442 2065 : }
443 :
444 : std::shared_ptr<BlockQuery> StorageImpl::getBlockQuery() const {
445 2452 : std::shared_lock<std::shared_timed_mutex> lock(drop_mutex);
446 2452 : if (not connection_) {
447 2 : log_->info("connection to database is not initialised");
448 2 : return nullptr;
449 : }
450 2450 : return std::make_shared<PostgresBlockQuery>(
451 2450 : std::make_unique<soci::session>(*connection_),
452 2450 : *block_store_,
453 2450 : converter_);
454 2452 : }
455 :
456 : rxcpp::observable<std::shared_ptr<shared_model::interface::Block>>
457 : StorageImpl::on_commit() {
458 247 : return notifier_.get_observable();
459 0 : }
460 :
461 : void StorageImpl::prepareBlock(std::unique_ptr<TemporaryWsv> wsv) {
462 712 : auto &wsv_impl = static_cast<TemporaryWsvImpl &>(*wsv);
463 712 : if (not prepared_blocks_enabled_) {
464 0 : log_->warn("prepared block are not enabled");
465 0 : return;
466 : }
467 712 : if (not block_is_prepared) {
468 712 : soci::session &sql = *wsv_impl.sql_;
469 : try {
470 712 : sql << "PREPARE TRANSACTION '" + prepared_block_name_ + "';";
471 712 : block_is_prepared = true;
472 712 : } catch (const std::exception &e) {
473 0 : log_->warn("failed to prepare state: {}", e.what());
474 0 : }
475 :
476 712 : log_->info("state prepared successfully");
477 712 : }
478 712 : }
479 :
480 : StorageImpl::~StorageImpl() {
481 434 : freeConnections();
482 434 : }
483 :
484 : void StorageImpl::rollbackPrepared(soci::session &sql) {
485 : try {
486 441 : sql << "ROLLBACK PREPARED '" + prepared_block_name_ + "';";
487 7 : block_is_prepared = false;
488 434 : } catch (const std::exception &e) {
489 434 : log_->info(e.what());
490 434 : }
491 441 : }
492 :
493 : bool StorageImpl::storeBlock(const shared_model::interface::Block &block) {
494 1241 : auto json_result = converter_->serialize(block);
495 1241 : return json_result.match(
496 : [this, &block](const expected::Value<std::string> &v) {
497 1241 : block_store_->add(block.height(), stringToBytes(v.value));
498 1241 : notifier_.get_subscriber().on_next(clone(block));
499 1241 : return true;
500 0 : },
501 : [this](const expected::Error<std::string> &e) {
502 0 : log_->error(e.error);
503 0 : return false;
504 : });
505 1241 : }
506 :
507 : const std::string &StorageImpl::drop_ = R"(
508 : DROP TABLE IF EXISTS account_has_signatory;
509 : DROP TABLE IF EXISTS account_has_asset;
510 : DROP TABLE IF EXISTS role_has_permissions CASCADE;
511 : DROP TABLE IF EXISTS account_has_roles;
512 : DROP TABLE IF EXISTS account_has_grantable_permissions CASCADE;
513 : DROP TABLE IF EXISTS account;
514 : DROP TABLE IF EXISTS asset;
515 : DROP TABLE IF EXISTS domain;
516 : DROP TABLE IF EXISTS signatory;
517 : DROP TABLE IF EXISTS peer;
518 : DROP TABLE IF EXISTS role;
519 : DROP TABLE IF EXISTS height_by_hash;
520 : DROP TABLE IF EXISTS tx_status_by_hash;
521 : DROP TABLE IF EXISTS height_by_account_set;
522 : DROP TABLE IF EXISTS index_by_creator_height;
523 : DROP TABLE IF EXISTS index_by_id_height_asset;
524 : )";
525 :
526 : const std::string &StorageImpl::reset_ = R"(
527 : DELETE FROM account_has_signatory;
528 : DELETE FROM account_has_asset;
529 : DELETE FROM role_has_permissions CASCADE;
530 : DELETE FROM account_has_roles;
531 : DELETE FROM account_has_grantable_permissions CASCADE;
532 : DELETE FROM account;
533 : DELETE FROM asset;
534 : DELETE FROM domain;
535 : DELETE FROM signatory;
536 : DELETE FROM peer;
537 : DELETE FROM role;
538 : DELETE FROM height_by_hash;
539 : DELETE FROM tx_status_by_hash;
540 : DELETE FROM height_by_account_set;
541 : DELETE FROM index_by_creator_height;
542 : DELETE FROM index_by_id_height_asset;
543 : )";
544 :
545 : const std::string &StorageImpl::init_ =
546 43 : R"(
547 : CREATE TABLE IF NOT EXISTS role (
548 : role_id character varying(32),
549 : PRIMARY KEY (role_id)
550 : );
551 : CREATE TABLE IF NOT EXISTS domain (
552 : domain_id character varying(255),
553 : default_role character varying(32) NOT NULL REFERENCES role(role_id),
554 : PRIMARY KEY (domain_id)
555 : );
556 : CREATE TABLE IF NOT EXISTS signatory (
557 : public_key varchar NOT NULL,
558 : PRIMARY KEY (public_key)
559 : );
560 : CREATE TABLE IF NOT EXISTS account (
561 : account_id character varying(288),
562 : domain_id character varying(255) NOT NULL REFERENCES domain,
563 : quorum int NOT NULL,
564 : data JSONB,
565 : PRIMARY KEY (account_id)
566 : );
567 : CREATE TABLE IF NOT EXISTS account_has_signatory (
568 : account_id character varying(288) NOT NULL REFERENCES account,
569 : public_key varchar NOT NULL REFERENCES signatory,
570 : PRIMARY KEY (account_id, public_key)
571 : );
572 : CREATE TABLE IF NOT EXISTS peer (
573 : public_key varchar NOT NULL,
574 : address character varying(261) NOT NULL UNIQUE,
575 : PRIMARY KEY (public_key)
576 : );
577 : CREATE TABLE IF NOT EXISTS asset (
578 : asset_id character varying(288),
579 : domain_id character varying(255) NOT NULL REFERENCES domain,
580 : precision int NOT NULL,
581 : data json,
582 : PRIMARY KEY (asset_id)
583 : );
584 : CREATE TABLE IF NOT EXISTS account_has_asset (
585 : account_id character varying(288) NOT NULL REFERENCES account,
586 : asset_id character varying(288) NOT NULL REFERENCES asset,
587 : amount decimal NOT NULL,
588 : PRIMARY KEY (account_id, asset_id)
589 : );
590 : CREATE TABLE IF NOT EXISTS role_has_permissions (
591 : role_id character varying(32) NOT NULL REFERENCES role,
592 : permission bit()"
593 43 : + std::to_string(shared_model::interface::RolePermissionSet::size())
594 43 : + R"() NOT NULL,
595 : PRIMARY KEY (role_id)
596 : );
597 : CREATE TABLE IF NOT EXISTS account_has_roles (
598 : account_id character varying(288) NOT NULL REFERENCES account,
599 : role_id character varying(32) NOT NULL REFERENCES role,
600 : PRIMARY KEY (account_id, role_id)
601 : );
602 : CREATE TABLE IF NOT EXISTS account_has_grantable_permissions (
603 : permittee_account_id character varying(288) NOT NULL REFERENCES account,
604 : account_id character varying(288) NOT NULL REFERENCES account,
605 : permission bit()"
606 43 : + std::to_string(
607 43 : shared_model::interface::GrantablePermissionSet::size())
608 43 : + R"() NOT NULL,
609 : PRIMARY KEY (permittee_account_id, account_id)
610 : );
611 : CREATE TABLE IF NOT EXISTS height_by_hash (
612 : hash varchar,
613 : height text
614 : );
615 :
616 : CREATE TABLE IF NOT EXISTS tx_status_by_hash (
617 : hash varchar,
618 : status boolean
619 : );
620 :
621 : CREATE TABLE IF NOT EXISTS height_by_account_set (
622 : account_id text,
623 : height text
624 : );
625 : CREATE TABLE IF NOT EXISTS index_by_creator_height (
626 : id serial,
627 : creator_id text,
628 : height text,
629 : index text
630 : );
631 : CREATE TABLE IF NOT EXISTS index_by_id_height_asset (
632 : id text,
633 : height text,
634 : asset_id text,
635 : index text
636 : );
637 : )";
638 : } // namespace ametsuchi
639 : } // namespace iroha
|