Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(indexer): postgres schema creation + CI config #20701

Open
wants to merge 97 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
76f6f32
feat: indexer base types
aaronc Jun 11, 2024
63aeb85
WIP on tests
aaronc Jun 11, 2024
216e8f8
update listener
aaronc Jun 12, 2024
b65f3cd
feat(indexer): add listener test fixture
aaronc Jun 12, 2024
d8b6831
feat(indexer): postgres create table
aaronc Jun 12, 2024
6deca01
WIP
aaronc Jun 12, 2024
4390b46
WIP
aaronc Jun 13, 2024
b9fb6c9
Merge branch 'main' of github.com:cosmos/cosmos-sdk into aaronc/index…
aaronc Jun 13, 2024
663ed17
rename column to field
aaronc Jun 13, 2024
4311357
delete code, simplify
aaronc Jun 13, 2024
c52655a
add error return
aaronc Jun 13, 2024
46669d3
remove ability to filter subscribed modules - this is a bit dangerous
aaronc Jun 13, 2024
0921977
Merge branch 'aaronc/indexer-base' into aaronc/indexer-testing
aaronc Jun 13, 2024
b7b6914
fixes
aaronc Jun 13, 2024
0a47c39
add docs about fields
aaronc Jun 13, 2024
0f07baf
updates
aaronc Jun 13, 2024
1cbe5bc
WIP
aaronc Jun 13, 2024
aacab9e
update table and entity language to object
aaronc Jun 13, 2024
7fd604f
update table and entity language to object
aaronc Jun 13, 2024
4130b32
WIP
aaronc Jun 13, 2024
e840496
rename to type
aaronc Jun 13, 2024
4a00094
rename to type
aaronc Jun 13, 2024
cff2b78
Merge branch 'aaronc/indexer-base' into aaronc/indexer-testing
aaronc Jun 13, 2024
e44dea2
spin out into its own go.mod
aaronc Jun 13, 2024
0c7f529
add CHANGELOG.md
aaronc Jun 13, 2024
d83b80a
Merge branch 'aaronc/indexer-base' into aaronc/indexer-testing
aaronc Jun 13, 2024
a94dd47
better faking
aaronc Jun 13, 2024
7990487
WIP
aaronc Jun 13, 2024
408ddc4
add DecodableModule interface
aaronc Jun 13, 2024
599e7cf
Merge branch 'main' of github.com:cosmos/cosmos-sdk into aaronc/index…
aaronc Jun 14, 2024
bc98756
make compatible with go 1.12
aaronc Jun 14, 2024
68d0afc
remove CommitCatchupSync - catch-up design in flux, may be premature …
aaronc Jun 17, 2024
2a52b97
Merge branch 'aaronc/indexer-base' into aaronc/indexer-postgres1
aaronc Jun 17, 2024
0c77dc3
Merge branch 'aaronc/indexer-base' into aaronc/indexer-testing
aaronc Jun 17, 2024
3398d06
Merge branch 'aaronc/indexer-testing' into aaronc/indexer-postgres1
aaronc Jun 17, 2024
f3a4832
create table
aaronc Jun 17, 2024
5403d9e
testing WIP
aaronc Jun 17, 2024
ba706a5
WIP
aaronc Jun 17, 2024
50a8c37
restore validation code
aaronc Jun 17, 2024
0a4b055
Merge branch 'aaronc/indexer-base-validation' into aaronc/indexer-pos…
aaronc Jun 17, 2024
f4de201
working create table
aaronc Jun 17, 2024
92dc112
merge wip branch work
aaronc Jul 2, 2024
0ac81d3
Merge branch 'main' of github.com:cosmos/cosmos-sdk into aaronc/index…
aaronc Jul 2, 2024
6d0c8e1
deletions
aaronc Jul 2, 2024
66beb7b
cleanup
aaronc Jul 2, 2024
e2ee268
cleanup
aaronc Jul 2, 2024
a6cf9aa
cleanup
aaronc Jul 2, 2024
82114b0
refactoring
aaronc Jul 2, 2024
102353e
cleanup
aaronc Jul 2, 2024
6343cd2
cleanup
aaronc Jul 2, 2024
055b2b0
refactoring
aaronc Jul 2, 2024
19c74af
updates
aaronc Jul 2, 2024
1c49db5
docs, cleanup
aaronc Jul 2, 2024
396a65b
time fix
aaronc Jul 2, 2024
447c013
docs, fixes
aaronc Jul 2, 2024
04e925e
docs, cleanup
aaronc Jul 2, 2024
005e2ff
refactoring and docs
aaronc Jul 3, 2024
06bfeab
feat(schema)!: updates based on postgres testing
aaronc Jul 3, 2024
b9abce4
renaming for clarity
aaronc Jul 3, 2024
c46600d
renaming for clarity
aaronc Jul 3, 2024
a00badd
docs, enum compatibility test
aaronc Jul 3, 2024
9cabe6e
add null string validation
aaronc Jul 3, 2024
9e14917
validate UTF-8
aaronc Jul 3, 2024
6571694
Merge branch 'main' into aaronc/schema-updates
aaronc Jul 3, 2024
7ac3ee6
Merge branch 'main' of github.com:cosmos/cosmos-sdk into aaronc/index…
aaronc Jul 3, 2024
a10a14e
Merge branch 'aaronc/schema-updates' into aaronc/indexer-postgres1
aaronc Jul 3, 2024
9dd1c99
Merge branch 'main' of github.com:cosmos/cosmos-sdk into aaronc/index…
aaronc Jul 3, 2024
15f95c1
limit scope, add example tests
aaronc Jul 3, 2024
cf36f83
limit PR scope
aaronc Jul 3, 2024
74abccd
WIP
aaronc Jul 3, 2024
b11bb2c
integration test
aaronc Jul 3, 2024
64bf68f
add READMEs, CHANGELOG.md
aaronc Jul 3, 2024
f84dbc2
Merge branch 'main' of github.com:cosmos/cosmos-sdk into aaronc/index…
aaronc Jul 4, 2024
fcb4feb
fix
aaronc Jul 4, 2024
b014956
Merge branch 'main' of github.com:cosmos/cosmos-sdk into aaronc/index…
aaronc Jul 5, 2024
3b97e4c
add CI config
aaronc Jul 8, 2024
54f7379
merge coverage files
aaronc Jul 8, 2024
3d45244
update indexer API and tests
aaronc Jul 8, 2024
a02093f
test fix
aaronc Jul 8, 2024
d7a7b8a
Merge branch 'main' of github.com:cosmos/cosmos-sdk into aaronc/index…
aaronc Jul 8, 2024
0c16fce
Merge branch 'main' into aaronc/indexer-postgres1
aaronc Jul 8, 2024
e1a8ce0
fix %w
aaronc Jul 8, 2024
844ac5b
go mod tidy
aaronc Jul 8, 2024
2784347
go mod tidy
aaronc Jul 8, 2024
04fd8b4
lint, build updates
aaronc Jul 8, 2024
8c79a6c
lint
aaronc Jul 8, 2024
b48d768
lint
aaronc Jul 8, 2024
7c22412
increase test coverage, gofumpt
aaronc Jul 8, 2024
28d8413
increase test coverage
aaronc Jul 8, 2024
d1dd8bd
Update indexer/postgres/README.md
aaronc Jul 8, 2024
f7c592c
Update indexer/postgres/README.md
aaronc Jul 8, 2024
0d40d76
Update indexer/postgres/README.md
aaronc Jul 8, 2024
283e603
Merge branch 'main' of github.com:cosmos/cosmos-sdk into aaronc/index…
aaronc Jul 9, 2024
f63bc4a
improve test coverage, remove go.mod replaces
aaronc Jul 9, 2024
9e96012
lint fix
aaronc Jul 10, 2024
da3203c
rename managers to indexers
aaronc Jul 14, 2024
16cf734
rename
aaronc Jul 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,24 @@ updates:
labels:
- "A:automerge"
- dependencies
- package-ecosystem: gomod
directory: "/indexer/postgres"
schedule:
interval: weekly
day: wednesday
time: "01:53"
labels:
- "A:automerge"
- dependencies
- package-ecosystem: gomod
directory: "/indexer/postgres/tests"
schedule:
interval: weekly
day: wednesday
time: "01:53"
labels:
- "A:automerge"
- dependencies
- package-ecosystem: gomod
directory: "/schema"
schedule:
Expand Down
2 changes: 2 additions & 0 deletions .github/pr_labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
- orm/**/*
"C:schema":
- schema/**/*
"C:indexer/postgres":
- indexer/postgres/**/*
"C:x/accounts":
- x/accounts/**/*
"C:x/auth":
Expand Down
36 changes: 36 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,42 @@ jobs:
with:
projectBaseDir: schema/

test-indexer-postgres:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: "1.22"
julienrbrt marked this conversation as resolved.
Show resolved Hide resolved
cache: true
cache-dependency-path: indexer/postgres/tests/go.sum
- uses: technote-space/get-diff-action@v6.1.2
id: git_diff
with:
PATTERNS: |
indexer/postgres/**/*.go
indexer/postgres/go.mod
indexer/postgres/go.sum
indexer/postgres/tests/go.mod
indexer/postgres/tests/go.sum
- name: tests
if: env.GIT_DIFF
run: |
cd indexer/postgres
go test -mod=readonly -timeout 30m -coverprofile=cov.out -covermode=atomic ./...
cd tests
go test -mod=readonly -timeout 30m -coverprofile=cov.out -covermode=atomic -coverpkg=cosmossdk.io/indexer/postgres ./...
cd ..
go run github.com/dylandreimerink/gocovmerge/cmd/gocovmerge@latest cov.out tests/cov.out > coverage.out
- name: sonarcloud
if: ${{ env.GIT_DIFF && !github.event.pull_request.draft && env.SONAR_TOKEN != null }}
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
with:
projectBaseDir: indexer/postgres/

test-simapp:
runs-on: ubuntu-latest
steps:
Expand Down
1 change: 1 addition & 0 deletions go.work.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use (
./core/testing
./depinject
./errors
./indexer/postgres
./log
./math
./orm
Expand Down
37 changes: 37 additions & 0 deletions indexer/postgres/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<!--
Guiding Principles:

Changelogs are for humans, not machines.
There should be an entry for every single version.
The same types of changes should be grouped.
Versions and sections should be linkable.
The latest version comes first.
The release date of each version is displayed.
Mention whether you follow Semantic Versioning.

Usage:

Change log entries are to be added to the Unreleased section under the
appropriate stanza (see below). Each entry should ideally include a tag and
the Github issue reference in the following format:

* (<tag>) \#<issue-number> message

The issue numbers will later be link-ified during the release process so you do
not have to worry about including a link manually, but you can if you wish.

Types of changes (Stanzas):

"Features" for new features.
"Improvements" for changes in existing functionality.
"Deprecated" for soon-to-be removed features.
"Bug Fixes" for any bug fixes.
"Client Breaking" for breaking Protobuf, gRPC and REST routes used by end-users.
"CLI Breaking" for breaking CLI commands.
"API Breaking" for breaking exported APIs used by developers building on SDK.
Ref: https://keepachangelog.com/en/1.0.0/
-->

# Changelog

## [Unreleased]
41 changes: 41 additions & 0 deletions indexer/postgres/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# PostgreSQL Indexer

The PostgreSQL indexer can fully index the current state for all modules that implement `cosmossdk.io/schema.HasModuleCodec`.
implement `cosmossdk.io/schema.HasModuleCodec`.

## Table, Column and Enum Naming

`ObjectType`s names are converted to table names prefixed with the module name and an underscore. i.e. the `ObjectType` `foo` in module `bar` will be stored in a table named `bar_foo`.

Column names are identical to field names. All identifiers are quoted with double quotes so that they are case-sensitive and won't clash with any reserved names.

Like, table names, enum types are prefixed with the module name and an underscore.

## Schema Type Mapping

The mapping of `cosmossdk.io/schema` `Kind`s to PostgreSQL types is as follows:

| Kind | PostgreSQL Type | Notes |
|---------------------|----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `StringKind` | `TEXT` | |
| `BoolKind` | `BOOLEAN` | |
| `BytesKind` | `BYTEA` | |
| `Int8Kind` | `SMALLINT` | |
| `Int16Kind` | `SMALLINT` | |
| `Int32Kind` | `INTEGER` | |
| `Int64Kind` | `BIGINT` | |
| `Uint8Kind` | `SMALLINT` | |
| `Uint16Kind` | `INTEGER` | |
| `Uint32Kind` | `BIGINT` | |
| `Uint64Kind` | `NUMERIC` | |
| `Float32Kind` | `REAL` | |
| `Float64Kind` | `DOUBLE PRECISION` | |
| `IntegerStringKind` | `NUMERIC` | |
| `DecimalStringKind` | `NUMERIC` | |
| `JSONKind` | `JSONB` | |
| `Bech32AddressKind` | `TEXT` | addresses are converted to strings with the specified address prefix |
| `TimeKind` | `BIGINT` and `TIMESTAMPTZ` | time types are stored as two columns, one with the `_nanos` suffix with full nanoseconds precision, and another as a `TIMESTAMPTZ` generated column with microsecond precision |
| `DurationKind` | `BIGINT` | durations are stored as a single column in nanoseconds |
| `EnumKind` | `<module_name>_<enum_name>` | a custom enum type is created for each module prefixed with the module name it pertains to |


julienrbrt marked this conversation as resolved.
Show resolved Hide resolved
8 changes: 8 additions & 0 deletions indexer/postgres/base_sql.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package postgres

// BaseSQL is the base SQL that is always included in the schema.
const BaseSQL = `
CREATE OR REPLACE FUNCTION nanos_to_timestamptz(nanos bigint) RETURNS timestamptz AS $$
SELECT to_timestamp(nanos / 1000000000) + (nanos / 1000000000) * INTERVAL '1 microsecond'
$$ LANGUAGE SQL IMMUTABLE;
`
120 changes: 120 additions & 0 deletions indexer/postgres/column.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package postgres

import (
"fmt"
"io"

"cosmossdk.io/schema"
)

// createColumnDefinition writes a column definition within a CREATE TABLE statement for the field.
func (tm *ObjectIndexer) createColumnDefinition(writer io.Writer, field schema.Field) error {
_, err := fmt.Fprintf(writer, "%q ", field.Name)
if err != nil {
return err
}

simple := simpleColumnType(field.Kind)
if simple != "" {
_, err = fmt.Fprintf(writer, "%s", simple)
if err != nil {
return err
}

return writeNullability(writer, field.Nullable)
} else {
switch field.Kind {
case schema.EnumKind:
_, err = fmt.Fprintf(writer, "%q", enumTypeName(tm.moduleName, field.EnumDefinition))
if err != nil {
return err
}
case schema.TimeKind:
// for time fields, we generate two columns:
// - one with nanoseconds precision for lossless storage, suffixed with _nanos
// - one as a timestamptz (microsecond precision) for ease of use, that is GENERATED
nanosColName := fmt.Sprintf("%s_nanos", field.Name)
_, err = fmt.Fprintf(writer, "TIMESTAMPTZ GENERATED ALWAYS AS (nanos_to_timestamptz(%q)) STORED,\n\t", nanosColName)
if err != nil {
return err
}

_, err = fmt.Fprintf(writer, `%q BIGINT`, nanosColName)
if err != nil {
return err
}
default:
return fmt.Errorf("unexpected kind: %v, this should have been handled earlier", field.Kind)
}

return writeNullability(writer, field.Nullable)
}
}

// writeNullability writes column nullability.
func writeNullability(writer io.Writer, nullable bool) error {
if nullable {
_, err := fmt.Fprintf(writer, " NULL,\n\t")
return err
} else {
_, err := fmt.Fprintf(writer, " NOT NULL,\n\t")
return err
}
}

// simpleColumnType returns the postgres column type for the kind for simple types.
func simpleColumnType(kind schema.Kind) string {
//nolint:goconst // adding constants for these postgres type names would impede readability
switch kind {
case schema.StringKind:
return "TEXT"
case schema.BoolKind:
return "BOOLEAN"
case schema.BytesKind:
return "BYTEA"
case schema.Int8Kind:
return "SMALLINT"
case schema.Int16Kind:
return "SMALLINT"
case schema.Int32Kind:
return "INTEGER"
case schema.Int64Kind:
return "BIGINT"
case schema.Uint8Kind:
return "SMALLINT"
case schema.Uint16Kind:
return "INTEGER"
case schema.Uint32Kind:
return "BIGINT"
case schema.Uint64Kind:
return "NUMERIC"
case schema.IntegerStringKind:
return "NUMERIC"
case schema.DecimalStringKind:
return "NUMERIC"
case schema.Float32Kind:
return "REAL"
case schema.Float64Kind:
return "DOUBLE PRECISION"
case schema.JSONKind:
return "JSONB"
case schema.DurationKind:
return "BIGINT"
case schema.Bech32AddressKind:

Check warning

Code scanning / CodeQL

Directly using the bech32 constants Warning

Directly using the bech32 constants instead of the configuration values
return "TEXT"
default:
return ""
}
}

// updatableColumnName is the name of the insertable/updatable column name for the field.
// This is the field name in most cases, except for time columns which are stored as nanos
// and then converted to timestamp generated columns.
func (tm *ObjectIndexer) updatableColumnName(field schema.Field) (name string, err error) {
name = field.Name
if field.Kind == schema.TimeKind {
name = fmt.Sprintf("%s_nanos", name)
}
name = fmt.Sprintf("%q", name)
return
}
14 changes: 14 additions & 0 deletions indexer/postgres/conn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package postgres

import (
"context"
"database/sql"
)

// DBConn is an interface that abstracts the *sql.DB, *sql.Tx and *sql.Conn types.
type DBConn interface {
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row
}
Loading
Loading