Conditionals#

edg supports two conditional constructs for branching logic: if/then/else for binary conditions and match/when/default for multi-way dispatch. Both work inside transactions and as standalone run items.

if/then/else#

Evaluates an expression and executes the then branch if true, the else branch if false. The else branch is optional.

Inside a transaction#

run {
  transaction place_order {
    read_buyer `SELECT id, market FROM customer WHERE id = $1::UUID` (ref('fetch_customers').id)

    read_product `SELECT id, price FROM product WHERE id = $1::UUID` (ref('fetch_products').id)

    if ref_same('read_buyer').market == 'uk' {
      let tax_rate = 0.20
      let currency = 'GBP'
    } else {
      let tax_rate = 0.10
      let currency = 'USD'
    }

    insert_order(type: exec) `INSERT INTO order_log (customer_id, product_id, subtotal, tax, currency, market)
      VALUES ($1::UUID, $2::UUID, $3::FLOAT, $4::FLOAT, $5::STRING, $6::STRING)` (
      ref_same('read_buyer').id,
      ref_same('read_product').id,
      ref_same('read_product').price,
      ref_same('read_product').price * local('tax_rate'),
      local('currency'),
      ref_same('read_buyer').market
    )
  }
}

UK customers get 20% VAT in GBP; all others get 10% tax in USD. The let statements inside each branch set transaction-scoped locals that persist after the conditional, so the query below can reference them via local().

Standalone (outside a transaction)#

run {
  read_customer `SELECT id, market FROM customer WHERE id = $1::UUID` (ref('fetch_customers').id)

  if ref_same('read_customer').market == 'uk' {
    log_gdpr_check(type: exec) `INSERT INTO compliance_log (customer_id, market, check_type)
      VALUES ($1::UUID, $2::STRING, 'gdpr_data_access')` (
      ref_same('read_customer').id,
      ref_same('read_customer').market
    )
  } else {
    noop
  }
}

Standalone conditionals run outside any transaction context. Each branch query executes independently.

match/when/default#

Evaluates a match expression once, then compares the result to each eq value in order. The first matching when clause executes. If nothing matches, the default branch runs (if present).

Inside a transaction#

run {
  transaction place_order {
    read_buyer `SELECT id, market FROM customer WHERE id = $1::UUID` (ref('fetch_customers').id)

    read_product `SELECT id, price FROM product WHERE id = $1::UUID` (ref('fetch_products').id)

    match ref_same('read_buyer').market {
      when 'uk' {
        let tax_rate = 0.20
        let currency = 'GBP'
      }
      when 'us' {
        let tax_rate = 0.10
        let currency = 'USD'
      }
      default {
        let tax_rate = 0.23
        let currency = 'EUR'
      }
    }

    insert_order(type: exec) `INSERT INTO order_log (customer_id, product_id, subtotal, tax, currency, market)
      VALUES ($1::UUID, $2::UUID, $3::FLOAT, $4::FLOAT, $5::STRING, $6::STRING)` (
      ref_same('read_buyer').id,
      ref_same('read_product').id,
      ref_same('read_product').price,
      ref_same('read_product').price * local('tax_rate'),
      local('currency'),
      ref_same('read_buyer').market
    )
  }
}

UK gets 20% VAT in GBP, US gets 10% sales tax in USD, everyone else (default) gets 23% VAT in EUR. Each branch sets locals that the query below references via local().

Standalone (outside a transaction)#

run {
  read_customer `SELECT id, market FROM customer WHERE id = $1::UUID` (ref('fetch_customers').id)

  match ref_same('read_customer').market {
    when 'uk' {
      log_gdpr(type: exec) `INSERT INTO compliance_log (customer_id, market, check_type)
        VALUES ($1::UUID, $2::STRING, 'gdpr')` (
        ref_same('read_customer').id,
        ref_same('read_customer').market
      )
    }
    when 'us' {
      log_ccpa(type: exec) `INSERT INTO compliance_log (customer_id, market, check_type)
        VALUES ($1::UUID, $2::STRING, 'ccpa')` (
        ref_same('read_customer').id,
        ref_same('read_customer').market
      )
    }
    default {
      log_standard(type: exec) `INSERT INTO compliance_log (customer_id, market, check_type)
        VALUES ($1::UUID, $2::STRING, 'standard')` (
        ref_same('read_customer').id,
        ref_same('read_customer').market
      )
    }
  }
}

Special entries#

Two naked scalar entries can be used as branch targets:

noop#

Does nothing. Use in branches where no action is needed.

} else {
  noop
}

Works both inside and outside transactions.

rollback#

Immediately rolls back the current transaction. Only valid inside a transaction.

} else {
  rollback
}

Conditional rollbacks are not errors. The worker continues to the next iteration and the rollback is counted in transaction stats.

Nesting#

Conditionals can be nested. An if or match block can appear inside the then, else, when, or default branches of another conditional.

if ref_same('read_buyer').market == 'uk' {
  match ref_same('read_product').category {
    when 'digital' {
      insert_digital_uk(type: exec) `INSERT INTO order_log (customer_id, tax, currency)
        VALUES ($1::UUID, $2::FLOAT, 'GBP')` (
        ref_same('read_buyer').id,
        ref_same('read_product').price * 0.20
      )
    }
    when 'food' {
      insert_food_uk(type: exec) `INSERT INTO order_log (customer_id, tax, currency)
        VALUES ($1::UUID, $2::FLOAT, 'GBP')` (
        ref_same('read_buyer').id,
        ref_same('read_product').price * 0.0
      )
    }
  }
} else {
  rollback
}

Expressions#

The if condition must evaluate to a boolean. The match expression and each eq value can be any type; types don’t need to match because both sides are stringified before comparison.

All conditional expressions have access to:

  • ref_same(), ref(), ref_diff() and other reference functions
  • local('name') for transaction-scoped variables
  • global('name') for global variables
  • Any other function available in the expression environment

Constraints#

  • if entries must have a then branch
  • match entries must have at least one when clause
  • Each when clause must have both eq and queries
  • rollback can only appear inside a transaction
  • rollback_if can only appear inside a transaction
  • An entry cannot combine multiple special fields (e.g. if + rollback_if, if + match)
  • Conditional entries must not have name, type, args, or query fields
  • Standalone conditionals (outside transactions) cannot be used with run_weights