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_rand('fetch_customers').id)

    read_product `SELECT id, price FROM product WHERE id = $1::UUID` (ref_rand('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
    )
  }
}
run:

  - transaction: place_order
    queries:
      - name: read_buyer
        type: query
        args: [ref_rand('fetch_customers').id]
        query: SELECT id, market FROM customer WHERE id = $1::UUID

      - name: read_product
        type: query
        args: [ref_rand('fetch_products').id]
        query: SELECT id, price FROM product WHERE id = $1::UUID

      - if: "ref_same('read_buyer').market == 'uk'"
        then:
          - locals:
              tax_rate: "0.20"
          - locals:
              currency: "'GBP'"
        else:
          - locals:
              tax_rate: "0.10"
          - locals:
              currency: "'USD'"

      - name: insert_order
        type: exec
        args:
          - 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
        query: |-
          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)

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_rand('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
  }
}
run:

  - name: read_customer
    type: query
    args:
      - ref_rand('fetch_customers').id
    query: SELECT id, market FROM customer WHERE id = $1::UUID

  - if: "ref_same('read_customer').market == 'uk'"
    then:
      - name: log_gdpr_check
        type: exec
        args:
          - ref_same('read_customer').id
          - ref_same('read_customer').market
        query: |-
          INSERT INTO compliance_log (customer_id, market, check_type)
          VALUES ($1::UUID, $2::STRING, 'gdpr_data_access')
    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_rand('fetch_customers').id)

    read_product `SELECT id, price FROM product WHERE id = $1::UUID` (ref_rand('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
    )
  }
}
run:

  - transaction: place_order
    queries:
      - name: read_buyer
        type: query
        args: [ref_rand('fetch_customers').id]
        query: SELECT id, market FROM customer WHERE id = $1::UUID

      - name: read_product
        type: query
        args: [ref_rand('fetch_products').id]
        query: SELECT id, price FROM product WHERE id = $1::UUID

      - match: "ref_same('read_buyer').market"
        when:
          - eq: "'uk'"
            queries:
              - locals:
                  tax_rate: "0.20"
              - locals:
                  currency: "'GBP'"
          - eq: "'us'"
            queries:
              - locals:
                  tax_rate: "0.10"
              - locals:
                  currency: "'USD'"
        default:
          - locals:
              tax_rate: "0.23"
          - locals:
              currency: "'EUR'"

      - name: insert_order
        type: exec
        args:
          - 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
        query: |-
          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)

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_rand('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
      )
    }
  }
}
run:

  - name: read_customer
    type: query
    args: [ref_rand('fetch_customers').id]
    query: SELECT id, market FROM customer WHERE id = $1::UUID

  - match: "ref_same('read_customer').market"
    when:
      - eq: "'uk'"
        queries:
          - name: log_gdpr
            type: exec
            args:
              - ref_same('read_customer').id
              - ref_same('read_customer').market
            query: |-
              INSERT INTO compliance_log (customer_id, market, check_type)
              VALUES ($1::UUID, $2::STRING, 'gdpr')
      - eq: "'us'"
        queries:
          - name: log_ccpa
            type: exec
            args:
              - ref_same('read_customer').id
              - ref_same('read_customer').market
            query: |-
              INSERT INTO compliance_log (customer_id, market, check_type)
              VALUES ($1::UUID, $2::STRING, 'ccpa')
    default:
      - name: log_standard
        type: exec
        args:
          - ref_same('read_customer').id
          - ref_same('read_customer').market
        query: |-
          INSERT INTO compliance_log (customer_id, market, check_type)
          VALUES ($1::UUID, $2::STRING, 'standard')

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
}
else:
  - noop

Works both inside and outside transactions.

rollback#

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

} else {
  rollback
}
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
}
- if: "ref_same('read_buyer').market == 'uk'"
  then:
    - match: "ref_same('read_product').category"
      when:
        - eq: "'digital'"
          queries:
            - name: insert_digital_uk
              type: exec
              args: [ref_same('read_buyer').id, ref_same('read_product').price * 0.20]
              query: |-
                INSERT INTO order_log (customer_id, tax, currency)
                VALUES ($1::UUID, $2::FLOAT, 'GBP')
        - eq: "'food'"
          queries:
            - name: insert_food_uk
              type: exec
              args: [ref_same('read_buyer').id, ref_same('read_product').price * 0.0]
              query: |-
                INSERT INTO order_log (customer_id, tax, currency)
                VALUES ($1::UUID, $2::FLOAT, 'GBP')
  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_rand(), 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