feat(database): support for postgres (#3998)

* beginning with postgres statements

* try pgx

* use pgx

* database

* init works for postgres

* arrays working

* init for cockroach

* init

* start tests

* tests

* TESTS

* ch

* ch

* chore: use go 1.18

* read stmts

* fix typo

* tests

* connection string

* add missing error handler

* cleanup

* start all apis

* go mod tidy

* old update

* switch back to minute

* on conflict

* replace string slice with `database.StringArray` in db models

* fix tests and start

* update go version in dockerfile

* setup go

* clean up

* remove notification migration

* update

* docs: add deploy guide for postgres

* fix: revert sonyflake

* use `database.StringArray` for daos

* use `database.StringArray` every where

* new tables

* index naming,
metadata primary key,
project grant role key type

* docs(postgres): change to beta

* chore: correct compose

* fix(defaults): add empty postgres config

* refactor: remove unused code

* docs: add postgres to self hosted

* fix broken link

* so?

* change title

* add mdx to link

* fix stmt

* update goreleaser in test-code

* docs: improve postgres example

* update more projections

* fix: add beta log for postgres

* revert index name change

* prerelease

* fix: add sequence to v1 "reduce paniced"

* log if nil

* add logging

* fix: log output

* fix(import): check if org exists and user

* refactor: imports

* fix(user): ignore malformed events

* refactor: method naming

* fix: test

* refactor: correct errors.Is call

* ci: don't build dev binaries on main

* fix(go releaser): update version to 1.11.0

* fix(user): projection should not break

* fix(user): handle error properly

* docs: correct config example

* Update .releaserc.js

* Update .releaserc.js

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: Elio Bischof <eliobischof@gmail.com>
This commit is contained in:
Silvan 2022-08-31 09:52:43 +02:00 committed by GitHub
parent d6c9815945
commit 77b4fc5487
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
189 changed files with 3401 additions and 2956 deletions

View File

@ -13,6 +13,10 @@ jobs:
env: env:
DOCKER_BUILDKIT: 1 DOCKER_BUILDKIT: 1
steps: steps:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.19
- name: Source checkout - name: Source checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up QEMU - name: Set up QEMU
@ -26,7 +30,7 @@ jobs:
uses: goreleaser/goreleaser-action@v3 uses: goreleaser/goreleaser-action@v3
with: with:
install-only: true install-only: true
version: v1.8.3 version: v1.10.3
- name: Build and Unit Test - name: Build and Unit Test
run: GOOS="linux" GOARCH="amd64" goreleaser build --id prod --snapshot --single-target --rm-dist --output .artifacts/zitadel/zitadel run: GOOS="linux" GOARCH="amd64" goreleaser build --id prod --snapshot --single-target --rm-dist --output .artifacts/zitadel/zitadel
- name: Publish go coverage - name: Publish go coverage

View File

@ -19,7 +19,7 @@ jobs:
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.17 go-version: 1.19
- name: Source checkout - name: Source checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
@ -65,7 +65,7 @@ jobs:
if: steps.semantic.outputs.new_release_published == 'true' && github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' if: steps.semantic.outputs.new_release_published == 'true' && github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch'
with: with:
distribution: goreleaser distribution: goreleaser
version: v1.8.3 version: v1.11.0
args: release args: release
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -24,8 +24,7 @@ before:
- sh -c "cp -r .artifacts/console/* internal/api/ui/console/static/" - sh -c "cp -r .artifacts/console/* internal/api/ui/console/static/"
builds: builds:
- id: prod - env:
env:
- CGO_ENABLED=0 - CGO_ENABLED=0
goos: goos:
- linux - linux
@ -36,13 +35,7 @@ builds:
- arm64 - arm64
ldflags: ldflags:
-s -w -X github.com/zitadel/zitadel/cmd/build.version={{.Version}} -X github.com/zitadel/zitadel/cmd/build.commit={{.Commit}} -X github.com/zitadel/zitadel/cmd/build.date={{.Date}} -s -w -X github.com/zitadel/zitadel/cmd/build.version={{.Version}} -X github.com/zitadel/zitadel/cmd/build.commit={{.Commit}} -X github.com/zitadel/zitadel/cmd/build.date={{.Date}}
- id: dev
env:
- CGO_ENABLED=0
binary: zitadel-debug
gcflags: all=-N -l
ldflags: ""
dist: .artifacts/goreleaser dist: .artifacts/goreleaser
dockers: dockers:
@ -54,8 +47,6 @@ dockers:
dockerfile: build/Dockerfile dockerfile: build/Dockerfile
build_flag_templates: build_flag_templates:
- "--platform=linux/amd64" - "--platform=linux/amd64"
ids:
- prod
- image_templates: - image_templates:
- ghcr.io/zitadel/zitadel:{{ .Tag }}-arm64 - ghcr.io/zitadel/zitadel:{{ .Tag }}-arm64
- ghcr.io/zitadel/zitadel:{{ .ShortCommit }}-arm64 - ghcr.io/zitadel/zitadel:{{ .ShortCommit }}-arm64
@ -64,8 +55,6 @@ dockers:
dockerfile: build/Dockerfile dockerfile: build/Dockerfile
build_flag_templates: build_flag_templates:
- "--platform=linux/arm64" - "--platform=linux/arm64"
ids:
- prod
docker_manifests: docker_manifests:
- id: zitadel-latest - id: zitadel-latest
@ -84,8 +73,6 @@ docker_manifests:
archives: archives:
- name_template: '{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}' - name_template: '{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
builds:
- prod
replacements: replacements:
darwin: Darwin darwin: Darwin
linux: Linux linux: Linux
@ -138,4 +125,4 @@ brews:
announce: announce:
discord: discord:
enabled: true enabled: true
message_template: 'ZITADEL {{ .Tag }} is ready! Check the notes: https://github.com/zitadel/zitadel/releases/tag/{{ .Tag }}' message_template: 'ZITADEL {{ .Tag }} is ready! Check the notes: https://github.com/zitadel/zitadel/releases/tag/{{ .Tag }}'

View File

@ -1,4 +1,4 @@
ARG GO_VERSION=1.17 ARG GO_VERSION=1.19
####################### #######################
## Go dependencies ## Go dependencies

View File

@ -45,6 +45,7 @@ HTTP1HostHeader: "host"
WebAuthNName: ZITADEL WebAuthNName: ZITADEL
Database: Database:
# CockroachDB is the default datbase of ZITADEL
cockroach: cockroach:
Host: localhost Host: localhost
Port: 26257 Port: 26257
@ -69,6 +70,32 @@ Database:
RootCert: "" RootCert: ""
Cert: "" Cert: ""
Key: "" Key: ""
# Postgres is used as soon as a value is set
# The values describe the possible fields to set values
postgres:
Host:
Port:
Database:
MaxOpenConns:
MaxConnLifetime:
MaxConnIdleTime:
Options:
User:
Username:
Password:
SSL:
Mode:
RootCert:
Cert:
Key:
Admin:
Username:
Password:
SSL:
Mode:
RootCert:
Cert:
Key:
Machine: Machine:
# Cloud hosted VMs need to specify their metadata endpoint so that the machine can be uniquely identified. # Cloud hosted VMs need to specify their metadata endpoint so that the machine can be uniquely identified.

View File

@ -2,27 +2,20 @@ package initialise
import ( import (
"database/sql" "database/sql"
"errors"
"github.com/jackc/pgconn"
) )
func exists(query string, args ...interface{}) func(*sql.DB) (exists bool, err error) { func exec(db *sql.DB, stmt string, possibleErrCodes []string, args ...interface{}) error {
return func(db *sql.DB) (exists bool, err error) { _, err := db.Exec(stmt, args...)
row := db.QueryRow("SELECT EXISTS("+query+")", args...) pgErr := new(pgconn.PgError)
err = row.Scan(&exists) if errors.As(err, &pgErr) {
return exists, err for _, possibleCode := range possibleErrCodes {
if possibleCode == pgErr.Code {
return nil
}
}
} }
} return err
func exec(stmt string, args ...interface{}) func(*sql.DB) error {
return func(db *sql.DB) error {
_, err := db.Exec(stmt, args...)
return err
}
}
func verify(db *sql.DB, checkExists func(*sql.DB) (bool, error), create func(*sql.DB) error) error {
exists, err := checkExists(db)
if exists || err != nil {
return err
}
return create(db)
} }

View File

@ -2,7 +2,7 @@ package initialise
import ( import (
"database/sql" "database/sql"
_ "embed" "embed"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
@ -12,6 +12,26 @@ import (
"github.com/zitadel/zitadel/internal/id" "github.com/zitadel/zitadel/internal/id"
) )
var (
//go:embed sql/cockroach/*
//go:embed sql/postgres/*
stmts embed.FS
createUserStmt string
grantStmt string
databaseStmt string
createEventstoreStmt string
createProjectionsStmt string
createSystemStmt string
createEncryptionKeysStmt string
createEventsStmt string
createSystemSequenceStmt string
createUniqueConstraints string
roleAlreadyExistsCode = "42710"
dbAlreadyExistsCode = "42P04"
)
func New() *cobra.Command { func New() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "init", Use: "init",
@ -39,6 +59,7 @@ The user provided by flags needs privileges to
func InitAll(config *Config) { func InitAll(config *Config) {
id.Configure(config.Machine) id.Configure(config.Machine)
err := initialise(config.Database, err := initialise(config.Database,
VerifyUser(config.Database.Username(), config.Database.Password()), VerifyUser(config.Database.Username(), config.Database.Password()),
VerifyDatabase(config.Database.Database()), VerifyDatabase(config.Database.Database()),
@ -53,22 +74,85 @@ func InitAll(config *Config) {
func initialise(config database.Config, steps ...func(*sql.DB) error) error { func initialise(config database.Config, steps ...func(*sql.DB) error) error {
logging.Info("initialization started") logging.Info("initialization started")
err := ReadStmts(config.Type())
if err != nil {
return err
}
db, err := database.Connect(config, true) db, err := database.Connect(config, true)
if err != nil { if err != nil {
return err return err
} }
err = Initialise(db, steps...) defer db.Close()
if err != nil {
return err return Init(db, steps...)
}
return db.Close()
} }
func Initialise(db *sql.DB, steps ...func(*sql.DB) error) error { func Init(db *sql.DB, steps ...func(*sql.DB) error) error {
for _, step := range steps { for _, step := range steps {
if err := step(db); err != nil { if err := step(db); err != nil {
return err return err
} }
} }
return nil return nil
} }
func ReadStmts(typ string) (err error) {
createUserStmt, err = readStmt(typ, "01_user")
if err != nil {
return err
}
databaseStmt, err = readStmt(typ, "02_database")
if err != nil {
return err
}
grantStmt, err = readStmt(typ, "03_grant_user")
if err != nil {
return err
}
createEventstoreStmt, err = readStmt(typ, "04_eventstore")
if err != nil {
return err
}
createProjectionsStmt, err = readStmt(typ, "05_projections")
if err != nil {
return err
}
createSystemStmt, err = readStmt(typ, "06_system")
if err != nil {
return err
}
createEncryptionKeysStmt, err = readStmt(typ, "07_encryption_keys_table")
if err != nil {
return err
}
createEventsStmt, err = readStmt(typ, "08_events_table")
if err != nil {
return err
}
createSystemSequenceStmt, err = readStmt(typ, "09_system_sequence")
if err != nil {
return err
}
createUniqueConstraints, err = readStmt(typ, "10_unique_constraints_table")
if err != nil {
return err
}
return nil
}
func readStmt(typ, step string) (string, error) {
stmt, err := stmts.ReadFile("sql/" + typ + "/" + step + ".sql")
return string(stmt), err
}

View File

@ -31,18 +31,6 @@ func prepareDB(t *testing.T, expectations ...expectation) db {
type expectation func(m sqlmock.Sqlmock) type expectation func(m sqlmock.Sqlmock)
func expectExists(query string, value bool, args ...driver.Value) expectation {
return func(m sqlmock.Sqlmock) {
m.ExpectQuery(regexp.QuoteMeta(query)).WithArgs(args...).WillReturnRows(sqlmock.NewRows([]string{"exists"}).AddRow(value))
}
}
func expectQueryErr(query string, err error, args ...driver.Value) expectation {
return func(m sqlmock.Sqlmock) {
m.ExpectQuery(regexp.QuoteMeta(query)).WithArgs(args...).WillReturnError(err)
}
}
func expectExec(stmt string, err error, args ...driver.Value) expectation { func expectExec(stmt string, err error, args ...driver.Value) expectation {
return func(m sqlmock.Sqlmock) { return func(m sqlmock.Sqlmock) {
query := m.ExpectExec(regexp.QuoteMeta(stmt)).WithArgs(args...) query := m.ExpectExec(regexp.QuoteMeta(stmt)).WithArgs(args...)

View File

@ -1 +0,0 @@
CREATE SCHEMA eventstore

View File

@ -1 +0,0 @@
CREATE SCHEMA projections

View File

@ -1 +0,0 @@
CREATE SCHEMA system;

View File

@ -1 +0,0 @@
SET experimental_enable_hash_sharded_indexes = on

View File

@ -1 +0,0 @@
CREATE SEQUENCE eventstore.system_seq

View File

@ -1,2 +1,2 @@
-- replace %[1]s with the name of the user -- replace %[1]s with the name of the user
CREATE USER %[1]s WITH PASSWORD $1 CREATE USER IF NOT EXISTS %[1]s

View File

@ -1,2 +1,2 @@
-- replace %[1]s with the name of the database -- replace %[1]s with the name of the database
CREATE DATABASE %[1]s CREATE DATABASE IF NOT EXISTS %[1]s

View File

@ -0,0 +1,3 @@
CREATE SCHEMA IF NOT EXISTS eventstore;
GRANT ALL ON ALL TABLES IN SCHEMA eventstore TO %[1]s;

View File

@ -0,0 +1,3 @@
CREATE SCHEMA IF NOT EXISTS projections;
GRANT ALL ON ALL TABLES IN SCHEMA projections TO %[1]s;

View File

@ -0,0 +1,3 @@
CREATE SCHEMA IF NOT EXISTS system;
GRANT ALL ON ALL TABLES IN SCHEMA system TO %[1]s;

View File

@ -1,4 +1,4 @@
CREATE TABLE system.encryption_keys ( CREATE TABLE IF NOT EXISTS system.encryption_keys (
id TEXT NOT NULL id TEXT NOT NULL
, key TEXT NOT NULL , key TEXT NOT NULL

View File

@ -1,4 +1,6 @@
CREATE TABLE eventstore.events ( SET experimental_enable_hash_sharded_indexes = on;
CREATE TABLE IF NOT EXISTS eventstore.events (
id UUID DEFAULT gen_random_uuid() id UUID DEFAULT gen_random_uuid()
, event_type TEXT NOT NULL , event_type TEXT NOT NULL
, aggregate_type TEXT NOT NULL , aggregate_type TEXT NOT NULL
@ -22,4 +24,4 @@ CREATE TABLE eventstore.events (
, INDEX max_sequence (aggregate_type, aggregate_id, event_sequence DESC, instance_id) , INDEX max_sequence (aggregate_type, aggregate_id, event_sequence DESC, instance_id)
, CONSTRAINT previous_sequence_unique UNIQUE (previous_aggregate_sequence DESC, instance_id) , CONSTRAINT previous_sequence_unique UNIQUE (previous_aggregate_sequence DESC, instance_id)
, CONSTRAINT prev_agg_type_seq_unique UNIQUE(previous_aggregate_type_sequence, instance_id) , CONSTRAINT prev_agg_type_seq_unique UNIQUE(previous_aggregate_type_sequence, instance_id)
) );

View File

@ -0,0 +1 @@
CREATE SEQUENCE IF NOT EXISTS eventstore.system_seq

View File

@ -1,4 +1,4 @@
CREATE TABLE eventstore.unique_constraints ( CREATE TABLE IF NOT EXISTS eventstore.unique_constraints (
instance_id TEXT, instance_id TEXT,
unique_type TEXT, unique_type TEXT,
unique_field TEXT, unique_field TEXT,

View File

@ -0,0 +1 @@
CREATE USER %[1]s

View File

@ -0,0 +1 @@
CREATE DATABASE %[1]s

View File

@ -0,0 +1,3 @@
-- replace the first %[1]s with the database
-- replace the second \%[2]s with the user
GRANT ALL ON DATABASE %[1]s TO %[2]s;

View File

@ -0,0 +1,3 @@
CREATE SCHEMA IF NOT EXISTS eventstore;
GRANT ALL ON ALL TABLES IN SCHEMA eventstore TO %[1]s;

View File

@ -0,0 +1,3 @@
CREATE SCHEMA IF NOT EXISTS projections;
GRANT ALL ON ALL TABLES IN SCHEMA projections TO %[1]s;

View File

@ -0,0 +1,3 @@
CREATE SCHEMA IF NOT EXISTS system;
GRANT ALL ON ALL TABLES IN SCHEMA system TO %[1]s;

View File

@ -0,0 +1,6 @@
CREATE TABLE IF NOT EXISTS system.encryption_keys (
id TEXT NOT NULL
, key TEXT NOT NULL
, PRIMARY KEY (id)
);

View File

@ -0,0 +1,25 @@
CREATE TABLE IF NOT EXISTS eventstore.events (
id UUID DEFAULT gen_random_uuid()
, event_type TEXT NOT NULL
, aggregate_type TEXT NOT NULL
, aggregate_id TEXT NOT NULL
, aggregate_version TEXT NOT NULL
, event_sequence BIGINT NOT NULL
, previous_aggregate_sequence BIGINT
, previous_aggregate_type_sequence INT8
, creation_date TIMESTAMPTZ NOT NULL DEFAULT now()
, event_data JSONB
, editor_user TEXT NOT NULL
, editor_service TEXT NOT NULL
, resource_owner TEXT NOT NULL
, instance_id TEXT NOT NULL
, PRIMARY KEY (event_sequence, instance_id)
, CONSTRAINT previous_sequence_unique UNIQUE(previous_aggregate_sequence, instance_id)
, CONSTRAINT prev_agg_type_seq_unique UNIQUE(previous_aggregate_type_sequence, instance_id)
);
CREATE INDEX IF NOT EXISTS agg_type_agg_id ON eventstore.events (aggregate_type, aggregate_id, instance_id);
CREATE INDEX IF NOT EXISTS agg_type ON eventstore.events (aggregate_type, instance_id);
CREATE INDEX IF NOT EXISTS agg_type_seq ON eventstore.events (aggregate_type, event_sequence DESC, instance_id);
CREATE INDEX IF NOT EXISTS max_sequence ON eventstore.events (aggregate_type, aggregate_id, event_sequence DESC, instance_id);

View File

@ -0,0 +1 @@
CREATE SEQUENCE IF NOT EXISTS eventstore.system_seq;

View File

@ -0,0 +1,6 @@
CREATE TABLE IF NOT EXISTS eventstore.unique_constraints (
instance_id TEXT,
unique_type TEXT,
unique_field TEXT,
PRIMARY KEY (instance_id, unique_type, unique_field)
);

View File

@ -10,13 +10,6 @@ import (
"github.com/zitadel/logging" "github.com/zitadel/logging"
) )
var (
searchDatabase = "SELECT database_name FROM [show databases] WHERE database_name = $1"
//go:embed sql/02_database.sql
databaseStmt string
)
func newDatabase() *cobra.Command { func newDatabase() *cobra.Command {
return &cobra.Command{ return &cobra.Command{
Use: "database", Use: "database",
@ -40,11 +33,10 @@ The user provided by flags needs priviledge to
} }
} }
func VerifyDatabase(database string) func(*sql.DB) error { func VerifyDatabase(databaseName string) func(*sql.DB) error {
return func(db *sql.DB) error { return func(db *sql.DB) error {
return verify(db, logging.WithFields("database", databaseName).Info("verify database")
exists(searchDatabase, database),
exec(fmt.Sprintf(databaseStmt, database)), return exec(db, fmt.Sprintf(string(databaseStmt), databaseName), []string{dbAlreadyExistsCode})
)
} }
} }

View File

@ -7,6 +7,12 @@ import (
) )
func Test_verifyDB(t *testing.T) { func Test_verifyDB(t *testing.T) {
err := ReadStmts("cockroach") //TODO: check all dialects
if err != nil {
t.Errorf("unable to read stmts: %v", err)
t.FailNow()
}
type args struct { type args struct {
db db db db
database string database string
@ -16,20 +22,11 @@ func Test_verifyDB(t *testing.T) {
args args args args
targetErr error targetErr error
}{ }{
{
name: "exists fails",
args: args{
db: prepareDB(t, expectQueryErr("SELECT EXISTS(SELECT database_name FROM [show databases] WHERE database_name = $1)", sql.ErrConnDone, "zitadel")),
database: "zitadel",
},
targetErr: sql.ErrConnDone,
},
{ {
name: "doesn't exists, create fails", name: "doesn't exists, create fails",
args: args{ args: args{
db: prepareDB(t, db: prepareDB(t,
expectExists("SELECT EXISTS(SELECT database_name FROM [show databases] WHERE database_name = $1)", false, "zitadel"), expectExec("-- replace zitadel with the name of the database\nCREATE DATABASE IF NOT EXISTS zitadel", sql.ErrTxDone),
expectExec("CREATE DATABASE zitadel", sql.ErrTxDone),
), ),
database: "zitadel", database: "zitadel",
}, },
@ -39,8 +36,7 @@ func Test_verifyDB(t *testing.T) {
name: "doesn't exists, create successful", name: "doesn't exists, create successful",
args: args{ args: args{
db: prepareDB(t, db: prepareDB(t,
expectExists("SELECT EXISTS(SELECT database_name FROM [show databases] WHERE database_name = $1)", false, "zitadel"), expectExec("-- replace zitadel with the name of the database\nCREATE DATABASE IF NOT EXISTS zitadel", nil),
expectExec("CREATE DATABASE zitadel", nil),
), ),
database: "zitadel", database: "zitadel",
}, },
@ -50,7 +46,7 @@ func Test_verifyDB(t *testing.T) {
name: "already exists", name: "already exists",
args: args{ args: args{
db: prepareDB(t, db: prepareDB(t,
expectExists("SELECT EXISTS(SELECT database_name FROM [show databases] WHERE database_name = $1)", true, "zitadel"), expectExec("-- replace zitadel with the name of the database\nCREATE DATABASE IF NOT EXISTS zitadel", nil),
), ),
database: "zitadel", database: "zitadel",
}, },

View File

@ -10,12 +10,6 @@ import (
"github.com/zitadel/logging" "github.com/zitadel/logging"
) )
var (
searchGrant = "SELECT * FROM [SHOW GRANTS ON DATABASE %s] where grantee = $1 AND privilege_type = 'ALL'"
//go:embed sql/03_grant_user.sql
grantStmt string
)
func newGrant() *cobra.Command { func newGrant() *cobra.Command {
return &cobra.Command{ return &cobra.Command{
Use: "grant", Use: "grant",
@ -34,12 +28,10 @@ Prereqesits:
} }
} }
func VerifyGrant(database, username string) func(*sql.DB) error { func VerifyGrant(databaseName, username string) func(*sql.DB) error {
return func(db *sql.DB) error { return func(db *sql.DB) error {
logging.WithFields("user", username, "database", database).Info("verify grant") logging.WithFields("user", username, "database", databaseName).Info("verify grant")
return verify(db,
exists(fmt.Sprintf(searchGrant, database), username), return exec(db, fmt.Sprintf(grantStmt, databaseName, username), nil)
exec(fmt.Sprintf(grantStmt, database, username)),
)
} }
} }

View File

@ -17,20 +17,10 @@ func Test_verifyGrant(t *testing.T) {
args args args args
targetErr error targetErr error
}{ }{
{
name: "exists fails",
args: args{
db: prepareDB(t, expectQueryErr("SELECT EXISTS(SELECT * FROM [SHOW GRANTS ON DATABASE zitadel] where grantee = $1 AND privilege_type = 'ALL'", sql.ErrConnDone, "zitadel-user")),
database: "zitadel",
username: "zitadel-user",
},
targetErr: sql.ErrConnDone,
},
{ {
name: "doesn't exists, create fails", name: "doesn't exists, create fails",
args: args{ args: args{
db: prepareDB(t, db: prepareDB(t,
expectExists("SELECT EXISTS(SELECT * FROM [SHOW GRANTS ON DATABASE zitadel] where grantee = $1 AND privilege_type = 'ALL'", false, "zitadel-user"),
expectExec("GRANT ALL ON DATABASE zitadel TO zitadel-user", sql.ErrTxDone), expectExec("GRANT ALL ON DATABASE zitadel TO zitadel-user", sql.ErrTxDone),
), ),
database: "zitadel", database: "zitadel",
@ -42,7 +32,6 @@ func Test_verifyGrant(t *testing.T) {
name: "correct", name: "correct",
args: args{ args: args{
db: prepareDB(t, db: prepareDB(t,
expectExists("SELECT EXISTS(SELECT * FROM [SHOW GRANTS ON DATABASE zitadel] where grantee = $1 AND privilege_type = 'ALL'", false, "zitadel-user"),
expectExec("GRANT ALL ON DATABASE zitadel TO zitadel-user", nil), expectExec("GRANT ALL ON DATABASE zitadel TO zitadel-user", nil),
), ),
database: "zitadel", database: "zitadel",
@ -54,7 +43,7 @@ func Test_verifyGrant(t *testing.T) {
name: "already exists", name: "already exists",
args: args{ args: args{
db: prepareDB(t, db: prepareDB(t,
expectExists("SELECT EXISTS(SELECT * FROM [SHOW GRANTS ON DATABASE zitadel] where grantee = $1 AND privilege_type = 'ALL'", true, "zitadel-user"), expectExec("GRANT ALL ON DATABASE zitadel TO zitadel-user", nil),
), ),
database: "zitadel", database: "zitadel",
username: "zitadel-user", username: "zitadel-user",

View File

@ -10,12 +10,6 @@ import (
"github.com/zitadel/logging" "github.com/zitadel/logging"
) )
var (
searchUser = "SELECT username FROM [show roles] WHERE username = $1"
//go:embed sql/01_user.sql
createUserStmt string
)
func newUser() *cobra.Command { func newUser() *cobra.Command {
return &cobra.Command{ return &cobra.Command{
Use: "user", Use: "user",
@ -42,9 +36,11 @@ The user provided by flags needs priviledge to
func VerifyUser(username, password string) func(*sql.DB) error { func VerifyUser(username, password string) func(*sql.DB) error {
return func(db *sql.DB) error { return func(db *sql.DB) error {
logging.WithFields("username", username).Info("verify user") logging.WithFields("username", username).Info("verify user")
return verify(db,
exists(searchUser, username), if password != "" {
exec(fmt.Sprintf(createUserStmt, username), &sql.NullString{String: password, Valid: password != ""}), createUserStmt += " WITH PASSWORD '" + password + "'"
) }
return exec(db, fmt.Sprintf(createUserStmt, username), []string{roleAlreadyExistsCode})
} }
} }

View File

@ -7,6 +7,12 @@ import (
) )
func Test_verifyUser(t *testing.T) { func Test_verifyUser(t *testing.T) {
err := ReadStmts("cockroach") //TODO: check all dialects
if err != nil {
t.Errorf("unable to read stmts: %v", err)
t.FailNow()
}
type args struct { type args struct {
db db db db
username string username string
@ -17,21 +23,11 @@ func Test_verifyUser(t *testing.T) {
args args args args
targetErr error targetErr error
}{ }{
{
name: "exists fails",
args: args{
db: prepareDB(t, expectQueryErr("SELECT EXISTS(SELECT username FROM [show roles] WHERE username = $1)", sql.ErrConnDone, "zitadel-user")),
username: "zitadel-user",
password: "",
},
targetErr: sql.ErrConnDone,
},
{ {
name: "doesn't exists, create fails", name: "doesn't exists, create fails",
args: args{ args: args{
db: prepareDB(t, db: prepareDB(t,
expectExists("SELECT EXISTS(SELECT username FROM [show roles] WHERE username = $1)", false, "zitadel-user"), expectExec("-- replace zitadel-user with the name of the user\nCREATE USER IF NOT EXISTS zitadel-user", sql.ErrTxDone),
expectExec("CREATE USER zitadel-user WITH PASSWORD $1", sql.ErrTxDone, nil),
), ),
username: "zitadel-user", username: "zitadel-user",
password: "", password: "",
@ -42,8 +38,7 @@ func Test_verifyUser(t *testing.T) {
name: "correct without password", name: "correct without password",
args: args{ args: args{
db: prepareDB(t, db: prepareDB(t,
expectExists("SELECT EXISTS(SELECT username FROM [show roles] WHERE username = $1)", false, "zitadel-user"), expectExec("-- replace zitadel-user with the name of the user\nCREATE USER IF NOT EXISTS zitadel-user", nil),
expectExec("CREATE USER zitadel-user WITH PASSWORD $1", nil, nil),
), ),
username: "zitadel-user", username: "zitadel-user",
password: "", password: "",
@ -54,8 +49,7 @@ func Test_verifyUser(t *testing.T) {
name: "correct with password", name: "correct with password",
args: args{ args: args{
db: prepareDB(t, db: prepareDB(t,
expectExists("SELECT EXISTS(SELECT username FROM [show roles] WHERE username = $1)", false, "zitadel-user"), expectExec("-- replace zitadel-user with the name of the user\nCREATE USER IF NOT EXISTS zitadel-user WITH PASSWORD 'password'", nil),
expectExec("CREATE USER zitadel-user WITH PASSWORD $1", nil, "password"),
), ),
username: "zitadel-user", username: "zitadel-user",
password: "password", password: "password",
@ -66,7 +60,7 @@ func Test_verifyUser(t *testing.T) {
name: "already exists", name: "already exists",
args: args{ args: args{
db: prepareDB(t, db: prepareDB(t,
expectExists("SELECT EXISTS(SELECT username FROM [show roles] WHERE username = $1)", true, "zitadel-user"), expectExec("-- replace zitadel-user with the name of the user\nCREATE USER IF NOT EXISTS zitadel-user WITH PASSWORD 'password'", nil),
), ),
username: "zitadel-user", username: "zitadel-user",
password: "", password: "",

View File

@ -3,11 +3,12 @@ package initialise
import ( import (
"database/sql" "database/sql"
_ "embed" _ "embed"
"fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/zitadel/logging"
"github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/database"
) )
@ -20,29 +21,6 @@ const (
encryptionKeysTable = "encryption_keys" encryptionKeysTable = "encryption_keys"
) )
var (
searchSchema = "SELECT schema_name FROM [SHOW SCHEMAS] WHERE schema_name = $1"
searchTable = "SELECT table_name FROM [SHOW TABLES] WHERE table_name = $1"
searchSystemSequence = "SELECT sequence_name FROM [SHOW SEQUENCES] WHERE sequence_name = 'system_seq'"
//go:embed sql/04_eventstore.sql
createEventstoreStmt string
//go:embed sql/05_projections.sql
createProjectionsStmt string
//go:embed sql/06_system.sql
createSystemStmt string
//go:embed sql/07_encryption_keys_table.sql
createEncryptionKeysStmt string
//go:embed sql/08_enable_hash_sharded_indexes.sql
enableHashShardedIdx string
//go:embed sql/09_events_table.sql
createEventsStmt string
//go:embed sql/10_system_sequence.sql
createSystemSequenceStmt string
//go:embed sql/11_unique_constraints_table.sql
createUniqueConstraints string
)
func newZitadel() *cobra.Command { func newZitadel() *cobra.Command {
return &cobra.Command{ return &cobra.Command{
Use: "zitadel", Use: "zitadel",
@ -62,44 +40,44 @@ Prereqesits:
} }
} }
func VerifyZitadel(db *sql.DB) error { func VerifyZitadel(db *sql.DB, config database.Config) error {
if err := verify(db, exists(searchSchema, systemSchema), exec(createSystemStmt)); err != nil { if err := exec(db, fmt.Sprintf(createSystemStmt, config.Username()), nil); err != nil {
return err return err
} }
if err := verify(db, exists(searchTable, encryptionKeysTable), createEncryptionKeys); err != nil { if err := createEncryptionKeys(db); err != nil {
return err return err
} }
if err := verify(db, exists(searchSchema, projectionsSchema), exec(createProjectionsStmt)); err != nil { if err := exec(db, fmt.Sprintf(createProjectionsStmt, config.Username()), nil); err != nil {
return err return err
} }
if err := verify(db, exists(searchSchema, eventstoreSchema), exec(createEventstoreStmt)); err != nil { if err := exec(db, fmt.Sprintf(createEventstoreStmt, config.Username()), nil); err != nil {
return err return err
} }
if err := verify(db, exists(searchTable, eventsTable), createEvents); err != nil { if err := createEvents(db); err != nil {
return err return err
} }
if err := verify(db, exists(searchSystemSequence), exec(createSystemSequenceStmt)); err != nil { if err := exec(db, createSystemSequenceStmt, nil); err != nil {
return err return err
} }
if err := verify(db, exists(searchTable, uniqueConstraintsTable), exec(createUniqueConstraints)); err != nil { if err := exec(db, createUniqueConstraints, nil); err != nil {
return err return err
} }
return nil return nil
} }
func verifyZitadel(config database.Config) error { func verifyZitadel(config database.Config) error {
logging.WithFields("database", config.Database).Info("verify zitadel") logging.WithFields("database", config.Database()).Info("verify zitadel")
db, err := database.Connect(config, false) db, err := database.Connect(config, false)
if err != nil { if err != nil {
return err return err
} }
if err := VerifyZitadel(db); err != nil { if err := VerifyZitadel(db, config); err != nil {
return nil return nil
} }
@ -124,10 +102,6 @@ func createEvents(db *sql.DB) error {
if err != nil { if err != nil {
return err return err
} }
if _, err = tx.Exec(enableHashShardedIdx); err != nil {
tx.Rollback()
return err
}
if _, err = tx.Exec(createEventsStmt); err != nil { if _, err = tx.Exec(createEventsStmt); err != nil {
tx.Rollback() tx.Rollback()

View File

@ -7,6 +7,12 @@ import (
) )
func Test_verifyEvents(t *testing.T) { func Test_verifyEvents(t *testing.T) {
err := ReadStmts("cockroach") //TODO: check all dialects
if err != nil {
t.Errorf("unable to read stmts: %v", err)
t.FailNow()
}
type args struct { type args struct {
db db db db
} }
@ -24,23 +30,11 @@ func Test_verifyEvents(t *testing.T) {
}, },
targetErr: sql.ErrConnDone, targetErr: sql.ErrConnDone,
}, },
{
name: "hash sharded indexes fails",
args: args{
db: prepareDB(t,
expectBegin(nil),
expectExec("SET experimental_enable_hash_sharded_indexes = on", sql.ErrNoRows),
expectRollback(nil),
),
},
targetErr: sql.ErrNoRows,
},
{ {
name: "create table fails", name: "create table fails",
args: args{ args: args{
db: prepareDB(t, db: prepareDB(t,
expectBegin(nil), expectBegin(nil),
expectExec("SET experimental_enable_hash_sharded_indexes = on", nil),
expectExec(createEventsStmt, sql.ErrNoRows), expectExec(createEventsStmt, sql.ErrNoRows),
expectRollback(nil), expectRollback(nil),
), ),
@ -52,7 +46,6 @@ func Test_verifyEvents(t *testing.T) {
args: args{ args: args{
db: prepareDB(t, db: prepareDB(t,
expectBegin(nil), expectBegin(nil),
expectExec("SET experimental_enable_hash_sharded_indexes = on", nil),
expectExec(createEventsStmt, nil), expectExec(createEventsStmt, nil),
expectCommit(nil), expectCommit(nil),
), ),
@ -60,6 +53,7 @@ func Test_verifyEvents(t *testing.T) {
targetErr: nil, targetErr: nil,
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
if err := createEvents(tt.args.db.db); !errors.Is(err, tt.targetErr) { if err := createEvents(tt.args.db.db); !errors.Is(err, tt.targetErr) {

View File

@ -11,8 +11,6 @@ var (
createAdminViews string createAdminViews string
//go:embed 01_sql/auth.sql //go:embed 01_sql/auth.sql
createAuthViews string createAuthViews string
//go:embed 01_sql/notification.sql
createNotificationViews string
//go:embed 01_sql/projections.sql //go:embed 01_sql/projections.sql
createProjections string createProjections string
) )
@ -22,7 +20,7 @@ type ProjectionTable struct {
} }
func (mig *ProjectionTable) Execute(ctx context.Context) error { func (mig *ProjectionTable) Execute(ctx context.Context) error {
stmt := createAdminViews + createAuthViews + createNotificationViews + createProjections stmt := createAdminViews + createAuthViews + createProjections
_, err := mig.dbClient.ExecContext(ctx, stmt) _, err := mig.dbClient.ExecContext(ctx, stmt)
return err return err
} }

View File

@ -30,28 +30,28 @@ CREATE TABLE adminapi.failed_events (
); );
CREATE TABLE adminapi.styling ( CREATE TABLE adminapi.styling (
aggregate_id STRING NOT NULL, aggregate_id TEXT NOT NULL,
creation_date TIMESTAMPTZ NULL, creation_date TIMESTAMPTZ NULL,
change_date TIMESTAMPTZ NULL, change_date TIMESTAMPTZ NULL,
label_policy_state INT2 NOT NULL DEFAULT 0:::INT2, label_policy_state INT2 NOT NULL DEFAULT 0::INT2,
sequence INT8 NULL, sequence INT8 NULL,
primary_color STRING NULL, primary_color TEXT NULL,
background_color STRING NULL, background_color TEXT NULL,
warn_color STRING NULL, warn_color TEXT NULL,
font_color STRING NULL, font_color TEXT NULL,
primary_color_dark STRING NULL, primary_color_dark TEXT NULL,
background_color_dark STRING NULL, background_color_dark TEXT NULL,
warn_color_dark STRING NULL, warn_color_dark TEXT NULL,
font_color_dark STRING NULL, font_color_dark TEXT NULL,
logo_url STRING NULL, logo_url TEXT NULL,
icon_url STRING NULL, icon_url TEXT NULL,
logo_dark_url STRING NULL, logo_dark_url TEXT NULL,
icon_dark_url STRING NULL, icon_dark_url TEXT NULL,
font_url STRING NULL, font_url TEXT NULL,
err_msg_popup BOOL NULL, err_msg_popup BOOL NULL,
disable_watermark BOOL NULL, disable_watermark BOOL NULL,
hide_login_name_suffix BOOL NULL, hide_login_name_suffix BOOL NULL,
instance_id STRING NOT NULL, instance_id TEXT NOT NULL,
PRIMARY KEY (aggregate_id, label_policy_state, instance_id) PRIMARY KEY (aggregate_id, label_policy_state, instance_id)
); );

View File

@ -30,48 +30,48 @@ CREATE TABLE auth.failed_events (
); );
CREATE TABLE auth.users ( CREATE TABLE auth.users (
id STRING NULL, id TEXT NULL,
creation_date TIMESTAMPTZ NULL, creation_date TIMESTAMPTZ NULL,
change_date TIMESTAMPTZ NULL, change_date TIMESTAMPTZ NULL,
resource_owner STRING NULL, resource_owner TEXT NULL,
user_state INT2 NULL, user_state INT2 NULL,
password_set BOOL NULL, password_set BOOL NULL,
password_change_required BOOL NULL, password_change_required BOOL NULL,
password_change TIMESTAMPTZ NULL, password_change TIMESTAMPTZ NULL,
last_login TIMESTAMPTZ NULL, last_login TIMESTAMPTZ NULL,
user_name STRING NULL, user_name TEXT NULL,
login_names STRING[] NULL, login_names TEXT[] NULL,
preferred_login_name STRING NULL, preferred_login_name TEXT NULL,
first_name STRING NULL, first_name TEXT NULL,
last_name STRING NULL, last_name TEXT NULL,
nick_name STRING NULL, nick_name TEXT NULL,
display_name STRING NULL, display_name TEXT NULL,
preferred_language STRING NULL, preferred_language TEXT NULL,
gender INT2 NULL, gender INT2 NULL,
email STRING NULL, email TEXT NULL,
is_email_verified BOOL NULL, is_email_verified BOOL NULL,
phone STRING NULL, phone TEXT NULL,
is_phone_verified BOOL NULL, is_phone_verified BOOL NULL,
country STRING NULL, country TEXT NULL,
locality STRING NULL, locality TEXT NULL,
postal_code STRING NULL, postal_code TEXT NULL,
region STRING NULL, region TEXT NULL,
street_address STRING NULL, street_address TEXT NULL,
otp_state INT2 NULL, otp_state INT2 NULL,
mfa_max_set_up INT2 NULL, mfa_max_set_up INT2 NULL,
mfa_init_skipped TIMESTAMPTZ NULL, mfa_init_skipped TIMESTAMPTZ NULL,
sequence INT8 NULL, sequence INT8 NULL,
init_required BOOL NULL, init_required BOOL NULL,
username_change_required BOOL NULL, username_change_required BOOL NULL,
machine_name STRING NULL, machine_name TEXT NULL,
machine_description STRING NULL, machine_description TEXT NULL,
user_type STRING NULL, user_type TEXT NULL,
u2f_tokens BYTES NULL, u2f_tokens BYTEA NULL,
passwordless_tokens BYTES NULL, passwordless_tokens BYTEA NULL,
avatar_key STRING NULL, avatar_key TEXT NULL,
passwordless_init_required BOOL NULL, passwordless_init_required BOOL NULL,
password_init_required BOOL NULL, password_init_required BOOL NULL,
instance_id STRING NOT NULL, instance_id TEXT NOT NULL,
PRIMARY KEY (id, instance_id) PRIMARY KEY (id, instance_id)
); );
@ -79,148 +79,151 @@ CREATE TABLE auth.users (
CREATE TABLE auth.user_sessions ( CREATE TABLE auth.user_sessions (
creation_date TIMESTAMPTZ NULL, creation_date TIMESTAMPTZ NULL,
change_date TIMESTAMPTZ NULL, change_date TIMESTAMPTZ NULL,
resource_owner STRING NULL, resource_owner TEXT NULL,
state INT2 NULL, state INT2 NULL,
user_agent_id STRING NULL, user_agent_id TEXT NULL,
user_id STRING NULL, user_id TEXT NULL,
user_name STRING NULL, user_name TEXT NULL,
password_verification TIMESTAMPTZ NULL, password_verification TIMESTAMPTZ NULL,
second_factor_verification TIMESTAMPTZ NULL, second_factor_verification TIMESTAMPTZ NULL,
multi_factor_verification TIMESTAMPTZ NULL, multi_factor_verification TIMESTAMPTZ NULL,
sequence INT8 NULL, sequence INT8 NULL,
second_factor_verification_type INT2 NULL, second_factor_verification_type INT2 NULL,
multi_factor_verification_type INT2 NULL, multi_factor_verification_type INT2 NULL,
user_display_name STRING NULL, user_display_name TEXT NULL,
login_name STRING NULL, login_name TEXT NULL,
external_login_verification TIMESTAMPTZ NULL, external_login_verification TIMESTAMPTZ NULL,
selected_idp_config_id STRING NULL, selected_idp_config_id TEXT NULL,
passwordless_verification TIMESTAMPTZ NULL, passwordless_verification TIMESTAMPTZ NULL,
avatar_key STRING NULL, avatar_key TEXT NULL,
instance_id STRING NOT NULL, instance_id TEXT NOT NULL,
PRIMARY KEY (user_agent_id, user_id, instance_id) PRIMARY KEY (user_agent_id, user_id, instance_id)
); );
CREATE TABLE auth.user_external_idps ( CREATE TABLE auth.user_external_idps (
external_user_id STRING NOT NULL, external_user_id TEXT NOT NULL,
idp_config_id STRING NOT NULL, idp_config_id TEXT NOT NULL,
user_id STRING NULL, user_id TEXT NULL,
idp_name STRING NULL, idp_name TEXT NULL,
user_display_name STRING NULL, user_display_name TEXT NULL,
creation_date TIMESTAMPTZ NULL, creation_date TIMESTAMPTZ NULL,
change_date TIMESTAMPTZ NULL, change_date TIMESTAMPTZ NULL,
sequence INT8 NULL, sequence INT8 NULL,
resource_owner STRING NULL, resource_owner TEXT NULL,
instance_id STRING NOT NULL, instance_id TEXT NOT NULL,
PRIMARY KEY (external_user_id, idp_config_id, instance_id) PRIMARY KEY (external_user_id, idp_config_id, instance_id)
); );
CREATE TABLE auth.tokens ( CREATE TABLE auth.tokens (
id STRING NOT NULL, id TEXT NOT NULL,
creation_date TIMESTAMPTZ NULL, creation_date TIMESTAMPTZ NULL,
change_date TIMESTAMPTZ NULL, change_date TIMESTAMPTZ NULL,
resource_owner STRING NULL, resource_owner TEXT NULL,
application_id STRING NULL, application_id TEXT NULL,
user_agent_id STRING NULL, user_agent_id TEXT NULL,
user_id STRING NULL, user_id TEXT NULL,
expiration TIMESTAMPTZ NULL, expiration TIMESTAMPTZ NULL,
sequence INT8 NULL, sequence INT8 NULL,
scopes STRING[] NULL, scopes TEXT[] NULL,
audience STRING[] NULL, audience TEXT[] NULL,
preferred_language STRING NULL, preferred_language TEXT NULL,
refresh_token_id STRING NULL, refresh_token_id TEXT NULL,
is_pat BOOL NOT NULL DEFAULT false, is_pat BOOL NOT NULL DEFAULT false,
instance_id STRING NOT NULL, instance_id TEXT NOT NULL,
PRIMARY KEY (id, instance_id), PRIMARY KEY (id, instance_id)
INDEX user_user_agent_idx (user_id, user_agent_id)
); );
CREATE INDEX user_user_agent_idx ON auth.tokens (user_id, user_agent_id);
CREATE TABLE auth.refresh_tokens ( CREATE TABLE auth.refresh_tokens (
id STRING NOT NULL, id TEXT NOT NULL,
creation_date TIMESTAMPTZ NULL, creation_date TIMESTAMPTZ NULL,
change_date TIMESTAMPTZ NULL, change_date TIMESTAMPTZ NULL,
resource_owner STRING NULL, resource_owner TEXT NULL,
token STRING NULL, token TEXT NULL,
client_id STRING NOT NULL, client_id TEXT NOT NULL,
user_agent_id STRING NOT NULL, user_agent_id TEXT NOT NULL,
user_id STRING NOT NULL, user_id TEXT NOT NULL,
auth_time TIMESTAMPTZ NULL, auth_time TIMESTAMPTZ NULL,
idle_expiration TIMESTAMPTZ NULL, idle_expiration TIMESTAMPTZ NULL,
expiration TIMESTAMPTZ NULL, expiration TIMESTAMPTZ NULL,
sequence INT8 NULL, sequence INT8 NULL,
scopes STRING[] NULL, scopes TEXT[] NULL,
audience STRING[] NULL, audience TEXT[] NULL,
amr STRING[] NULL, amr TEXT[] NULL,
instance_id STRING NOT NULL, instance_id TEXT NOT NULL,
PRIMARY KEY (id, instance_id), PRIMARY KEY (id, instance_id)
UNIQUE INDEX unique_client_user_index (client_id, user_agent_id, user_id)
); );
CREATE UNIQUE INDEX unique_client_user_index ON auth.refresh_tokens (client_id, user_agent_id, user_id);
CREATE TABLE auth.org_project_mapping ( CREATE TABLE auth.org_project_mapping (
org_id STRING NOT NULL, org_id TEXT NOT NULL,
project_id STRING NOT NULL, project_id TEXT NOT NULL,
project_grant_id STRING NULL, project_grant_id TEXT NULL,
instance_id STRING NOT NULL, instance_id TEXT NOT NULL,
PRIMARY KEY (org_id, project_id, instance_id) PRIMARY KEY (org_id, project_id, instance_id)
); );
CREATE TABLE auth.idp_providers ( CREATE TABLE auth.idp_providers (
aggregate_id STRING NOT NULL, aggregate_id TEXT NOT NULL,
idp_config_id STRING NOT NULL, idp_config_id TEXT NOT NULL,
creation_date TIMESTAMPTZ NULL, creation_date TIMESTAMPTZ NULL,
change_date TIMESTAMPTZ NULL, change_date TIMESTAMPTZ NULL,
sequence INT8 NULL, sequence INT8 NULL,
name STRING NULL, name TEXT NULL,
idp_config_type INT2 NULL, idp_config_type INT2 NULL,
idp_provider_type INT2 NULL, idp_provider_type INT2 NULL,
idp_state INT2 NULL, idp_state INT2 NULL,
styling_type INT2 NULL, styling_type INT2 NULL,
instance_id STRING NOT NULL, instance_id TEXT NOT NULL,
PRIMARY KEY (aggregate_id, idp_config_id, instance_id) PRIMARY KEY (aggregate_id, idp_config_id, instance_id)
); );
CREATE TABLE auth.idp_configs ( CREATE TABLE auth.idp_configs (
idp_config_id STRING NOT NULL, idp_config_id TEXT NOT NULL,
creation_date TIMESTAMPTZ NULL, creation_date TIMESTAMPTZ NULL,
change_date TIMESTAMPTZ NULL, change_date TIMESTAMPTZ NULL,
sequence INT8 NULL, sequence INT8 NULL,
aggregate_id STRING NULL, aggregate_id TEXT NULL,
name STRING NULL, name TEXT NULL,
idp_state INT2 NULL, idp_state INT2 NULL,
idp_provider_type INT2 NULL, idp_provider_type INT2 NULL,
is_oidc BOOL NULL, is_oidc BOOL NULL,
oidc_client_id STRING NULL, oidc_client_id TEXT NULL,
oidc_client_secret JSONB NULL, oidc_client_secret JSONB NULL,
oidc_issuer STRING NULL, oidc_issuer TEXT NULL,
oidc_scopes STRING[] NULL, oidc_scopes TEXT[] NULL,
oidc_idp_display_name_mapping INT2 NULL, oidc_idp_display_name_mapping INT2 NULL,
oidc_idp_username_mapping INT2 NULL, oidc_idp_username_mapping INT2 NULL,
styling_type INT2 NULL, styling_type INT2 NULL,
oauth_authorization_endpoint STRING NULL, oauth_authorization_endpoint TEXT NULL,
oauth_token_endpoint STRING NULL, oauth_token_endpoint TEXT NULL,
auto_register BOOL NULL, auto_register BOOL NULL,
jwt_endpoint STRING NULL, jwt_endpoint TEXT NULL,
jwt_keys_endpoint STRING NULL, jwt_keys_endpoint TEXT NULL,
jwt_header_name STRING NULL, jwt_header_name TEXT NULL,
instance_id STRING NOT NULL, instance_id TEXT NOT NULL,
PRIMARY KEY (idp_config_id, instance_id) PRIMARY KEY (idp_config_id, instance_id)
); );
CREATE TABLE auth.auth_requests ( CREATE TABLE auth.auth_requests (
id STRING NOT NULL, id TEXT NOT NULL,
request JSONB NULL, request JSONB NULL,
code STRING NULL, code TEXT NULL,
request_type INT2 NULL, request_type INT2 NULL,
creation_date TIMESTAMPTZ NULL, creation_date TIMESTAMPTZ NULL,
change_date TIMESTAMPTZ NULL, change_date TIMESTAMPTZ NULL,
instance_id STRING NOT NULL, instance_id TEXT NOT NULL,
PRIMARY KEY (id, instance_id), PRIMARY KEY (id, instance_id)
INDEX auth_code_idx (code)
); );
CREATE INDEX auth_code_idx ON auth.auth_requests (code);

View File

@ -1,55 +0,0 @@
CREATE SCHEMA notification;
CREATE TABLE notification.locks (
locker_id TEXT,
locked_until TIMESTAMPTZ(3),
view_name TEXT,
instance_id TEXT NOT NULL,
PRIMARY KEY (view_name, instance_id)
);
CREATE TABLE notification.current_sequences (
view_name TEXT,
current_sequence BIGINT,
event_timestamp TIMESTAMPTZ,
last_successful_spooler_run TIMESTAMPTZ,
instance_id TEXT NOT NULL,
PRIMARY KEY (view_name, instance_id)
);
CREATE TABLE notification.failed_events (
view_name TEXT,
failed_sequence BIGINT,
failure_count SMALLINT,
err_msg TEXT,
instance_id TEXT NOT NULL,
PRIMARY KEY (view_name, failed_sequence, instance_id)
);
CREATE TABLE notification.notify_users (
id STRING NOT NULL,
creation_date TIMESTAMPTZ NULL,
change_date TIMESTAMPTZ NULL,
resource_owner STRING NULL,
user_name STRING NULL,
first_name STRING NULL,
last_name STRING NULL,
nick_name STRING NULL,
display_name STRING NULL,
preferred_language STRING NULL,
gender INT2 NULL,
last_email STRING NULL,
verified_email STRING NULL,
last_phone STRING NULL,
verified_phone STRING NULL,
sequence INT8 NULL,
password_set BOOL NULL,
login_names STRING NULL,
preferred_login_name STRING NULL,
instance_id STRING NULL,
PRIMARY KEY (id)
);

View File

@ -13,8 +13,8 @@ CREATE TABLE system.assets (
resource_owner TEXT, resource_owner TEXT,
name TEXT, name TEXT,
content_type TEXT, content_type TEXT,
hash TEXT AS (md5(data)) STORED, hash TEXT GENERATED ALWAYS AS (md5(data)) STORED,
data BYTES, data BYTEA,
updated_at TIMESTAMPTZ, updated_at TIMESTAMPTZ,
PRIMARY KEY (instance_id, resource_owner, name) PRIMARY KEY (instance_id, resource_owner, name)

View File

@ -0,0 +1,47 @@
package start
import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/zitadel/logging"
"github.com/zitadel/zitadel/cmd/key"
"github.com/zitadel/zitadel/cmd/setup"
"github.com/zitadel/zitadel/cmd/tls"
)
func NewStartFromSetup() *cobra.Command {
cmd := &cobra.Command{
Use: "start-from-setup",
Short: "cold starts zitadel",
Long: `cold starts ZITADEL.
First the initial events are created.
Last ZITADEL starts.
Requirements:
- database
- database is initialized
`,
Run: func(cmd *cobra.Command, args []string) {
err := tls.ModeFromFlag(cmd)
logging.OnError(err).Fatal("invalid tlsMode")
masterKey, err := key.MasterKey(cmd)
logging.OnError(err).Panic("No master key provided")
setupConfig := setup.MustNewConfig(viper.GetViper())
setupSteps := setup.MustNewSteps(viper.New())
setup.Setup(setupConfig, setupSteps, masterKey)
startConfig := MustNewConfig(viper.GetViper())
err = startZitadel(startConfig, masterKey)
logging.OnError(err).Fatal("unable to start zitadel")
},
}
startFlags(cmd)
setup.Flags(cmd)
return cmd
}

View File

@ -51,6 +51,7 @@ func New(out io.Writer, in io.Reader, args []string) *cobra.Command {
setup.New(), setup.New(),
start.New(), start.New(),
start.NewStartFromInit(), start.NewStartFromInit(),
start.NewStartFromSetup(),
key.New(), key.New(),
) )

View File

@ -144,4 +144,4 @@ The storage layer of ZITADEL is responsible for multiple things. For example:
- Backup and restore operation for disaster recovery purpose - Backup and restore operation for disaster recovery purpose
ZITADEL currently supports CockroachDB as first choice of storage due to its perfect match for ZITADELs needs. ZITADEL currently supports CockroachDB as first choice of storage due to its perfect match for ZITADELs needs.
Postgresql support is work in progress and should be available soon as well. Postgresql support is currently in beta.

View File

@ -10,7 +10,7 @@ Since the storage layer takes the heavy lifting of making sure that data in sync
Depending on your projects needs our general recommendation is to run ZITADEL and ZITADELs storage layer across multiple availability zones in the same region or if you need higher guarantees run the storage layer across multiple regions. Depending on your projects needs our general recommendation is to run ZITADEL and ZITADELs storage layer across multiple availability zones in the same region or if you need higher guarantees run the storage layer across multiple regions.
Consult the [CockroachDB documentation](https://www.cockroachlabs.com/docs/) for more details or use the [CockroachCloud Service](https://www.cockroachlabs.com/docs/cockroachcloud/create-an-account.html) Consult the [CockroachDB documentation](https://www.cockroachlabs.com/docs/) for more details or use the [CockroachCloud Service](https://www.cockroachlabs.com/docs/cockroachcloud/create-an-account.html)
> Soon ZITADEL will also support Postgres as database. > Postgres support of ZITADEL is currently in beta.
## Scalability ## Scalability

View File

@ -8,19 +8,19 @@ services:
image: 'ghcr.io/zitadel/zitadel:stable' image: 'ghcr.io/zitadel/zitadel:stable'
command: 'start-from-init --masterkey "MasterkeyNeedsToHave32Characters" --tlsMode disabled' command: 'start-from-init --masterkey "MasterkeyNeedsToHave32Characters" --tlsMode disabled'
environment: environment:
- 'ZITADEL_DATABASE_COCKROACH_HOST=db' - 'ZITADEL_DATABASE_COCKROACH_HOST=crdb'
- 'ZITADEL_EXTERNALSECURE=false' - 'ZITADEL_EXTERNALSECURE=false'
depends_on: depends_on:
db: crdb:
condition: 'service_healthy' condition: 'service_healthy'
ports: ports:
- '8080:8080' - '8080:8080'
db: crdb:
restart: 'always' restart: 'always'
networks: networks:
- 'zitadel' - 'zitadel'
image: 'cockroachdb/cockroach:v22.1.0' image: 'cockroachdb/cockroach:v22.1.3'
command: 'start-single-node --insecure' command: 'start-single-node --insecure'
healthcheck: healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health?ready=1"] test: ["CMD", "curl", "-f", "http://localhost:8080/health?ready=1"]

View File

@ -15,7 +15,7 @@ By default, it runs a highly available ZITADEL instance along with a secure and
## Prerequisits ## Prerequisits
- ZITADEL does not need many resources, 1 CPU and 512MB memory are more than enough. (With more CPU, the password hashing might be faster) - ZITADEL does not need many resources, 1 CPU and 512MB memory are more than enough. (With more CPU, the password hashing might be faster)
- A cockroachDB or [🚧 Postgresql coming soon](https://github.com/zitadel/zitadel/pull/3998) as only needed storage - A cockroachDB or Postgresql (currently in beta) as only needed storage
- If you want to front ZITADEL with a reverse proxy, web application firewall or content delivery network, make sure to support [HTTP/2](../manage/self-hosted/http2) - If you want to front ZITADEL with a reverse proxy, web application firewall or content delivery network, make sure to support [HTTP/2](../manage/self-hosted/http2)

View File

@ -0,0 +1,32 @@
## Cockroach
The default database of ZITADEL is [CockroachDB](https://www.cockroachlabs.com). The SQL database provides a bunch of features like horizontal scalability, data reginality and many more.
The default configuration of the database looks like this:
```yaml
Database:
cockroach:
Host: localhost
Port: 26257
Database: zitadel
MaxOpenConns: 20
MaxConnLifetime: 30m
MaxConnIdleTime: 30m
Options: ""
User:
Username: zitadel
Password: ""
SSL:
Mode: disable
RootCert: ""
Cert: ""
Key: ""
Admin:
Username: root
Password: ""
SSL:
Mode: disable
RootCert: ""
Cert: ""
Key: ""
```

View File

@ -0,0 +1,36 @@
## Postgres
:::caution
Postgres extension is currently in beta.
:::
If you want to use a Postgres database instead of CockroachDB you can [overwrite the default configuration](../configure/configure.mdx).
Postgres can be configured as follows:
```yaml
Database:
postgres:
Host: localhost
Port: 5432
Database: zitadel
MaxOpenConns: 25
MaxConnLifetime: 1h
MaxConnIdleTime: 5m
Options:
User:
Username: zitadel
Password: zitadel
SSL:
Mode: disable
RootCert:
Cert:
Key:
Admin:
Username: postgres
Password: postgres
SSL:
Mode: disable
RootCert:
Cert:
Key:
```

View File

@ -0,0 +1,28 @@
---
title: Database
---
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Cockroach from './_cockroachdb.mdx'
import Postgres from './_postgres.mdx'
# Database Configuration
<Tabs
groupId="database-vendor"
default="cockroach"
values={[
{'label': 'Cockroach', 'value': 'crdb'},
{'label': 'Postgres', 'value': 'pg'},
]}
>
<TabItem value="crdb">
<Cockroach/>
<More/>
</TabItem>
<TabItem value="pg">
<Postgres/>
<More/>
</TabItem>
</Tabs>

View File

@ -86,6 +86,7 @@ module.exports = {
"guides/manage/self-hosted/custom-domain", "guides/manage/self-hosted/custom-domain",
"guides/manage/self-hosted/http2", "guides/manage/self-hosted/http2",
"guides/manage/self-hosted/tls_modes", "guides/manage/self-hosted/tls_modes",
"guides/manage/self-hosted/database/database",
] ]
}, },
{ {
@ -95,7 +96,6 @@ module.exports = {
"guides/manage/console/organizations", "guides/manage/console/organizations",
"guides/manage/console/projects", "guides/manage/console/projects",
"guides/manage/console/applications", "guides/manage/console/applications",
] ]
}, },
{ {

16
go.mod
View File

@ -1,6 +1,6 @@
module github.com/zitadel/zitadel module github.com/zitadel/zitadel
go 1.17 go 1.19
require ( require (
cloud.google.com/go/storage v1.14.0 cloud.google.com/go/storage v1.14.0
@ -29,6 +29,9 @@ require (
github.com/grpc-ecosystem/grpc-gateway v1.16.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.1 github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.1
github.com/improbable-eng/grpc-web v0.15.0 github.com/improbable-eng/grpc-web v0.15.0
github.com/jackc/pgconn v1.12.1
github.com/jackc/pgtype v1.11.0
github.com/jackc/pgx/v4 v4.16.1
github.com/jinzhu/gorm v1.9.16 github.com/jinzhu/gorm v1.9.16
github.com/k3a/html2text v1.0.8 github.com/k3a/html2text v1.0.8
github.com/kevinburke/twilio-go v0.0.0-20210327194925-1623146bcf73 github.com/kevinburke/twilio-go v0.0.0-20210327194925-1623146bcf73
@ -66,7 +69,7 @@ require (
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/text v0.3.7 golang.org/x/text v0.3.7
golang.org/x/tools v0.1.8 golang.org/x/tools v0.1.11
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa
google.golang.org/grpc v1.43.0 google.golang.org/grpc v1.43.0
google.golang.org/protobuf v1.27.1 google.golang.org/protobuf v1.27.1
@ -115,7 +118,7 @@ require (
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/snappy v0.0.4 // indirect github.com/golang/snappy v0.0.4 // indirect
github.com/google/certificate-transparency-go v1.0.21 // indirect github.com/google/certificate-transparency-go v1.0.21 // indirect
github.com/google/go-cmp v0.5.6 // indirect github.com/google/go-cmp v0.5.8 // indirect
github.com/google/uuid v1.3.0 // indirect github.com/google/uuid v1.3.0 // indirect
github.com/googleapis/gax-go/v2 v2.1.1 // indirect github.com/googleapis/gax-go/v2 v2.1.1 // indirect
github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/handlers v1.5.1 // indirect
@ -125,6 +128,11 @@ require (
github.com/huandu/xstrings v1.3.2 // indirect github.com/huandu/xstrings v1.3.2 // indirect
github.com/imdario/mergo v0.3.12 // indirect github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.3.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jarcoal/jpath v0.0.0-20140328210829-f76b8b2dbf52 github.com/jarcoal/jpath v0.0.0-20140328210829-f76b8b2dbf52
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
@ -169,7 +177,7 @@ require (
go.opentelemetry.io/otel/internal/metric v0.25.0 // indirect go.opentelemetry.io/otel/internal/metric v0.25.0 // indirect
go.opentelemetry.io/proto/otlp v0.10.0 // indirect go.opentelemetry.io/proto/otlp v0.10.0 // indirect
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 // indirect golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 // indirect
golang.org/x/mod v0.5.1 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/api v0.63.0 google.golang.org/api v0.63.0

58
go.sum
View File

@ -69,6 +69,8 @@ github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJ
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/Masterminds/squirrel v1.5.2 h1:UiOEi2ZX4RCSkpiNDQN5kro/XIBpSRk9iTqdIRPzUXE= github.com/Masterminds/squirrel v1.5.2 h1:UiOEi2ZX4RCSkpiNDQN5kro/XIBpSRk9iTqdIRPzUXE=
@ -117,7 +119,6 @@ github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBW
github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs= github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo= github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=
@ -125,7 +126,6 @@ github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInq
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.3.0 h1:t/LhUZLVitR1Ow2YOnduCsavhwFUklBMoGVYUCqmCqk= github.com/census-instrumentation/opencensus-proto v0.3.0 h1:t/LhUZLVitR1Ow2YOnduCsavhwFUklBMoGVYUCqmCqk=
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
@ -151,6 +151,7 @@ github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490 h1:KwaoQzs/WeUxxJqiJsZ4euOly1Az/IgZXXSxlD/UBNk= github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490 h1:KwaoQzs/WeUxxJqiJsZ4euOly1Az/IgZXXSxlD/UBNk=
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/cockroachdb/cockroach-go/v2 v2.2.4 h1:VuiBJKut2Imgrzl+TNk+U5+GxLOh3hnIFxU0EzjTCnI= github.com/cockroachdb/cockroach-go/v2 v2.2.4 h1:VuiBJKut2Imgrzl+TNk+U5+GxLOh3hnIFxU0EzjTCnI=
github.com/cockroachdb/cockroach-go/v2 v2.2.4/go.mod h1:u3MiKYGupPPjkn3ozknpMUpxPaNLTFWAya419/zv6eI= github.com/cockroachdb/cockroach-go/v2 v2.2.4/go.mod h1:u3MiKYGupPPjkn3ozknpMUpxPaNLTFWAya419/zv6eI=
@ -263,13 +264,10 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
@ -362,8 +360,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github/v31 v31.0.0/go.mod h1:NQPZol8/1sMoWYGN2yaALIBytu17gAWfhbweiEed3pM= github.com/google/go-github/v31 v31.0.0/go.mod h1:NQPZol8/1sMoWYGN2yaALIBytu17gAWfhbweiEed3pM=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@ -391,7 +390,6 @@ github.com/google/pprof v0.0.0-20210715191844-86eeefc3e471/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -436,7 +434,6 @@ github.com/h2non/filetype v1.1.1 h1:xvOwnXKAckvtLWsN398qS9QhlxlnVXBjXBydK2/UFB4=
github.com/h2non/filetype v1.1.1/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= github.com/h2non/filetype v1.1.1/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@ -490,6 +487,7 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
@ -498,8 +496,17 @@ github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5
github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.12.1 h1:rsDFzIpRk7xT4B8FufgpCCeyjdNpKyghZeSefViE5W8=
github.com/jackc/pgconn v1.12.1/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
@ -508,7 +515,11 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvW
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.3.0 h1:brH0pCGBDkBW07HWlN/oSBXrmo3WB0UvZd1pIuDcL8Y=
github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
@ -517,6 +528,9 @@ github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4
github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po= github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po=
github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ= github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
github.com/jackc/pgtype v1.6.2/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig= github.com/jackc/pgtype v1.6.2/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig=
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
github.com/jackc/pgtype v1.11.0 h1:u4uiGPz/1hryuXzyaBhSk6dnIyyG2683olG2OV+UUgs=
github.com/jackc/pgtype v1.11.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
@ -524,11 +538,15 @@ github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXg
github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o= github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg= github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
github.com/jackc/pgx/v4 v4.10.1/go.mod h1:QlrWebbs3kqEZPHCTGyxecvzG6tvIsYu+A5b1raylkA= github.com/jackc/pgx/v4 v4.10.1/go.mod h1:QlrWebbs3kqEZPHCTGyxecvzG6tvIsYu+A5b1raylkA=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v4 v4.16.1 h1:JzTglcal01DrghUqt+PmzWsZx/Yh7SC/CTQmSBMTd0Y=
github.com/jackc/pgx/v4 v4.16.1/go.mod h1:SIhx0D5hoADaiXZVyv+3gSm3LCIIINTVO0PficsvWGQ=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jarcoal/jpath v0.0.0-20140328210829-f76b8b2dbf52 h1:jny9eqYPwkG8IVy7foUoRjQmFLcArCSz+uPsL6KS0HQ= github.com/jarcoal/jpath v0.0.0-20140328210829-f76b8b2dbf52 h1:jny9eqYPwkG8IVy7foUoRjQmFLcArCSz+uPsL6KS0HQ=
github.com/jarcoal/jpath v0.0.0-20140328210829-f76b8b2dbf52/go.mod h1:RDZ+4PR3mDOtTpVbI0qBE+rdhmtIrtbssiNn38/1OWA= github.com/jarcoal/jpath v0.0.0-20140328210829-f76b8b2dbf52/go.mod h1:RDZ+4PR3mDOtTpVbI0qBE+rdhmtIrtbssiNn38/1OWA=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
@ -570,7 +588,6 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.14.2 h1:S0OHlFk/Gbon/yauFJ4FfJJF5V0fc5HbBTJazi28pRw= github.com/klauspost/compress v1.14.2 h1:S0OHlFk/Gbon/yauFJ4FfJJF5V0fc5HbBTJazi28pRw=
github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
@ -596,13 +613,13 @@ github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhR
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk=
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
@ -779,13 +796,13 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
github.com/sagikazarmark/crypt v0.4.0/go.mod h1:ALv2SRj7GxYV4HO9elxH9nS6M9gW+xDNxqmyJ6RfDFM=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
@ -864,7 +881,6 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
github.com/zitadel/logging v0.3.4 h1:9hZsTjMMTE3X2LUi0xcF9Q9EdLo+FAezeu52ireBbHM= github.com/zitadel/logging v0.3.4 h1:9hZsTjMMTE3X2LUi0xcF9Q9EdLo+FAezeu52ireBbHM=
github.com/zitadel/logging v0.3.4/go.mod h1:aPpLQhE+v6ocNK0TWrBrd363hZ95KcI17Q1ixAQwZF0= github.com/zitadel/logging v0.3.4/go.mod h1:aPpLQhE+v6ocNK0TWrBrd363hZ95KcI17Q1ixAQwZF0=
@ -953,9 +969,11 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
@ -999,8 +1017,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -1054,8 +1072,6 @@ golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20210716203947-853a461950ff/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210716203947-853a461950ff/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220121210141-e204ce36a2ba h1:6u6sik+bn/y7vILcYkK3iwTBWN7WtBvB0+SZswQnbf8= golang.org/x/net v0.0.0-20220121210141-e204ce36a2ba h1:6u6sik+bn/y7vILcYkK3iwTBWN7WtBvB0+SZswQnbf8=
golang.org/x/net v0.0.0-20220121210141-e204ce36a2ba/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220121210141-e204ce36a2ba/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -1168,17 +1184,14 @@ golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220207234003-57398862261d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220207234003-57398862261d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1257,8 +1270,8 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w= golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -1446,7 +1459,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=

View File

@ -130,7 +130,7 @@ func OIDCGrantTypesFromModel(grantTypes []domain.OIDCGrantType) []app_pb.OIDCGra
} }
func OIDCGrantTypesToDomain(grantTypes []app_pb.OIDCGrantType) []domain.OIDCGrantType { func OIDCGrantTypesToDomain(grantTypes []app_pb.OIDCGrantType) []domain.OIDCGrantType {
if grantTypes == nil || len(grantTypes) == 0 { if len(grantTypes) == 0 {
return []domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode} return []domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode}
} }
oidcGrantTypes := make([]domain.OIDCGrantType, len(grantTypes)) oidcGrantTypes := make([]domain.OIDCGrantType, len(grantTypes))

View File

@ -6,9 +6,6 @@ import (
"strings" "strings"
"time" "time"
//sql import
_ "github.com/lib/pq"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
"github.com/zitadel/logging" "github.com/zitadel/logging"
@ -61,13 +58,15 @@ func (c *Config) Decode(configs []interface{}) (dialect.Connector, error) {
} }
func (c *Config) Connect(useAdmin bool) (*sql.DB, error) { func (c *Config) Connect(useAdmin bool) (*sql.DB, error) {
client, err := sql.Open("postgres", c.String(useAdmin)) client, err := sql.Open("pgx", c.String(useAdmin))
if err != nil { if err != nil {
return nil, err return nil, err
} }
client.SetMaxOpenConns(int(c.MaxOpenConns)) client.SetMaxOpenConns(int(c.MaxOpenConns))
client.SetConnMaxLifetime(c.MaxConnLifetime) client.SetConnMaxLifetime(c.MaxConnLifetime)
client.SetConnMaxIdleTime(c.MaxConnIdleTime) client.SetConnMaxIdleTime(c.MaxConnIdleTime)
return client, nil return client, nil
} }

View File

@ -6,6 +6,8 @@ import (
_ "github.com/zitadel/zitadel/internal/database/cockroach" _ "github.com/zitadel/zitadel/internal/database/cockroach"
"github.com/zitadel/zitadel/internal/database/dialect" "github.com/zitadel/zitadel/internal/database/dialect"
_ "github.com/zitadel/zitadel/internal/database/postgres"
"github.com/zitadel/zitadel/internal/errors"
) )
type Config struct { type Config struct {
@ -17,23 +19,6 @@ func (c *Config) SetConnector(connector dialect.Connector) {
c.connector = connector c.connector = connector
} }
type User struct {
Username string
Password string
SSL SSL
}
type SSL struct {
// type of connection security
Mode string
// RootCert Path to the CA certificate
RootCert string
// Cert Path to the client certificate
Cert string
// Key Path to the client private key
Key string
}
func Connect(config Config, useAdmin bool) (*sql.DB, error) { func Connect(config Config, useAdmin bool) (*sql.DB, error) {
client, err := config.connector.Connect(useAdmin) client, err := config.connector.Connect(useAdmin)
if err != nil { if err != nil {
@ -41,7 +26,7 @@ func Connect(config Config, useAdmin bool) (*sql.DB, error) {
} }
if err := client.Ping(); err != nil { if err := client.Ping(); err != nil {
return nil, err return nil, errors.ThrowPreconditionFailed(err, "DATAB-0pIWD", "Errors.Database.Connection.Failed")
} }
return client, nil return client, nil

View File

@ -0,0 +1,153 @@
package postgres
import (
"database/sql"
"strconv"
"strings"
"time"
"github.com/mitchellh/mapstructure"
"github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/database/dialect"
)
const (
sslDisabledMode = "disable"
)
type Config struct {
Host string
Port int32
Database string
MaxOpenConns uint32
MaxConnLifetime time.Duration
MaxConnIdleTime time.Duration
User User
Admin User
//Additional options to be appended as options=<Options>
//The value will be taken as is. Multiple options are space separated.
Options string
}
func (c *Config) MatchName(name string) bool {
for _, key := range []string{"pg", "postgres"} {
if strings.TrimSpace(strings.ToLower(name)) == key {
return true
}
}
return false
}
func (c *Config) Decode(configs []interface{}) (dialect.Connector, error) {
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
Result: c,
})
if err != nil {
return nil, err
}
for _, config := range configs {
if err = decoder.Decode(config); err != nil {
return nil, err
}
}
return c, nil
}
func (c *Config) Connect(useAdmin bool) (*sql.DB, error) {
logging.Warn("postgres is currently in beta")
db, err := sql.Open("pgx", c.String(useAdmin))
if err != nil {
return nil, err
}
db.SetMaxOpenConns(int(c.MaxOpenConns))
db.SetConnMaxLifetime(c.MaxConnLifetime)
db.SetConnMaxIdleTime(c.MaxConnIdleTime)
return db, nil
}
func (c *Config) DatabaseName() string {
return c.Database
}
func (c *Config) Username() string {
return c.User.Username
}
func (c *Config) Password() string {
return c.User.Password
}
func (c *Config) Type() string {
return "postgres"
}
type User struct {
Username string
Password string
SSL SSL
}
type SSL struct {
// type of connection security
Mode string
// RootCert Path to the CA certificate
RootCert string
// Cert Path to the client certificate
Cert string
// Key Path to the client private key
Key string
}
func (s *Config) checkSSL(user User) {
if user.SSL.Mode == sslDisabledMode || user.SSL.Mode == "" {
user.SSL = SSL{Mode: sslDisabledMode}
return
}
if user.SSL.RootCert == "" {
logging.WithFields(
"cert set", user.SSL.Cert != "",
"key set", user.SSL.Key != "",
"rootCert set", user.SSL.RootCert != "",
).Fatal("at least ssl root cert has to be set")
}
}
func (c Config) String(useAdmin bool) string {
user := c.User
if useAdmin {
user = c.Admin
}
c.checkSSL(user)
fields := []string{
"host=" + c.Host,
"port=" + strconv.Itoa(int(c.Port)),
"user=" + user.Username,
"application_name=zitadel",
"sslmode=" + user.SSL.Mode,
}
if c.Options != "" {
fields = append(fields, "options="+c.Options)
}
if user.Password != "" {
fields = append(fields, "password="+user.Password)
}
if !useAdmin {
fields = append(fields, "dbname="+c.Database)
}
if user.SSL.Mode != sslDisabledMode {
fields = append(fields, "sslrootcert="+user.SSL.RootCert)
if user.SSL.Cert != "" {
fields = append(fields, "sslcert="+user.SSL.Cert)
}
if user.SSL.Key != "" {
fields = append(fields, "sslkey="+user.SSL.Key)
}
}
return strings.Join(fields, " ")
}

View File

@ -0,0 +1,14 @@
package postgres
import (
//sql import
_ "github.com/jackc/pgx/v4/stdlib"
"github.com/zitadel/zitadel/internal/database/dialect"
)
func init() {
config := &Config{}
dialect.Register(config, config, false)
}

72
internal/database/type.go Normal file
View File

@ -0,0 +1,72 @@
package database
import (
"database/sql/driver"
"github.com/jackc/pgtype"
)
type StringArray []string
// Scan implements the `database/sql.Scanner` interface.
func (s *StringArray) Scan(src any) error {
array := new(pgtype.TextArray)
if err := array.Scan(src); err != nil {
return err
}
if err := array.AssignTo(s); err != nil {
return err
}
return nil
}
// Value implements the `database/sql/driver.Valuer`` interface.
func (s StringArray) Value() (driver.Value, error) {
if len(s) == 0 {
return nil, nil
}
array := pgtype.TextArray{}
if err := array.Set(s); err != nil {
return nil, err
}
return array.Value()
}
type enumField interface {
~int8 | ~uint8 | ~int16 | ~uint16 | ~int32 | ~uint32
}
type EnumArray[F enumField] []F
// Scan implements the `database/sql.Scanner` interface.
func (s *EnumArray[F]) Scan(src any) error {
array := new(pgtype.Int2Array)
if err := array.Scan(src); err != nil {
return err
}
ints := make([]int32, 0, len(array.Elements))
if err := array.AssignTo(&ints); err != nil {
return err
}
*s = make([]F, len(ints))
for i, a := range ints {
(*s)[i] = F(a)
}
return nil
}
// Value implements the `database/sql/driver.Valuer`` interface.
func (s EnumArray[F]) Value() (driver.Value, error) {
if len(s) == 0 {
return nil, nil
}
array := pgtype.Int2Array{}
if err := array.Set(s); err != nil {
return nil, err
}
return array.Value()
}

View File

@ -6,15 +6,15 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/lib/pq" "github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore"
) )
const ( const (
currentSequenceStmtFormat = `SELECT current_sequence, aggregate_type, instance_id FROM %s WHERE projection_name = $1 AND instance_id = ANY ($2) FOR UPDATE` currentSequenceStmtFormat = `SELECT current_sequence, aggregate_type, instance_id FROM %s WHERE projection_name = $1 AND instance_id = ANY ($2) FOR UPDATE`
updateCurrentSequencesStmtFormat = `UPSERT INTO %s (projection_name, aggregate_type, current_sequence, instance_id, timestamp) VALUES ` updateCurrentSequencesStmtFormat = `INSERT INTO %s (projection_name, aggregate_type, current_sequence, instance_id, timestamp) VALUES `
updateCurrentSequencesConflictStmt = ` ON CONFLICT (projection_name, aggregate_type, instance_id) DO UPDATE SET current_sequence = EXCLUDED.current_sequence, timestamp = EXCLUDED.timestamp`
) )
type currentSequences map[eventstore.AggregateType][]*instanceSequence type currentSequences map[eventstore.AggregateType][]*instanceSequence
@ -24,8 +24,8 @@ type instanceSequence struct {
sequence uint64 sequence uint64
} }
func (h *StatementHandler) currentSequences(ctx context.Context, query func(context.Context, string, ...interface{}) (*sql.Rows, error), instanceIDs []string) (currentSequences, error) { func (h *StatementHandler) currentSequences(ctx context.Context, query func(context.Context, string, ...interface{}) (*sql.Rows, error), instanceIDs database.StringArray) (currentSequences, error) {
rows, err := query(ctx, h.currentSequenceStmt, h.ProjectionName, pq.StringArray(instanceIDs)) rows, err := query(ctx, h.currentSequenceStmt, h.ProjectionName, instanceIDs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -74,7 +74,7 @@ func (h *StatementHandler) updateCurrentSequences(tx *sql.Tx, sequences currentS
} }
} }
res, err := tx.Exec(h.updateSequencesBaseStmt+strings.Join(valueQueries, ", "), values...) res, err := tx.Exec(h.updateSequencesBaseStmt+strings.Join(valueQueries, ", ")+updateCurrentSequencesConflictStmt, values...)
if err != nil { if err != nil {
return errors.ThrowInternal(err, "CRDB-TrH2Z", "unable to exec update sequence") return errors.ThrowInternal(err, "CRDB-TrH2Z", "unable to exec update sequence")
} }

View File

@ -8,8 +8,8 @@ import (
"time" "time"
"github.com/DATA-DOG/go-sqlmock" "github.com/DATA-DOG/go-sqlmock"
"github.com/lib/pq"
"github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore"
) )
@ -17,7 +17,7 @@ type mockExpectation func(sqlmock.Sqlmock)
func expectFailureCount(tableName string, projectionName, instanceID string, failedSeq, failureCount uint64) func(sqlmock.Sqlmock) { func expectFailureCount(tableName string, projectionName, instanceID string, failedSeq, failureCount uint64) func(sqlmock.Sqlmock) {
return func(m sqlmock.Sqlmock) { return func(m sqlmock.Sqlmock) {
m.ExpectQuery(`WITH failures AS \(SELECT failure_count FROM `+tableName+` WHERE projection_name = \$1 AND failed_sequence = \$2\ AND instance_id = \$3\) SELECT IF\(EXISTS\(SELECT failure_count FROM failures\), \(SELECT failure_count FROM failures\), 0\) AS failure_count`). m.ExpectQuery(`WITH failures AS \(SELECT failure_count FROM `+tableName+` WHERE projection_name = \$1 AND failed_sequence = \$2 AND instance_id = \$3\) SELECT COALESCE\(\(SELECT failure_count FROM failures\), 0\) AS failure_count`).
WithArgs(projectionName, failedSeq, instanceID). WithArgs(projectionName, failedSeq, instanceID).
WillReturnRows( WillReturnRows(
sqlmock.NewRows([]string{"failure_count"}). sqlmock.NewRows([]string{"failure_count"}).
@ -28,7 +28,7 @@ func expectFailureCount(tableName string, projectionName, instanceID string, fai
func expectUpdateFailureCount(tableName string, projectionName, instanceID string, seq, failureCount uint64) func(sqlmock.Sqlmock) { func expectUpdateFailureCount(tableName string, projectionName, instanceID string, seq, failureCount uint64) func(sqlmock.Sqlmock) {
return func(m sqlmock.Sqlmock) { return func(m sqlmock.Sqlmock) {
m.ExpectExec(`UPSERT INTO `+tableName+` \(projection_name, failed_sequence, failure_count, error, instance_id\) VALUES \(\$1, \$2, \$3, \$4\, \$5\)`). m.ExpectExec(`INSERT INTO `+tableName+` \(projection_name, failed_sequence, failure_count, error, instance_id\) VALUES \(\$1, \$2, \$3, \$4\, \$5\) ON CONFLICT \(projection_name, failed_sequence, instance_id\) DO UPDATE SET failure_count = EXCLUDED\.failure_count, error = EXCLUDED\.error`).
WithArgs(projectionName, seq, failureCount, sqlmock.AnyArg(), instanceID).WillReturnResult(sqlmock.NewResult(1, 1)) WithArgs(projectionName, seq, failureCount, sqlmock.AnyArg(), instanceID).WillReturnResult(sqlmock.NewResult(1, 1))
} }
} }
@ -133,7 +133,7 @@ func expectCurrentSequence(tableName, projection string, seq uint64, aggregateTy
m.ExpectQuery(`SELECT current_sequence, aggregate_type, instance_id FROM `+tableName+` WHERE projection_name = \$1 AND instance_id = ANY \(\$2\) FOR UPDATE`). m.ExpectQuery(`SELECT current_sequence, aggregate_type, instance_id FROM `+tableName+` WHERE projection_name = \$1 AND instance_id = ANY \(\$2\) FOR UPDATE`).
WithArgs( WithArgs(
projection, projection,
pq.StringArray(instanceIDs), database.StringArray(instanceIDs),
). ).
WillReturnRows( WillReturnRows(
rows, rows,
@ -146,7 +146,7 @@ func expectCurrentSequenceErr(tableName, projection string, instanceIDs []string
m.ExpectQuery(`SELECT current_sequence, aggregate_type, instance_id FROM `+tableName+` WHERE projection_name = \$1 AND instance_id = ANY \(\$2\) FOR UPDATE`). m.ExpectQuery(`SELECT current_sequence, aggregate_type, instance_id FROM `+tableName+` WHERE projection_name = \$1 AND instance_id = ANY \(\$2\) FOR UPDATE`).
WithArgs( WithArgs(
projection, projection,
pq.StringArray(instanceIDs), database.StringArray(instanceIDs),
). ).
WillReturnError(err) WillReturnError(err)
} }
@ -157,7 +157,7 @@ func expectCurrentSequenceNoRows(tableName, projection string, instanceIDs []str
m.ExpectQuery(`SELECT current_sequence, aggregate_type, instance_id FROM `+tableName+` WHERE projection_name = \$1 AND instance_id = ANY \(\$2\) FOR UPDATE`). m.ExpectQuery(`SELECT current_sequence, aggregate_type, instance_id FROM `+tableName+` WHERE projection_name = \$1 AND instance_id = ANY \(\$2\) FOR UPDATE`).
WithArgs( WithArgs(
projection, projection,
pq.StringArray(instanceIDs), database.StringArray(instanceIDs),
). ).
WillReturnRows( WillReturnRows(
sqlmock.NewRows([]string{"current_sequence", "aggregate_type", "instance_id"}), sqlmock.NewRows([]string{"current_sequence", "aggregate_type", "instance_id"}),
@ -170,7 +170,7 @@ func expectCurrentSequenceScanErr(tableName, projection string, instanceIDs []st
m.ExpectQuery(`SELECT current_sequence, aggregate_type, instance_id FROM `+tableName+` WHERE projection_name = \$1 AND instance_id = ANY \(\$2\) FOR UPDATE`). m.ExpectQuery(`SELECT current_sequence, aggregate_type, instance_id FROM `+tableName+` WHERE projection_name = \$1 AND instance_id = ANY \(\$2\) FOR UPDATE`).
WithArgs( WithArgs(
projection, projection,
pq.StringArray(instanceIDs), database.StringArray(instanceIDs),
). ).
WillReturnRows( WillReturnRows(
sqlmock.NewRows([]string{"current_sequence", "aggregate_type", "instance_id"}). sqlmock.NewRows([]string{"current_sequence", "aggregate_type", "instance_id"}).
@ -182,7 +182,7 @@ func expectCurrentSequenceScanErr(tableName, projection string, instanceIDs []st
func expectUpdateCurrentSequence(tableName, projection string, seq uint64, aggregateType, instanceID string) func(sqlmock.Sqlmock) { func expectUpdateCurrentSequence(tableName, projection string, seq uint64, aggregateType, instanceID string) func(sqlmock.Sqlmock) {
return func(m sqlmock.Sqlmock) { return func(m sqlmock.Sqlmock) {
m.ExpectExec("UPSERT INTO "+tableName+` \(projection_name, aggregate_type, current_sequence, instance_id, timestamp\) VALUES \(\$1, \$2, \$3, \$4, NOW\(\)\)`). m.ExpectExec("INSERT INTO "+tableName+` \(projection_name, aggregate_type, current_sequence, instance_id, timestamp\) VALUES \(\$1, \$2, \$3, \$4, NOW\(\)\) ON CONFLICT \(projection_name, aggregate_type, instance_id\) DO UPDATE SET current_sequence = EXCLUDED.current_sequence, timestamp = EXCLUDED.timestamp`).
WithArgs( WithArgs(
projection, projection,
aggregateType, aggregateType,
@ -213,7 +213,7 @@ func expectUpdateThreeCurrentSequence(t *testing.T, tableName, projection string
matchers[i] = matcher matchers[i] = matcher
} }
return func(m sqlmock.Sqlmock) { return func(m sqlmock.Sqlmock) {
m.ExpectExec("UPSERT INTO " + tableName + ` \(projection_name, aggregate_type, current_sequence, instance_id, timestamp\) VALUES \(\$1, \$2, \$3, \$4, NOW\(\)\), \(\$5, \$6, \$7, \$8, NOW\(\)\), \(\$9, \$10, \$11, \$12, NOW\(\)\)`). m.ExpectExec("INSERT INTO " + tableName + ` \(projection_name, aggregate_type, current_sequence, instance_id, timestamp\) VALUES \(\$1, \$2, \$3, \$4, NOW\(\)\), \(\$5, \$6, \$7, \$8, NOW\(\)\), \(\$9, \$10, \$11, \$12, NOW\(\)\) ON CONFLICT \(projection_name, aggregate_type, instance_id\) DO UPDATE SET current_sequence = EXCLUDED.current_sequence, timestamp = EXCLUDED.timestamp`).
WithArgs( WithArgs(
matchers..., matchers...,
). ).
@ -262,7 +262,7 @@ func (m *currentSequenceMatcher) Match(value driver.Value) bool {
func expectUpdateCurrentSequenceErr(tableName, projection string, seq uint64, err error, aggregateType, instanceID string) func(sqlmock.Sqlmock) { func expectUpdateCurrentSequenceErr(tableName, projection string, seq uint64, err error, aggregateType, instanceID string) func(sqlmock.Sqlmock) {
return func(m sqlmock.Sqlmock) { return func(m sqlmock.Sqlmock) {
m.ExpectExec("UPSERT INTO "+tableName+` \(projection_name, aggregate_type, current_sequence, instance_id, timestamp\) VALUES \(\$1, \$2, \$3, \$4, NOW\(\)\)`). m.ExpectExec("INSERT INTO "+tableName+` \(projection_name, aggregate_type, current_sequence, instance_id, timestamp\) VALUES \(\$1, \$2, \$3, \$4, NOW\(\)\) ON CONFLICT \(projection_name, aggregate_type, instance_id\) DO UPDATE SET current_sequence = EXCLUDED.current_sequence, timestamp = EXCLUDED.timestamp`).
WithArgs( WithArgs(
projection, projection,
aggregateType, aggregateType,
@ -275,7 +275,7 @@ func expectUpdateCurrentSequenceErr(tableName, projection string, seq uint64, er
func expectUpdateCurrentSequenceNoRows(tableName, projection string, seq uint64, aggregateType, instanceID string) func(sqlmock.Sqlmock) { func expectUpdateCurrentSequenceNoRows(tableName, projection string, seq uint64, aggregateType, instanceID string) func(sqlmock.Sqlmock) {
return func(m sqlmock.Sqlmock) { return func(m sqlmock.Sqlmock) {
m.ExpectExec("UPSERT INTO "+tableName+` \(projection_name, aggregate_type, current_sequence, instance_id, timestamp\) VALUES \(\$1, \$2, \$3, \$4, NOW\(\)\)`). m.ExpectExec("INSERT INTO "+tableName+` \(projection_name, aggregate_type, current_sequence, instance_id, timestamp\) VALUES \(\$1, \$2, \$3, \$4, NOW\(\)\) ON CONFLICT \(projection_name, aggregate_type, instance_id\) DO UPDATE SET current_sequence = EXCLUDED.current_sequence, timestamp = EXCLUDED.timestamp`).
WithArgs( WithArgs(
projection, projection,
aggregateType, aggregateType,
@ -297,10 +297,10 @@ func expectLock(lockTable, workerName string, d time.Duration, instanceID string
` WHERE `+lockTable+`\.projection_name = \$3 AND `+lockTable+`\.instance_id = ANY \(\$5\) AND \(`+lockTable+`\.locker_id = \$1 OR `+lockTable+`\.locked_until < now\(\)\)`). ` WHERE `+lockTable+`\.projection_name = \$3 AND `+lockTable+`\.instance_id = ANY \(\$5\) AND \(`+lockTable+`\.locker_id = \$1 OR `+lockTable+`\.locked_until < now\(\)\)`).
WithArgs( WithArgs(
workerName, workerName,
float64(d), d,
projectionName, projectionName,
instanceID, instanceID,
pq.StringArray{instanceID}, database.StringArray{instanceID},
). ).
WillReturnResult( WillReturnResult(
sqlmock.NewResult(1, 1), sqlmock.NewResult(1, 1),
@ -317,11 +317,11 @@ func expectLockMultipleInstances(lockTable, workerName string, d time.Duration,
` WHERE `+lockTable+`\.projection_name = \$3 AND `+lockTable+`\.instance_id = ANY \(\$6\) AND \(`+lockTable+`\.locker_id = \$1 OR `+lockTable+`\.locked_until < now\(\)\)`). ` WHERE `+lockTable+`\.projection_name = \$3 AND `+lockTable+`\.instance_id = ANY \(\$6\) AND \(`+lockTable+`\.locker_id = \$1 OR `+lockTable+`\.locked_until < now\(\)\)`).
WithArgs( WithArgs(
workerName, workerName,
float64(d), d,
projectionName, projectionName,
instanceID1, instanceID1,
instanceID2, instanceID2,
pq.StringArray{instanceID1, instanceID2}, database.StringArray{instanceID1, instanceID2},
). ).
WillReturnResult( WillReturnResult(
sqlmock.NewResult(1, 1), sqlmock.NewResult(1, 1),
@ -338,10 +338,10 @@ func expectLockNoRows(lockTable, workerName string, d time.Duration, instanceID
` WHERE `+lockTable+`\.projection_name = \$3 AND `+lockTable+`\.instance_id = ANY \(\$5\) AND \(`+lockTable+`\.locker_id = \$1 OR `+lockTable+`\.locked_until < now\(\)\)`). ` WHERE `+lockTable+`\.projection_name = \$3 AND `+lockTable+`\.instance_id = ANY \(\$5\) AND \(`+lockTable+`\.locker_id = \$1 OR `+lockTable+`\.locked_until < now\(\)\)`).
WithArgs( WithArgs(
workerName, workerName,
float64(d), d,
projectionName, projectionName,
instanceID, instanceID,
pq.StringArray{instanceID}, database.StringArray{instanceID},
). ).
WillReturnResult(driver.ResultNoRows) WillReturnResult(driver.ResultNoRows)
} }
@ -356,10 +356,10 @@ func expectLockErr(lockTable, workerName string, d time.Duration, instanceID str
` WHERE `+lockTable+`\.projection_name = \$3 AND `+lockTable+`\.instance_id = ANY \(\$5\) AND \(`+lockTable+`\.locker_id = \$1 OR `+lockTable+`\.locked_until < now\(\)\)`). ` WHERE `+lockTable+`\.projection_name = \$3 AND `+lockTable+`\.instance_id = ANY \(\$5\) AND \(`+lockTable+`\.locker_id = \$1 OR `+lockTable+`\.locked_until < now\(\)\)`).
WithArgs( WithArgs(
workerName, workerName,
float64(d), d,
projectionName, projectionName,
instanceID, instanceID,
pq.StringArray{instanceID}, database.StringArray{instanceID},
). ).
WillReturnError(err) WillReturnError(err)
} }

View File

@ -10,15 +10,12 @@ import (
) )
const ( const (
setFailureCountStmtFormat = "UPSERT INTO %s" + setFailureCountStmtFormat = "INSERT INTO %s" +
" (projection_name, failed_sequence, failure_count, error, instance_id)" + " (projection_name, failed_sequence, failure_count, error, instance_id)" +
" VALUES ($1, $2, $3, $4, $5)" " VALUES ($1, $2, $3, $4, $5) ON CONFLICT (projection_name, failed_sequence, instance_id)" +
" DO UPDATE SET failure_count = EXCLUDED.failure_count, error = EXCLUDED.error"
failureCountStmtFormat = "WITH failures AS (SELECT failure_count FROM %s WHERE projection_name = $1 AND failed_sequence = $2 AND instance_id = $3)" + failureCountStmtFormat = "WITH failures AS (SELECT failure_count FROM %s WHERE projection_name = $1 AND failed_sequence = $2 AND instance_id = $3)" +
" SELECT IF(" + " SELECT COALESCE((SELECT failure_count FROM failures), 0) AS failure_count"
"EXISTS(SELECT failure_count FROM failures)," +
" (SELECT failure_count FROM failures)," +
" 0" +
") AS failure_count"
) )
func (h *StatementHandler) handleFailedStmt(tx *sql.Tx, stmt *handler.Statement, execErr error) (shouldContinue bool) { func (h *StatementHandler) handleFailedStmt(tx *sql.Tx, stmt *handler.Statement, execErr error) (shouldContinue bool) {

View File

@ -72,12 +72,12 @@ func NewStatementHandler(
aggregates: aggregateTypes, aggregates: aggregateTypes,
reduces: reduces, reduces: reduces,
bulkLimit: config.BulkLimit, bulkLimit: config.BulkLimit,
Locker: NewLocker(config.Client, config.LockTable, config.ProjectionHandlerConfig.ProjectionName), Locker: NewLocker(config.Client, config.LockTable, config.ProjectionName),
} }
h.ProjectionHandler = handler.NewProjectionHandler(ctx, config.ProjectionHandlerConfig, h.reduce, h.Update, h.SearchQuery, h.Lock, h.Unlock) h.ProjectionHandler = handler.NewProjectionHandler(ctx, config.ProjectionHandlerConfig, h.reduce, h.Update, h.SearchQuery, h.Lock, h.Unlock)
err := h.Init(ctx, config.InitCheck) err := h.Init(ctx, config.InitCheck)
logging.OnError(err).Fatal("unable to initialize projections") logging.OnError(err).WithField("projection", config.ProjectionName).Fatal("unable to initialize projections")
h.Subscribe(h.aggregates...) h.Subscribe(h.aggregates...)

View File

@ -6,11 +6,10 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/lib/pq" "github.com/jackc/pgconn"
"github.com/zitadel/logging" "github.com/zitadel/logging"
caos_errs "github.com/zitadel/zitadel/internal/errors" caos_errs "github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore/handler" "github.com/zitadel/zitadel/internal/eventstore/handler"
) )
@ -186,7 +185,7 @@ type ForeignKey struct {
RefColumns []string RefColumns []string
} }
//Init implements handler.Init // Init implements handler.Init
func (h *StatementHandler) Init(ctx context.Context, checks ...*handler.Check) error { func (h *StatementHandler) Init(ctx context.Context, checks ...*handler.Check) error {
for _, check := range checks { for _, check := range checks {
if check == nil || check.IsNoop() { if check == nil || check.IsNoop() {
@ -280,7 +279,7 @@ func isErrAlreadyExists(err error) bool {
if !errors.As(err, &caosErr) { if !errors.As(err, &caosErr) {
return false return false
} }
sqlErr, ok := caosErr.GetParent().(*pq.Error) sqlErr, ok := caosErr.GetParent().(*pgconn.PgError)
if !ok { if !ok {
return false return false
} }
@ -288,14 +287,11 @@ func isErrAlreadyExists(err error) bool {
} }
func createTableStatement(table *Table, tableName string, suffix string) string { func createTableStatement(table *Table, tableName string, suffix string) string {
stmt := fmt.Sprintf("CREATE TABLE %s (%s, PRIMARY KEY (%s)", stmt := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (%s, PRIMARY KEY (%s)",
tableName+suffix, tableName+suffix,
createColumnsStatement(table.columns, tableName), createColumnsStatement(table.columns, tableName),
strings.Join(table.primaryKey, ", "), strings.Join(table.primaryKey, ", "),
) )
for _, index := range table.indices {
stmt += fmt.Sprintf(", INDEX %s (%s)", index.Name, strings.Join(index.Columns, ","))
}
for _, key := range table.foreignKeys { for _, key := range table.foreignKeys {
ref := tableName ref := tableName
if len(key.RefColumns) > 0 { if len(key.RefColumns) > 0 {
@ -309,7 +305,13 @@ func createTableStatement(table *Table, tableName string, suffix string) string
for _, constraint := range table.constraints { for _, constraint := range table.constraints {
stmt += fmt.Sprintf(", CONSTRAINT %s UNIQUE (%s)", constraint.Name, strings.Join(constraint.Columns, ",")) stmt += fmt.Sprintf(", CONSTRAINT %s UNIQUE (%s)", constraint.Name, strings.Join(constraint.Columns, ","))
} }
return stmt + ");"
stmt += ");"
for _, index := range table.indices {
stmt += fmt.Sprintf("CREATE INDEX IF NOT EXISTS %s ON %s (%s);", index.Name, tableName+suffix, strings.Join(index.Columns, ","))
}
return stmt
} }
func createViewStatement(viewName string, selectStmt string) string { func createViewStatement(viewName string, selectStmt string) string {
@ -321,7 +323,7 @@ func createViewStatement(viewName string, selectStmt string) string {
func createIndexStatement(index *Index) func(config execConfig) string { func createIndexStatement(index *Index) func(config execConfig) string {
return func(config execConfig) string { return func(config execConfig) string {
stmt := fmt.Sprintf("CREATE INDEX %s ON %s (%s)", stmt := fmt.Sprintf("CREATE INDEX IF NOT EXISTS %s ON %s (%s)",
index.Name, index.Name,
config.tableName, config.tableName,
strings.Join(index.Columns, ","), strings.Join(index.Columns, ","),
@ -380,9 +382,8 @@ func columnType(columnType ColumnType) string {
case ColumnTypeJSONB: case ColumnTypeJSONB:
return "JSONB" return "JSONB"
case ColumnTypeBytes: case ColumnTypeBytes:
return "BYTES" return "BYTEA"
default: default:
panic("unknown column type") panic("unknown column type")
return ""
} }
} }

View File

@ -8,9 +8,9 @@ import (
"strings" "strings"
"time" "time"
"github.com/lib/pq"
"github.com/zitadel/logging" "github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/id" "github.com/zitadel/zitadel/internal/id"
) )
@ -91,17 +91,17 @@ func (h *locker) Unlock(instanceIDs ...string) error {
return nil return nil
} }
func (h *locker) lockStatement(lockDuration time.Duration, instanceIDs []string) (string, []interface{}) { func (h *locker) lockStatement(lockDuration time.Duration, instanceIDs database.StringArray) (string, []interface{}) {
valueQueries := make([]string, len(instanceIDs)) valueQueries := make([]string, len(instanceIDs))
values := make([]interface{}, len(instanceIDs)+4) values := make([]interface{}, len(instanceIDs)+4)
values[0] = h.workerName values[0] = h.workerName
//the unit of crdb interval is seconds (https://www.cockroachlabs.com/docs/stable/interval.html). //the unit of crdb interval is seconds (https://www.cockroachlabs.com/docs/stable/interval.html).
values[1] = lockDuration.Seconds() values[1] = lockDuration
values[2] = h.projectionName values[2] = h.projectionName
for i, instanceID := range instanceIDs { for i, instanceID := range instanceIDs {
valueQueries[i] = "($1, now()+$2::INTERVAL, $3, $" + strconv.Itoa(i+4) + ")" valueQueries[i] = "($1, now()+$2::INTERVAL, $3, $" + strconv.Itoa(i+4) + ")"
values[i+3] = instanceID values[i+3] = instanceID
} }
values[len(values)-1] = pq.StringArray(instanceIDs) values[len(values)-1] = instanceIDs
return h.lockStmt(strings.Join(valueQueries, ", "), len(values)), values return h.lockStmt(strings.Join(valueQueries, ", "), len(values)), values
} }

View File

@ -10,6 +10,7 @@ import (
"github.com/DATA-DOG/go-sqlmock" "github.com/DATA-DOG/go-sqlmock"
"github.com/zitadel/zitadel/internal/database"
z_errs "github.com/zitadel/zitadel/internal/errors" z_errs "github.com/zitadel/zitadel/internal/errors"
) )
@ -43,9 +44,9 @@ func TestStatementHandler_handleLock(t *testing.T) {
name: "lock fails", name: "lock fails",
want: want{ want: want{
expectations: []mockExpectation{ expectations: []mockExpectation{
expectLock(lockTable, workerName, 2, "instanceID"), expectLock(lockTable, workerName, 2*time.Second, "instanceID"),
expectLock(lockTable, workerName, 2, "instanceID"), expectLock(lockTable, workerName, 2*time.Second, "instanceID"),
expectLockErr(lockTable, workerName, 2, "instanceID", errLock), expectLockErr(lockTable, workerName, 2*time.Second, "instanceID", errLock),
}, },
}, },
args: args{ args: args{
@ -63,8 +64,8 @@ func TestStatementHandler_handleLock(t *testing.T) {
name: "success", name: "success",
want: want{ want: want{
expectations: []mockExpectation{ expectations: []mockExpectation{
expectLock(lockTable, workerName, 2, "instanceID"), expectLock(lockTable, workerName, 2*time.Second, "instanceID"),
expectLock(lockTable, workerName, 2, "instanceID"), expectLock(lockTable, workerName, 2*time.Second, "instanceID"),
}, },
}, },
args: args{ args: args{
@ -81,8 +82,8 @@ func TestStatementHandler_handleLock(t *testing.T) {
name: "success with multiple", name: "success with multiple",
want: want{ want: want{
expectations: []mockExpectation{ expectations: []mockExpectation{
expectLockMultipleInstances(lockTable, workerName, 2, "instanceID1", "instanceID2"), expectLockMultipleInstances(lockTable, workerName, 2*time.Second, "instanceID1", "instanceID2"),
expectLockMultipleInstances(lockTable, workerName, 2, "instanceID1", "instanceID2"), expectLockMultipleInstances(lockTable, workerName, 2*time.Second, "instanceID1", "instanceID2"),
}, },
}, },
args: args{ args: args{
@ -149,7 +150,7 @@ func TestStatementHandler_renewLock(t *testing.T) {
name: "lock fails", name: "lock fails",
want: want{ want: want{
expectations: []mockExpectation{ expectations: []mockExpectation{
expectLockErr(lockTable, workerName, 1, "instanceID", sql.ErrTxDone), expectLockErr(lockTable, workerName, 1*time.Second, "instanceID", sql.ErrTxDone),
}, },
isErr: func(err error) bool { isErr: func(err error) bool {
return errors.Is(err, sql.ErrTxDone) return errors.Is(err, sql.ErrTxDone)
@ -157,14 +158,14 @@ func TestStatementHandler_renewLock(t *testing.T) {
}, },
args: args{ args: args{
lockDuration: 1 * time.Second, lockDuration: 1 * time.Second,
instanceIDs: []string{"instanceID"}, instanceIDs: database.StringArray{"instanceID"},
}, },
}, },
{ {
name: "lock no rows", name: "lock no rows",
want: want{ want: want{
expectations: []mockExpectation{ expectations: []mockExpectation{
expectLockNoRows(lockTable, workerName, 2, "instanceID"), expectLockNoRows(lockTable, workerName, 2*time.Second, "instanceID"),
}, },
isErr: func(err error) bool { isErr: func(err error) bool {
return errors.Is(err, renewNoRowsAffectedErr) return errors.Is(err, renewNoRowsAffectedErr)
@ -172,14 +173,14 @@ func TestStatementHandler_renewLock(t *testing.T) {
}, },
args: args{ args: args{
lockDuration: 2 * time.Second, lockDuration: 2 * time.Second,
instanceIDs: []string{"instanceID"}, instanceIDs: database.StringArray{"instanceID"},
}, },
}, },
{ {
name: "success", name: "success",
want: want{ want: want{
expectations: []mockExpectation{ expectations: []mockExpectation{
expectLock(lockTable, workerName, 3, "instanceID"), expectLock(lockTable, workerName, 3*time.Second, "instanceID"),
}, },
isErr: func(err error) bool { isErr: func(err error) bool {
return errors.Is(err, nil) return errors.Is(err, nil)
@ -187,14 +188,14 @@ func TestStatementHandler_renewLock(t *testing.T) {
}, },
args: args{ args: args{
lockDuration: 3 * time.Second, lockDuration: 3 * time.Second,
instanceIDs: []string{"instanceID"}, instanceIDs: database.StringArray{"instanceID"},
}, },
}, },
{ {
name: "success with multiple", name: "success with multiple",
want: want{ want: want{
expectations: []mockExpectation{ expectations: []mockExpectation{
expectLockMultipleInstances(lockTable, workerName, 3, "instanceID1", "instanceID2"), expectLockMultipleInstances(lockTable, workerName, 3*time.Second, "instanceID1", "instanceID2"),
}, },
isErr: func(err error) bool { isErr: func(err error) bool {
return errors.Is(err, nil) return errors.Is(err, nil)

View File

@ -4,8 +4,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/lib/pq" "github.com/zitadel/zitadel/internal/database"
caos_errs "github.com/zitadel/zitadel/internal/errors" caos_errs "github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/handler" "github.com/zitadel/zitadel/internal/eventstore/handler"
@ -51,10 +50,13 @@ func NewCreateStatement(event eventstore.Event, values []handler.Column, opts ..
} }
} }
func NewUpsertStatement(event eventstore.Event, values []handler.Column, opts ...execOption) *handler.Statement { func NewUpsertStatement(event eventstore.Event, conflictCols []handler.Column, values []handler.Column, opts ...execOption) *handler.Statement {
cols, params, args := columnsToQuery(values) cols, params, args := columnsToQuery(values)
columnNames := strings.Join(cols, ", ")
valuesPlaceholder := strings.Join(params, ", ") conflictTarget := make([]string, len(conflictCols))
for i, col := range conflictCols {
conflictTarget[i] = col.Name
}
config := execConfig{ config := execConfig{
args: args, args: args,
@ -64,8 +66,14 @@ func NewUpsertStatement(event eventstore.Event, values []handler.Column, opts ..
config.err = handler.ErrNoValues config.err = handler.ErrNoValues
} }
updateCols, updateVals := getUpdateCols(cols, conflictTarget)
if len(updateCols) == 0 || len(updateVals) == 0 {
config.err = handler.ErrNoValues
}
q := func(config execConfig) string { q := func(config execConfig) string {
return "UPSERT INTO " + config.tableName + " (" + columnNames + ") VALUES (" + valuesPlaceholder + ")" return "INSERT INTO " + config.tableName + " (" + strings.Join(cols, ", ") + ") VALUES (" + strings.Join(params, ", ") + ")" +
" ON CONFLICT (" + strings.Join(conflictTarget, ", ") + ") DO UPDATE SET (" + strings.Join(updateCols, ", ") + ") = (" + strings.Join(updateVals, ", ") + ")"
} }
return &handler.Statement{ return &handler.Statement{
@ -77,15 +85,38 @@ func NewUpsertStatement(event eventstore.Event, values []handler.Column, opts ..
} }
} }
func getUpdateCols(cols, conflictTarget []string) (updateCols, updateVals []string) {
updateCols = make([]string, len(cols))
updateVals = make([]string, len(cols))
copy(updateCols, cols)
for i := len(updateCols) - 1; i >= 0; i-- {
updateVals[i] = "EXCLUDED." + updateCols[i]
for _, conflict := range conflictTarget {
if conflict == updateCols[i] {
copy(updateCols[i:], updateCols[i+1:])
updateCols[len(updateCols)-1] = ""
updateCols = updateCols[:len(updateCols)-1]
copy(updateVals[i:], updateVals[i+1:])
updateVals[len(updateVals)-1] = ""
updateVals = updateVals[:len(updateVals)-1]
break
}
}
}
return updateCols, updateVals
}
func NewUpdateStatement(event eventstore.Event, values []handler.Column, conditions []handler.Condition, opts ...execOption) *handler.Statement { func NewUpdateStatement(event eventstore.Event, values []handler.Column, conditions []handler.Condition, opts ...execOption) *handler.Statement {
cols, params, args := columnsToQuery(values) cols, params, args := columnsToQuery(values)
wheres, whereArgs := conditionsToWhere(conditions, len(params)) wheres, whereArgs := conditionsToWhere(conditions, len(params))
args = append(args, whereArgs...) args = append(args, whereArgs...)
columnNames := strings.Join(cols, ", ")
valuesPlaceholder := strings.Join(params, ", ")
wheresPlaceholders := strings.Join(wheres, " AND ")
config := execConfig{ config := execConfig{
args: args, args: args,
} }
@ -99,7 +130,7 @@ func NewUpdateStatement(event eventstore.Event, values []handler.Column, conditi
} }
q := func(config execConfig) string { q := func(config execConfig) string {
return "UPDATE " + config.tableName + " SET (" + columnNames + ") = (" + valuesPlaceholder + ") WHERE " + wheresPlaceholders return "UPDATE " + config.tableName + " SET (" + strings.Join(cols, ", ") + ") = (" + strings.Join(params, ", ") + ") WHERE " + strings.Join(wheres, " AND ")
} }
return &handler.Statement{ return &handler.Statement{
@ -171,9 +202,9 @@ func AddCreateStatement(columns []handler.Column, opts ...execOption) func(event
} }
} }
func AddUpsertStatement(values []handler.Column, opts ...execOption) func(eventstore.Event) Exec { func AddUpsertStatement(indexCols []handler.Column, values []handler.Column, opts ...execOption) func(eventstore.Event) Exec {
return func(event eventstore.Event) Exec { return func(event eventstore.Event) Exec {
return NewUpsertStatement(event, values, opts...).Execute return NewUpsertStatement(event, indexCols, values, opts...).Execute
} }
} }
@ -189,9 +220,9 @@ func AddDeleteStatement(conditions []handler.Condition, opts ...execOption) func
} }
} }
func AddCopyStatement(from, to []handler.Column, conditions []handler.Condition, opts ...execOption) func(eventstore.Event) Exec { func AddCopyStatement(conflict, from, to []handler.Column, conditions []handler.Condition, opts ...execOption) func(eventstore.Event) Exec {
return func(event eventstore.Event) Exec { return func(event eventstore.Event) Exec {
return NewCopyStatement(event, from, to, conditions, opts...).Execute return NewCopyStatement(event, conflict, from, to, conditions, opts...).Execute
} }
} }
@ -218,11 +249,9 @@ func NewArrayRemoveCol(column string, value interface{}) handler.Column {
func NewArrayIntersectCol(column string, value interface{}) handler.Column { func NewArrayIntersectCol(column string, value interface{}) handler.Column {
var arrayType string var arrayType string
switch value.(type) { switch value.(type) {
case pq.StringArray:
arrayType = "STRING" case []string, database.StringArray:
case pq.Int32Array, arrayType = "TEXT"
pq.Int64Array:
arrayType = "INT"
//TODO: handle more types if necessary //TODO: handle more types if necessary
} }
return handler.Column{ return handler.Column{
@ -234,25 +263,29 @@ func NewArrayIntersectCol(column string, value interface{}) handler.Column {
} }
} }
//NewCopyStatement creates a new upsert statement which updates a column from an existing row // NewCopyStatement creates a new upsert statement which updates a column from an existing row
// cols represent the columns which are objective to change. // cols represent the columns which are objective to change.
// if the value of a col is empty the data will be copied from the selected row // if the value of a col is empty the data will be copied from the selected row
// if the value of a col is not empty the data will be set by the static value // if the value of a col is not empty the data will be set by the static value
// conds represent the conditions for the selection subquery // conds represent the conditions for the selection subquery
func NewCopyStatement(event eventstore.Event, from, to []handler.Column, conds []handler.Condition, opts ...execOption) *handler.Statement { func NewCopyStatement(event eventstore.Event, conflictCols, from, to []handler.Column, conds []handler.Condition, opts ...execOption) *handler.Statement {
columnNames := make([]string, len(to)) columnNames := make([]string, len(to))
selectColumns := make([]string, len(from)) selectColumns := make([]string, len(from))
updateColumns := make([]string, len(columnNames))
argCounter := 0 argCounter := 0
args := []interface{}{} args := []interface{}{}
for i := range from { for i, col := range from {
columnNames[i] = to[i].Name columnNames[i] = to[i].Name
selectColumns[i] = from[i].Name selectColumns[i] = from[i].Name
if from[i].Value != nil { updateColumns[i] = "EXCLUDED." + col.Name
if col.Value != nil {
argCounter++ argCounter++
selectColumns[i] = "$" + strconv.Itoa(argCounter) selectColumns[i] = "$" + strconv.Itoa(argCounter)
args = append(args, from[i].Value) updateColumns[i] = selectColumns[i]
args = append(args, col.Value)
} }
} }
wheres := make([]string, len(conds)) wheres := make([]string, len(conds))
@ -262,6 +295,11 @@ func NewCopyStatement(event eventstore.Event, from, to []handler.Column, conds [
args = append(args, cond.Value) args = append(args, cond.Value)
} }
conflictTargets := make([]string, len(conflictCols))
for i, conflictCol := range conflictCols {
conflictTargets[i] = conflictCol.Name
}
config := execConfig{ config := execConfig{
args: args, args: args,
} }
@ -275,7 +313,7 @@ func NewCopyStatement(event eventstore.Event, from, to []handler.Column, conds [
} }
q := func(config execConfig) string { q := func(config execConfig) string {
return "UPSERT INTO " + return "INSERT INTO " +
config.tableName + config.tableName +
" (" + " (" +
strings.Join(columnNames, ", ") + strings.Join(columnNames, ", ") +
@ -283,7 +321,14 @@ func NewCopyStatement(event eventstore.Event, from, to []handler.Column, conds [
strings.Join(selectColumns, ", ") + strings.Join(selectColumns, ", ") +
" FROM " + " FROM " +
config.tableName + " AS copy_table WHERE " + config.tableName + " AS copy_table WHERE " +
strings.Join(wheres, " AND ") strings.Join(wheres, " AND ") +
" ON CONFLICT (" +
strings.Join(conflictTargets, ", ") +
") DO UPDATE SET (" +
strings.Join(columnNames, ", ") +
") = (" +
strings.Join(updateColumns, ", ") +
")"
} }
return &handler.Statement{ return &handler.Statement{

View File

@ -178,9 +178,10 @@ func TestNewCreateStatement(t *testing.T) {
func TestNewUpsertStatement(t *testing.T) { func TestNewUpsertStatement(t *testing.T) {
type args struct { type args struct {
table string table string
event *testEvent event *testEvent
values []handler.Column conflictCols []handler.Column
values []handler.Column
} }
type want struct { type want struct {
aggregateType eventstore.AggregateType aggregateType eventstore.AggregateType
@ -249,7 +250,7 @@ func TestNewUpsertStatement(t *testing.T) {
}, },
}, },
{ {
name: "correct", name: "no update cols",
args: args{ args: args{
table: "my_table", table: "my_table",
event: &testEvent{ event: &testEvent{
@ -257,6 +258,9 @@ func TestNewUpsertStatement(t *testing.T) {
sequence: 1, sequence: 1,
previousSequence: 0, previousSequence: 0,
}, },
conflictCols: []handler.Column{
handler.NewCol("col1", nil),
},
values: []handler.Column{ values: []handler.Column{
{ {
Name: "col1", Name: "col1",
@ -264,6 +268,42 @@ func TestNewUpsertStatement(t *testing.T) {
}, },
}, },
}, },
want: want{
table: "my_table",
aggregateType: "agg",
sequence: 1,
previousSequence: 1,
executer: &wantExecuter{
shouldExecute: false,
},
isErr: func(err error) bool {
return errors.Is(err, handler.ErrNoValues)
},
},
},
{
name: "correct",
args: args{
table: "my_table",
event: &testEvent{
aggregateType: "agg",
sequence: 1,
previousSequence: 0,
},
conflictCols: []handler.Column{
handler.NewCol("col1", nil),
},
values: []handler.Column{
{
Name: "col1",
Value: "val",
},
{
Name: "col2",
Value: "val",
},
},
},
want: want{ want: want{
table: "my_table", table: "my_table",
aggregateType: "agg", aggregateType: "agg",
@ -272,8 +312,8 @@ func TestNewUpsertStatement(t *testing.T) {
executer: &wantExecuter{ executer: &wantExecuter{
params: []params{ params: []params{
{ {
query: "UPSERT INTO my_table (col1) VALUES ($1)", query: "INSERT INTO my_table (col1, col2) VALUES ($1, $2) ON CONFLICT (col1) DO UPDATE SET (col2) = (EXCLUDED.col2)",
args: []interface{}{"val"}, args: []interface{}{"val", "val"},
}, },
}, },
shouldExecute: true, shouldExecute: true,
@ -287,7 +327,7 @@ func TestNewUpsertStatement(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
tt.want.executer.t = t tt.want.executer.t = t
stmt := NewUpsertStatement(tt.args.event, tt.args.values) stmt := NewUpsertStatement(tt.args.event, tt.args.conflictCols, tt.args.values)
err := stmt.Execute(tt.want.executer, tt.args.table) err := stmt.Execute(tt.want.executer, tt.args.table)
if !tt.want.isErr(err) { if !tt.want.isErr(err) {
@ -724,11 +764,18 @@ func TestNewMultiStatement(t *testing.T) {
}, },
}), }),
AddUpsertStatement( AddUpsertStatement(
[]handler.Column{
handler.NewCol("col1", nil),
},
[]handler.Column{ []handler.Column{
{ {
Name: "col1", Name: "col1",
Value: 1, Value: 1,
}, },
{
Name: "col2",
Value: 2,
},
}), }),
AddUpdateStatement( AddUpdateStatement(
[]handler.Column{ []handler.Column{
@ -761,8 +808,8 @@ func TestNewMultiStatement(t *testing.T) {
args: []interface{}{1}, args: []interface{}{1},
}, },
{ {
query: "UPSERT INTO my_table (col1) VALUES ($1)", query: "INSERT INTO my_table (col1, col2) VALUES ($1, $2) ON CONFLICT (col1) DO UPDATE SET (col2) = (EXCLUDED.col2)",
args: []interface{}{1}, args: []interface{}{1, 2},
}, },
{ {
query: "UPDATE my_table SET (col1) = ($1) WHERE (col1 = $2)", query: "UPDATE my_table SET (col1) = ($1) WHERE (col1 = $2)",
@ -799,11 +846,12 @@ func TestNewMultiStatement(t *testing.T) {
func TestNewCopyStatement(t *testing.T) { func TestNewCopyStatement(t *testing.T) {
type args struct { type args struct {
table string table string
event *testEvent event *testEvent
from []handler.Column conflictingCols []handler.Column
to []handler.Column from []handler.Column
conds []handler.Condition to []handler.Column
conds []handler.Condition
} }
type want struct { type want struct {
aggregateType eventstore.AggregateType aggregateType eventstore.AggregateType
@ -1004,7 +1052,7 @@ func TestNewCopyStatement(t *testing.T) {
executer: &wantExecuter{ executer: &wantExecuter{
params: []params{ params: []params{
{ {
query: "UPSERT INTO my_table (state, id, col_a, col_b) SELECT $1, id, col_a, col_b FROM my_table AS copy_table WHERE copy_table.id = $2 AND copy_table.state = $3", query: "INSERT INTO my_table (state, id, col_a, col_b) SELECT $1, id, col_a, col_b FROM my_table AS copy_table WHERE copy_table.id = $2 AND copy_table.state = $3 ON CONFLICT () DO UPDATE SET (state, id, col_a, col_b) = ($1, EXCLUDED.id, EXCLUDED.col_a, EXCLUDED.col_b)",
args: []interface{}{1, 2, 3}, args: []interface{}{1, 2, 3},
}, },
}, },
@ -1071,7 +1119,7 @@ func TestNewCopyStatement(t *testing.T) {
executer: &wantExecuter{ executer: &wantExecuter{
params: []params{ params: []params{
{ {
query: "UPSERT INTO my_table (state, id, col_c, col_d) SELECT $1, id, col_a, col_b FROM my_table AS copy_table WHERE copy_table.id = $2 AND copy_table.state = $3", query: "INSERT INTO my_table (state, id, col_c, col_d) SELECT $1, id, col_a, col_b FROM my_table AS copy_table WHERE copy_table.id = $2 AND copy_table.state = $3 ON CONFLICT () DO UPDATE SET (state, id, col_c, col_d) = ($1, EXCLUDED.id, EXCLUDED.col_a, EXCLUDED.col_b)",
args: []interface{}{1, 2, 3}, args: []interface{}{1, 2, 3},
}, },
}, },
@ -1086,7 +1134,7 @@ func TestNewCopyStatement(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
tt.want.executer.t = t tt.want.executer.t = t
stmt := NewCopyStatement(tt.args.event, tt.args.from, tt.args.to, tt.args.conds) stmt := NewCopyStatement(tt.args.event, tt.args.conflictingCols, tt.args.from, tt.args.to, tt.args.conds)
err := stmt.Execute(tt.want.executer, tt.args.table) err := stmt.Execute(tt.want.executer, tt.args.table)
if !tt.want.isErr(err) { if !tt.want.isErr(err) {

View File

@ -9,6 +9,8 @@ import (
"github.com/zitadel/logging" "github.com/zitadel/logging"
"github.com/zitadel/zitadel/cmd/initialise" "github.com/zitadel/zitadel/cmd/initialise"
"github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/database/cockroach"
) )
var ( var (
@ -45,13 +47,20 @@ func TestMain(m *testing.M) {
} }
func initDB(db *sql.DB) error { func initDB(db *sql.DB) error {
username := "zitadel" initialise.ReadStmts("cockroach")
database := "zitadel" config := new(database.Config)
err := initialise.Initialise(db, initialise.VerifyUser(username, ""), config.SetConnector(&cockroach.Config{
initialise.VerifyDatabase(database), User: cockroach.User{
initialise.VerifyGrant(database, username)) Username: "zitadel",
},
Database: "zitadel",
})
err := initialise.Init(db,
initialise.VerifyUser(config.Username(), ""),
initialise.VerifyDatabase(config.Database()),
initialise.VerifyGrant(config.Database(), config.Username()))
if err != nil { if err != nil {
return err return err
} }
return initialise.VerifyZitadel(db) return initialise.VerifyZitadel(db, *config)
} }

View File

@ -9,6 +9,7 @@ import (
"strings" "strings"
"github.com/cockroachdb/cockroach-go/v2/crdb" "github.com/cockroachdb/cockroach-go/v2/crdb"
"github.com/jackc/pgconn"
"github.com/lib/pq" "github.com/lib/pq"
"github.com/zitadel/logging" "github.com/zitadel/logging"
@ -30,7 +31,7 @@ const (
" SELECT MAX(event_sequence) seq, 1 join_me" + " SELECT MAX(event_sequence) seq, 1 join_me" +
" FROM eventstore.events" + " FROM eventstore.events" +
" WHERE aggregate_type = $2" + " WHERE aggregate_type = $2" +
" AND (CASE WHEN $9::STRING IS NULL THEN instance_id is null else instance_id = $9::STRING END)" + " AND (CASE WHEN $9::TEXT IS NULL THEN instance_id is null else instance_id = $9::TEXT END)" +
") AS agg_type " + ") AS agg_type " +
// combined with // combined with
"LEFT JOIN " + "LEFT JOIN " +
@ -39,7 +40,7 @@ const (
" SELECT event_sequence seq, resource_owner ro, 1 join_me" + " SELECT event_sequence seq, resource_owner ro, 1 join_me" +
" FROM eventstore.events" + " FROM eventstore.events" +
" WHERE aggregate_type = $2 AND aggregate_id = $3" + " WHERE aggregate_type = $2 AND aggregate_id = $3" +
" AND (CASE WHEN $9::STRING IS NULL THEN instance_id is null else instance_id = $9::STRING END)" + " AND (CASE WHEN $9::TEXT IS NULL THEN instance_id is null else instance_id = $9::TEXT END)" +
" ORDER BY event_sequence DESC" + " ORDER BY event_sequence DESC" +
" LIMIT 1" + " LIMIT 1" +
") AS agg USING(join_me)" + ") AS agg USING(join_me)" +
@ -69,9 +70,9 @@ const (
" $5::JSONB AS event_data," + " $5::JSONB AS event_data," +
" $6::VARCHAR AS editor_user," + " $6::VARCHAR AS editor_user," +
" $7::VARCHAR AS editor_service," + " $7::VARCHAR AS editor_service," +
" IFNULL((resource_owner), $8::VARCHAR) AS resource_owner," + " COALESCE((resource_owner), $8::VARCHAR) AS resource_owner," +
" $9::VARCHAR AS instance_id," + " $9::VARCHAR AS instance_id," +
" NEXTVAL(CONCAT('eventstore.', IF($9 <> '', CONCAT('i_', $9), 'system'), '_seq'))," + " NEXTVAL(CONCAT('eventstore.', (CASE WHEN $9 <> '' THEN CONCAT('i_', $9) ELSE 'system' END), '_seq'))," +
" aggregate_sequence AS previous_aggregate_sequence," + " aggregate_sequence AS previous_aggregate_sequence," +
" aggregate_type_sequence AS previous_aggregate_type_sequence " + " aggregate_type_sequence AS previous_aggregate_type_sequence " +
"FROM previous_data " + "FROM previous_data " +
@ -156,7 +157,7 @@ func (db *CRDB) Push(ctx context.Context, events []*repository.Event, uniqueCons
var instanceRegexp = regexp.MustCompile(`eventstore\.i_[0-9a-zA-Z]{1,}_seq`) var instanceRegexp = regexp.MustCompile(`eventstore\.i_[0-9a-zA-Z]{1,}_seq`)
func (db *CRDB) CreateInstance(ctx context.Context, instanceID string) error { func (db *CRDB) CreateInstance(ctx context.Context, instanceID string) error {
row := db.client.QueryRowContext(ctx, "SELECT CONCAT('eventstore.i_', $1, '_seq')", instanceID) row := db.client.QueryRowContext(ctx, "SELECT CONCAT('eventstore.i_', $1::TEXT, '_seq')", instanceID)
if row.Err() != nil { if row.Err() != nil {
return caos_errs.ThrowInvalidArgument(row.Err(), "SQL-7gtFA", "Errors.InvalidArgument") return caos_errs.ThrowInvalidArgument(row.Err(), "SQL-7gtFA", "Errors.InvalidArgument")
} }
@ -218,7 +219,7 @@ func (db *CRDB) Filter(ctx context.Context, searchQuery *repository.SearchQuery)
return events, nil return events, nil
} }
//LatestSequence returns the latest sequence found by the search query // LatestSequence returns the latest sequence found by the search query
func (db *CRDB) LatestSequence(ctx context.Context, searchQuery *repository.SearchQuery) (uint64, error) { func (db *CRDB) LatestSequence(ctx context.Context, searchQuery *repository.SearchQuery) (uint64, error) {
var seq Sequence var seq Sequence
err := query(ctx, db, searchQuery, &seq) err := query(ctx, db, searchQuery, &seq)
@ -228,7 +229,7 @@ func (db *CRDB) LatestSequence(ctx context.Context, searchQuery *repository.Sear
return uint64(seq), nil return uint64(seq), nil
} }
//InstanceIDs returns the instance ids found by the search query // InstanceIDs returns the instance ids found by the search query
func (db *CRDB) InstanceIDs(ctx context.Context, searchQuery *repository.SearchQuery) ([]string, error) { func (db *CRDB) InstanceIDs(ctx context.Context, searchQuery *repository.SearchQuery) ([]string, error) {
var ids []string var ids []string
err := query(ctx, db, searchQuery, &ids) err := query(ctx, db, searchQuery, &ids)
@ -331,7 +332,7 @@ var (
placeholder = regexp.MustCompile(`\?`) placeholder = regexp.MustCompile(`\?`)
) )
//placeholder replaces all "?" with postgres placeholders ($<NUMBER>) // placeholder replaces all "?" with postgres placeholders ($<NUMBER>)
func (db *CRDB) placeholder(query string) string { func (db *CRDB) placeholder(query string) string {
occurances := placeholder.FindAllStringIndex(query, -1) occurances := placeholder.FindAllStringIndex(query, -1)
if len(occurances) == 0 { if len(occurances) == 0 {
@ -355,5 +356,10 @@ func (db *CRDB) isUniqueViolationError(err error) bool {
return true return true
} }
} }
if pgxErr, ok := err.(*pgconn.PgError); ok {
if pgxErr.Code == "23505" {
return true
}
}
return false return false
} }

View File

@ -6,8 +6,7 @@ import (
"sync" "sync"
"testing" "testing"
"github.com/lib/pq" "github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/repository"
) )
@ -279,7 +278,7 @@ func TestCRDB_Push_OneAggregate(t *testing.T) {
uniqueCount int uniqueCount int
assetCount int assetCount int
aggType repository.AggregateType aggType repository.AggregateType
aggID []string aggID database.StringArray
} }
type res struct { type res struct {
wantErr bool wantErr bool
@ -430,7 +429,7 @@ func TestCRDB_Push_OneAggregate(t *testing.T) {
t.Errorf("CRDB.Push() error = %v, wantErr %v", err, tt.res.wantErr) t.Errorf("CRDB.Push() error = %v, wantErr %v", err, tt.res.wantErr)
} }
countEventRow := testCRDBClient.QueryRow("SELECT COUNT(*) FROM eventstore.events where aggregate_type = $1 AND aggregate_id = ANY($2)", tt.res.eventsRes.aggType, pq.Array(tt.res.eventsRes.aggID)) countEventRow := testCRDBClient.QueryRow("SELECT COUNT(*) FROM eventstore.events where aggregate_type = $1 AND aggregate_id = ANY($2)", tt.res.eventsRes.aggType, tt.res.eventsRes.aggID)
var eventCount int var eventCount int
err := countEventRow.Scan(&eventCount) err := countEventRow.Scan(&eventCount)
if err != nil { if err != nil {
@ -462,8 +461,8 @@ func TestCRDB_Push_MultipleAggregate(t *testing.T) {
} }
type eventsRes struct { type eventsRes struct {
pushedEventsCount int pushedEventsCount int
aggType []repository.AggregateType aggType database.StringArray
aggID []string aggID database.StringArray
} }
type res struct { type res struct {
wantErr bool wantErr bool
@ -487,7 +486,7 @@ func TestCRDB_Push_MultipleAggregate(t *testing.T) {
eventsRes: eventsRes{ eventsRes: eventsRes{
pushedEventsCount: 2, pushedEventsCount: 2,
aggID: []string{"100", "101"}, aggID: []string{"100", "101"},
aggType: []repository.AggregateType{repository.AggregateType(t.Name())}, aggType: database.StringArray{t.Name()},
}, },
}, },
}, },
@ -506,7 +505,7 @@ func TestCRDB_Push_MultipleAggregate(t *testing.T) {
eventsRes: eventsRes{ eventsRes: eventsRes{
pushedEventsCount: 4, pushedEventsCount: 4,
aggID: []string{"102", "103"}, aggID: []string{"102", "103"},
aggType: []repository.AggregateType{repository.AggregateType(t.Name())}, aggType: database.StringArray{t.Name()},
}, },
}, },
}, },
@ -533,7 +532,7 @@ func TestCRDB_Push_MultipleAggregate(t *testing.T) {
eventsRes: eventsRes{ eventsRes: eventsRes{
pushedEventsCount: 12, pushedEventsCount: 12,
aggID: []string{"106", "107", "108"}, aggID: []string{"106", "107", "108"},
aggType: []repository.AggregateType{repository.AggregateType(t.Name())}, aggType: database.StringArray{t.Name()},
}, },
}, },
}, },
@ -547,7 +546,7 @@ func TestCRDB_Push_MultipleAggregate(t *testing.T) {
t.Errorf("CRDB.Push() error = %v, wantErr %v", err, tt.res.wantErr) t.Errorf("CRDB.Push() error = %v, wantErr %v", err, tt.res.wantErr)
} }
countRow := testCRDBClient.QueryRow("SELECT COUNT(*) FROM eventstore.events where aggregate_type = ANY($1) AND aggregate_id = ANY($2)", pq.Array(tt.res.eventsRes.aggType), pq.Array(tt.res.eventsRes.aggID)) countRow := testCRDBClient.QueryRow("SELECT COUNT(*) FROM eventstore.events where aggregate_type = ANY($1) AND aggregate_id = ANY($2)", tt.res.eventsRes.aggType, tt.res.eventsRes.aggID)
var count int var count int
err := countRow.Scan(&count) err := countRow.Scan(&count)
if err != nil { if err != nil {
@ -645,8 +644,8 @@ func TestCRDB_Push_Parallel(t *testing.T) {
} }
type eventsRes struct { type eventsRes struct {
pushedEventsCount int pushedEventsCount int
aggTypes []repository.AggregateType aggTypes database.StringArray
aggIDs []string aggIDs database.StringArray
} }
type res struct { type res struct {
errCount int errCount int
@ -681,7 +680,7 @@ func TestCRDB_Push_Parallel(t *testing.T) {
eventsRes: eventsRes{ eventsRes: eventsRes{
aggIDs: []string{"200", "201", "202", "203"}, aggIDs: []string{"200", "201", "202", "203"},
pushedEventsCount: 9, pushedEventsCount: 9,
aggTypes: []repository.AggregateType{repository.AggregateType(t.Name())}, aggTypes: database.StringArray{t.Name()},
}, },
}, },
}, },
@ -718,7 +717,7 @@ func TestCRDB_Push_Parallel(t *testing.T) {
eventsRes: eventsRes{ eventsRes: eventsRes{
aggIDs: []string{"204", "205", "206"}, aggIDs: []string{"204", "205", "206"},
pushedEventsCount: 14, pushedEventsCount: 14,
aggTypes: []repository.AggregateType{repository.AggregateType(t.Name())}, aggTypes: database.StringArray{t.Name()},
}, },
}, },
}, },
@ -748,7 +747,7 @@ func TestCRDB_Push_Parallel(t *testing.T) {
eventsRes: eventsRes{ eventsRes: eventsRes{
aggIDs: []string{"207", "208"}, aggIDs: []string{"207", "208"},
pushedEventsCount: 11, pushedEventsCount: 11,
aggTypes: []repository.AggregateType{repository.AggregateType(t.Name())}, aggTypes: database.StringArray{t.Name()},
}, },
}, },
}, },
@ -781,7 +780,7 @@ func TestCRDB_Push_Parallel(t *testing.T) {
t.Errorf("CRDB.Push() error count = %d, wanted err count %d, errs: %v", len(errs), tt.res.errCount, errs) t.Errorf("CRDB.Push() error count = %d, wanted err count %d, errs: %v", len(errs), tt.res.errCount, errs)
} }
rows, err := testCRDBClient.Query("SELECT event_data FROM eventstore.events where aggregate_type = ANY($1) AND aggregate_id = ANY($2) order by event_sequence", pq.Array(tt.res.eventsRes.aggTypes), pq.Array(tt.res.eventsRes.aggIDs)) rows, err := testCRDBClient.Query("SELECT event_data FROM eventstore.events where aggregate_type = ANY($1) AND aggregate_id = ANY($2) order by event_sequence", tt.res.eventsRes.aggTypes, tt.res.eventsRes.aggIDs)
if err != nil { if err != nil {
t.Error("unable to query inserted rows: ", err) t.Error("unable to query inserted rows: ", err)
return return
@ -993,10 +992,10 @@ func TestCRDB_Push_ResourceOwner(t *testing.T) {
events []*repository.Event events []*repository.Event
} }
type res struct { type res struct {
resourceOwners []string resourceOwners database.StringArray
} }
type fields struct { type fields struct {
aggregateIDs []string aggregateIDs database.StringArray
aggregateType string aggregateType string
} }
tests := []struct { tests := []struct {
@ -1128,7 +1127,7 @@ func TestCRDB_Push_ResourceOwner(t *testing.T) {
} }
} }
rows, err := testCRDBClient.Query("SELECT resource_owner FROM eventstore.events WHERE aggregate_type = $1 AND aggregate_id = ANY($2) ORDER BY event_sequence", tt.fields.aggregateType, pq.Array(tt.fields.aggregateIDs)) rows, err := testCRDBClient.Query("SELECT resource_owner FROM eventstore.events WHERE aggregate_type = $1 AND aggregate_id = ANY($2) ORDER BY event_sequence", tt.fields.aggregateType, tt.fields.aggregateIDs)
if err != nil { if err != nil {
t.Error("unable to query inserted rows: ", err) t.Error("unable to query inserted rows: ", err)
return return

View File

@ -9,6 +9,8 @@ import (
"github.com/zitadel/logging" "github.com/zitadel/logging"
"github.com/zitadel/zitadel/cmd/initialise" "github.com/zitadel/zitadel/cmd/initialise"
"github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/database/cockroach"
) )
var ( var (
@ -42,15 +44,22 @@ func TestMain(m *testing.M) {
} }
func initDB(db *sql.DB) error { func initDB(db *sql.DB) error {
username := "zitadel" config := new(database.Config)
database := "zitadel" config.SetConnector(&cockroach.Config{User: cockroach.User{Username: "zitadel"}, Database: "zitadel"})
err := initialise.Initialise(db, initialise.VerifyUser(username, ""),
initialise.VerifyDatabase(database), if err := initialise.ReadStmts("cockroach"); err != nil {
initialise.VerifyGrant(database, username)) return err
}
err := initialise.Init(db,
initialise.VerifyUser(config.Username(), ""),
initialise.VerifyDatabase(config.Database()),
initialise.VerifyGrant(config.Database(), config.Username()))
if err != nil { if err != nil {
return err return err
} }
return initialise.VerifyZitadel(db)
return initialise.VerifyZitadel(db, *config)
} }
func fillUniqueData(unique_type, field, instanceID string) error { func fillUniqueData(unique_type, field, instanceID string) error {

View File

@ -8,7 +8,6 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/lib/pq"
"github.com/zitadel/logging" "github.com/zitadel/logging"
z_errors "github.com/zitadel/zitadel/internal/errors" z_errors "github.com/zitadel/zitadel/internal/errors"
@ -170,8 +169,6 @@ func prepareCondition(criteria querier, filters [][]*repository.Filter) (clause
for _, f := range filter { for _, f := range filter {
value := f.Value value := f.Value
switch value.(type) { switch value.(type) {
case []bool, []float64, []int64, []string, []repository.AggregateType, []repository.EventType, *[]bool, *[]float64, *[]int64, *[]string, *[]repository.AggregateType, *[]repository.EventType:
value = pq.Array(value)
case map[string]interface{}: case map[string]interface{}:
var err error var err error
value, err = json.Marshal(value) value, err = json.Marshal(value)

View File

@ -9,7 +9,6 @@ import (
"time" "time"
"github.com/DATA-DOG/go-sqlmock" "github.com/DATA-DOG/go-sqlmock"
"github.com/lib/pq"
"github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/repository"
@ -265,7 +264,7 @@ func Test_prepareCondition(t *testing.T) {
}, },
res: res{ res: res{
clause: " WHERE ( aggregate_type = ANY(?) )", clause: " WHERE ( aggregate_type = ANY(?) )",
values: []interface{}{pq.Array([]repository.AggregateType{"user", "org"})}, values: []interface{}{[]repository.AggregateType{"user", "org"}},
}, },
}, },
{ {
@ -281,7 +280,7 @@ func Test_prepareCondition(t *testing.T) {
}, },
res: res{ res: res{
clause: " WHERE ( aggregate_type = ANY(?) AND aggregate_id = ? AND event_type = ANY(?) )", clause: " WHERE ( aggregate_type = ANY(?) AND aggregate_id = ? AND event_type = ANY(?) )",
values: []interface{}{pq.Array([]repository.AggregateType{"user", "org"}), "1234", pq.Array([]repository.EventType{"user.created", "org.created"})}, values: []interface{}{[]repository.AggregateType{"user", "org"}, "1234", []repository.EventType{"user.created", "org.created"}},
}, },
}, },
} }

View File

@ -3,11 +3,12 @@ package eventstore
import ( import (
"database/sql" "database/sql"
"github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/repository"
) )
//SearchQueryBuilder represents the builder for your filter // SearchQueryBuilder represents the builder for your filter
// if invalid data are set the filter will fail // if invalid data are set the filter will fail
type SearchQueryBuilder struct { type SearchQueryBuilder struct {
columns repository.Columns columns repository.Columns
@ -79,51 +80,51 @@ func (builder *SearchQueryBuilder) Matches(event Event, existingLen int) (matche
return false return false
} }
//Columns defines which fields are set // Columns defines which fields are set
func (builder *SearchQueryBuilder) Columns(columns Columns) *SearchQueryBuilder { func (builder *SearchQueryBuilder) Columns(columns Columns) *SearchQueryBuilder {
builder.columns = repository.Columns(columns) builder.columns = repository.Columns(columns)
return builder return builder
} }
//Limit defines how many events are returned maximally. // Limit defines how many events are returned maximally.
func (builder *SearchQueryBuilder) Limit(limit uint64) *SearchQueryBuilder { func (builder *SearchQueryBuilder) Limit(limit uint64) *SearchQueryBuilder {
builder.limit = limit builder.limit = limit
return builder return builder
} }
//ResourceOwner defines the resource owner (org) of the events // ResourceOwner defines the resource owner (org) of the events
func (builder *SearchQueryBuilder) ResourceOwner(resourceOwner string) *SearchQueryBuilder { func (builder *SearchQueryBuilder) ResourceOwner(resourceOwner string) *SearchQueryBuilder {
builder.resourceOwner = resourceOwner builder.resourceOwner = resourceOwner
return builder return builder
} }
//InstanceID defines the instanceID (system) of the events // InstanceID defines the instanceID (system) of the events
func (builder *SearchQueryBuilder) InstanceID(instanceID string) *SearchQueryBuilder { func (builder *SearchQueryBuilder) InstanceID(instanceID string) *SearchQueryBuilder {
builder.instanceID = instanceID builder.instanceID = instanceID
return builder return builder
} }
//OrderDesc changes the sorting order of the returned events to descending // OrderDesc changes the sorting order of the returned events to descending
func (builder *SearchQueryBuilder) OrderDesc() *SearchQueryBuilder { func (builder *SearchQueryBuilder) OrderDesc() *SearchQueryBuilder {
builder.desc = true builder.desc = true
return builder return builder
} }
//OrderAsc changes the sorting order of the returned events to ascending // OrderAsc changes the sorting order of the returned events to ascending
func (builder *SearchQueryBuilder) OrderAsc() *SearchQueryBuilder { func (builder *SearchQueryBuilder) OrderAsc() *SearchQueryBuilder {
builder.desc = false builder.desc = false
return builder return builder
} }
//SetTx ensures that the eventstore library uses the existing transaction // SetTx ensures that the eventstore library uses the existing transaction
func (builder *SearchQueryBuilder) SetTx(tx *sql.Tx) *SearchQueryBuilder { func (builder *SearchQueryBuilder) SetTx(tx *sql.Tx) *SearchQueryBuilder {
builder.tx = tx builder.tx = tx
return builder return builder
} }
//AddQuery creates a new sub query. // AddQuery creates a new sub query.
//All fields in the sub query are AND-connected in the storage request. // All fields in the sub query are AND-connected in the storage request.
//Multiple sub queries are OR-connected in the storage request. // Multiple sub queries are OR-connected in the storage request.
func (builder *SearchQueryBuilder) AddQuery() *SearchQuery { func (builder *SearchQueryBuilder) AddQuery() *SearchQuery {
query := &SearchQuery{ query := &SearchQuery{
builder: builder, builder: builder,
@ -133,61 +134,61 @@ func (builder *SearchQueryBuilder) AddQuery() *SearchQuery {
return query return query
} }
//Or creates a new sub query on the search query builder // Or creates a new sub query on the search query builder
func (query SearchQuery) Or() *SearchQuery { func (query SearchQuery) Or() *SearchQuery {
return query.builder.AddQuery() return query.builder.AddQuery()
} }
//AggregateTypes filters for events with the given aggregate types // AggregateTypes filters for events with the given aggregate types
func (query *SearchQuery) AggregateTypes(types ...AggregateType) *SearchQuery { func (query *SearchQuery) AggregateTypes(types ...AggregateType) *SearchQuery {
query.aggregateTypes = types query.aggregateTypes = types
return query return query
} }
//SequenceGreater filters for events with sequence greater the requested sequence // SequenceGreater filters for events with sequence greater the requested sequence
func (query *SearchQuery) SequenceGreater(sequence uint64) *SearchQuery { func (query *SearchQuery) SequenceGreater(sequence uint64) *SearchQuery {
query.eventSequenceGreater = sequence query.eventSequenceGreater = sequence
return query return query
} }
//SequenceLess filters for events with sequence less the requested sequence // SequenceLess filters for events with sequence less the requested sequence
func (query *SearchQuery) SequenceLess(sequence uint64) *SearchQuery { func (query *SearchQuery) SequenceLess(sequence uint64) *SearchQuery {
query.eventSequenceLess = sequence query.eventSequenceLess = sequence
return query return query
} }
//AggregateIDs filters for events with the given aggregate id's // AggregateIDs filters for events with the given aggregate id's
func (query *SearchQuery) AggregateIDs(ids ...string) *SearchQuery { func (query *SearchQuery) AggregateIDs(ids ...string) *SearchQuery {
query.aggregateIDs = ids query.aggregateIDs = ids
return query return query
} }
//InstanceID filters for events with the given instanceID // InstanceID filters for events with the given instanceID
func (query *SearchQuery) InstanceID(instanceID string) *SearchQuery { func (query *SearchQuery) InstanceID(instanceID string) *SearchQuery {
query.instanceID = instanceID query.instanceID = instanceID
return query return query
} }
//ExcludedInstanceID filters for events not having the given instanceIDs // ExcludedInstanceID filters for events not having the given instanceIDs
func (query *SearchQuery) ExcludedInstanceID(instanceIDs ...string) *SearchQuery { func (query *SearchQuery) ExcludedInstanceID(instanceIDs ...string) *SearchQuery {
query.excludedInstanceIDs = instanceIDs query.excludedInstanceIDs = instanceIDs
return query return query
} }
//EventTypes filters for events with the given event types // EventTypes filters for events with the given event types
func (query *SearchQuery) EventTypes(types ...EventType) *SearchQuery { func (query *SearchQuery) EventTypes(types ...EventType) *SearchQuery {
query.eventTypes = types query.eventTypes = types
return query return query
} }
//EventData filters for events with the given event data. // EventData filters for events with the given event data.
//Use this call with care as it will be slower than the other filters. // Use this call with care as it will be slower than the other filters.
func (query *SearchQuery) EventData(data map[string]interface{}) *SearchQuery { func (query *SearchQuery) EventData(data map[string]interface{}) *SearchQuery {
query.eventData = data query.eventData = data
return query return query
} }
//Builder returns the SearchQueryBuilder of the sub query // Builder returns the SearchQueryBuilder of the sub query
func (query *SearchQuery) Builder() *SearchQueryBuilder { func (query *SearchQuery) Builder() *SearchQueryBuilder {
return query.builder return query.builder
} }
@ -262,7 +263,7 @@ func (query *SearchQuery) aggregateIDFilter() *repository.Filter {
if len(query.aggregateIDs) == 1 { if len(query.aggregateIDs) == 1 {
return repository.NewFilter(repository.FieldAggregateID, query.aggregateIDs[0], repository.OperationEquals) return repository.NewFilter(repository.FieldAggregateID, query.aggregateIDs[0], repository.OperationEquals)
} }
return repository.NewFilter(repository.FieldAggregateID, query.aggregateIDs, repository.OperationIn) return repository.NewFilter(repository.FieldAggregateID, database.StringArray(query.aggregateIDs), repository.OperationIn)
} }
func (query *SearchQuery) eventTypeFilter() *repository.Filter { func (query *SearchQuery) eventTypeFilter() *repository.Filter {
@ -272,9 +273,9 @@ func (query *SearchQuery) eventTypeFilter() *repository.Filter {
if len(query.eventTypes) == 1 { if len(query.eventTypes) == 1 {
return repository.NewFilter(repository.FieldEventType, repository.EventType(query.eventTypes[0]), repository.OperationEquals) return repository.NewFilter(repository.FieldEventType, repository.EventType(query.eventTypes[0]), repository.OperationEquals)
} }
eventTypes := make([]repository.EventType, len(query.eventTypes)) eventTypes := make(database.StringArray, len(query.eventTypes))
for i, eventType := range query.eventTypes { for i, eventType := range query.eventTypes {
eventTypes[i] = repository.EventType(eventType) eventTypes[i] = string(eventType)
} }
return repository.NewFilter(repository.FieldEventType, eventTypes, repository.OperationIn) return repository.NewFilter(repository.FieldEventType, eventTypes, repository.OperationIn)
} }
@ -286,9 +287,9 @@ func (query *SearchQuery) aggregateTypeFilter() *repository.Filter {
if len(query.aggregateTypes) == 1 { if len(query.aggregateTypes) == 1 {
return repository.NewFilter(repository.FieldAggregateType, repository.AggregateType(query.aggregateTypes[0]), repository.OperationEquals) return repository.NewFilter(repository.FieldAggregateType, repository.AggregateType(query.aggregateTypes[0]), repository.OperationEquals)
} }
aggregateTypes := make([]repository.AggregateType, len(query.aggregateTypes)) aggregateTypes := make(database.StringArray, len(query.aggregateTypes))
for i, aggregateType := range query.aggregateTypes { for i, aggregateType := range query.aggregateTypes {
aggregateTypes[i] = repository.AggregateType(aggregateType) aggregateTypes[i] = string(aggregateType)
} }
return repository.NewFilter(repository.FieldAggregateType, aggregateTypes, repository.OperationIn) return repository.NewFilter(repository.FieldAggregateType, aggregateTypes, repository.OperationIn)
} }
@ -326,7 +327,7 @@ func (query *SearchQuery) excludedInstanceIDFilter() *repository.Filter {
if len(query.excludedInstanceIDs) == 0 { if len(query.excludedInstanceIDs) == 0 {
return nil return nil
} }
return repository.NewFilter(repository.FieldInstanceID, query.excludedInstanceIDs, repository.OperationNotIn) return repository.NewFilter(repository.FieldInstanceID, database.StringArray(query.excludedInstanceIDs), repository.OperationNotIn)
} }
func (builder *SearchQueryBuilder) resourceOwnerFilter() *repository.Filter { func (builder *SearchQueryBuilder) resourceOwnerFilter() *repository.Filter {

View File

@ -5,6 +5,7 @@ import (
"reflect" "reflect"
"testing" "testing"
"github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/repository"
) )
@ -312,7 +313,7 @@ func TestSearchQuerybuilderBuild(t *testing.T) {
Limit: 0, Limit: 0,
Filters: [][]*repository.Filter{ Filters: [][]*repository.Filter{
{ {
repository.NewFilter(repository.FieldAggregateType, []repository.AggregateType{"user", "org"}, repository.OperationIn), repository.NewFilter(repository.FieldAggregateType, database.StringArray{"user", "org"}, repository.OperationIn),
}, },
}, },
}, },
@ -483,7 +484,7 @@ func TestSearchQuerybuilderBuild(t *testing.T) {
Filters: [][]*repository.Filter{ Filters: [][]*repository.Filter{
{ {
repository.NewFilter(repository.FieldAggregateType, repository.AggregateType("user"), repository.OperationEquals), repository.NewFilter(repository.FieldAggregateType, repository.AggregateType("user"), repository.OperationEquals),
repository.NewFilter(repository.FieldAggregateID, []string{"1234", "0815"}, repository.OperationIn), repository.NewFilter(repository.FieldAggregateID, database.StringArray{"1234", "0815"}, repository.OperationIn),
}, },
}, },
}, },
@ -561,7 +562,7 @@ func TestSearchQuerybuilderBuild(t *testing.T) {
Filters: [][]*repository.Filter{ Filters: [][]*repository.Filter{
{ {
repository.NewFilter(repository.FieldAggregateType, repository.AggregateType("user"), repository.OperationEquals), repository.NewFilter(repository.FieldAggregateType, repository.AggregateType("user"), repository.OperationEquals),
repository.NewFilter(repository.FieldEventType, []repository.EventType{"user.created", "user.changed"}, repository.OperationIn), repository.NewFilter(repository.FieldEventType, database.StringArray{"user.created", "user.changed"}, repository.OperationIn),
}, },
}, },
}, },
@ -740,10 +741,10 @@ func assertRepoQuery(t *testing.T, want, got *repository.SearchQuery) {
if !reflect.DeepEqual(got.Columns, want.Columns) { if !reflect.DeepEqual(got.Columns, want.Columns) {
t.Errorf("wrong columns in query: got: %v want: %v", got.Columns, want.Columns) t.Errorf("wrong columns in query: got: %v want: %v", got.Columns, want.Columns)
} }
if !reflect.DeepEqual(got.Desc, want.Desc) { if got.Desc != want.Desc {
t.Errorf("wrong desc in query: got: %v want: %v", got.Desc, want.Desc) t.Errorf("wrong desc in query: got: %v want: %v", got.Desc, want.Desc)
} }
if !reflect.DeepEqual(got.Limit, want.Limit) { if got.Limit != want.Limit {
t.Errorf("wrong limit in query: got: %v want: %v", got.Limit, want.Limit) t.Errorf("wrong limit in query: got: %v want: %v", got.Limit, want.Limit)
} }

View File

@ -2,8 +2,6 @@ package sql
import ( import (
"database/sql" "database/sql"
_ "github.com/lib/pq"
) )
func Start(client *sql.DB) *SQL { func Start(client *sql.DB) *SQL {

View File

@ -7,7 +7,6 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/lib/pq"
"github.com/zitadel/logging" "github.com/zitadel/logging"
z_errors "github.com/zitadel/zitadel/internal/errors" z_errors "github.com/zitadel/zitadel/internal/errors"
@ -72,10 +71,6 @@ func prepareCondition(filters [][]*es_models.Filter) (clause string, values []in
subClauses := make([]string, 0, len(filter)) subClauses := make([]string, 0, len(filter))
for _, f := range filter { for _, f := range filter {
value := f.GetValue() value := f.GetValue()
switch value.(type) {
case []bool, []float64, []int64, []string, []es_models.AggregateType, []es_models.EventType, *[]bool, *[]float64, *[]int64, *[]string, *[]es_models.AggregateType, *[]es_models.EventType:
value = pq.Array(value)
}
subClauses = append(subClauses, getCondition(f)) subClauses = append(subClauses, getCondition(f))
if subClauses[len(subClauses)-1] == "" { if subClauses[len(subClauses)-1] == "" {

View File

@ -6,8 +6,6 @@ import (
"testing" "testing"
"time" "time"
"github.com/lib/pq"
"github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/errors"
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
) )
@ -365,7 +363,7 @@ func Test_prepareCondition(t *testing.T) {
}, },
res: res{ res: res{
clause: " WHERE ( aggregate_type = ANY(?) )", clause: " WHERE ( aggregate_type = ANY(?) )",
values: []interface{}{pq.Array([]es_models.AggregateType{"user", "org"})}, values: []interface{}{[]es_models.AggregateType{"user", "org"}},
}, },
}, },
{ {
@ -381,7 +379,7 @@ func Test_prepareCondition(t *testing.T) {
}, },
res: res{ res: res{
clause: " WHERE ( aggregate_type = ANY(?) AND aggregate_id = ? AND event_type = ANY(?) )", clause: " WHERE ( aggregate_type = ANY(?) AND aggregate_id = ? AND event_type = ANY(?) )",
values: []interface{}{pq.Array([]es_models.AggregateType{"user", "org"}), "1234", pq.Array([]es_models.EventType{"user.created", "org.created"})}, values: []interface{}{[]es_models.AggregateType{"user", "org"}, "1234", []es_models.EventType{"user.created", "org.created"}},
}, },
}, },
} }

View File

@ -3,9 +3,6 @@ package sql
import ( import (
"context" "context"
"database/sql" "database/sql"
//sql import
_ "github.com/lib/pq"
) )
type SQL struct { type SQL struct {

View File

@ -31,7 +31,7 @@ func Renew(dbClient *sql.DB, lockTable, lockerID, viewModel, instanceID string,
return crdb.ExecuteTx(context.Background(), dbClient, nil, func(tx *sql.Tx) error { return crdb.ExecuteTx(context.Background(), dbClient, nil, func(tx *sql.Tx) error {
insert := fmt.Sprintf(insertStmtFormat, lockTable) insert := fmt.Sprintf(insertStmtFormat, lockTable)
result, err := tx.Exec(insert, result, err := tx.Exec(insert,
lockerID, waitTime.Milliseconds()/millisecondsAsSeconds, viewModel, instanceID) lockerID, waitTime, viewModel, instanceID)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()

View File

@ -38,7 +38,12 @@ func ReduceEvent(handler Handler, event *models.Event) {
if err != nil { if err != nil {
handler.Subscription().Unsubscribe() handler.Subscription().Unsubscribe()
logging.WithFields("cause", err, "stack", string(debug.Stack())).Error("reduce panicked") logging.WithFields(
"cause", err,
"stack", string(debug.Stack()),
"sequence", event.Sequence,
"instnace", event.InstanceID,
).Error("reduce panicked")
} }
}() }()
currentSequence, err := handler.CurrentSequence(event.InstanceID) currentSequence, err := handler.CurrentSequence(event.InstanceID)

View File

@ -75,7 +75,10 @@ func (s *spooledHandler) load(workerID string) {
err := recover() err := recover()
if err != nil { if err != nil {
logging.WithFields("cause", err, "stack", string(debug.Stack())).Error("reduce panicked") logging.WithFields(
"cause", err,
"stack", string(debug.Stack()),
).Error("reduce panicked")
} }
}() }()
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
@ -167,7 +170,7 @@ func (s *spooledHandler) query(ctx context.Context, instanceIDs ...string) ([]*m
return s.eventstore.FilterEvents(ctx, query) return s.eventstore.FilterEvents(ctx, query)
} }
//lock ensures the lock on the database. // lock ensures the lock on the database.
// the returned channel will be closed if ctx is done or an error occured durring lock // the returned channel will be closed if ctx is done or an error occured durring lock
func (s *spooledHandler) lock(ctx context.Context, errs chan<- error, workerID string) chan bool { func (s *spooledHandler) lock(ctx context.Context, errs chan<- error, workerID string) chan bool {
renewTimer := time.After(0) renewTimer := time.After(0)

View File

@ -5,11 +5,11 @@ import (
"time" "time"
"github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/instance"
"github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/org"
"github.com/lib/pq"
"github.com/zitadel/logging" "github.com/zitadel/logging"
caos_errs "github.com/zitadel/zitadel/internal/errors" caos_errs "github.com/zitadel/zitadel/internal/errors"
@ -36,18 +36,18 @@ type IDPConfigView struct {
IDPProviderType int32 `json:"-" gorm:"column:idp_provider_type"` IDPProviderType int32 `json:"-" gorm:"column:idp_provider_type"`
AutoRegister bool `json:"autoRegister" gorm:"column:auto_register"` AutoRegister bool `json:"autoRegister" gorm:"column:auto_register"`
IsOIDC bool `json:"-" gorm:"column:is_oidc"` IsOIDC bool `json:"-" gorm:"column:is_oidc"`
OIDCClientID string `json:"clientId" gorm:"column:oidc_client_id"` OIDCClientID string `json:"clientId" gorm:"column:oidc_client_id"`
OIDCClientSecret *crypto.CryptoValue `json:"clientSecret" gorm:"column:oidc_client_secret"` OIDCClientSecret *crypto.CryptoValue `json:"clientSecret" gorm:"column:oidc_client_secret"`
OIDCIssuer string `json:"issuer" gorm:"column:oidc_issuer"` OIDCIssuer string `json:"issuer" gorm:"column:oidc_issuer"`
OIDCScopes pq.StringArray `json:"scopes" gorm:"column:oidc_scopes"` OIDCScopes database.StringArray `json:"scopes" gorm:"column:oidc_scopes"`
OIDCIDPDisplayNameMapping int32 `json:"idpDisplayNameMapping" gorm:"column:oidc_idp_display_name_mapping"` OIDCIDPDisplayNameMapping int32 `json:"idpDisplayNameMapping" gorm:"column:oidc_idp_display_name_mapping"`
OIDCUsernameMapping int32 `json:"usernameMapping" gorm:"column:oidc_idp_username_mapping"` OIDCUsernameMapping int32 `json:"usernameMapping" gorm:"column:oidc_idp_username_mapping"`
OAuthAuthorizationEndpoint string `json:"authorizationEndpoint" gorm:"column:oauth_authorization_endpoint"` OAuthAuthorizationEndpoint string `json:"authorizationEndpoint" gorm:"column:oauth_authorization_endpoint"`
OAuthTokenEndpoint string `json:"tokenEndpoint" gorm:"column:oauth_token_endpoint"` OAuthTokenEndpoint string `json:"tokenEndpoint" gorm:"column:oauth_token_endpoint"`
JWTEndpoint string `json:"jwtEndpoint" gorm:"jwt_endpoint"` JWTEndpoint string `json:"jwtEndpoint" gorm:"jwt_endpoint"`
JWTKeysEndpoint string `json:"keysEndpoint" gorm:"jwt_keys_endpoint"` JWTKeysEndpoint string `json:"keysEndpoint" gorm:"jwt_keys_endpoint"`
JWTHeaderName string `json:"headerName" gorm:"jwt_header_name"` JWTHeaderName string `json:"headerName" gorm:"jwt_header_name"`
Sequence uint64 `json:"-" gorm:"column:sequence"` Sequence uint64 `json:"-" gorm:"column:sequence"`
InstanceID string `json:"instanceID" gorm:"column:instance_id;primary_key"` InstanceID string `json:"instanceID" gorm:"column:instance_id;primary_key"`

View File

@ -51,7 +51,7 @@ func SonyFlakeGenerator() Generator {
} }
// the following is a copy of sonyflake (https://github.com/sony/sonyflake/blob/master/sonyflake.go) // the following is a copy of sonyflake (https://github.com/sony/sonyflake/blob/master/sonyflake.go)
//with the change of using the "POD-IP" if no private ip is found // with the change of using the "POD-IP" if no private ip is found
func privateIPv4() (net.IP, error) { func privateIPv4() (net.IP, error) {
as, err := net.InterfaceAddrs() as, err := net.InterfaceAddrs()
if err != nil { if err != nil {
@ -88,7 +88,7 @@ func isPrivateIPv4(ip net.IP) bool {
func machineID() (uint16, error) { func machineID() (uint16, error) {
if GeneratorConfig == nil { if GeneratorConfig == nil {
return 0, errors.New("Cannot create a unique ID for the machine, generator has not been configured.") return 0, errors.New("cannot create a unique id for the machine, generator has not been configured")
} }
errors := []string{} errors := []string{}

View File

@ -4,10 +4,10 @@ import (
"encoding/json" "encoding/json"
"time" "time"
"github.com/lib/pq"
"github.com/zitadel/logging" "github.com/zitadel/logging"
http_util "github.com/zitadel/zitadel/internal/api/http" http_util "github.com/zitadel/zitadel/internal/api/http"
"github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/domain"
caos_errs "github.com/zitadel/zitadel/internal/errors" caos_errs "github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore"
@ -37,30 +37,30 @@ type ApplicationView struct {
HasProjectCheck bool `json:"hasProjectCheck" gorm:"column:has_project_check"` HasProjectCheck bool `json:"hasProjectCheck" gorm:"column:has_project_check"`
PrivateLabelingSetting domain.PrivateLabelingSetting `json:"privateLabelingSetting" gorm:"column:private_labeling_setting"` PrivateLabelingSetting domain.PrivateLabelingSetting `json:"privateLabelingSetting" gorm:"column:private_labeling_setting"`
IsOIDC bool `json:"-" gorm:"column:is_oidc"` IsOIDC bool `json:"-" gorm:"column:is_oidc"`
OIDCVersion int32 `json:"oidcVersion" gorm:"column:oidc_version"` OIDCVersion int32 `json:"oidcVersion" gorm:"column:oidc_version"`
OIDCClientID string `json:"clientId" gorm:"column:oidc_client_id"` OIDCClientID string `json:"clientId" gorm:"column:oidc_client_id"`
OIDCRedirectUris pq.StringArray `json:"redirectUris" gorm:"column:oidc_redirect_uris"` OIDCRedirectUris database.StringArray `json:"redirectUris" gorm:"column:oidc_redirect_uris"`
OIDCResponseTypes pq.Int64Array `json:"responseTypes" gorm:"column:oidc_response_types"` OIDCResponseTypes database.EnumArray[domain.OIDCResponseType] `json:"responseTypes" gorm:"column:oidc_response_types"`
OIDCGrantTypes pq.Int64Array `json:"grantTypes" gorm:"column:oidc_grant_types"` OIDCGrantTypes database.EnumArray[domain.OIDCGrantType] `json:"grantTypes" gorm:"column:oidc_grant_types"`
OIDCApplicationType int32 `json:"applicationType" gorm:"column:oidc_application_type"` OIDCApplicationType int32 `json:"applicationType" gorm:"column:oidc_application_type"`
OIDCAuthMethodType int32 `json:"authMethodType" gorm:"column:oidc_auth_method_type"` OIDCAuthMethodType int32 `json:"authMethodType" gorm:"column:oidc_auth_method_type"`
OIDCPostLogoutRedirectUris pq.StringArray `json:"postLogoutRedirectUris" gorm:"column:oidc_post_logout_redirect_uris"` OIDCPostLogoutRedirectUris database.StringArray `json:"postLogoutRedirectUris" gorm:"column:oidc_post_logout_redirect_uris"`
NoneCompliant bool `json:"-" gorm:"column:none_compliant"` NoneCompliant bool `json:"-" gorm:"column:none_compliant"`
ComplianceProblems pq.StringArray `json:"-" gorm:"column:compliance_problems"` ComplianceProblems database.StringArray `json:"-" gorm:"column:compliance_problems"`
DevMode bool `json:"devMode" gorm:"column:dev_mode"` DevMode bool `json:"devMode" gorm:"column:dev_mode"`
OriginAllowList pq.StringArray `json:"-" gorm:"column:origin_allow_list"` OriginAllowList database.StringArray `json:"-" gorm:"column:origin_allow_list"`
AdditionalOrigins pq.StringArray `json:"additionalOrigins" gorm:"column:additional_origins"` AdditionalOrigins database.StringArray `json:"additionalOrigins" gorm:"column:additional_origins"`
AccessTokenType int32 `json:"accessTokenType" gorm:"column:access_token_type"` AccessTokenType int32 `json:"accessTokenType" gorm:"column:access_token_type"`
AccessTokenRoleAssertion bool `json:"accessTokenRoleAssertion" gorm:"column:access_token_role_assertion"` AccessTokenRoleAssertion bool `json:"accessTokenRoleAssertion" gorm:"column:access_token_role_assertion"`
IDTokenRoleAssertion bool `json:"idTokenRoleAssertion" gorm:"column:id_token_role_assertion"` IDTokenRoleAssertion bool `json:"idTokenRoleAssertion" gorm:"column:id_token_role_assertion"`
IDTokenUserinfoAssertion bool `json:"idTokenUserinfoAssertion" gorm:"column:id_token_userinfo_assertion"` IDTokenUserinfoAssertion bool `json:"idTokenUserinfoAssertion" gorm:"column:id_token_userinfo_assertion"`
ClockSkew time.Duration `json:"clockSkew" gorm:"column:clock_skew"` ClockSkew time.Duration `json:"clockSkew" gorm:"column:clock_skew"`
Sequence uint64 `json:"-" gorm:"sequence"` Sequence uint64 `json:"-" gorm:"sequence"`
} }
func OIDCResponseTypesToModel(oidctypes []int64) []model.OIDCResponseType { func OIDCResponseTypesToModel(oidctypes []domain.OIDCResponseType) []model.OIDCResponseType {
result := make([]model.OIDCResponseType, len(oidctypes)) result := make([]model.OIDCResponseType, len(oidctypes))
for i, t := range oidctypes { for i, t := range oidctypes {
result[i] = model.OIDCResponseType(t) result[i] = model.OIDCResponseType(t)
@ -68,7 +68,7 @@ func OIDCResponseTypesToModel(oidctypes []int64) []model.OIDCResponseType {
return result return result
} }
func OIDCGrantTypesToModel(granttypes []int64) []model.OIDCGrantType { func OIDCGrantTypesToModel(granttypes []domain.OIDCGrantType) []model.OIDCGrantType {
result := make([]model.OIDCGrantType, len(granttypes)) result := make([]model.OIDCGrantType, len(granttypes))
for i, t := range granttypes { for i, t := range granttypes {
result[i] = model.OIDCGrantType(t) result[i] = model.OIDCGrantType(t)
@ -169,7 +169,7 @@ func (a *ApplicationView) SetData(event *models.Event) error {
} }
func (a *ApplicationView) setOriginAllowList() error { func (a *ApplicationView) setOriginAllowList() error {
allowList := make([]string, 0) allowList := make(database.StringArray, 0)
for _, redirect := range a.OIDCRedirectUris { for _, redirect := range a.OIDCRedirectUris {
origin, err := http_util.GetOriginFromURLString(redirect) origin, err := http_util.GetOriginFromURLString(redirect)
if err != nil { if err != nil {

View File

@ -4,9 +4,9 @@ import (
"encoding/json" "encoding/json"
"time" "time"
"github.com/lib/pq"
"github.com/zitadel/logging" "github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/database"
caos_errs "github.com/zitadel/zitadel/internal/errors" caos_errs "github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/eventstore/v1/models"
@ -24,18 +24,18 @@ const (
) )
type ProjectGrantView struct { type ProjectGrantView struct {
GrantID string `json:"-" gorm:"column:grant_id;primary_key"` GrantID string `json:"-" gorm:"column:grant_id;primary_key"`
ProjectID string `json:"-" gorm:"column:project_id"` ProjectID string `json:"-" gorm:"column:project_id"`
OrgID string `json:"-" gorm:"column:org_id"` OrgID string `json:"-" gorm:"column:org_id"`
Name string `json:"name" gorm:"column:project_name"` Name string `json:"name" gorm:"column:project_name"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"` CreationDate time.Time `json:"-" gorm:"column:creation_date"`
ChangeDate time.Time `json:"-" gorm:"column:change_date"` ChangeDate time.Time `json:"-" gorm:"column:change_date"`
State int32 `json:"-" gorm:"column:project_state"` State int32 `json:"-" gorm:"column:project_state"`
ResourceOwner string `json:"-" gorm:"column:resource_owner"` ResourceOwner string `json:"-" gorm:"column:resource_owner"`
ResourceOwnerName string `json:"-" gorm:"column:resource_owner_name"` ResourceOwnerName string `json:"-" gorm:"column:resource_owner_name"`
OrgName string `json:"-" gorm:"column:org_name"` OrgName string `json:"-" gorm:"column:org_name"`
Sequence uint64 `json:"-" gorm:"column:sequence"` Sequence uint64 `json:"-" gorm:"column:sequence"`
GrantedRoleKeys pq.StringArray `json:"-" gorm:"column:granted_role_keys"` GrantedRoleKeys database.StringArray `json:"-" gorm:"column:granted_role_keys"`
} }
type ProjectGrant struct { type ProjectGrant struct {

View File

@ -4,9 +4,9 @@ import (
"encoding/json" "encoding/json"
"time" "time"
"github.com/lib/pq"
"github.com/zitadel/logging" "github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/database"
caos_errs "github.com/zitadel/zitadel/internal/errors" caos_errs "github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/eventstore/v1/models"
@ -24,19 +24,19 @@ const (
) )
type ProjectGrantMemberView struct { type ProjectGrantMemberView struct {
UserID string `json:"userId" gorm:"column:user_id;primary_key"` UserID string `json:"userId" gorm:"column:user_id;primary_key"`
GrantID string `json:"grantId" gorm:"column:grant_id;primary_key"` GrantID string `json:"grantId" gorm:"column:grant_id;primary_key"`
ProjectID string `json:"-" gorm:"column:project_id"` ProjectID string `json:"-" gorm:"column:project_id"`
UserName string `json:"-" gorm:"column:user_name"` UserName string `json:"-" gorm:"column:user_name"`
Email string `json:"-" gorm:"column:email_address"` Email string `json:"-" gorm:"column:email_address"`
FirstName string `json:"-" gorm:"column:first_name"` FirstName string `json:"-" gorm:"column:first_name"`
LastName string `json:"-" gorm:"column:last_name"` LastName string `json:"-" gorm:"column:last_name"`
DisplayName string `json:"-" gorm:"column:display_name"` DisplayName string `json:"-" gorm:"column:display_name"`
Roles pq.StringArray `json:"roles" gorm:"column:roles"` Roles database.StringArray `json:"roles" gorm:"column:roles"`
Sequence uint64 `json:"-" gorm:"column:sequence"` Sequence uint64 `json:"-" gorm:"column:sequence"`
PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"` PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"`
AvatarKey string `json:"-" gorm:"column:avatar_key"` AvatarKey string `json:"-" gorm:"column:avatar_key"`
UserResourceOwner string `json:"-" gorm:"column:user_resource_owner"` UserResourceOwner string `json:"-" gorm:"column:user_resource_owner"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"` CreationDate time.Time `json:"-" gorm:"column:creation_date"`
ChangeDate time.Time `json:"-" gorm:"column:change_date"` ChangeDate time.Time `json:"-" gorm:"column:change_date"`

View File

@ -5,8 +5,6 @@ import (
"reflect" "reflect"
"testing" "testing"
"github.com/lib/pq"
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
es_model "github.com/zitadel/zitadel/internal/project/repository/eventsourcing/model" es_model "github.com/zitadel/zitadel/internal/project/repository/eventsourcing/model"
"github.com/zitadel/zitadel/internal/repository/project" "github.com/zitadel/zitadel/internal/repository/project"
@ -30,18 +28,18 @@ func TestGrantedProjectMemberAppendEvent(t *testing.T) {
{ {
name: "append added member event", name: "append added member event",
args: args{ args: args{
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.GrantMemberAddedType), ResourceOwner: "OrgID", Data: mockProjectGrantMemberData(&es_model.ProjectGrantMember{GrantID: "ProjectGrantID", UserID: "UserID", Roles: pq.StringArray{"Role"}})}, event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.GrantMemberAddedType), ResourceOwner: "OrgID", Data: mockProjectGrantMemberData(&es_model.ProjectGrantMember{GrantID: "ProjectGrantID", UserID: "UserID", Roles: []string{"Role"}})},
member: &ProjectGrantMemberView{}, member: &ProjectGrantMemberView{},
}, },
result: &ProjectGrantMemberView{ProjectID: "AggregateID", UserID: "UserID", GrantID: "ProjectGrantID", Roles: pq.StringArray{"Role"}}, result: &ProjectGrantMemberView{ProjectID: "AggregateID", UserID: "UserID", GrantID: "ProjectGrantID", Roles: []string{"Role"}},
}, },
{ {
name: "append changed member event", name: "append changed member event",
args: args{ args: args{
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.GrantMemberAddedType), ResourceOwner: "OrgID", Data: mockProjectGrantMemberData(&es_model.ProjectGrantMember{GrantID: "ProjectGrantID", Roles: pq.StringArray{"RoleChanged"}})}, event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.GrantMemberAddedType), ResourceOwner: "OrgID", Data: mockProjectGrantMemberData(&es_model.ProjectGrantMember{GrantID: "ProjectGrantID", Roles: []string{"RoleChanged"}})},
member: &ProjectGrantMemberView{ProjectID: "AggregateID", UserID: "UserID", GrantID: "ProjectGrantID", Roles: pq.StringArray{"Role"}}, member: &ProjectGrantMemberView{ProjectID: "AggregateID", UserID: "UserID", GrantID: "ProjectGrantID", Roles: []string{"Role"}},
}, },
result: &ProjectGrantMemberView{ProjectID: "AggregateID", UserID: "UserID", GrantID: "ProjectGrantID", Roles: pq.StringArray{"RoleChanged"}}, result: &ProjectGrantMemberView{ProjectID: "AggregateID", UserID: "UserID", GrantID: "ProjectGrantID", Roles: []string{"RoleChanged"}},
}, },
} }
for _, tt := range tests { for _, tt := range tests {

View File

@ -5,8 +5,6 @@ import (
"reflect" "reflect"
"testing" "testing"
"github.com/lib/pq"
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
"github.com/zitadel/zitadel/internal/project/model" "github.com/zitadel/zitadel/internal/project/model"
es_model "github.com/zitadel/zitadel/internal/project/repository/eventsourcing/model" es_model "github.com/zitadel/zitadel/internal/project/repository/eventsourcing/model"
@ -36,34 +34,34 @@ func TestProjectGrantAppendEvent(t *testing.T) {
{ {
name: "append added project grant event", name: "append added project grant event",
args: args{ args: args{
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.GrantAddedType), ResourceOwner: "GrantedOrgID", Data: mockProjectGrantData(&es_model.ProjectGrant{GrantID: "ProjectGrantID", GrantedOrgID: "GrantedOrgID", RoleKeys: pq.StringArray{"Role"}})}, event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.GrantAddedType), ResourceOwner: "GrantedOrgID", Data: mockProjectGrantData(&es_model.ProjectGrant{GrantID: "ProjectGrantID", GrantedOrgID: "GrantedOrgID", RoleKeys: []string{"Role"}})},
project: &ProjectGrantView{}, project: &ProjectGrantView{},
}, },
result: &ProjectGrantView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", OrgID: "GrantedOrgID", State: int32(model.ProjectStateActive), GrantedRoleKeys: pq.StringArray{"Role"}}, result: &ProjectGrantView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", OrgID: "GrantedOrgID", State: int32(model.ProjectStateActive), GrantedRoleKeys: []string{"Role"}},
}, },
{ {
name: "append change project grant event", name: "append change project grant event",
args: args{ args: args{
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.GrantChangedType), ResourceOwner: "GrantedOrgID", Data: mockProjectGrantData(&es_model.ProjectGrant{GrantID: "ProjectGrantID", RoleKeys: pq.StringArray{"RoleChanged"}})}, event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.GrantChangedType), ResourceOwner: "GrantedOrgID", Data: mockProjectGrantData(&es_model.ProjectGrant{GrantID: "ProjectGrantID", RoleKeys: []string{"RoleChanged"}})},
project: &ProjectGrantView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", OrgID: "GrantedOrgID", State: int32(model.ProjectStateActive), GrantedRoleKeys: pq.StringArray{"Role"}}, project: &ProjectGrantView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", OrgID: "GrantedOrgID", State: int32(model.ProjectStateActive), GrantedRoleKeys: []string{"Role"}},
}, },
result: &ProjectGrantView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", OrgID: "GrantedOrgID", State: int32(model.ProjectStateActive), GrantedRoleKeys: pq.StringArray{"RoleChanged"}}, result: &ProjectGrantView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", OrgID: "GrantedOrgID", State: int32(model.ProjectStateActive), GrantedRoleKeys: []string{"RoleChanged"}},
}, },
{ {
name: "append deactivate project grant event", name: "append deactivate project grant event",
args: args{ args: args{
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.GrantDeactivatedType), ResourceOwner: "GrantedOrgID", Data: mockProjectGrantData(&es_model.ProjectGrant{GrantID: "ProjectGrantID"})}, event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.GrantDeactivatedType), ResourceOwner: "GrantedOrgID", Data: mockProjectGrantData(&es_model.ProjectGrant{GrantID: "ProjectGrantID"})},
project: &ProjectGrantView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", OrgID: "GrantedOrgID", State: int32(model.ProjectStateActive), GrantedRoleKeys: pq.StringArray{"Role"}}, project: &ProjectGrantView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", OrgID: "GrantedOrgID", State: int32(model.ProjectStateActive), GrantedRoleKeys: []string{"Role"}},
}, },
result: &ProjectGrantView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", OrgID: "GrantedOrgID", State: int32(model.ProjectStateInactive), GrantedRoleKeys: pq.StringArray{"Role"}}, result: &ProjectGrantView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", OrgID: "GrantedOrgID", State: int32(model.ProjectStateInactive), GrantedRoleKeys: []string{"Role"}},
}, },
{ {
name: "append reactivate project grant event", name: "append reactivate project grant event",
args: args{ args: args{
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.GrantReactivatedType), ResourceOwner: "GrantedOrgID", Data: mockProjectGrantData(&es_model.ProjectGrant{GrantID: "ProjectGrantID"})}, event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.GrantReactivatedType), ResourceOwner: "GrantedOrgID", Data: mockProjectGrantData(&es_model.ProjectGrant{GrantID: "ProjectGrantID"})},
project: &ProjectGrantView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", OrgID: "GrantedOrgID", State: int32(model.ProjectStateInactive), GrantedRoleKeys: pq.StringArray{"Role"}}, project: &ProjectGrantView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", OrgID: "GrantedOrgID", State: int32(model.ProjectStateInactive), GrantedRoleKeys: []string{"Role"}},
}, },
result: &ProjectGrantView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", OrgID: "GrantedOrgID", State: int32(model.ProjectStateActive), GrantedRoleKeys: pq.StringArray{"Role"}}, result: &ProjectGrantView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", OrgID: "GrantedOrgID", State: int32(model.ProjectStateActive), GrantedRoleKeys: []string{"Role"}},
}, },
} }
for _, tt := range tests { for _, tt := range tests {

View File

@ -4,9 +4,9 @@ import (
"encoding/json" "encoding/json"
"time" "time"
"github.com/lib/pq"
"github.com/zitadel/logging" "github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/database"
caos_errs "github.com/zitadel/zitadel/internal/errors" caos_errs "github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/eventstore/v1/models"
@ -23,18 +23,18 @@ const (
) )
type ProjectMemberView struct { type ProjectMemberView struct {
UserID string `json:"userId" gorm:"column:user_id;primary_key"` UserID string `json:"userId" gorm:"column:user_id;primary_key"`
ProjectID string `json:"-" gorm:"column:project_id;primary_key"` ProjectID string `json:"-" gorm:"column:project_id;primary_key"`
UserName string `json:"-" gorm:"column:user_name"` UserName string `json:"-" gorm:"column:user_name"`
Email string `json:"-" gorm:"column:email_address"` Email string `json:"-" gorm:"column:email_address"`
FirstName string `json:"-" gorm:"column:first_name"` FirstName string `json:"-" gorm:"column:first_name"`
LastName string `json:"-" gorm:"column:last_name"` LastName string `json:"-" gorm:"column:last_name"`
DisplayName string `json:"-" gorm:"column:display_name"` DisplayName string `json:"-" gorm:"column:display_name"`
Roles pq.StringArray `json:"roles" gorm:"column:roles"` Roles database.StringArray `json:"roles" gorm:"column:roles"`
Sequence uint64 `json:"-" gorm:"column:sequence"` Sequence uint64 `json:"-" gorm:"column:sequence"`
PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"` PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"`
AvatarKey string `json:"-" gorm:"column:avatar_key"` AvatarKey string `json:"-" gorm:"column:avatar_key"`
UserResourceOwner string `json:"-" gorm:"column:user_resource_owner"` UserResourceOwner string `json:"-" gorm:"column:user_resource_owner"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"` CreationDate time.Time `json:"-" gorm:"column:creation_date"`
ChangeDate time.Time `json:"-" gorm:"column:change_date"` ChangeDate time.Time `json:"-" gorm:"column:change_date"`

View File

@ -5,8 +5,6 @@ import (
"reflect" "reflect"
"testing" "testing"
"github.com/lib/pq"
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
es_model "github.com/zitadel/zitadel/internal/project/repository/eventsourcing/model" es_model "github.com/zitadel/zitadel/internal/project/repository/eventsourcing/model"
"github.com/zitadel/zitadel/internal/repository/project" "github.com/zitadel/zitadel/internal/repository/project"
@ -30,18 +28,18 @@ func TestProjectMemberAppendEvent(t *testing.T) {
{ {
name: "append added member event", name: "append added member event",
args: args{ args: args{
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.MemberAddedType), ResourceOwner: "OrgID", Data: mockProjectMemberData(&es_model.ProjectMember{UserID: "UserID", Roles: pq.StringArray{"Role"}})}, event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.MemberAddedType), ResourceOwner: "OrgID", Data: mockProjectMemberData(&es_model.ProjectMember{UserID: "UserID", Roles: []string{"Role"}})},
member: &ProjectMemberView{}, member: &ProjectMemberView{},
}, },
result: &ProjectMemberView{ProjectID: "AggregateID", UserID: "UserID", Roles: pq.StringArray{"Role"}}, result: &ProjectMemberView{ProjectID: "AggregateID", UserID: "UserID", Roles: []string{"Role"}},
}, },
{ {
name: "append changed member event", name: "append changed member event",
args: args{ args: args{
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.MemberAddedType), ResourceOwner: "OrgID", Data: mockProjectMemberData(&es_model.ProjectMember{UserID: "UserID", Roles: pq.StringArray{"RoleChanged"}})}, event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.MemberAddedType), ResourceOwner: "OrgID", Data: mockProjectMemberData(&es_model.ProjectMember{UserID: "UserID", Roles: []string{"RoleChanged"}})},
member: &ProjectMemberView{ProjectID: "AggregateID", UserID: "UserID", Roles: pq.StringArray{"Role"}}, member: &ProjectMemberView{ProjectID: "AggregateID", UserID: "UserID", Roles: []string{"Role"}},
}, },
result: &ProjectMemberView{ProjectID: "AggregateID", UserID: "UserID", Roles: pq.StringArray{"RoleChanged"}}, result: &ProjectMemberView{ProjectID: "AggregateID", UserID: "UserID", Roles: []string{"RoleChanged"}},
}, },
} }
for _, tt := range tests { for _, tt := range tests {

View File

@ -6,7 +6,6 @@ import (
"time" "time"
sq "github.com/Masterminds/squirrel" sq "github.com/Masterminds/squirrel"
"github.com/lib/pq"
"github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/authz"
@ -216,8 +215,8 @@ func prepareFlowQuery(flowType domain.FlowType) (sq.SelectBuilder, func(*sql.Row
for rows.Next() { for rows.Next() {
var ( var (
actionID sql.NullString actionID sql.NullString
actionCreationDate pq.NullTime actionCreationDate sql.NullTime
actionChangeDate pq.NullTime actionChangeDate sql.NullTime
actionResourceOwner sql.NullString actionResourceOwner sql.NullString
actionState sql.NullInt32 actionState sql.NullInt32
actionSequence sql.NullInt64 actionSequence sql.NullInt64

View File

@ -31,14 +31,14 @@ func Test_FlowPrepares(t *testing.T) {
}, },
want: want{ want: want{
sqlExpectations: mockQueries( sqlExpectations: mockQueries(
regexp.QuoteMeta(`SELECT projections.actions.id,`+ regexp.QuoteMeta(`SELECT projections.actions2.id,`+
` projections.actions.creation_date,`+ ` projections.actions2.creation_date,`+
` projections.actions.change_date,`+ ` projections.actions2.change_date,`+
` projections.actions.resource_owner,`+ ` projections.actions2.resource_owner,`+
` projections.actions.action_state,`+ ` projections.actions2.action_state,`+
` projections.actions.sequence,`+ ` projections.actions2.sequence,`+
` projections.actions.name,`+ ` projections.actions2.name,`+
` projections.actions.script,`+ ` projections.actions2.script,`+
` projections.flows_triggers.trigger_type,`+ ` projections.flows_triggers.trigger_type,`+
` projections.flows_triggers.trigger_sequence,`+ ` projections.flows_triggers.trigger_sequence,`+
` projections.flows_triggers.flow_type,`+ ` projections.flows_triggers.flow_type,`+
@ -46,7 +46,7 @@ func Test_FlowPrepares(t *testing.T) {
` projections.flows_triggers.sequence,`+ ` projections.flows_triggers.sequence,`+
` projections.flows_triggers.resource_owner`+ ` projections.flows_triggers.resource_owner`+
` FROM projections.flows_triggers`+ ` FROM projections.flows_triggers`+
` LEFT JOIN projections.actions ON projections.flows_triggers.action_id = projections.actions.id`), ` LEFT JOIN projections.actions2 ON projections.flows_triggers.action_id = projections.actions2.id`),
nil, nil,
nil, nil,
), ),
@ -63,14 +63,14 @@ func Test_FlowPrepares(t *testing.T) {
}, },
want: want{ want: want{
sqlExpectations: mockQueries( sqlExpectations: mockQueries(
regexp.QuoteMeta(`SELECT projections.actions.id,`+ regexp.QuoteMeta(`SELECT projections.actions2.id,`+
` projections.actions.creation_date,`+ ` projections.actions2.creation_date,`+
` projections.actions.change_date,`+ ` projections.actions2.change_date,`+
` projections.actions.resource_owner,`+ ` projections.actions2.resource_owner,`+
` projections.actions.action_state,`+ ` projections.actions2.action_state,`+
` projections.actions.sequence,`+ ` projections.actions2.sequence,`+
` projections.actions.name,`+ ` projections.actions2.name,`+
` projections.actions.script,`+ ` projections.actions2.script,`+
` projections.flows_triggers.trigger_type,`+ ` projections.flows_triggers.trigger_type,`+
` projections.flows_triggers.trigger_sequence,`+ ` projections.flows_triggers.trigger_sequence,`+
` projections.flows_triggers.flow_type,`+ ` projections.flows_triggers.flow_type,`+
@ -78,7 +78,7 @@ func Test_FlowPrepares(t *testing.T) {
` projections.flows_triggers.sequence,`+ ` projections.flows_triggers.sequence,`+
` projections.flows_triggers.resource_owner`+ ` projections.flows_triggers.resource_owner`+
` FROM projections.flows_triggers`+ ` FROM projections.flows_triggers`+
` LEFT JOIN projections.actions ON projections.flows_triggers.action_id = projections.actions.id`), ` LEFT JOIN projections.actions2 ON projections.flows_triggers.action_id = projections.actions2.id`),
[]string{ []string{
"id", "id",
"creation_date", "creation_date",
@ -144,14 +144,14 @@ func Test_FlowPrepares(t *testing.T) {
}, },
want: want{ want: want{
sqlExpectations: mockQueries( sqlExpectations: mockQueries(
regexp.QuoteMeta(`SELECT projections.actions.id,`+ regexp.QuoteMeta(`SELECT projections.actions2.id,`+
` projections.actions.creation_date,`+ ` projections.actions2.creation_date,`+
` projections.actions.change_date,`+ ` projections.actions2.change_date,`+
` projections.actions.resource_owner,`+ ` projections.actions2.resource_owner,`+
` projections.actions.action_state,`+ ` projections.actions2.action_state,`+
` projections.actions.sequence,`+ ` projections.actions2.sequence,`+
` projections.actions.name,`+ ` projections.actions2.name,`+
` projections.actions.script,`+ ` projections.actions2.script,`+
` projections.flows_triggers.trigger_type,`+ ` projections.flows_triggers.trigger_type,`+
` projections.flows_triggers.trigger_sequence,`+ ` projections.flows_triggers.trigger_sequence,`+
` projections.flows_triggers.flow_type,`+ ` projections.flows_triggers.flow_type,`+
@ -159,7 +159,7 @@ func Test_FlowPrepares(t *testing.T) {
` projections.flows_triggers.sequence,`+ ` projections.flows_triggers.sequence,`+
` projections.flows_triggers.resource_owner`+ ` projections.flows_triggers.resource_owner`+
` FROM projections.flows_triggers`+ ` FROM projections.flows_triggers`+
` LEFT JOIN projections.actions ON projections.flows_triggers.action_id = projections.actions.id`), ` LEFT JOIN projections.actions2 ON projections.flows_triggers.action_id = projections.actions2.id`),
[]string{ []string{
"id", "id",
"creation_date", "creation_date",
@ -253,14 +253,14 @@ func Test_FlowPrepares(t *testing.T) {
}, },
want: want{ want: want{
sqlExpectations: mockQueries( sqlExpectations: mockQueries(
regexp.QuoteMeta(`SELECT projections.actions.id,`+ regexp.QuoteMeta(`SELECT projections.actions2.id,`+
` projections.actions.creation_date,`+ ` projections.actions2.creation_date,`+
` projections.actions.change_date,`+ ` projections.actions2.change_date,`+
` projections.actions.resource_owner,`+ ` projections.actions2.resource_owner,`+
` projections.actions.action_state,`+ ` projections.actions2.action_state,`+
` projections.actions.sequence,`+ ` projections.actions2.sequence,`+
` projections.actions.name,`+ ` projections.actions2.name,`+
` projections.actions.script,`+ ` projections.actions2.script,`+
` projections.flows_triggers.trigger_type,`+ ` projections.flows_triggers.trigger_type,`+
` projections.flows_triggers.trigger_sequence,`+ ` projections.flows_triggers.trigger_sequence,`+
` projections.flows_triggers.flow_type,`+ ` projections.flows_triggers.flow_type,`+
@ -268,7 +268,7 @@ func Test_FlowPrepares(t *testing.T) {
` projections.flows_triggers.sequence,`+ ` projections.flows_triggers.sequence,`+
` projections.flows_triggers.resource_owner`+ ` projections.flows_triggers.resource_owner`+
` FROM projections.flows_triggers`+ ` FROM projections.flows_triggers`+
` LEFT JOIN projections.actions ON projections.flows_triggers.action_id = projections.actions.id`), ` LEFT JOIN projections.actions2 ON projections.flows_triggers.action_id = projections.actions2.id`),
[]string{ []string{
"id", "id",
"creation_date", "creation_date",
@ -321,14 +321,14 @@ func Test_FlowPrepares(t *testing.T) {
}, },
want: want{ want: want{
sqlExpectations: mockQueryErr( sqlExpectations: mockQueryErr(
regexp.QuoteMeta(`SELECT projections.actions.id,`+ regexp.QuoteMeta(`SELECT projections.actions2.id,`+
` projections.actions.creation_date,`+ ` projections.actions2.creation_date,`+
` projections.actions.change_date,`+ ` projections.actions2.change_date,`+
` projections.actions.resource_owner,`+ ` projections.actions2.resource_owner,`+
` projections.actions.action_state,`+ ` projections.actions2.action_state,`+
` projections.actions.sequence,`+ ` projections.actions2.sequence,`+
` projections.actions.name,`+ ` projections.actions2.name,`+
` projections.actions.script,`+ ` projections.actions2.script,`+
` projections.flows_triggers.trigger_type,`+ ` projections.flows_triggers.trigger_type,`+
` projections.flows_triggers.trigger_sequence,`+ ` projections.flows_triggers.trigger_sequence,`+
` projections.flows_triggers.flow_type,`+ ` projections.flows_triggers.flow_type,`+
@ -336,7 +336,7 @@ func Test_FlowPrepares(t *testing.T) {
` projections.flows_triggers.sequence,`+ ` projections.flows_triggers.sequence,`+
` projections.flows_triggers.resource_owner`+ ` projections.flows_triggers.resource_owner`+
` FROM projections.flows_triggers`+ ` FROM projections.flows_triggers`+
` LEFT JOIN projections.actions ON projections.flows_triggers.action_id = projections.actions.id`), ` LEFT JOIN projections.actions2 ON projections.flows_triggers.action_id = projections.actions2.id`),
sql.ErrConnDone, sql.ErrConnDone,
), ),
err: func(err error) (error, bool) { err: func(err error) (error, bool) {
@ -353,16 +353,16 @@ func Test_FlowPrepares(t *testing.T) {
prepare: prepareTriggerActionsQuery, prepare: prepareTriggerActionsQuery,
want: want{ want: want{
sqlExpectations: mockQueries( sqlExpectations: mockQueries(
regexp.QuoteMeta(`SELECT projections.actions.id,`+ regexp.QuoteMeta(`SELECT projections.actions2.id,`+
` projections.actions.creation_date,`+ ` projections.actions2.creation_date,`+
` projections.actions.change_date,`+ ` projections.actions2.change_date,`+
` projections.actions.resource_owner,`+ ` projections.actions2.resource_owner,`+
` projections.actions.action_state,`+ ` projections.actions2.action_state,`+
` projections.actions.sequence,`+ ` projections.actions2.sequence,`+
` projections.actions.name,`+ ` projections.actions2.name,`+
` projections.actions.script`+ ` projections.actions2.script`+
` FROM projections.flows_triggers`+ ` FROM projections.flows_triggers`+
` LEFT JOIN projections.actions ON projections.flows_triggers.action_id = projections.actions.id`), ` LEFT JOIN projections.actions2 ON projections.flows_triggers.action_id = projections.actions2.id`),
nil, nil,
nil, nil,
), ),
@ -374,16 +374,16 @@ func Test_FlowPrepares(t *testing.T) {
prepare: prepareTriggerActionsQuery, prepare: prepareTriggerActionsQuery,
want: want{ want: want{
sqlExpectations: mockQueries( sqlExpectations: mockQueries(
regexp.QuoteMeta(`SELECT projections.actions.id,`+ regexp.QuoteMeta(`SELECT projections.actions2.id,`+
` projections.actions.creation_date,`+ ` projections.actions2.creation_date,`+
` projections.actions.change_date,`+ ` projections.actions2.change_date,`+
` projections.actions.resource_owner,`+ ` projections.actions2.resource_owner,`+
` projections.actions.action_state,`+ ` projections.actions2.action_state,`+
` projections.actions.sequence,`+ ` projections.actions2.sequence,`+
` projections.actions.name,`+ ` projections.actions2.name,`+
` projections.actions.script`+ ` projections.actions2.script`+
` FROM projections.flows_triggers`+ ` FROM projections.flows_triggers`+
` LEFT JOIN projections.actions ON projections.flows_triggers.action_id = projections.actions.id`), ` LEFT JOIN projections.actions2 ON projections.flows_triggers.action_id = projections.actions2.id`),
[]string{ []string{
"id", "id",
"creation_date", "creation_date",
@ -426,16 +426,16 @@ func Test_FlowPrepares(t *testing.T) {
prepare: prepareTriggerActionsQuery, prepare: prepareTriggerActionsQuery,
want: want{ want: want{
sqlExpectations: mockQueries( sqlExpectations: mockQueries(
regexp.QuoteMeta(`SELECT projections.actions.id,`+ regexp.QuoteMeta(`SELECT projections.actions2.id,`+
` projections.actions.creation_date,`+ ` projections.actions2.creation_date,`+
` projections.actions.change_date,`+ ` projections.actions2.change_date,`+
` projections.actions.resource_owner,`+ ` projections.actions2.resource_owner,`+
` projections.actions.action_state,`+ ` projections.actions2.action_state,`+
` projections.actions.sequence,`+ ` projections.actions2.sequence,`+
` projections.actions.name,`+ ` projections.actions2.name,`+
` projections.actions.script`+ ` projections.actions2.script`+
` FROM projections.flows_triggers`+ ` FROM projections.flows_triggers`+
` LEFT JOIN projections.actions ON projections.flows_triggers.action_id = projections.actions.id`), ` LEFT JOIN projections.actions2 ON projections.flows_triggers.action_id = projections.actions2.id`),
[]string{ []string{
"id", "id",
"creation_date", "creation_date",
@ -498,16 +498,16 @@ func Test_FlowPrepares(t *testing.T) {
prepare: prepareTriggerActionsQuery, prepare: prepareTriggerActionsQuery,
want: want{ want: want{
sqlExpectations: mockQueryErr( sqlExpectations: mockQueryErr(
regexp.QuoteMeta(`SELECT projections.actions.id,`+ regexp.QuoteMeta(`SELECT projections.actions2.id,`+
` projections.actions.creation_date,`+ ` projections.actions2.creation_date,`+
` projections.actions.change_date,`+ ` projections.actions2.change_date,`+
` projections.actions.resource_owner,`+ ` projections.actions2.resource_owner,`+
` projections.actions.action_state,`+ ` projections.actions2.action_state,`+
` projections.actions.sequence,`+ ` projections.actions2.sequence,`+
` projections.actions.name,`+ ` projections.actions2.name,`+
` projections.actions.script`+ ` projections.actions2.script`+
` FROM projections.flows_triggers`+ ` FROM projections.flows_triggers`+
` LEFT JOIN projections.actions ON projections.flows_triggers.action_id = projections.actions.id`), ` LEFT JOIN projections.actions2 ON projections.flows_triggers.action_id = projections.actions2.id`),
sql.ErrConnDone, sql.ErrConnDone,
), ),
err: func(err error) (error, bool) { err: func(err error) (error, bool) {

View File

@ -29,18 +29,18 @@ func Test_ActionPrepares(t *testing.T) {
prepare: prepareActionsQuery, prepare: prepareActionsQuery,
want: want{ want: want{
sqlExpectations: mockQueries( sqlExpectations: mockQueries(
regexp.QuoteMeta(`SELECT projections.actions.id,`+ regexp.QuoteMeta(`SELECT projections.actions2.id,`+
` projections.actions.creation_date,`+ ` projections.actions2.creation_date,`+
` projections.actions.change_date,`+ ` projections.actions2.change_date,`+
` projections.actions.resource_owner,`+ ` projections.actions2.resource_owner,`+
` projections.actions.sequence,`+ ` projections.actions2.sequence,`+
` projections.actions.action_state,`+ ` projections.actions2.action_state,`+
` projections.actions.name,`+ ` projections.actions2.name,`+
` projections.actions.script,`+ ` projections.actions2.script,`+
` projections.actions.timeout,`+ ` projections.actions2.timeout,`+
` projections.actions.allowed_to_fail,`+ ` projections.actions2.allowed_to_fail,`+
` COUNT(*) OVER ()`+ ` COUNT(*) OVER ()`+
` FROM projections.actions`), ` FROM projections.actions2`),
nil, nil,
nil, nil,
), ),
@ -52,18 +52,18 @@ func Test_ActionPrepares(t *testing.T) {
prepare: prepareActionsQuery, prepare: prepareActionsQuery,
want: want{ want: want{
sqlExpectations: mockQueries( sqlExpectations: mockQueries(
regexp.QuoteMeta(`SELECT projections.actions.id,`+ regexp.QuoteMeta(`SELECT projections.actions2.id,`+
` projections.actions.creation_date,`+ ` projections.actions2.creation_date,`+
` projections.actions.change_date,`+ ` projections.actions2.change_date,`+
` projections.actions.resource_owner,`+ ` projections.actions2.resource_owner,`+
` projections.actions.sequence,`+ ` projections.actions2.sequence,`+
` projections.actions.action_state,`+ ` projections.actions2.action_state,`+
` projections.actions.name,`+ ` projections.actions2.name,`+
` projections.actions.script,`+ ` projections.actions2.script,`+
` projections.actions.timeout,`+ ` projections.actions2.timeout,`+
` projections.actions.allowed_to_fail,`+ ` projections.actions2.allowed_to_fail,`+
` COUNT(*) OVER ()`+ ` COUNT(*) OVER ()`+
` FROM projections.actions`), ` FROM projections.actions2`),
[]string{ []string{
"id", "id",
"creation_date", "creation_date",
@ -118,18 +118,18 @@ func Test_ActionPrepares(t *testing.T) {
prepare: prepareActionsQuery, prepare: prepareActionsQuery,
want: want{ want: want{
sqlExpectations: mockQueries( sqlExpectations: mockQueries(
regexp.QuoteMeta(`SELECT projections.actions.id,`+ regexp.QuoteMeta(`SELECT projections.actions2.id,`+
` projections.actions.creation_date,`+ ` projections.actions2.creation_date,`+
` projections.actions.change_date,`+ ` projections.actions2.change_date,`+
` projections.actions.resource_owner,`+ ` projections.actions2.resource_owner,`+
` projections.actions.sequence,`+ ` projections.actions2.sequence,`+
` projections.actions.action_state,`+ ` projections.actions2.action_state,`+
` projections.actions.name,`+ ` projections.actions2.name,`+
` projections.actions.script,`+ ` projections.actions2.script,`+
` projections.actions.timeout,`+ ` projections.actions2.timeout,`+
` projections.actions.allowed_to_fail,`+ ` projections.actions2.allowed_to_fail,`+
` COUNT(*) OVER ()`+ ` COUNT(*) OVER ()`+
` FROM projections.actions`), ` FROM projections.actions2`),
[]string{ []string{
"id", "id",
"creation_date", "creation_date",
@ -208,18 +208,18 @@ func Test_ActionPrepares(t *testing.T) {
prepare: prepareActionsQuery, prepare: prepareActionsQuery,
want: want{ want: want{
sqlExpectations: mockQueryErr( sqlExpectations: mockQueryErr(
regexp.QuoteMeta(`SELECT projections.actions.id,`+ regexp.QuoteMeta(`SELECT projections.actions2.id,`+
` projections.actions.creation_date,`+ ` projections.actions2.creation_date,`+
` projections.actions.change_date,`+ ` projections.actions2.change_date,`+
` projections.actions.resource_owner,`+ ` projections.actions2.resource_owner,`+
` projections.actions.sequence,`+ ` projections.actions2.sequence,`+
` projections.actions.action_state,`+ ` projections.actions2.action_state,`+
` projections.actions.name,`+ ` projections.actions2.name,`+
` projections.actions.script,`+ ` projections.actions2.script,`+
` projections.actions.timeout,`+ ` projections.actions2.timeout,`+
` projections.actions.allowed_to_fail,`+ ` projections.actions2.allowed_to_fail,`+
` COUNT(*) OVER ()`+ ` COUNT(*) OVER ()`+
` FROM projections.actions`), ` FROM projections.actions2`),
sql.ErrConnDone, sql.ErrConnDone,
), ),
err: func(err error) (error, bool) { err: func(err error) (error, bool) {
@ -236,17 +236,17 @@ func Test_ActionPrepares(t *testing.T) {
prepare: prepareActionQuery, prepare: prepareActionQuery,
want: want{ want: want{
sqlExpectations: mockQueries( sqlExpectations: mockQueries(
regexp.QuoteMeta(`SELECT projections.actions.id,`+ regexp.QuoteMeta(`SELECT projections.actions2.id,`+
` projections.actions.creation_date,`+ ` projections.actions2.creation_date,`+
` projections.actions.change_date,`+ ` projections.actions2.change_date,`+
` projections.actions.resource_owner,`+ ` projections.actions2.resource_owner,`+
` projections.actions.sequence,`+ ` projections.actions2.sequence,`+
` projections.actions.action_state,`+ ` projections.actions2.action_state,`+
` projections.actions.name,`+ ` projections.actions2.name,`+
` projections.actions.script,`+ ` projections.actions2.script,`+
` projections.actions.timeout,`+ ` projections.actions2.timeout,`+
` projections.actions.allowed_to_fail`+ ` projections.actions2.allowed_to_fail`+
` FROM projections.actions`), ` FROM projections.actions2`),
nil, nil,
nil, nil,
), ),
@ -264,17 +264,17 @@ func Test_ActionPrepares(t *testing.T) {
prepare: prepareActionQuery, prepare: prepareActionQuery,
want: want{ want: want{
sqlExpectations: mockQuery( sqlExpectations: mockQuery(
regexp.QuoteMeta(`SELECT projections.actions.id,`+ regexp.QuoteMeta(`SELECT projections.actions2.id,`+
` projections.actions.creation_date,`+ ` projections.actions2.creation_date,`+
` projections.actions.change_date,`+ ` projections.actions2.change_date,`+
` projections.actions.resource_owner,`+ ` projections.actions2.resource_owner,`+
` projections.actions.sequence,`+ ` projections.actions2.sequence,`+
` projections.actions.action_state,`+ ` projections.actions2.action_state,`+
` projections.actions.name,`+ ` projections.actions2.name,`+
` projections.actions.script,`+ ` projections.actions2.script,`+
` projections.actions.timeout,`+ ` projections.actions2.timeout,`+
` projections.actions.allowed_to_fail`+ ` projections.actions2.allowed_to_fail`+
` FROM projections.actions`), ` FROM projections.actions2`),
[]string{ []string{
"id", "id",
"creation_date", "creation_date",
@ -319,17 +319,17 @@ func Test_ActionPrepares(t *testing.T) {
prepare: prepareActionQuery, prepare: prepareActionQuery,
want: want{ want: want{
sqlExpectations: mockQueryErr( sqlExpectations: mockQueryErr(
regexp.QuoteMeta(`SELECT projections.actions.id,`+ regexp.QuoteMeta(`SELECT projections.actions2.id,`+
` projections.actions.creation_date,`+ ` projections.actions2.creation_date,`+
` projections.actions.change_date,`+ ` projections.actions2.change_date,`+
` projections.actions.resource_owner,`+ ` projections.actions2.resource_owner,`+
` projections.actions.sequence,`+ ` projections.actions2.sequence,`+
` projections.actions.action_state,`+ ` projections.actions2.action_state,`+
` projections.actions.name,`+ ` projections.actions2.name,`+
` projections.actions.script,`+ ` projections.actions2.script,`+
` projections.actions.timeout,`+ ` projections.actions2.timeout,`+
` projections.actions.allowed_to_fail`+ ` projections.actions2.allowed_to_fail`+
` FROM projections.actions`), ` FROM projections.actions2`),
sql.ErrConnDone, sql.ErrConnDone,
), ),
err: func(err error) (error, bool) { err: func(err error) (error, bool) {

Some files were not shown because too many files have changed in this diff Show More