×
Community Blog EOSIO Smart Contract Database Walkthrough

EOSIO Smart Contract Database Walkthrough

In this blog, we will learn the basics of building an EOSIO smart contract database as well as querying, modifying, and deleting data in the database.

EOS.IO is a blockchain protocol that emulates most of the attributes of a real computer, powered by the native cryptocurrency EOS. According to the official documentation,

The EOS.IO software introduces a new blockchain architecture designed to enable vertical and horizontal scaling of decentralized applications. This is achieved by creating an operating system-like construct upon which applications can be built. The software provides accounts, authentication, databases, asynchronous communication, and the scheduling of applications across many of CPU cores or clusters. The resulting technology is a blockchain architecture that may ultimately scale to millions of transactions per second, eliminates user fees, and allows for quick and easy deployment and maintenance of decentralized applications, in the context of a governed blockchain.

In this article, we will learn the basics of building an EOSIO smart contract database by covering the following topics:

  1. Create
  2. Query
  3. Modify
  4. Delete

Create

Generally, the emplace constructor is used to create database objects.

.cpp

void test_da::create(account_name user, string title, string content)
    {
        require_auth( user ); // Authenticate user permissions.
        das datable( _self, user); // Define a database object.
        datable.emplace(user, [&]( da & d){
            d.title = title;
            d.content = content;
            d.post_id = datable.available_primary_key();
            d.poster = user;
        });// Create data in a database.
    }

Precautions:

  1. In the definition of a database object, the first parameter _self indicates the contract owner. The second parameter user indicates the database payer, namely, the account to which the database storage belongs.
  2. The emplace constructor receives a payer parameter and a lamada expression. This structure is fixed.

Let's view the definition of the test_da class.

.hpp

class test_da : public contract {
     public:
           test_da( account_name self ):contract(self){}

           // @abi action
           void create(account_name user, string title, string content);

     private:
           // @abi table data i64
           struct da {
                 uint64_t     post_id;
                 account_name poster;
                 string       title;
                 string       content;

                 uint64_t primary_key()const { return post_id; }
                 account_name get_poster() const { return poster; }

                 EOSLIB_SERIALIZE(da, (post_id)(poster)(title)(content))
           };
           typedef eosio::multi_index<N(data), da, indexed_by<N(byposter), const_mem_fun<da, account_name, &da::get_poster>> > das;
  };
} /// namespace eosio

  1. All smart contracts are inherited from contract. test_da( account_name self ):contract(self){} is the constructor of the test_da contract.
  2. The row following test_da( account_name self ):contract(self){} is a declaration of the create function.
  3. The rows following the create function define data fields. Here we define the data structure da.
  4. The primary_key function defines the primary key.
  5. The row following the primary_key function defines a secondary key, that is, return poster.
  6. The first parameter of the macro EOSLIB_SERIALIZE is the data structure, and the other parameters are data members in the data structure.
  7. typedef defines the das type, which is a database object. In this example, we define a database object containing a primary key and a secondary key.

.abi

abi is very important. An incorrect abi will cause contract execution failure.

{
  "types": [],
  "structs": [{
      "name": "da",
      "base": "",
      "fields": [{
          "name": "post_id",
          "type": "uint64"
        },{
          "name": "poster",
          "type": "account_name"
        },{
          "name": "title",
          "type": "string"
        },{
          "name": "content",
          "type": "string"
        }
      ]
    },{
      "name": "create",
      "base": "",
      "fields": [{
          "name": "user",
          "type": "account_name"
        },{
          "name": "title",
          "type": "string"
        },{
          "name": "content",
          "type": "string"
        }
      ]
    }}
  ],
  "actions": [{
          "name": "create",
          "type": "create",
        }
  ],
  "tables": [{
      "name": "data",
      "index_type": "i64",
      "key_names": [
        "post_id"
      ],
      "key_types": [
        "uint64"
      ],
      "type": "da"
    }
  ],
  "ricardian_clauses": []
}

Data Creation Example

Publish a contract.

cleos set contract eosio test_da     
Reading WAST/WASM from test_da/test_da.wasm...
Using already assembled WASM...
Publishing contract...
executed transaction: 3d6f04278617d3807fe876a33057f1155acf9c9e5a392ac6ed8ad51e79506009  6752 bytes  24679 us
#         eosio <= eosio::setcode               {"account":"eosio","vmtype":0,"vmversion":0,"code":"0061736d0100000001ad011a60037f7e7e0060057f7e7e7f...
#         eosio <= eosio::setabi                {"account":"eosio","abi":{"types":[],"structs":[{"name":"da","base":"","fields":[{"name":"post_id","...

Create data.

cleos push action eosio create '{"user":"eosio","title":"first","content":"create a first one"}' -p eosio
executed transaction: 830057f270fa499b1d61b82e80ad8cda1774cdc1786c1e786f558a3e0a48974c  216 bytes  17229 us
#         eosio <= eosio::create                {"user":"eosio","title":"first","content":"create a first one"}

Query the data table.

cleos get table eosio eosio data
{
  "rows": [{
      "post_id": 0,
      "poster": "eosio",
      "title": "first",
      "content": "create a first one"
    }
  ],
  "more": false
}

The query result indicates that data has been created successfully. Will it be successful if we create data using another account?

cleos push action eosio create '{"user":"eostea","title":"eostea first","content":"eostea create a first one"}' -p eostea
executed transaction: 8542a87e563a9c62b7dbe46ae09ccf829c7821f8879167066b658096718de148  232 bytes  2243 us
#         eosio <= eosio::create                {"user":"eostea","title":"eostea first","content":"eostea create a first one"}

Query the data table.

cleos get table eosio eostea data
{
  "rows": [{
      "post_id": 0,
      "poster": "eostea",
      "title": "eostea first",
      "content": "eostea create a first one"
    }
  ],
  "more": false
}

Great, it works! Here we shall have no questions about data creation.

Query

The most important function of a database is data query. If there is no query function, data in the database cannot be presented and therefore becomes meaningless. The database can be queried by primary key or secondary index.

In this document, all data is stored in one table to diversify the table data.

The preceding das datable( _self, user); in .cpp is replaced with das datable( _self, _self);. In this way, all data is stored in the table under the contract account.

Query data by primary key

In this example, a method is added to query and print data.

void test_da::getd(uint64_t post_id){
        das datable(_self, _self);
        auto post_da = datable.find( post_id);
        eosio::print("Post_id: ", post_da->post_id, "  Post_Tile: ", post_da->title.c_str(), " Content: ", post_da->content.c_str());
    }

The .abi file is also modified accordingly.

Run the following commands:

cleos push action eosio getd '{"post_id":1}' -p eosio
executed transaction: ac8663235462d947c74542af848cca54a059c3991d193237025da7d4767d6725  192 bytes  1724 us
#         eosio <= eosio::getd                  {"post_id":1}
>> Post_id: 1  Post_Tile: first Content: eosio create a first one

Query data by secondary index

Add the following code to query data by secondary index:

auto poster_index = datable.template get_index<N(byposter)>();
auto pos = poster_index.find( user );

for (; pos != poster_index.end(); pos++)
{
    eosio::print("content:", pos->content.c_str(), " post_id:", pos->post_id, " title:", pos->title.c_str());
}

Obtain the secondary index and use it to query data. In this example, only the find function is used for query.

Run getd.

cleos push action eosio getd '{"post_id":2,"user": "eostea"}' -p eosio
executed transaction: 2370e1fb1ee8a581f7321f02fb40645e51269e579d183c33ef470dba0b3afdbc  200 bytes  5403 us
#         eosio <= eosio::getd                  {"post_id":2,"user":"eostea"}
>> Post_id: 2  Post_Tile: eostea first Content: eostea create a first onecontent:eostea create a first one post_id:2 title:eostea first

The data query result is as follows:

cleos get table eosio eosio data
{
 "rows": [{
     "post_id": 0,
     "poster": "eosio",
     "title": "first",
     "content": "eostea create a first one"
   },{
     "post_id": 1,
     "poster": "eosio",
     "title": "first",
     "content": "eostea create a first one"
   },{
     "post_id": 2,
     "poster": "eostea",
     "title": "eostea first",
     "content": "eostea create a first one"
   }
 ],
 "more": false
}

Modify

Modify data in the database.

The existing data in the database is as follows:

cleos get table eosio eosio data
{
 "rows": [{
     "post_id": 0,
     "poster": "eosio",
     "title": "first",
     "content": "eostea create a first one"
   },{
     "post_id": 1,
     "poster": "eosio",
     "title": "first",
     "content": "eostea create a first one"
   },{
     "post_id": 2,
     "poster": "eostea",
     "title": "eostea first",
     "content": "eostea create a first one"
   }
 ],
 "more": false
}

Use the following action code to modify the data:

void test_da::change(account_name user, uint64_t post_id, string title, string content)
    {
        require_auth(user);
        das datable( _self, user);
        auto post = datable.find(post_id);
        eosio_assert(post->poster == user, "yonghucuowu");
        datable.modify(post, user, [&](auto& p){
            if (title != "")
                p.title = title;
            if (content != "")
                p.content = content;
        });
    }

The first few rows of the code have been described before. We only describe the modify method in the code. The first parameter post is the object you have found for modification, and the second parameter user indicates the payer.

Run the following commands:

cleos push action eosio change '{"user":"eosio","post_id":1,"title":"change","content":"change action"}' -p eosio
executed transaction: 8cb561a712f2741560118651aefd49efd161e3d73c56f6d24cf1d699c265e2dc  224 bytes  2130 us
#         eosio <= eosio::change                {"user":"eosio","post_id":1,"title":"change","content":"change action"}

Query the database data.

cleos get table eosio eosio data
{
  "rows": [{
      "post_id": 0,
      "poster": "eosio",
      "title": "first",
      "content": "eostea create a first one"
    },{
      "post_id": 1,
      "poster": "eosio",
      "title": "change",
      "content": "change action"
    },{
      "post_id": 2,
      "poster": "eostea",
      "title": "eostea first",
      "content": "eostea create a first one"
    }
  ],
  "more": false
}

The query result indicates that the data record containing post_id=1 has been modified.

Delete

The following action function is added to delete data:

void test_da::dele(account_name user, uint64_t post_id)
    {
        require_auth(user);
        das datable( _self, user);
        auto post = datable.find(post_id);
        eosio::print(post->title.c_str());

        eosio_assert(post->poster == user, "yonghucuowu");
        datable.erase(post);
    }

The erase method is called to delete data, and the parameter is a data object. Check the command output.

cleos push action eosio dele '{"user":"eosio","post_id":1}' -p eosioexecuted transaction: 3affbbbbd1da328ddcf37753f1f2f6c5ecc36cd81a0e12fea0c789e75b59714e  200 bytes  2383 us
#         eosio <= eosio::dele                  {"user":"eosio","post_id":1}

Query the database data.

cleos get table eosio eosio data
{
  "rows": [{
      "post_id": 0,
      "poster": "eosio",
      "title": "first",
      "content": "eostea create a first one"
    },{
      "post_id": 2,
      "poster": "eostea",
      "title": "eostea first",
      "content": "eostea create a first one"
    }
  ],
  "more": false
}

The query result indicates that the data record containing post_id=1 has been deleted.

1 1 1
Share on

Alibaba Clouder

2,599 posts | 765 followers

You may also like

Comments

Raja_KT March 5, 2019 at 5:09 am

Hope to see its progressive use in many smart contracts