1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//! # IC DBMS Canister
//!
//! The `ic-dbms-canister` crate provides a database management system (DBMS) canister for the Internet Computer (IC).
//!
//! You can create a database canister by just defining your data tables using Rust structs and deriving the `Table` trait.
//!
//! ## Usage
//!
//! ### Add dependencies
//!
//! ```toml
//! [dependencies]
//! candid = { version = "0.10", features = ["value"] }
//! ic-cdk = "0.19"
//! ic-dbms-api = "0.9"
//! ic-dbms-canister = "0.9"
//! serde = "1"
//! ```
//!
//! ### Define Tables
//!
//! ```rust
//! use candid::CandidType;
//! use ic_dbms_api::prelude::{Nullable, Text, Uint32, Uint64};
//! use ic_dbms_canister::prelude::{DatabaseSchema, DbmsCanister, Table};
//! use serde::Deserialize;
//!
//! // define a simple `User` table
//!
//! #[derive(Debug, Table, CandidType, Deserialize, Clone, PartialEq, Eq)]
//! #[candid]
//! #[table = "users"]
//! pub struct User {
//! #[primary_key]
//! id: Uint64,
//! name: Text,
//! email: Text,
//! age: Nullable<Uint32>,
//! }
//!
//! // define a table referencing `User`
//!
//! #[derive(Debug, Table, CandidType, Deserialize, Clone, PartialEq, Eq)]
//! #[candid]
//! #[table = "posts"]
//! pub struct Post {
//! #[primary_key]
//! id: Uint32,
//! title: Text,
//! content: Text,
//! #[foreign_key(entity = "User", table = "users", column = "id")]
//! author: Uint32,
//! }
//!
//! // Finally define the schema. This will generate the API for the canister.
//!
//! #[derive(DatabaseSchema, DbmsCanister)]
//! #[tables(User = "users", Post = "posts")]
//! pub struct IcDbmsCanisterGenerator;
//! ```
//!
//! This will generate the following API:
//!
//! ```txt
//! service : (IcDbmsCanisterArgs) -> {
//! acl_add_principal : (principal) -> (Result);
//! acl_allowed_principals : () -> (vec principal) query;
//! acl_remove_principal : (principal) -> (Result);
//! begin_transaction : () -> (nat);
//! commit : (nat) -> (Result);
//! delete_posts : (DeleteBehavior, opt Filter_1, opt nat) -> (Result_1);
//! delete_users : (DeleteBehavior, opt Filter_1, opt nat) -> (Result_1);
//! insert_posts : (PostInsertRequest, opt nat) -> (Result);
//! insert_users : (UserInsertRequest, opt nat) -> (Result);
//! rollback : (nat) -> (Result);
//! select_posts : (Query, opt nat) -> (Result_2) query;
//! select_users : (Query_1, opt nat) -> (Result_3) query;
//! update_posts : (PostUpdateRequest, opt nat) -> (Result_1);
//! update_users : (UserUpdateRequest, opt nat) -> (Result_1);
//! }
//! ```
//!
//! ## API Documentation
//!
//! The API generated by the `ic-dbms-canister` is the following:
//!
//! ### ACL Management
//!
//! - `acl_add_principal(principal)`: Adds a principal to the ACL.
//! - `acl_allowed_principals()`: Returns the list of principals in the ACL.
//! - `acl_remove_principal(principal)`: Removes a principal from the ACL.
//!
//! ### Transaction Management
//!
//! - `begin_transaction()`: Starts a new transaction and returns its ID.
//! - `commit(transaction_id)`: Commits the transaction with the given ID. The user must own the transaction to commit it.
//! - `rollback(transaction_id)`: Rolls back the transaction with the given ID. The user must own the transaction to roll it back.
//!
//! ### Data Manipulation
//!
//! For each table defined in the schema, the following methods are generated:
//!
//! - `insert_<table_name>(records, transaction_id)`: Inserts records into the specified table. Optionally within a transaction.
//! - `select_<table_name>(query, transaction_id)`: Selects records from the specified table based on the query. Optionally within a transaction.
//! - `update_<table_name>(updates, transaction_id)`: Updates records in the specified table. Optionally within a transaction.
//! - `delete_<table_name>(delete_behavior, filter, transaction_id)`: Deletes records from the specified table based on the filter and delete behavior. Optionally within a transaction.
//!
//! ## Interacting with the Canister
//!
//! See the [ic-dbms-client](https://crates.io/crates/ic-dbms-client) crate for a client library to interact with the canister.
//!
//! ## How Internet Computer Memory Works
//!
//! On the Internet Computer, canisters have access to a stable memory that persists across upgrades.
//! This stable memory is divided into pages, each of which is 64 KiB in size.
//!
//! When a canister is first created, it starts with a small amount of stable memory,
//! and it can grow this memory as needed by allocating additional pages.
//! The canister can read from and write to this stable memory using specific APIs provided by the Internet Computer SDK.
//!
//! ## Memory Model
//!
//! ```mermaid
//! ---
//! title: "Memory Model"
//! ---
//! packet
//! +32: "Schema Table (65k)"
//! +32: "ACL Table (65k)"
//! +16: "Table XX Page Ledger (65k)"
//! +16: "Table XX Free Segments Ledger (65k)"
//! +16: "Table YY Page Ledger (65k)"
//! +16: "Table YY Free Segments Ledger (65k)"
//! +32: "Table XX Records - Page 1 (65k)"
//! +32: "Table XX Records - Page 2 (65k)"
//! +32: "Table YY Records - Page 1 (65k)"
//! +32: "Table XX Records - Page 3 (65k)"
//! +32: "Table YY Records - Page 2 (65k)"
//! ```
// makes the crate accessible as `ic_dbms_canister` in macros
extern crate self as ic_dbms_canister;