Categorygithub.com/covrom/pgparty
repositorypackage
1.3.4
Repository: https://github.com/covrom/pgparty.git
Documentation: pkg.go.dev

# Packages

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# README

pgparty

Postgres database/sql access layer with Go generics 1.18+.

You can easily create go structures as models with tag descriptions for tables, fields and indexes in postgres schemas.

pgparty implements it in the database, with automatic migration between model descriptions in your service and database.

We use a special table with model descriptions for migration.

Basic usage

Create shards repository with only one shard with one database connect

shs, ctx := pgparty.NewShards(pgparty.WithLoggingQuery(context.Background()))

db, err = pgparty.InitDB(pgparty.DatabaseDSN{Postgres: databaseUrl})
if err != nil {
    return err
}

shard := shs.SetShard("shard1", db, "shard1")

One shard contains one postgres schema. Its automatically created if not exist.

Define some models, that implements Storable interface:

type BasicModel struct {
	ID       pgparty.UUID[BasicModel]      `json:"id"`
	Data     pgparty.NullJsonB             `json:"data"`
	AppXID   pgparty.XID[pgparty.AppXID]   `json:"appId"`
	TraceXID pgparty.XID[pgparty.TraceXID] `json:"traceId"`
}

func (BasicModel) DatabaseName() string { return "basic_models" }
func (BasicModel) UUIDPrefix() string { return "basic_model_" }

Now, register some models in shard:

if err := pgparty.Register(shard, pgparty.MD[BasicModel]{}); err != nil {
    t.Errorf("pgparty.Register error: %s", err)
    return
}

With each shard we can migrate models in database to current structure. At any time.

if err := shard.Migrate(ctx, nil); err != nil {
    t.Errorf("shard.Migrate error: %s", err)
    return
}

This produces sql queries:

CREATE TABLE shard1.basic_models (app_xid CHAR(20) NOT NULL,data jsonb,id UUID NOT NULL,trace_xid CHAR(20) NOT NULL,PRIMARY KEY (id))
CREATE UNIQUE INDEX basic_modelsappidx ON shard1.basic_models(app_xid )
CREATE INDEX basic_modelstraceappidx ON shard1.basic_models(app_xid, trace_xid )
INSERT INTO shard1._config (table_name,storej) VALUES($1,$2) ON CONFLICT(table_name) DO UPDATE SET storej=excluded.storej

where $1 = basic_models , $2 =

{
    "table":"basic_models",
    "cols":[
        {"ColName":"app_xid","DataType":"CHAR(20)","DefaultValue":"","NotNull":true,"PrimaryKey":false},
        {"ColName":"data","DataType":"jsonb","DefaultValue":"","NotNull":false,"PrimaryKey":false},
        {"ColName":"id","DataType":"UUID","DefaultValue":"","NotNull":true,"PrimaryKey":true},
        {"ColName":"trace_xid","DataType":"CHAR(20)","DefaultValue":"","NotNull":true,"PrimaryKey":false}
        ],
    "idxs":[
        {"name":"appidx","isUnique":true,"columns":["app_xid"]},
        {"name":"traceappidx","columns":["app_xid","trace_xid"]}
        ]
}

Future migrations use this '_config' table for building differencies as ALTER DDL queries.

Next time, create a model element

el := BasicModel{
	ID: pgparty.NewUUID[BasicModel](),
	Data: *pgparty.NewNullJsonB(map[string]any{
		"field1": "string data",
		"field2": 1344,
		"field3": pgparty.NowUTC(),
	}),
    AppXID:   pgparty.NewXID[pgparty.AppXID](),
    TraceXID: pgparty.NewXID[pgparty.TraceXID](),
}

Then replace it in database by id

if err := pgparty.WithTxInShard(ctx, shard.ID, func(ctx context.Context) error {
    return pgparty.Replace[BasicModel](ctx, el)
}); err != nil {
    t.Errorf("pgparty.Replace error: %s", err)
    return
}

This produces sql queries:

INSERT INTO shard1.basic_models (id,data,app_xid,trace_xid) VALUES($1,$2,$3,$4) 
ON CONFLICT(id) DO UPDATE SET (data,app_xid,trace_xid)=(excluded.data,excluded.app_xid,excluded.trace_xid)

Now, select written data from database:

var els []BasicModel
if err := shard.WithTx(ctx, func(ctx context.Context) error {
    return 
        pgparty.Select[BasicModel](ctx, 
            `SELECT * FROM &BasicModel`, // &BasicModel - model named by golang struct type name
        &els)
}); err != nil {
    t.Errorf("pgparty.Select error: %s", err)
    return
}

This produces sql queries:

SELECT * FROM shard1.basic_models