Chromia
Start Building

08 / 08

UPDATE statement

You can only change an attribute's value if it is declared with mutable:

rell
entity my_entity {    key my_key: text;    mutable my_mutable: text;}

Once you hold a reference to an entity, you update an attribute with a plain assignment:

rell
my_entity_instance.my_mutable = "new value";

Rell translates this into the following SQL:

sql
UPDATE "c0.my_entity" A00 SET "my_mutable" = ? WHERE A00."rowid" = ?

When several mutable attributes need new values, assigning each one separately costs a database roundtrip per assignment. Use Rell's update keyword instead. Its syntax resembles the at-operator:

rell
FROM        CARDINALITY  WHERE         WHATupdate entity_name @            { condition } ( .my_mutable = "new value" );

An assignment takes the place of the WHAT part, and there is no TAIL part.

This updates multiple fields in one roundtrip. If you already have a reference to the entity, use $ == entity_instance as the condition.

Example

Consider the following code:

rell
entity user_info {    key name;    key pubkey;    mutable address: text;}operation update_address(name, new_address: text) {    val user = find_and_check_signer(name);    user.address = new_address;}function find_and_check_signer(name): user_info {    val user_pubkey = user_info @ { name } (user = $, pubkey = .pubkey);    require(op_context.is_signer(user_pubkey.pubkey), "User must sign operation");    return user_pubkey.user;}

The user entity has a name that serves as a unique key, a public key, and a mutable address. The update_address operation looks up the user and changes the address. In its first at-expression, find_and_check_signer fetches the user entity with the given name together with its pubkey property. Having the key already loaded means the function can require a matching signature without running a second SQL select. A simple assignment then sets the new address.

Now suppose the address should be stored as a street name and a zip code. The model changes to:

rell
entity user_info {    key name;    key pubkey;    mutable street_address: text;    mutable zip_code: integer;}

The operation becomes:

rell
operation update_address(name, new_street_address: text, new_zip_code: integer) {    val verified_user = find_and_check_signer(name);    update user_info @ { $ == verified_user } (       .street_address = new_street_address,       .zip_code = new_zip_code    );}

Both attributes are now updated together.

Practical example

The following walkthrough creates a user, updates that user, and checks the updated data against the user's public key.

In the src/main directory, add the user_info entity to entities.rell:

src/main/entities.rell
entity user_info {    key name;    key pubkey;    mutable street_address: text;    mutable zip_code: integer;}

Add the find_and_check_signer function to src/main/functions.rell. It verifies the user's public key:

src/main/functions.rell
function find_and_check_signer(name): user_info {    val user_pubkey = user_info @ { name } (user = $, pubkey = .pubkey);    require(op_context.is_signer(user_pubkey.pubkey), "User must sign operation");    return user_pubkey.user;}

Add the operation below to src/main/operations.rell. It creates a new user:

src/main/operations.rell
operation create_user_info(name: text, pubkey: byte_array, street_address: text, zip_code: integer) {    create user_info( name, pubkey, street_address, zip_code );}

To change user information, add the update_address operation, which depends on verifying the user's public key, to src/main/operations.rell:

shell
operation update_address(name, new_street_address: text, new_zip_code: integer) {    val verified_user = find_and_check_signer(name);    update user_info @ { $ == verified_user } (       .street_address = new_street_address,       .zip_code = new_zip_code    );}

To list all existing users, add this query to src/main/queries.rell:

src/main/queries.rell
query get_users_info() = user_info @* { } ( $.to_struct() );

Start the node so you can work with the database:

shell
chr node start

Create a new key pair with this command:

shell
chr keygen --file .secret

Call the create_user_info operation to add users:

shell
chr tx create_user_info Artem 028A589B9EAA836ECFC52C59039A04231DBA4925873803BCD9B5E7151CF8611D89 "Drottninggatan 2" "100000"
shell
chr tx create_user_info Erik 0835A7B8C9D0E1F2A3B4C5D6E7F8A9B0C1D2E3F4A5B6C7D8E9F0A1B2C3D4E5F6A7"Stureplan 52" "100001"

Change the user's address with the update_address operation:

shell
chr tx update_address Erik "Stureplan 60" "200002" --secret .secret

Query all existing users:

shell
chr query get_users_info

You should see output like this:

result
[  [    "name": "Artem",    "pubkey": x"028A589B9EAA836ECFC52C59039A04231DBA4925873803BCD9B5E7151CF8611D89",    "street_address": "Drottninggatan 2",    "zip_code": 100000  ],  [    "name": "Erik",    "pubkey": x"0835A7B8C9D0E1F2A3B4C5D6E7F8A9B0C1D2E3F4A5B6C7D8E9F0A1B2C3D4E5F6A7",    "street_address": "Stureplan 60",    "zip_code": 200002  ]]
Written for this site from Chromia’s public course. Code samples are the course’s own.