Skip to content
/ pg Public

PG is a Go library that simplifies PostgreSQL database interaction with struct-based entities, schema management, and repository pattern.

License

Notifications You must be signed in to change notification settings

kataras/pg

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

55 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

kataras/pg

build status report card godocs view examples

A high-performance Go library that provides a simple and elegant way to interact with PostgreSQL databases (works perfectly with the latest 16.x versions). It allows you to define your entities as structs with pg tags, register them in a schema, and perform CRUD operations using a repository pattern. It also handles database connection, schema creation and verification, and query generation and execution. You can use PG to write concise and readable code that works with PostgreSQL databases.


💻 Installation

The only requirement is the Go Programming Language.

Create a new project

$ mkdir myapp
$ cd myapp
$ go mod init myapp
$ go get github.com/kataras/pg@latest
Install on existing project
$ cd myapp
$ go get github.com/kataras/pg@latest

Run

$ go mod tidy -compat=1.21 # -compat="1.21" for windows.
$ go run .

📖 Example

PG contains extensive and thorough documentation making it easy to get started with the library.

package main

import (
  "context"
  "fmt"
  "log"
  "time"

  "github.com/kataras/pg"
)

// Base is a struct that contains common fields for all entities.
type Base struct {
  ID        string    `pg:"type=uuid,primary"` // UUID as primary key
  CreatedAt time.Time `pg:"type=timestamp,default=clock_timestamp()"` // Timestamp of creation
  UpdatedAt time.Time `pg:"type=timestamp,default=clock_timestamp()"` // Last update
}

// Customer is a struct that represents a customer entity.
type Customer struct {
  Base // Embed Base struct

  Firstname string `pg:"type=varchar(255)"` // First name of the customer
}

func main() {
  // Default value for struct field tag `pg`.
  // It can be modified to a custom one as well, e.g.
  // pg.SetDefaultTag("db")

  // Create Schema instance.
  schema := pg.NewSchema()
  // First argument is the table name, second is the struct entity.
  schema.MustRegister("customers", Customer{})

  // Create Database instance.
  connString := "postgres://postgres:admin!123@localhost:5432/test_db?sslmode=disable"
  db, err := pg.Open(context.Background(), schema, connString)
  if err != nil {
    log.Fatal(err)
  }
  defer db.Close()

  // If needed, create and verify the database schema
  // based on the pg tags of the structs.
  //
  // Alternatively, you can generate
  // Go schema files from an existing database:
  // see the ./gen sub-package for more details.
  if err = db.CreateSchema(context.Background()); err != nil {
    log.Fatal(err)
  }

  if err = db.CheckSchema(context.Background()); err != nil {
    log.Fatal(err)
  }

  // Create a Repository of Customer type.
  customers := pg.NewRepository[Customer](db)

  var newCustomer = Customer{
    Firstname: John,
  }

  // Insert a new Customer.
  err = customers.InsertSingle(context.Background(), newCustomer, &newCustomer.ID)
  if err != nil {
    log.Fatal(err)
  }

  // Get by id.
  /*
  query := `SELECT * FROM customers WHERE id = $1 LIMIT 1;`
  existing, err := customers.SelectSingle(context.Background(), query, newCustomer.ID)
  OR:
  */
  existing, err := customers.SelectByID(context.Background(), newCustomer.ID)
  if err != nil {
    log.Fatal(err)
  }

  log.Printf("Existing Customer (SelectSingle):\n%#+v\n", existing)

  // List all.
  query = `SELECT * FROM customers ORDER BY created_at DESC;`
  allCustomers, err := customers.Select(context.Background(), query)
  if err != nil {
    log.Fatal(err)
  }

  log.Printf("All Customers (%d):", len(allCustomers))
  for _, customer := range allCustomers {
    fmt.Printf("- (%s) %s\n", customer.ID, customer.Firstname)
  }
}

If you already have a database, you can use the gen sub-package to create structs that match its schema.

✒️ ASCII art

┌───────────────────────┐
│  NewSchema() *Schema  ├───────────────────────────────────┐
├───────────────────────┘                                   │
│                                                           │
├─────────────────────────────────────────────────────┐     │
│  Schema                                             │     │
├─────────────────────────────────────────────────────┤     │
│  - MustRegister(tableName string, emptyStruct any)  │     │
└─────────────────────────────────────────────────────┘     │
                                                            │
                                                            │
                                                            │
                                ┌───────────────────────────┘     ┌─────────────────────────┐
                                │                                 │                         │
┌───────────────────────────────▼─────────────────────────────────┴───────────┐             │
│  Open(ctx context.Context, schema *Schema, connString string) (*DB, error)  │             │
├─────────────────────────────────────────────────────────────────────────────┘             │
│                                                                                           │
├─────────────────────────────────────────────────────────────────────────────────────┐     │
│  DB                                                                                 │     │
├─────────────────────────────────────────────────────────────────────────────────────┤     │
│                                                                                     │     │
│  - CreateSchema(ctx context.Context) error                                          │     │
│  - CheckSchema(ctx context.Context) error                                           │     │
│                                                                                     │     │
│  - InTransaction(ctx context.Context, fn (*DB) error) error                         │     │
│  - IsTransaction() bool                                                             │     │
│                                                                                     │     │
│  - Query(ctx context.Context, query string, args ...any) (Rows, error)              │     │
│  - QueryRow(ctx context.Context, query string, args ...any) Row                     │     │
│                                                                                     │     │
│  - Exec(ctx context.Context, query string, args... any) (pgconn.CommandTag, error)  │     │
│                                                                                     │     │
│  - Listen(ctx context.Context, channel string) (*Listener, error)                   │     │
│  - Notify(ctx context.Context, channel string, payload any) error                   │     │
│  - Unlisten(ctx context.Context, channel string) error                              │     │
│                                                                                     │     │
│  - Close() error                                                                    │     │
└─────────────────────────────────────────────────────────────────────────────────────┘     │
                                                                                            │
                                                                                            │
                                                                                            │
                                                                                            │
                      ┌─────────────────────────────────────────────────────────────────────┘
                      │
┌─────────────────────▼─────────────────────┐
│  NewRepository[T](db *DB) *Repository[T]  │
├───────────────────────────────────────────┘
│
├────────────────────────────────────────────────────────────────────────────────────────────┐
│  Repository[T]                                                                             │
├────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                            │
│  - InTransaction(ctx context.Context, fn func(*Repository[T]) error) error                 │
│  - IsTransaction() bool                                                                    │
│                                                                                            │
│  - Select(ctx context.Context, query string, args ...any) ([]T, error)                     │
│  - SelectSingle(ctx context.Context, query string, args ...any) (T, error)                 │
│  - SelectByID(ctx context.Context, id any) (T, error)                                      │
│  - SelectByUsernameAndPassword(ctx context.Context, username, plainPwd string) (T, error)  │
│                                                                                            │
│  - Insert(ctx context.Context, values ...T) error                                          │
│  - InsertSingle(ctx context.Context, value T, destIdPtr any) error                         │
│                                                                                            │
│  - Update(ctx context.Context, values ...T) (int64, error)                                 │
│  - UpdateOnlyColumns(ctx context.Context, columns []string, values ...T) (int64, error)    │
│                                                                                            │
│  - Upsert(ctx context.Context, uniqueIndex string, values ...T) error                      │
│  - UpsertSingle(ctx context.Context, uniqueIndex string, value T, destIdPtr any) error     │
│                                                                                            │
│  - Delete(ctx context.Context, values ...T) (int64, error)                                 │
│                                                                                            │
│  - ListenTable(ctx context.Context, cb func(notification, error) error) (Closer, error)    │
└────────────────────────────────────────────────────────────────────────────────────────────┘

🛄 Data types

PostgreSQL data type Struct field tag type options
BigInt bigint, int8
BigIntArray bigint[], int8[]
BigSerial bigserial, serial8
Bit bit
BitVarying bit varying, varbit
Boolean boolean, bool
Box box
Bytea bytea
Character character, char
CharacterArray character[], char[]
CharacterVarying character varying, varchar
CharacterVaryingArray character varying[], varchar[]
Cidr cidr
Circle circle
Date date
DoublePrecision double precision, float8
Inet inet
Integer integer, int, int4
IntegerArray integer[], int[], int4[]
IntegerDoubleArray integer[][], int[][], int4[][]
Interval interval
JSON json
JSONB jsonb
Line line
Lseg lseg
MACAddr macaddr
MACAddr8 macaddr8
Money money
Numeric numeric, decimal
Path path
PgLSN pg_lsn
Point point
Polygon polygon
Real real, float4
SmallInt smallint, int2
SmallSerial smallserial, serial2
Serial serial4
Text text
TextArray text[]
TextDoubleArray text[][]
Time time, time without time zone
TimeTZ timetz, time with time zone
Timestamp timestamp, timestamp without time zone
TimestampTZ timestamptz, timestamp with time zone
TsQuery tsquery
TsVector tsvector
TxIDSnapshot txid_snapshot
UUID uuid
UUIDArray uuid[]
XML xml
Int4Range int4range
Int4MultiRange int4multirange
Int8Range int8range
Int8MultiRange int8multirange
NumRange numrange
NumMultiRange nummultirange
TsRange tsrange
TsMultirange tsmultirange
TsTzRange tstzrange
TsTzMultiRange tstzmultirange
DateRange daterange
DateMultiRange datemultirange
CIText citext
HStore hstore

Data type examples

UUID

type Entity struct {
  ID string `pg:"type=uuid,primary"`
}

Timestamp

type Entity struct {
  CreatedAt time.Time `pg:"type=timestamp,default=clock_timestamp()"`
}

Varchar

type Entity struct {
  PhotoURL string `pg:"type=varchar(255)"`
}

Varchar Array

type Entity struct {
  SearchTerms []string `pg:"type=varchar[]"`
}

Integer

type Entity struct {
  ReadTimeMinutes int `pg:"type=smallint,default=1,check=read_time_minutes > 0"`
}

Custom JSON Object

type Entity struct {
  Feature Feature `pg:"type=jsonb"`
}

Array of custom JSON objects

type Entity struct {
  Tags []Tag `pg:"type=jsonb"`
}

📦 3rd-party Packages

List of 3rd-party packages based on PG.

🛡 Security Vulnerabilities

If you discover a security vulnerability within pg, please send an e-mail to kataras2006@hotmail.com. All security vulnerabilities will be promptly addressed.

📝 License

This project is licensed under the MIT license.

About

PG is a Go library that simplifies PostgreSQL database interaction with struct-based entities, schema management, and repository pattern.

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

No packages published