- Overview
- Tutorials
- Getting started
- Get started with Canton and the JSON Ledger API
- Get Started with Canton, the JSON Ledger API, and TypeScript
- Get started with Canton Network App Dev Quickstart
- Get started with smart contract development
- Basic contracts
- Test templates using Daml scripts
- Build the Daml Archive (.dar) file
- Data types
- Transform contracts using choices
- Add constraints to a contract
- Parties and authority
- Compose choices
- Handle exceptions
- Work with dependencies
- Functional programming 101
- The Daml standard library
- Test Daml contracts
- Next steps
- Application development
- Getting started
- Development how-tos
- Component how-tos
- Explanations
- References
- Application development
- Smart contract development
- Daml language cheat sheet
- Daml language reference
- Daml standard library
- DA.Action.State.Class
- DA.Action.State
- DA.Action
- DA.Assert
- DA.Bifunctor
- DA.Crypto.Text
- DA.Date
- DA.Either
- DA.Exception
- DA.Fail
- DA.Foldable
- DA.Functor
- DA.Internal.Interface.AnyView.Types
- DA.Internal.Interface.AnyView
- DA.List.BuiltinOrder
- DA.List.Total
- DA.List
- DA.Logic
- DA.Map
- DA.Math
- DA.Monoid
- DA.NonEmpty.Types
- DA.NonEmpty
- DA.Numeric
- DA.Optional
- DA.Record
- DA.Semigroup
- DA.Set
- DA.Stack
- DA.Text
- DA.TextMap
- DA.Time
- DA.Traversable
- DA.Tuple
- DA.Validation
- GHC.Show.Text
- GHC.Tuple.Check
- Prelude
- Smart contract upgrading reference
- Glossary of concepts
SQL API¶
While data consumers do not communicate with the PQS process directly, they do use an API that PQS has provisioned in the database itself. This SQL API is designed to provide a consistent and stable interface for users to access the ledger. It consists of a set of functions that should be the only database artifacts readers interact with.
Ledger time model¶
A key aspect to consider when querying the ledger is the fact that it makes the history over time available. Additionally, understanding time in a distributed environment can be challenging because there are many different clocks available.
Offset¶
A participant models time using an index called an offset. An offset is a unique index of the participant’s local ledger. You can think of this as selecting an item in the ledger using a specific offset (or index) into the ledger.
Offsets are ordered, representing the order of transactions on the ledger of a participant. Due to privacy and filtering, the sequence of offsets of a participant usually appears to contain gaps.
Offsets are specific to a participant and are not consistent between peer participants, even when processing common transactions. This is because each participant has its own ledger and allocates its own offsets based on it’s permissioned view of transactions.
Offsets are represented as strings (in Daml 2.x) or integers (in Daml 3.3+) in columns
created_at_offset
, archived_at_offset
and exercised_at_offset
(see
Types of returned data).
Ledger time¶
Ledger time is an approximate wall-clock time (within a bounded skew) that preserves causal ordering. That is, if a
contract is created at a certain time, it cannot be used until after that time. The ledger time is represented by
created_effective_at
, archived_effective_at
and exercised_effective_at
columns (see
Types of returned data).
Transaction ID¶
A transaction ID corresponds to an offset in the following ways:
Not every offset has a transaction ID. For example, the completion event of a rejected transaction does not have a transaction ID because it was unsuccessful.
There is, at most, one transaction ID at a given offset.
Each transaction ID is unique and always has a single offset.
While offsets are allocated by, and are specific to, a participant; transaction ID values are common to all participants.
Transaction ordering (as represented by associated offset) can vary between participants.
A transaction ID is entirely opaque and does not communicate any information, other than identification.
Which should I use?¶
Different types of data analysis require different tools. For example in these types of analysis the following identifiers can be useful:
Causal: Offset provides an understanding of events in causal order, consistent with the participant-determined ledger commit ordering.
Systematic: Transaction ID is required for correlating over multiple participants, serving as a common identifier for individual transactions.
Temporal: Ledger Time provides an ordering of events in wall-clock time, with bounded skew. This can be useful depending on your need for precision.
PQS time model¶
PQS provides all three identifiers, but offset defines the order. With this PQS is able to provide a consistent view of ledger transactions.
Offsets are deeply embedded in the SQL API, allowing users to query the ledger in a manner that provides consistency. Users can nominate the offsets they wish to query, or simply query the latest available offset.
The following figure shows a pair of participants and their respective ledgers. Each participant has its own PQS instance, and you can see that it always has the portion of the ledger it is authorized to see:
--- title: Time Model --- classDiagram SyncDomain <-- Participant-A SyncDomain <-- Participant-B class Participant-A { 511: tx-A 513: tx-G 514: tx-P 515: tx-S 516: tx-V } class Participant-B { 21O: tx-A 211: tx-D 212: tx-J 213: tx-M 215: tx-P } Participant-A <-- PQS-A Participant-B <-- PQS-B class PQS-A { 511: tx-A 514: tx-P } class PQS-B { 21O: tx-A 212: tx-J 215: tx-P }
You can also see that the offsets (prefix) are common to the participant and PQS, but the Transaction IDs (suffix) are shared throughout.
Offset management¶
The following functions control the temporal perspective of the ledger, and allow you to control how you consider time in your queries. Since PQS exposes an eventually-consistent perspective of the ledger, you may wish to query:
Ignore; The latest available state.
Pin; The state of the ledger at a specific time.
Span; The ledger events across a time range, such as for an audit trail.
Consistency; The ledger in a way that maintains consistency with other interactions you have had with the ledger (read or write).
The following functions allow you to control the temporal scope of the ledger. This establishes the context in which subsequent queries execute:
set_latest(offset)
: nominates the offset of the latest data to include in observing the ledger. IfNULL
it uses the latest available. The actual offset to be used is returned. If the supplied offset is beyond what is available, an error occurs.validate_offset_exists(offset)
: validates that the datastore has a complete history up to and including the offset provided. Returns an error if the nominated offset is not available (too old, or too new).set_oldest(offset)
: nominates the offset of the oldest events to include in query scope. IfNULL
then it uses the oldest available. Function returns the actual offset used. If the supplied offset is beyond what is available, an error occurs.nearest_offset(time)
: a helper function to determine the offset of a given time (or interval prior to now).
Accessing contracts and exercises¶
Under this scope, the following table functions [1] allow access to the ledger and are used directly in queries. They are designed to be used in a similar manner to tables or views, and allow users to focus on the data they wish to query, with the impact of offsets removed.
active(name, [at_offset])
: active instances of the target template/interface views that existed at the time of the latest offsetcreates(name, [from_offset], [to_offset])
: create events of the target template/interface views that occurred between the oldest and latest offsetarchives(name, [from_offset], [to_offset])
: archive events of the target template/interface views that occurred between the oldest and latest offsetexercises(name, [from_offset], [to_offset])
: exercise events of the target choice that occurred between the oldest and latest offset
The name
identifier can be used with or without the package specified:
Fully qualified:
<package>:<module>:<template|interface|choice>
Partially qualified:
<module>:<template|interface|choice>
or<template|interface|choice>
(if unambiguous)
Caution
Partially qualified identifiers fail if there is an ambiguous result.
These functions have optional parameters to allow the user to specify the offset range to be used. Providing these
arguments is alternative to using set_*
functions prior in the session. The following queries are equivalent:
Implicit: geared towards context-oriented exploration
select set_oldest('from_offset');
select set_latest('to_offset');
select * from creates('package:My.Module:Template');
Explicit: beneficial to inline the entire context, to emit in a single statement
select *
from creates('package:My.Module:Template', 'from_offset', 'to_offset');
Summary functions¶
Summary functions are available to provide an overview of the ledger data available within the nominated offset range:
summary_transients(from_offset, to_offset)
: the number of transients per Daml fully qualified name within the offset range.summary_updates(from_offset, to_offset)
: summary of create and archive counts per Daml fully qualified name within the offset range.
The following functions retrieve event counts per template_fqn
:
summary_active(at_offset)
summary_creates(from_offset, to_offset)
summary_archives(from_offset, to_offset)
summary_exercises(from_offset, to_offset)
Lookup functions¶
lookup_contract(contract_id)
is a mechanism to retrieve contract data without needing to know its Daml qualified name. The function returns both contract and all associated interface view projections, distinguishable by thepayload_type
column.lookup_exercises(contract_id)
is a mechanism to retrieve choice exercise data without needing to know the Daml qualified name; knowing the contract ID is sufficient.
Types of returned data¶
All functions returning contract data return the following columns:
Name |
Type |
Description |
---|---|---|
|
|
Fully-qualified name of the template or interface |
|
|
Type of contract payload |
|
|
Reference to contract creation event primary key |
|
|
Orderable addressing type |
|
|
Ordinal index of the transaction containing the creation event |
|
|
Ledger offset of the transaction containing the creation event |
|
|
Ledger effective time of the transaction containing the creation event |
|
|
Reference to contract archival event primary key |
|
|
Orderable addressing type |
|
|
Ordinal index of the transaction containing the archival event |
|
|
Ledger offset of the transaction containing the archival event |
|
|
Ledger effective time of the transaction containing the archival event |
|
|
Contract’s lifespan expressed in ordinal indexes |
|
|
Ledger-assigned contract ID |
|
|
JSONB representation of contract data |
|
|
Explicit contract disclosure metadata (see How do stakeholders disclose contracts to submitters?) |
|
|
Daml package name |
|
|
Daml package version |
|
|
Daml package ID |
|
|
Redaction process reference |
|
|
Parties consenting to the creation of the contract |
|
|
Additional stakeholders whom the contract is visible to |
All functions returning exercise data return the following columns:
Name |
Type |
Description |
---|---|---|
|
|
Fully-qualified name of the template where choice is defined |
|
|
Fully-qualified name of the choice |
|
|
Choice name |
|
|
Whether the choice is consuming |
|
|
Reference to choice exercise event primary key |
|
|
Orderable addressing type |
|
|
Ordinal index of the transaction containing the exercise event |
|
|
Ledger offset of the transaction containing the exercise event |
|
|
Ledger effective time of the transaction containing the exercise event |
|
|
Ledger-assigned contract ID |
|
|
JSONB representation of the choice argument type |
|
|
JSONB representation of the choice return type |
|
|
Daml package name |
|
|
Daml package version |
|
|
Daml package ID |
|
|
Redaction process reference |
|
|
Parties that consented to the creation of the contract that choice was exercised on |
|
|
Additional stakeholders made aware of the creation of the contract that choice was exercised on |
|
|
Parties that collectively exercised this choice (see com.daml.ledger.api.v2.ExercisedEvent.acting_parties) |
JSONB encoding¶
PQS stores the ledger using a Daml-LF JSON-based encoding (see Daml-LF JSON encoding) of Daml-LF values. An overview of the encoding is provided below.
Users should consult the PostgreSQL documentation to understand how to work with JSONB data [2] natively in SQL.
Values on the ledger (contract payloads and keys, interface views, exercise arguments, and return values) can be primitive types, user-defined records, variants, or enums. These types translate to JSON types [3] as follows:
Primitive types¶
Daml type |
JSON type |
---|---|
|
represented as string |
|
represented as string |
|
represented as string |
|
represented as array |
|
represented as string |
|
ISO 8601 date represented as string |
|
ISO 8601 time (in UTC) represented as string |
|
represented as boolean |
|
represented as string |
|
represented as empty object |
|
User-defined types¶
Daml type |
JSON type |
---|---|
|
represented as object, where each create parameter’s name is a key, and the parameter’s value is the JSON-encoded value |
|
represented as object, using the
|
|
represented as string, where the value is the constructor name. |