eos源码赏析(十六):EOS智能合约数据表查询

2021-11-23 10:33:17 浏览数 (1)

今天群内讨论较多的内容为数据表的查询,集中在两个问题上:

  • 为什么我的数据表查出来是个空的?
  • 能否获取到某个数据表的数据量,即该表中存在几条数据?

针对这两个问题简单的给出答案就是:

  • 基于eos进行智能合约开发时的abi注释要规范
  • 可以修改一部分代码来获取某张表中的数据量,也可以获取到本次查询结果中的数据量。

数据表查询为空解决方案

仍旧以前文中的智能合约为例,在结构体声明及Multi-Index定义的时候要规范,且注释要准确,代码如下:

代码语言:javascript复制
private:
        account_name _this_contract;
        // @abi table heros i64
        struct heros
        {
                uint64_t heroid;          //天龙英雄的id
                string heroname;          //天龙英雄的名字
                string herodes;           //天龙英雄的个人描述
                string heroborn;          //天龙英雄的出生地
                uint64_t heroforceidx;    //天龙英雄的武力值
                uint64_t heroinsideidx;   //天龙英雄的内力值

                uint64_t primary_key() const{return heroid;}
                //等等,不再赘述

                EOSLIB_SERIALIZE(heros,(heroid)(heroname)(herodes)(heroborn)(heroforceidx)(heroinsideidx))
        };
        typedef eosio::multi_index<N(heros),heros,indexed_by<N(heroforceidx),const_mem_fun<heros,uint64_t,&heros::get_heroforceidx>>> heros_table;
        heros_table ht;

在这里我们生命了一个结构体heros,同时将结构体中的各个变量使用EOSLIB_SERIALIZE序列化,然后使用multi-index定义一个数据表heros_table(此处添加了二级索引,本文关注点不在此)。在我们平时的开发过程中,只是中的内容是对最终的实现效果影响不大的,而在eos智能合约开发的过程中,如果需要查询由multi-index生成的数据表中的内容,就要严格注意注释的使用。

此处的注释

代码语言:javascript复制
// @abi table heros i64

以及multi-index的定义的表名

代码语言:javascript复制
eosio::multi_index<N(heros),heros,indexed_by<N(heroforceidx),const_mem_fun<heros,uint64_t,&heros::get_heroforceidx>>> heros_table;

三者尽量保持一致,这样方便进行查询。当然没有注释的情况下,通过代码跟踪我们可以发现,数据也是可以写入到chainbase中,只不过使用命令行进行查询的时候查询结果为空。正常的查询结果如下:

代码语言:javascript复制
//查询表命令行,记得对应【合约】、【谁为这张表支付RAM的账户】、【表名】
cleos get table tlbb.code tlbb.code heros
//查询结果
{
  "rows": [{
      "heroid": 0,
      "heroname": "",
      "herodes": "丐帮帮主",
      "heroborn": "辽国",
      "heroforceidx": 1000,
      "heroinsideidx": 500
    },
   //等等内容
}

而如果注释不存在或者注释的表名不一致的则会出现如该合约中本表不存在或者说查出的结果是空的现象,基于此我们在智能合约的书写过程中,abi的生成还是重要的,尽管不会影响数据的写入操作或者action的执行操作,但如果没有规范的注释使用命令行查询或者调用RPC接口将会出现问题。

查询某数据表中的数据总量

在使用命令行查询数据表中的数据的时候,对查询的总量是做了限制的,我们在cleos的main.cpp中可以看出默认返回数据设置了限制,也就是不管我们查询的表中有多少数据,最多展示10个,其他的可以通过lower或者upper的方式展示:

代码语言:javascript复制
//查询表命令行,记得对应【合约】、【合约账户】、【表名】
cleos get table tlbb.code tlbb.code heros
//get table中关于limit的说明
uint32_t limit = 10;
getTable->add_option( "-l,--limit", limit, localized("The maximum number of rows to return") );

对于有分页需求的开发者来讲,表中一共存在有多少数据对他们来说是至关重要的,那么我们是否可以尝试获取到表中的数据总量呢?我们可以看到在遍历的过程中,如果数据量达到了limit的限制,则会跳出整个遍历过程,也就是查询结果中的数据量是受限于limit的值的:

代码语言:javascript复制
 for (; itr != upper;   itr) 
{
     copy_inline_row(*itr, data);
     if (p.json) 
     {   
         result.rows.emplace_back(abis.binary_to_variant(abis.get_table_type(p.table), data, abi_serializer_max_time));
     } else 
     {
         result.rows.emplace_back(fc::variant(data));
     }
     if (  count == p.limit || fc::time_point::now() > end) 
     {
         break;
     }
}

我们在查询数据表返回值的结构体加上一个字段来统计数据表中的总数据量,并在这个遍历的过程中使用自增的方式来统计从lower到upper的总值,即可得出总的数据量:

代码语言:javascript复制
//数据表查询返回值结构体
struct get_table_rows_result 
{
      vector<fc::variant> rows; ///< one row per item, either encoded as hex String or JSON object
      uint64_t            totalRows;  ///<统计总的数据量
      bool                more = false; ///< true if last element in data is not the end and sizeof data() < limit
};
//统计数据表中数据的总量
for (; itr != upper;   itr)
{
     result.totalRows  ;
}
//切记,该结构的FC映射也要加入该变量的映射
FC_REFLECT( eosio::chain_apis::read_only::get_table_rows_result, (rows)(more)(totalRows) );
//在cleos查询表数据的时候,加上以下打印
auto res = result.as<eosio::chain_apis::read_only::get_table_rows_result>();
auto totalRows = res.totalRows;
std::cout << "Total Row is:"<<totalRows<<std::endl;
std::cout << "Get Total Row is:"<<res.rows.size()<<std::endl;

经过以下步骤,再次执行命令行来查询数据表,便可获得这张表中的数据总量,本次查询返回的数据量,以及数据的具体信息:

  • 数据表查询返回值结构体中加入统计总量的参数。
  • FC_REFLECT加入新的变量的映射。
  • 查询时以自增的方式来统计总量。
  • cleos命令行加相关打印进行观察。

再次执行上面的命令行,返回的结果如下:

代码语言:javascript复制
cleos get table tlbb.code tlbb.code heros
Total Row is:17
Get Total Row is:10
{
  "rows": [{
      "heroid": 0,
      "heroname": "",
      "herodes": "丐帮帮主",
      "heroborn": "辽国",
      "heroforceidx": 1000,
      "heroinsideidx": 500
    },
   //等等,更多的数据
  "more": true,
  "totalRows": 17
}

也就是这张表中共有17条数据,而本次查询结果只返回了10条,也可以看到more为true代表着有更多信息等待展示。

本文从群内朋友提出的问题出发,介绍了智能合约开发中注释的规范使用,以及如何通过修改代码的方式来获取某张表中数据的总量,当然这是在本地测试节点下完成的,使用主网的过程中,这些代码我们是无法做出更改的。

0 人点赞