Mercury Documentation
  • Get Started with Mercury
    • Pricing
    • Endpoints
    • Authentication
  • The Definitive Guide to Understanding The Mercury's Stack Vision
  • Retroshades
    • Introduction to Retroshades
    • Get Started
      • Importing the Retroshades SDK
      • Writing and Emitting Retroshades
      • Deploying to Mercury Retroshades
      • Querying Retroshades
  • Zephyr: Full Customization
    • Introduction
    • General concepts
      • Zephyr Programs
      • Accessing the Ledger: Contract Entries
      • Accessing the Ledger Meta: Contract Events
      • Database Interactions
      • Create Custom Callable APIs: Serverless Functions
      • Catchups
      • Using Soroban inside Zephyr
    • Quickstart
    • Learn
      • Introduction to Mercury's Cloud and the Zephyr Stack
      • Get Started: Set Up and Manage the Project
        • Create the Project
        • Writing the Program
        • Local Testing
        • Deploy
        • Data catchups/backfill
        • Monitor Execution
        • Querying
      • Database Interactions
        • Zephyr.toml: Define the Tables Structure
        • Understanding Database Interactions
        • Database Operations
      • Accessing the Ledger
      • Accessing Ledger Transition: Soroban Events
      • Working with Contract Custom Types
      • Working with Soroban SDK Types
      • Web Requests, Automation and Alerts.
      • Zephyr.toml Extensions: Indexes And Dashboard
      • Reading From Indexes/External Tables
      • Custom APIs - Serverless Functions
        • General Concepts
        • Custom RPC-alike Endpoints
        • Querying APIs for Composable Data
      • Data Catchups/Backfill
      • Custom Dashboards
        • Creating the Dashboard
        • Plotting: Simple
        • Complex Plotting
    • Support
  • Mercury "Classic"
    • Subscriptions
      • API Definition
    • Queries
      • Contract Events
      • Contract Data Entry Updates
      • Stellar Operations, Balances, and Account Objects
  • TUTORIALS
    • Zephyr
      • Self-hosting Zephyr
      • No-RPC Dapp
      • Indexing a DeFi liquidity pool (Blend)
      • Building a Secure DeFi Real-Time Bot Through Smart Accounts
      • Monitoring Large Deposits with Zephyr and Sending Web Alerts
    • Mercury Classic
      • Index and query contract events
Powered by GitBook
On this page
  • Accessing the Tables
  • DatabaseDerive and Defining Tables Rust-Side
  1. Zephyr: Full Customization
  2. Learn
  3. Database Interactions

Understanding Database Interactions

Now that our tables are defined, let's learn how to interact with those form a Zephyr program.

Accessing the Tables

Before starting to write to/read from the tables we defined in the zephyr.toml, we need to define them in our program.

#[derive(DatabaseDerive, Clone)]
#[with_name("feedback")]
pub struct Feedback {
    pub source: ScVal,
    pub hash: ScVal,
    pub text: ScVal,
    pub votes: ScVal,
}

We're defining here the table "feedback" of the one of the zephyr.toml as a struct and assigning it the DatabaseDerive macro (more on that later). When using DatabaseDerive we also need to specify the table's name. Moreover, the field names in the structure must be the same as defined in your zephyr.toml columns definition.

Now to access (in this case to write) the table from the program we just have to create an instance of the struct, which will represent a row in the table, and call the Environment functions to interact with the database, such as EnvClient::put(). More about database operations on the next page.

pub fn write_feedback(env: &EnvClient, feedback: Feedback) {
    env.put(&feedback)
}

DatabaseDerive and Defining Tables Rust-Side

The magic of rust metaprogramming comes into play when working with the database. Assuming you've already read through the quickstart, let's jump to an immediate example. Before the DatabaseDerive macro, reading, and writing to the database was done as follows:

// This snippet is taken from xycLoans' zephyr program code.
env.db_write(
    "xlsupply",
    &["contract", "timestamp", "supply"],
    &[
        contract_id,
        &self.timestamp.to_be_bytes(),
        &supply.to_be_bytes(),
    ],
)
.unwrap()

Reading from the database and updating was even worse. This is unsound and non-idiomatic, but more importantly very unsafe. With the DatabaseDerive macro, this becomes much better and safer:

// Snipped from Script3's soroban governor indexer.

#[derive(DatabaseDerive, Clone)]
#[with_name("votes")]
pub struct Votes {
    pub contract: Hash,  // governor contract address
    // other fields ...
}

pub fn write_votes(env: &EnvClient, votes: Votes) {
    env.put(&votes)
}

The DatabaseDerive macro builds the required trait implementation to construct a code similar to the one used in the xycLoans example under the hood. For this to work, however, you need to make sure that the field names in the structure are the same as defined in your zephyr.toml columns definition.

PreviousZephyr.toml: Define the Tables StructureNextDatabase Operations

Last updated 10 months ago