diff --git a/Readme.md b/Readme.md index ee68f5deb..5e8fd6395 100644 --- a/Readme.md +++ b/Readme.md @@ -166,7 +166,7 @@ possible solutions. * [BuildingRunning][BuildingRunning]: Building and running the db-sync node. * [Docker][Docker]: Instruction for docker-compose, and building the images using nix. -* [ERD][ERD]: The entity relationship diagram. +* [ERD][ERD]: The entity relationship diagram. Regenerate with `scripts/generate-erd.sh`. * [Example SQL queries][ExampleQueries]: Some example SQL and Haskell/Esqueleto queries. * [OffChainPoolData][OffChainPoolData]: Explanation of how stake pool offchain data is retried. * [Schema Documentation][Schema Documentation]: The database schema documentation. @@ -180,7 +180,7 @@ possible solutions. [BuildingRunning]: doc/building-running.md [Docker]: doc/docker.md [Running]: doc/running.md -[ERD]: doc/ERD.png +[ERD]: doc/ERD.svg [ExampleQueries]: doc/interesting-queries.md [PostgresView]: https://www.postgresql.org/docs/current/sql-createview.html [OffChainPoolData]: doc/pool-offchain-data.md diff --git a/doc/ERD.png b/doc/ERD.png deleted file mode 100644 index 66a2c5558..000000000 Binary files a/doc/ERD.png and /dev/null differ diff --git a/doc/ERD.svg b/doc/ERD.svg new file mode 100644 index 000000000..bf178e10a --- /dev/null +++ b/doc/ERD.svg @@ -0,0 +1,2884 @@ + + + + + + +erd + + + +ada_pots + + +ada_pots + + +PK +id + : bigint + + +   slot_no : bigint + + +   epoch_no : integer + + +   treasury : numeric + + +   reserves : numeric + + +   rewards : numeric + + +   utxo : numeric + + +   deposits_stake : numeric + + +   fees : numeric + + +FK +block_id + : bigint + + +   deposits_drep : numeric + + +   deposits_proposal : numeric + + + +block + + +block + + +PK +id + : bigint + + +   hash : bytea + + +   epoch_no : integer + + +   slot_no : bigint + + +   epoch_slot_no : integer + + +   block_no : integer + + +FK +previous_id + : bigint + + +FK +slot_leader_id + : bigint + + +   size : integer + + +   time : timestamp without time zone + + +   tx_count : bigint + + +   proto_major : integer + + +   proto_minor : integer + + +   vrf_key : character varying + + +   op_cert : bytea + + +   op_cert_counter : bigint + + + +ada_pots:block_id->block:id + + + + + +block:previous_id->block:id + + + + + +slot_leader + + +slot_leader + + +PK +id + : bigint + + +   hash : bytea + + +FK +pool_hash_id + : bigint + + +   description : character varying + + + +block:slot_leader_id->slot_leader:id + + + + + +collateral_tx_in + + +collateral_tx_in + + +PK +id + : bigint + + +FK +tx_in_id + : bigint + + +FK +tx_out_id + : bigint + + +   tx_out_index : smallint + + + +tx + + +tx + + +PK +id + : bigint + + +   hash : bytea + + +FK +block_id + : bigint + + +   block_index : integer + + +   out_sum : numeric + + +   fee : numeric + + +   deposit : bigint + + +   size : integer + + +   invalid_before : numeric + + +   invalid_hereafter : numeric + + +   valid_contract : boolean + + +   script_size : integer + + +   treasury_donation : numeric + + + +collateral_tx_in:tx_in_id->tx:id + + + + + +collateral_tx_in:tx_out_id->tx:id + + + + + +collateral_tx_out + + +collateral_tx_out + + +PK +id + : bigint + + +FK +tx_id + : bigint + + +   index : smallint + + +   address : character varying + + +   address_has_script : boolean + + +   payment_cred : bytea + + +FK +stake_address_id + : bigint + + +   value : numeric + + +   data_hash : bytea + + +   multi_assets_descr : character varying + + +FK +inline_datum_id + : bigint + + +FK +reference_script_id + : bigint + + + +datum + + +datum + + +PK +id + : bigint + + +   hash : bytea + + +FK +tx_id + : bigint + + +   value : jsonb + + +   bytes : bytea + + + +collateral_tx_out:inline_datum_id->datum:id + + + + + +script + + +script + + +PK +id + : bigint + + +FK +tx_id + : bigint + + +   hash : bytea + + +   type : USER-DEFINED + + +   json : jsonb + + +   bytes : bytea + + +   serialised_size : integer + + + +collateral_tx_out:reference_script_id->script:id + + + + + +stake_address + + +stake_address + + +PK +id + : bigint + + +   hash_raw : bytea + + +   view : character varying + + +   script_hash : bytea + + + +collateral_tx_out:stake_address_id->stake_address:id + + + + + +collateral_tx_out:tx_id->tx:id + + + + + +committee + + +committee + + +PK +id + : bigint + + +   gov_action_proposal_id : bigint + + +   quorum_numerator : bigint + + +   quorum_denominator : bigint + + + +committee_de_registration + + +committee_de_registration + + +PK +id + : bigint + + +   tx_id : bigint + + +   cert_index : integer + + +   voting_anchor_id : bigint + + +   cold_key_id : bigint + + + +committee_hash + + +committee_hash + + +PK +id + : bigint + + +   raw : bytea + + +   has_script : boolean + + + +committee_member + + +committee_member + + +PK +id + : bigint + + +FK +committee_id + : bigint + + +   committee_hash_id : bigint + + +   expiration_epoch : integer + + + +committee_member:committee_id->committee:id + + + + + +committee_registration + + +committee_registration + + +PK +id + : bigint + + +   tx_id : bigint + + +   cert_index : integer + + +   cold_key_id : bigint + + +   hot_key_id : bigint + + + +constitution + + +constitution + + +PK +id + : bigint + + +   gov_action_proposal_id : bigint + + +   voting_anchor_id : bigint + + +   script_hash : bytea + + + +cost_model + + +cost_model + + +PK +id + : bigint + + +   costs : jsonb + + +   hash : bytea + + + +datum:tx_id->tx:id + + + + + +delegation + + +delegation + + +PK +id + : bigint + + +FK +addr_id + : bigint + + +   cert_index : integer + + +FK +pool_hash_id + : bigint + + +   active_epoch_no : bigint + + +FK +tx_id + : bigint + + +   slot_no : bigint + + +FK +redeemer_id + : bigint + + + +pool_hash + + +pool_hash + + +PK +id + : bigint + + +   hash_raw : bytea + + +   view : character varying + + + +delegation:pool_hash_id->pool_hash:id + + + + + +redeemer + + +redeemer + + +PK +id + : bigint + + +FK +tx_id + : bigint + + +   unit_mem : bigint + + +   unit_steps : bigint + + +   fee : numeric + + +   purpose : USER-DEFINED + + +   index : integer + + +   script_hash : bytea + + +FK +redeemer_data_id + : bigint + + + +delegation:redeemer_id->redeemer:id + + + + + +delegation:addr_id->stake_address:id + + + + + +delegation:tx_id->tx:id + + + + + +delegation_vote + + +delegation_vote + + +PK +id + : bigint + + +   addr_id : bigint + + +   cert_index : integer + + +   drep_hash_id : bigint + + +   tx_id : bigint + + +   redeemer_id : bigint + + + +delisted_pool + + +delisted_pool + + +PK +id + : bigint + + +   hash_raw : bytea + + + +drep_distr + + +drep_distr + + +PK +id + : bigint + + +   hash_id : bigint + + +   amount : bigint + + +   epoch_no : integer + + +   active_until : integer + + + +drep_hash + + +drep_hash + + +PK +id + : bigint + + +   raw : bytea + + +   view : character varying + + +   has_script : boolean + + + +drep_registration + + +drep_registration + + +PK +id + : bigint + + +   tx_id : bigint + + +   cert_index : integer + + +   deposit : bigint + + +   drep_hash_id : bigint + + +   voting_anchor_id : bigint + + + +epoch_finalized + + +epoch_finalized + + +PK +id + : bigint + + +   out_sum : numeric + + +   fees : numeric + + +   tx_count : integer + + +   blk_count : integer + + +   no : integer + + +   start_time : timestamp without time zone + + +   end_time : timestamp without time zone + + + +epoch_param + + +epoch_param + + +PK +id + : bigint + + +   epoch_no : integer + + +   min_fee_a : integer + + +   min_fee_b : integer + + +   max_block_size : integer + + +   max_tx_size : integer + + +   max_bh_size : integer + + +   key_deposit : numeric + + +   pool_deposit : numeric + + +   max_epoch : integer + + +   optimal_pool_count : integer + + +   influence : double precision + + +   monetary_expand_rate : double precision + + +   treasury_growth_rate : double precision + + +   decentralisation : double precision + + +   protocol_major : integer + + +   protocol_minor : integer + + +   min_utxo_value : numeric + + +   min_pool_cost : numeric + + +   nonce : bytea + + +FK +cost_model_id + : bigint + + +   price_mem : double precision + + +   price_step : double precision + + +   max_tx_ex_mem : numeric + + +   max_tx_ex_steps : numeric + + +   max_block_ex_mem : numeric + + +   max_block_ex_steps : numeric + + +   max_val_size : numeric + + +   collateral_percent : integer + + +   max_collateral_inputs : integer + + +FK +block_id + : bigint + + +   extra_entropy : bytea + + +   coins_per_utxo_size : numeric + + +   pvt_motion_no_confidence : double precision + + +   pvt_committee_normal : double precision + + +   pvt_committee_no_confidence : double precision + + +   pvt_hard_fork_initiation : double precision + + +   dvt_motion_no_confidence : double precision + + +   dvt_committee_normal : double precision + + +   dvt_committee_no_confidence : double precision + + +   dvt_update_to_constitution : double precision + + +   dvt_hard_fork_initiation : double precision + + +   dvt_p_p_network_group : double precision + + +   dvt_p_p_economic_group : double precision + + +   dvt_p_p_technical_group : double precision + + +   dvt_p_p_gov_group : double precision + + +   dvt_treasury_withdrawal : double precision + + +   committee_min_size : numeric + + +   committee_max_term_length : numeric + + +   gov_action_lifetime : numeric + + +   gov_action_deposit : numeric + + +   drep_deposit : numeric + + +   drep_activity : numeric + + +   pvtpp_security_group : double precision + + +   min_fee_ref_script_cost_per_byte : double precision + + + +epoch_param:block_id->block:id + + + + + +epoch_param:cost_model_id->cost_model:id + + + + + +epoch_stake + + +epoch_stake + + +PK +id + : bigint + + +FK +addr_id + : bigint + + +FK +pool_id + : bigint + + +   amount : numeric + + +   epoch_no : integer + + + +epoch_stake:pool_id->pool_hash:id + + + + + +epoch_stake:addr_id->stake_address:id + + + + + +epoch_stake_progress + + +epoch_stake_progress + + +PK +id + : bigint + + +   epoch_no : integer + + +   completed : boolean + + + +epoch_state + + +epoch_state + + +PK +id + : bigint + + +   committee_id : bigint + + +   no_confidence_id : bigint + + +   constitution_id : bigint + + +   epoch_no : integer + + + +epoch_sync_enabled + + +epoch_sync_enabled + + +PK +singleton + : boolean + + +   enabled : boolean + + + +epoch_sync_time + + +epoch_sync_time + + +PK +id + : bigint + + +   no : bigint + + +   seconds : bigint + + +   state : USER-DEFINED + + + +event_info + + +event_info + + +PK +id + : bigint + + +   tx_id : bigint + + +   epoch : integer + + +   type : character varying + + +   explanation : character varying + + + +extra_key_witness + + +extra_key_witness + + +PK +id + : bigint + + +   hash : bytea + + +FK +tx_id + : bigint + + + +extra_key_witness:tx_id->tx:id + + + + + +extra_migrations + + +extra_migrations + + +PK +id + : bigint + + +   token : character varying + + +   description : character varying + + + +gov_action_proposal + + +gov_action_proposal + + +PK +id + : bigint + + +   tx_id : bigint + + +   index : bigint + + +   prev_gov_action_proposal : bigint + + +   deposit : numeric + + +   return_address : bigint + + +   expiration : integer + + +   voting_anchor_id : bigint + + +   type : USER-DEFINED + + +   description : jsonb + + +   param_proposal : bigint + + +   ratified_epoch : integer + + +   enacted_epoch : integer + + +   dropped_epoch : integer + + +   expired_epoch : integer + + + +ma_tx_mint + + +ma_tx_mint + + +PK +id + : bigint + + +   quantity : numeric + + +FK +tx_id + : bigint + + +FK +ident + : bigint + + + +multi_asset + + +multi_asset + + +PK +id + : bigint + + +   policy : bytea + + +   name : bytea + + +   fingerprint : character varying + + + +ma_tx_mint:ident->multi_asset:id + + + + + +ma_tx_mint:tx_id->tx:id + + + + + +ma_tx_out + + +ma_tx_out + + +PK +id + : bigint + + +   quantity : numeric + + +FK +tx_out_id + : bigint + + +FK +ident + : bigint + + + +ma_tx_out:ident->multi_asset:id + + + + + +tx_out + + +tx_out + + +PK +id + : bigint + + +FK +tx_id + : bigint + + +   index : smallint + + +   address : character varying + + +   address_has_script : boolean + + +   payment_cred : bytea + + +FK +stake_address_id + : bigint + + +   value : numeric + + +   data_hash : bytea + + +FK +inline_datum_id + : bigint + + +FK +reference_script_id + : bigint + + +   consumed_by_tx_id : bigint + + + +ma_tx_out:tx_out_id->tx_out:id + + + + + +meta + + +meta + + +PK +id + : bigint + + +   start_time : timestamp without time zone + + +   network_name : character varying + + +   version : character varying + + + +new_committee + + +new_committee + + +PK +id + : bigint + + +   gov_action_proposal_id : bigint + + +   deleted_members : character varying + + +   added_members : character varying + + +   quorum_numerator : bigint + + +   quorum_denominator : bigint + + + +off_chain_pool_data + + +off_chain_pool_data + + +PK +id + : bigint + + +   pool_id : bigint + + +   ticker_name : character varying + + +   hash : bytea + + +   json : jsonb + + +   bytes : bytea + + +   pmr_id : bigint + + + +off_chain_pool_fetch_error + + +off_chain_pool_fetch_error + + +PK +id + : bigint + + +   pool_id : bigint + + +   fetch_time : timestamp without time zone + + +   pmr_id : bigint + + +   fetch_error : character varying + + +   retry_count : integer + + + +off_chain_vote_author + + +off_chain_vote_author + + +PK +id + : bigint + + +   off_chain_vote_data_id : bigint + + +   name : character varying + + +   witness_algorithm : character varying + + +   public_key : character varying + + +   signature : character varying + + +   warning : character varying + + + +off_chain_vote_data + + +off_chain_vote_data + + +PK +id + : bigint + + +   voting_anchor_id : bigint + + +   hash : bytea + + +   json : jsonb + + +   bytes : bytea + + +   warning : character varying + + +   language : character varying + + +   comment : character varying + + +   is_valid : boolean + + + +off_chain_vote_drep_data + + +off_chain_vote_drep_data + + +PK +id + : bigint + + +   off_chain_vote_data_id : bigint + + +   payment_address : character varying + + +   given_name : character varying + + +   objectives : character varying + + +   motivations : character varying + + +   qualifications : character varying + + +   image_url : character varying + + +   image_hash : character varying + + + +off_chain_vote_external_update + + +off_chain_vote_external_update + + +PK +id + : bigint + + +   off_chain_vote_data_id : bigint + + +   title : character varying + + +   uri : character varying + + + +off_chain_vote_fetch_error + + +off_chain_vote_fetch_error + + +PK +id + : bigint + + +   voting_anchor_id : bigint + + +   fetch_error : character varying + + +   fetch_time : timestamp without time zone + + +   retry_count : integer + + + +off_chain_vote_gov_action_data + + +off_chain_vote_gov_action_data + + +PK +id + : bigint + + +   off_chain_vote_data_id : bigint + + +   title : character varying + + +   abstract : character varying + + +   motivation : character varying + + +   rationale : character varying + + + +off_chain_vote_reference + + +off_chain_vote_reference + + +PK +id + : bigint + + +   off_chain_vote_data_id : bigint + + +   label : character varying + + +   uri : character varying + + +   hash_digest : character varying + + +   hash_algorithm : character varying + + + +param_proposal + + +param_proposal + + +PK +id + : bigint + + +   epoch_no : integer + + +   key : bytea + + +   min_fee_a : numeric + + +   min_fee_b : numeric + + +   max_block_size : numeric + + +   max_tx_size : numeric + + +   max_bh_size : numeric + + +   key_deposit : numeric + + +   pool_deposit : numeric + + +   max_epoch : numeric + + +   optimal_pool_count : numeric + + +   influence : double precision + + +   monetary_expand_rate : double precision + + +   treasury_growth_rate : double precision + + +   decentralisation : double precision + + +   entropy : bytea + + +   protocol_major : integer + + +   protocol_minor : integer + + +   min_utxo_value : numeric + + +   min_pool_cost : numeric + + +FK +cost_model_id + : bigint + + +   price_mem : double precision + + +   price_step : double precision + + +   max_tx_ex_mem : numeric + + +   max_tx_ex_steps : numeric + + +   max_block_ex_mem : numeric + + +   max_block_ex_steps : numeric + + +   max_val_size : numeric + + +   collateral_percent : integer + + +   max_collateral_inputs : integer + + +FK +registered_tx_id + : bigint + + +   coins_per_utxo_size : numeric + + +   pvt_motion_no_confidence : double precision + + +   pvt_committee_normal : double precision + + +   pvt_committee_no_confidence : double precision + + +   pvt_hard_fork_initiation : double precision + + +   dvt_motion_no_confidence : double precision + + +   dvt_committee_normal : double precision + + +   dvt_committee_no_confidence : double precision + + +   dvt_update_to_constitution : double precision + + +   dvt_hard_fork_initiation : double precision + + +   dvt_p_p_network_group : double precision + + +   dvt_p_p_economic_group : double precision + + +   dvt_p_p_technical_group : double precision + + +   dvt_p_p_gov_group : double precision + + +   dvt_treasury_withdrawal : double precision + + +   committee_min_size : numeric + + +   committee_max_term_length : numeric + + +   gov_action_lifetime : numeric + + +   gov_action_deposit : numeric + + +   drep_deposit : numeric + + +   drep_activity : numeric + + +   pvtpp_security_group : double precision + + +   min_fee_ref_script_cost_per_byte : double precision + + + +param_proposal:cost_model_id->cost_model:id + + + + + +param_proposal:registered_tx_id->tx:id + + + + + +pool_metadata_ref + + +pool_metadata_ref + + +PK +id + : bigint + + +FK +pool_id + : bigint + + +   url : character varying + + +   hash : bytea + + +FK +registered_tx_id + : bigint + + + +pool_metadata_ref:pool_id->pool_hash:id + + + + + +pool_metadata_ref:registered_tx_id->tx:id + + + + + +pool_owner + + +pool_owner + + +PK +id + : bigint + + +FK +addr_id + : bigint + + +FK +pool_update_id + : bigint + + + +pool_update + + +pool_update + + +PK +id + : bigint + + +FK +hash_id + : bigint + + +   cert_index : integer + + +   vrf_key_hash : bytea + + +   pledge : numeric + + +   active_epoch_no : bigint + + +FK +meta_id + : bigint + + +   margin : double precision + + +   fixed_cost : numeric + + +FK +registered_tx_id + : bigint + + +FK +reward_addr_id + : bigint + + +   deposit : numeric + + + +pool_owner:pool_update_id->pool_update:id + + + + + +pool_owner:addr_id->stake_address:id + + + + + +pool_relay + + +pool_relay + + +PK +id + : bigint + + +FK +update_id + : bigint + + +   ipv4 : character varying + + +   ipv6 : character varying + + +   dns_name : character varying + + +   dns_srv_name : character varying + + +   port : integer + + + +pool_relay:update_id->pool_update:id + + + + + +pool_retire + + +pool_retire + + +PK +id + : bigint + + +FK +hash_id + : bigint + + +   cert_index : integer + + +FK +announced_tx_id + : bigint + + +   retiring_epoch : integer + + + +pool_retire:hash_id->pool_hash:id + + + + + +pool_retire:announced_tx_id->tx:id + + + + + +pool_stat + + +pool_stat + + +PK +id + : bigint + + +   pool_hash_id : bigint + + +   epoch_no : integer + + +   number_of_blocks : numeric + + +   number_of_delegators : numeric + + +   stake : numeric + + +   voting_power : numeric + + + +pool_update:hash_id->pool_hash:id + + + + + +pool_update:meta_id->pool_metadata_ref:id + + + + + +pool_update:reward_addr_id->stake_address:id + + + + + +pool_update:registered_tx_id->tx:id + + + + + +pot_transfer + + +pot_transfer + + +PK +id + : bigint + + +   cert_index : integer + + +   treasury : numeric + + +   reserves : numeric + + +FK +tx_id + : bigint + + + +pot_transfer:tx_id->tx:id + + + + + +redeemer_data + + +redeemer_data + + +PK +id + : bigint + + +   hash : bytea + + +FK +tx_id + : bigint + + +   value : jsonb + + +   bytes : bytea + + + +redeemer:redeemer_data_id->redeemer_data:id + + + + + +redeemer:tx_id->tx:id + + + + + +redeemer_data:tx_id->tx:id + + + + + +reference_tx_in + + +reference_tx_in + + +PK +id + : bigint + + +FK +tx_in_id + : bigint + + +FK +tx_out_id + : bigint + + +   tx_out_index : smallint + + + +reference_tx_in:tx_in_id->tx:id + + + + + +reference_tx_in:tx_out_id->tx:id + + + + + +reserve + + +reserve + + +PK +id + : bigint + + +FK +addr_id + : bigint + + +   cert_index : integer + + +   amount : numeric + + +FK +tx_id + : bigint + + + +reserve:addr_id->stake_address:id + + + + + +reserve:tx_id->tx:id + + + + + +reserved_pool_ticker + + +reserved_pool_ticker + + +PK +id + : bigint + + +   name : character varying + + +   pool_hash : bytea + + + +reverse_index + + +reverse_index + + +PK +id + : bigint + + +   block_id : bigint + + +   min_ids : character varying + + + +reward + + +reward + + +FK +addr_id + : bigint + + +   type : USER-DEFINED + + +   amount : numeric + + +   spendable_epoch : bigint + + +FK +pool_id + : bigint + + +   earned_epoch : bigint + + + +reward:pool_id->pool_hash:id + + + + + +reward:addr_id->stake_address:id + + + + + +reward_rest + + +reward_rest + + +   addr_id : bigint + + +   type : USER-DEFINED + + +   amount : numeric + + +   spendable_epoch : bigint + + +   earned_epoch : bigint + + + +schema_version + + +schema_version + + +PK +id + : bigint + + +   stage_one : bigint + + +   stage_two : bigint + + +   stage_three : bigint + + + +script:tx_id->tx:id + + + + + +slot_leader:pool_hash_id->pool_hash:id + + + + + +stake_deregistration + + +stake_deregistration + + +PK +id + : bigint + + +FK +addr_id + : bigint + + +   cert_index : integer + + +   epoch_no : integer + + +FK +tx_id + : bigint + + +FK +redeemer_id + : bigint + + + +stake_deregistration:redeemer_id->redeemer:id + + + + + +stake_deregistration:addr_id->stake_address:id + + + + + +stake_deregistration:tx_id->tx:id + + + + + +stake_registration + + +stake_registration + + +PK +id + : bigint + + +FK +addr_id + : bigint + + +   cert_index : integer + + +   epoch_no : integer + + +FK +tx_id + : bigint + + +   deposit : numeric + + + +stake_registration:addr_id->stake_address:id + + + + + +stake_registration:tx_id->tx:id + + + + + +treasury + + +treasury + + +PK +id + : bigint + + +FK +addr_id + : bigint + + +   cert_index : integer + + +   amount : numeric + + +FK +tx_id + : bigint + + + +treasury:addr_id->stake_address:id + + + + + +treasury:tx_id->tx:id + + + + + +treasury_withdrawal + + +treasury_withdrawal + + +PK +id + : bigint + + +   gov_action_proposal_id : bigint + + +   stake_address_id : bigint + + +   amount : numeric + + + +tx:block_id->block:id + + + + + +tx_cbor + + +tx_cbor + + +PK +id + : bigint + + +   tx_id : bigint + + +   bytes : bytea + + + +tx_in + + +tx_in + + +PK +id + : bigint + + +FK +tx_in_id + : bigint + + +FK +tx_out_id + : bigint + + +   tx_out_index : smallint + + +FK +redeemer_id + : bigint + + + +tx_in:redeemer_id->redeemer:id + + + + + +tx_in:tx_in_id->tx:id + + + + + +tx_in:tx_out_id->tx:id + + + + + +tx_metadata + + +tx_metadata + + +PK +id + : bigint + + +   key : numeric + + +   json : jsonb + + +   bytes : bytea + + +FK +tx_id + : bigint + + + +tx_metadata:tx_id->tx:id + + + + + +tx_out:inline_datum_id->datum:id + + + + + +tx_out:reference_script_id->script:id + + + + + +tx_out:stake_address_id->stake_address:id + + + + + +tx_out:tx_id->tx:id + + + + + +voting_anchor + + +voting_anchor + + +PK +id + : bigint + + +   url : character varying + + +   data_hash : bytea + + +   type : USER-DEFINED + + +   block_id : bigint + + + +voting_procedure + + +voting_procedure + + +PK +id + : bigint + + +   tx_id : bigint + + +   index : integer + + +   gov_action_proposal_id : bigint + + +   voter_role : USER-DEFINED + + +   drep_voter : bigint + + +   pool_voter : bigint + + +   vote : USER-DEFINED + + +   voting_anchor_id : bigint + + +   committee_voter : bigint + + +   invalid : bigint + + + +withdrawal + + +withdrawal + + +PK +id + : bigint + + +FK +addr_id + : bigint + + +   amount : numeric + + +FK +redeemer_id + : bigint + + +FK +tx_id + : bigint + + + +withdrawal:redeemer_id->redeemer:id + + + + + +withdrawal:addr_id->stake_address:id + + + + + +withdrawal:tx_id->tx:id + + + + + +legend + +Legend + + +blocks / tx / outputs + + +stake / rewards + + +pools + + +governance + + +off-chain metadata + + +epoch / protocol + + +multi-asset + + +bookkeeping +PK = primary key, FK = foreign key. Crow foot = many side. +FK constraints are dropped at run time for insert speed; edges are the logical references. + + + + diff --git a/flake.nix b/flake.nix index 2b46193cb..7dab820c1 100644 --- a/flake.nix +++ b/flake.nix @@ -247,6 +247,8 @@ git protobuf snappy + graphviz # scripts/generate-erd.sh (ER diagram rendering) + python3 # scripts/generate-erd.sh (ER diagram generator) ] ++ lib.optionals (system == "x86_64-linux") [ liburing # io_uring is linux-only ]; diff --git a/scripts/gen-erd.py b/scripts/gen-erd.py new file mode 100755 index 000000000..a4fec41bd --- /dev/null +++ b/scripts/gen-erd.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +# Emit a graphviz .dot ER diagram from extracted schema data. +# Usage: gen-erd.py COLUMNS_FILE PKS_FILE FK_FILE OUT_DOT +# COLUMNS_FILE lines: table|ordinal|column|type +# PKS_FILE lines: table|column +# FK_FILE lines: child|column|parent (logical FKs, e.g. parsed from migrations) +import sys, html + +cols_file, pks_file, fk_file, out_dot = sys.argv[1:5] + +cols = {} +for line in open(cols_file): + line = line.rstrip("\n") + if not line: continue + t, _o, c, ty = line.split("|", 3) + cols.setdefault(t, []).append((c, ty)) +tables = set(cols) + +pk = {} +for line in open(pks_file): + line = line.rstrip("\n") + if not line: continue + t, c = line.split("|", 1) + pk.setdefault(t, set()).add(c) + +edges, fkcols, seen = [], {}, set() +for line in open(fk_file): + line = line.rstrip("\n") + if not line: continue + child, col, parent = line.split("|", 2) + if child in tables and parent in tables and any(c == col for c, _ in cols[child]): + if (child, col, parent) in seen: continue + seen.add((child, col, parent)) + edges.append((child, col, parent)) + fkcols.setdefault(child, set()).add(col) + +def domain(t): + if t.startswith("off_chain"): return "offchain" + if t.startswith("ma_") or t == "multi_asset": return "multiasset" + if t.startswith("pool_") or t in {"delisted_pool", "reserved_pool_ticker"}: return "pool" + if t.startswith("stake_") or t in {"delegation", "reward", "reward_rest", + "epoch_stake", "epoch_stake_progress", "treasury", "reserve", "pot_transfer"}: return "stake" + if t.startswith("drep_") or t.startswith("committee") or t in {"constitution", + "new_committee", "gov_action_proposal", "voting_procedure", "voting_anchor", + "treasury_withdrawal", "delegation_vote", "param_proposal", "event_info"}: return "gov" + if t.startswith("epoch") or t in {"ada_pots", "cost_model"}: return "epoch" + if t in {"block", "tx", "tx_out", "tx_in", "collateral_tx_in", "collateral_tx_out", + "reference_tx_in", "tx_metadata", "tx_cbor", "datum", "redeemer", "redeemer_data", + "script", "extra_key_witness", "withdrawal", "slot_leader", "reverse_index", "meta"}: return "core" + return "misc" + +FILL = {"core": ("#BDD7EE", "#EAF3FB"), "stake": ("#C6E0B4", "#EDF6E7"), + "pool": ("#FFE699", "#FFF7DF"), "gov": ("#D9C2EC", "#F1E9F8"), + "offchain": ("#F8CBAD", "#FCE9DE"), "epoch": ("#B4E5E0", "#E3F6F4"), + "multiasset": ("#FFD9CC", "#FFEDE6"), "misc": ("#D9D9D9", "#F2F2F2")} +ORDER = ["core", "stake", "pool", "gov", "offchain", "epoch", "multiasset", "misc"] +LABELS = {"core": "blocks / tx / outputs", "stake": "stake / rewards", "pool": "pools", + "gov": "governance", "offchain": "off-chain metadata", "epoch": "epoch / protocol", + "multiasset": "multi-asset", "misc": "bookkeeping"} + +def esc(s): return html.escape(s, quote=True) + +def node(t): + hc, bc = FILL[domain(t)] + rows = [f'{esc(t)}'] + for c, ty in cols[t]: + mark = "PK " if c in pk.get(t, set()) else ("FK " if c in fkcols.get(t, set()) else " ") + b0, b1 = ("", "") if mark.strip() else ("", "") + rows.append(f'' + f'{mark}{b0}{esc(c)}{b1} : {esc(ty)}') + return (f' "{t}" [label=<{"".join(rows)}
>];') + +def legend(): + rows = ['Legend'] + for d in ORDER: + hc, _ = FILL[d] + rows.append(f' ' + f'{LABELS[d]}') + rows.append('' + 'PK = primary key, FK = foreign key. Crow foot = many side.
' + 'FK constraints are dropped at run time for insert speed; edges are the logical references.' + '
') + return (' "legend" [shape=plaintext, label=<{"".join(rows)}
>];') + +out = ["digraph erd {", + ' graph [layout=sfdp, overlap=prism, splines=true, K=1.2, repulsiveforce=1.6, ' + 'fontname="Helvetica", bgcolor="white", pad=0.4];', + ' node [shape=plaintext, fontname="Helvetica"];', + ' edge [color="#8C8C8C", arrowsize=0.8, dir=both, arrowtail=crow, arrowhead=none, penwidth=1.0];'] +for t in sorted(tables): + out.append(node(t)) +out.append(legend()) +for child, col, parent in edges: + out.append(f' "{child}":"{col}" -> "{parent}":"id" ;') +out.append("}") +open(out_dot, "w").write("\n".join(out)) +sys.stderr.write(f"tables={len(tables)} edges={len(edges)} -> {out_dot}\n") diff --git a/scripts/generate-erd.sh b/scripts/generate-erd.sh new file mode 100755 index 000000000..d41c14831 --- /dev/null +++ b/scripts/generate-erd.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +# Generate the entity-relationship diagram doc/ERD.svg from the current schema. +# +# It builds a throwaway database, applies every migration in schema/, reads the +# resulting tables/columns, takes the foreign-key relationships from the migration +# SQL (the FK constraints are dropped at run time for insert speed, so we read the +# declarations rather than the live constraints), and renders a colour-coded SVG. +# +# Usage: ./scripts/generate-erd.sh +# Output: doc/ERD.svg +# +# Requirements: postgresql client (createdb/psql/dropdb), graphviz (dot), python3. +# In the nix dev shell these are already provided. Connection uses the standard +# PG* environment variables (PGHOST, PGUSER, ...); the default local socket works. +# +# Config via env: +# ERD_DB name of the throwaway database (default: cardano_db_sync_erd_tmp) +# SCHEMA_DIR migrations directory (default: schema) +# OUT output file (default: doc/ERD.svg) + +set -euo pipefail + +export PGOPTIONS="${PGOPTIONS:-} -c client_min_messages=warning" # quiet migration NOTICEs + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +SCHEMA_DIR="${SCHEMA_DIR:-$REPO_DIR/schema}" +OUT="${OUT:-$REPO_DIR/doc/ERD.svg}" +ERD_DB="${ERD_DB:-cardano_db_sync_erd_tmp}" + +work="$(mktemp -d)" +cleanup() { dropdb --if-exists "$ERD_DB" >/dev/null 2>&1 || true; rm -rf "$work"; } +trap cleanup EXIT + +echo "Creating throwaway database '$ERD_DB' and applying migrations..." +dropdb --if-exists "$ERD_DB" >/dev/null 2>&1 || true +createdb "$ERD_DB" +for f in $(ls "$SCHEMA_DIR"/migration-*.sql | sort); do + psql -q -v ON_ERROR_STOP=1 -d "$ERD_DB" -f "$f" >/dev/null +done + +echo "Reading tables, columns and primary keys..." +psql -d "$ERD_DB" -tAF'|' -c " + SELECT c.table_name, c.ordinal_position, c.column_name, c.data_type + FROM information_schema.columns c + JOIN information_schema.tables t + ON t.table_schema = c.table_schema AND t.table_name = c.table_name + AND t.table_type = 'BASE TABLE' + WHERE c.table_schema = 'public' + ORDER BY c.table_name, c.ordinal_position" > "$work/columns.txt" +psql -d "$ERD_DB" -tAF'|' -c " + SELECT tc.table_name, kcu.column_name + FROM information_schema.table_constraints tc + JOIN information_schema.key_column_usage kcu ON kcu.constraint_name = tc.constraint_name + WHERE tc.constraint_type = 'PRIMARY KEY' AND tc.table_schema = 'public'" > "$work/pks.txt" + +echo "Collecting foreign-key relationships from the migration SQL..." +grep -rhoE 'ALTER TABLE "[a-z_]+" ADD CONSTRAINT "[^"]+" FOREIGN KEY\("[a-z_]+"\) REFERENCES "[a-z_]+"' "$SCHEMA_DIR"/*.sql \ + | sed -E 's/ALTER TABLE "([a-z_]+)" ADD CONSTRAINT "[^"]+" FOREIGN KEY\("([a-z_]+)"\) REFERENCES "([a-z_]+)"/\1|\2|\3/' \ + | sort -u > "$work/fk.txt" + +echo "Generating graphviz source and rendering $OUT ..." +python3 "$SCRIPT_DIR/gen-erd.py" "$work/columns.txt" "$work/pks.txt" "$work/fk.txt" "$work/erd.dot" +dot -Tsvg "$work/erd.dot" -o "$OUT" + +echo "Done: $OUT"