08 / 08
UPDATE statement
You can only change an attribute's value if it is declared with mutable:
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:
my_entity_instance.my_mutable = "new value";Rell translates this into the following 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:
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:
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:
entity user_info { key name; key pubkey; mutable street_address: text; mutable zip_code: integer;}The operation becomes:
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:
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:
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:
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:
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:
query get_users_info() = user_info @* { } ( $.to_struct() );Start the node so you can work with the database:
chr node startCreate a new key pair with this command:
chr keygen --file .secretCall the create_user_info operation to add users:
chr tx create_user_info Artem 028A589B9EAA836ECFC52C59039A04231DBA4925873803BCD9B5E7151CF8611D89 "Drottninggatan 2" "100000"chr tx create_user_info Erik 0835A7B8C9D0E1F2A3B4C5D6E7F8A9B0C1D2E3F4A5B6C7D8E9F0A1B2C3D4E5F6A7"Stureplan 52" "100001"Change the user's address with the update_address operation:
chr tx update_address Erik "Stureplan 60" "200002" --secret .secretQuery all existing users:
chr query get_users_infoYou should see output like this:
[ [ "name": "Artem", "pubkey": x"028A589B9EAA836ECFC52C59039A04231DBA4925873803BCD9B5E7151CF8611D89", "street_address": "Drottninggatan 2", "zip_code": 100000 ], [ "name": "Erik", "pubkey": x"0835A7B8C9D0E1F2A3B4C5D6E7F8A9B0C1D2E3F4A5B6C7D8E9F0A1B2C3D4E5F6A7", "street_address": "Stureplan 60", "zip_code": 200002 ]]