1. 程式人生 > >eos對資料庫的操作

eos對資料庫的操作

eosio的multi_index
概述
multi_index是eosio上的資料庫管理介面,通過eosio::multi_index智慧合約能夠寫入、讀取和修改eosio資料庫的資料
multi_index在eosio中的位置:eos/contracts/eosiolib/multi_index.hpp
eosio::multi_index來源於boost庫的boost::multi_index
eosio::multi_index在概念上和傳統資料庫的“表(table)”類似,資料“行(rows)”是獨立的物件(通常是class物件或struct物件),資料“列(columns)”是物件的成員屬性(class或struct的成員屬性)
eosio::multi_index提供和傳統資料庫的“鍵(key)”類似的成員屬性,用於快速查詢物件
eosio::multi_index支援主鍵(primary key),但必須是唯一的無符號64位整型(uint64_t)
eosio::multi_index按主鍵排序時,使用升序
eosio::multi_index允許使用自定義函式作為索引,但它的返回值是受限制的,只能是支援的鍵型別
multi_index表允許多索引排序,最多可以使用16個二級索引
二級索引作為multi_index表建構函式的一部分建立,不支援直接構建
multi_index迭代器可以雙向迭代,即const_iterator或const_reverse_iterator
建立multi_index表
使用C++類(class)或結構體(struct)定義物件
在class或struct中,定義一個const成員函式:primary_key(),返回uint64_t型別的主鍵值
確定二級索引(最多支援16個),二級索引不侷限於uint64_t,它支援更多型別
二級索引支援的鍵型別:
idx64:64位無符號整型鍵
idx128:128位無符號整型鍵
idx256:256位固定大小字典鍵
idx_double:雙精度浮點鍵
idx_long_double:四倍精度浮點鍵
1
2
3
4
5
6
為每個二級索引定義extractor,即一個函式,用於從Multi-Index表的函式中獲取鍵,這個函式會被indexed_by(後面會講)用到
一個完整的multi_index表定義如下:
//定義address表,i64表示索引使用預設的uint64_t型別

//@abi table address i64
struct address {
uint64_t account;
string name;
uint64_t phone;
uint64_t liked;

//定義address表的主鍵
uint64_t primary_key() const { return account; }
//定義extractor,二級索引是phone
uint64_t get_phone() const {return phone; }

//序列化
EOSLIB_SERIALIZE(address, (account)(name)(phone)(liked))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
使用multi_index表
(一).例項化
// 第一個引數是表名(即address),第二個引數是表物件的型別(address),其餘為可變引數Indices(二級索引),數量不能超過16個
typedef eosio::multi_index< N(address), address, indexed_by<N(phone), const_mem_fun<address, uint64_t, &address::get_phone>>> address_index;

// 建構函式,有兩個引數uint64_t型別引數:code, scope
// code: 擁有這張multi_index表的賬戶,該賬戶擁有對合約資料的讀寫許可權
// scope: code層級內的範圍識別符號
address_index addresses(_self, _self);
1
2
3
4
5
6
7
(二).表的操作

  1. emplace
    新增一個新物件(row)到表中
    const_iterator emplace( unit64_t payer, Lambda&& constructor )
    引數
    payer:為新物件使用的儲存付費的賬戶
    constructor:lambda函式,可以讓新建立的物件就地初始化
    返回值
    返回一個新建立的物件的主鍵迭代器
    前置條件
    payer是被當前Action授權的有效賬戶,允許為使用儲存付費
    操作結果
    帶有唯一主鍵的新物件在multi-index表中被建立;
    這個物件會被序列化,然後寫入表中;
    如果表不存在,則建立表。
    二級索引被更新,用以引用新新增的物件;
    如果二級索引表不存在,則建立它們。
    payer為建立新物件所使用的儲存付費;
    如果multi-index表和二級索引表需要被建立,則payer為表的建立付費。
    異常
    當前接收者(multi_index的code引數)不是表的擁有者時,丟擲異常
  2. erase
    使用主鍵從表中刪除現有物件(兩種形式)
    const_iterator erase( const_iterator itr )
    void erase( const object_type& obj )
    引數
    itr:指向待刪除物件的迭代器
    obj:待刪除物件的引用
    返回值
    使用itr查詢物件時,返回被刪除物件之後的物件的指標
    操作結果
    物件從表中刪除,相關的儲存被回收;
    表相關的二級索引被更新;
    退還被刪除物件的payer所支付的儲存費用和相關費用。
    異常
    待刪除物件不存在時、Action無權修改表資料時、給定迭代器無效時,丟擲異常
  3. modify
    修改表中已存在的物件(兩種形式)
    void modify( const_iterator itr, uint64_t payer, Lambda&& updater )
    void modify( const object_type &obj, uint64_t payer, Lambda&& updater )
    引數
    itr:指向待更新物件的迭代器
    obj:待更新物件的引用
    payer:為更新資料付費的賬戶,為0表示更新資料的payer和建立時的payer相同
    updater:用於更新目標物件的lambda函式
    前置條件
    itr指向的物件,或obj引用的物件是存在的
    payer是被當前Action授權的有效賬戶,允許為使用儲存付費
    操作結果
    更新後的物件被序列化,然後替換表中的現有物件;
    二級索引被更新,被更新物件的主鍵不變。
    payer為更新物件所使用的儲存付費;
    如果payer和該物件現有的payer相同,只需要為現有物件和更新物件不同的部分付費,如果差額為負,還會退還費用;
    如果payer和該物件現有的payer不同,則會退還費用給現有的payer。
    異常
    無效的前提條件下呼叫,會丟擲異常,並中止執行
    當前接收者(multi_index的code引數)不是表的擁有者時,丟擲異常
  4. get
    使用主鍵從表中查詢已存在的物件
    const object_type& get( uint64_t primary ) const
    引數
    primary:要查詢物件的主鍵值
    返回值
    包含指定主鍵的物件的常量引用
    異常
    沒有任何物件與給定的主鍵匹配時,丟擲異常
  5. find
    使用主鍵從表中查詢已存在的物件
    const_iterator find( uint64_t primary ) const
    引數
    primary:要查詢物件的主鍵值
    返回值
    返回一個查詢到的物件的迭代器
    如果沒有查詢到指定物件,返回一個end迭代器
    (三).成員訪問
    獲取擁有主表的賬戶名
    uint64_t get_code() const

在code下的範圍id(scope id),在該範圍內可以找到期望的主表例項
uint64_t get_scope() const

(四). 不支援的C++特性
eosio::multi_index不支援拷貝建構函式(Copy constructor)
eosio::multi_index不支援賦值運算子(Assignment operator)
(五).迭代器
multi_index迭代器遵循C++迭代器模式,所有迭代器都是雙向迭代,即const_iterator或const_reverse_iterator。
迭代器可以被間接引用,以提供對multi_index表中物件的訪問。

  1. begin & cbegin
    返回指向物件型別的、從最小主鍵值開始的迭代器
    const_iterator begin() const
    const_iterator cbegin() const

end & cend
返回指向虛擬行的迭代器,代表剛剛過去的最後一行,不能被間接引用;
可以向後推進,不能向前推進。
const_iterator end() const
const_iterator end() const

rbegin & crbegin
返回和begin/cbegin類似的,但反向的迭代器
const_iterator rbegin() const
const_iterator crbegin() const

rend & crend
返回和end/cend類似的,但反向的迭代器
const_iterator rend() const
const_iterator crend() const

lower_bound
查詢大於等於給定主鍵值的物件
const_iterator lower_bound( uint64_t primary ) const

upper_bound
查詢大於給定主鍵值的物件
const_iterator upper_bound( uint64_t primary ) const

get_index
返回一個適當型別的二級索引
secondary_index get_index

iterator_to
返回給定物件的迭代器
const_iterator iterator_to( const object_type& obj ) const

indexed_by
indexed_by結構體用於例項化multi_index表的索引
indexed_by在multi_index.hpp中的定義如下:

// 引數IndexName是索引的名稱,這個名稱是base32編碼後的64位整數,且需要符合EOS命名規範:
// 1. 最多13個字元,前12個字元只能是小寫字母、0-5、“.”
// 2. 如果有第13個字元,則只能是小寫字母a-p、“.”
// 引數Extractor是一個函式,用於從multi_index表的函式中獲取鍵,返回一個二級索引型別或二級索引型別的引用
template<uint64_t IndexName, typename Extractor>
struct indexed_by {
enum constants { index_name = IndexName };
typedef Extractor secondary_extractor_type;
};

// 推薦使用eosio::const_mem_fun模板,它是boost::multi_index::const_mem_fun的類型別名,例子:
// multi_index表的名字是N(address), N是一個巨集,可以把base32編碼後的字串轉換為uint64_t
// multi_index表的物件型別是address
// multi_index通過名為N(phone)的二級索引進行檢索
// const_mem_fun是一個用於address型別的鍵提取器(key extractor )
// const_mem_fun提取的鍵型別是uint64_t,通過address::get_phone函式獲取鍵
eosio::multi_index< N(address), address, indexed_by<N(phone), const_mem_fun<address, uint64_t, &address::get_phone>>>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
完整例子
//@abi action
void likebyphone(uint64_t phone) {
address_index addresses(_self, _self);

auto phone_index = addresses.get_index<N(phone)>();
auto itr = phone_index.lower_bound(phone);
for(; itr != phone_index.end() && itr->phone == phone; ++itr) {
    phone_index.modify(itr, 0, [&](auto& address){
        eosio::print("Liking: ", address.name.c_str(), "\n");
        address.liked++;
    });
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
(六). 工具函式
available_primary_key
返回一個可用(未使用)的主鍵值,用於主鍵嚴格自增的表,它不會被設定為自定義值
uint64_t available_primary_key() const