Help us shape the future of Go! Take the 2026 Go Developer Survey

postgres

package
v1.26.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 10, 2026 License: MIT Imports: 13 Imported by: 0

README

PostgreSQL Adapter - TDTP Framework

The PostgreSQL adapter: two-way integration with PostgreSQL 12 and later.

Status

In development (v0.9)

Done:

  • types.go — PostgreSQL to TDTP type mapping
  • adapter.go — connection through a pgx pool
  • export.go — table export (in progress)
  • import.go — data import (in progress)
  • integration_test.go — integration tests (in progress)

What it does

Data types

Standard types:

PostgreSQL          TDTP            Back again
─────────────────────────────────────────────
INTEGER             INTEGER         INTEGER
BIGINT              INTEGER         BIGINT
NUMERIC(18,2)       DECIMAL         NUMERIC(18,2)
VARCHAR(100)        TEXT            VARCHAR(100)
TEXT                TEXT            TEXT
BOOLEAN             BOOLEAN         BOOLEAN
DATE                DATE            DATE
TIMESTAMP           TIMESTAMP       TIMESTAMP
BYTEA               BLOB            BYTEA

PostgreSQL-specific types, carried through subtype:

PostgreSQL          TDTP                        Back again
───────────────────────────────────────────────────────────
UUID                TEXT (subtype="uuid")       UUID
JSONB               TEXT (subtype="jsonb")      JSONB
JSON                TEXT (subtype="json")       JSON
INET                TEXT (subtype="inet")       INET
CIDR                TEXT (subtype="cidr")       CIDR
MACADDR             TEXT (subtype="macaddr")    MACADDR
INTEGER[]           TEXT (subtype="array")      INTEGER[]
TIMESTAMPTZ         TIMESTAMP (subtype="tz")    TIMESTAMPTZ
SERIAL              INTEGER (subtype="serial")  SERIAL
Migration scenarios

SQLite → PostgreSQL:

source := sqlite.NewAdapter("app.db")
target := postgres.NewAdapter("postgresql://user:pass@localhost/db")

packets, _ := source.ExportTable("Users")
target.ImportPackets(packets, postgres.StrategyReplace)
// Types are converted for you

PostgreSQL → SQLite (downgrade):

source := postgres.NewAdapter("postgresql://...")
target := sqlite.NewAdapter("backup.db")

// UUID and JSONB become TEXT on export
packets, _ := source.ExportTable("Users")
target.ImportPackets(packets, sqlite.StrategyReplace)

Usage

Connecting
import "github.com/queuebridge/tdtp/pkg/adapters/postgres"

// The ordinary case (schema: public)
adapter, err := postgres.NewAdapter(
    "postgresql://tdtp_user:password@localhost:5432/tdtp_test"
)
defer adapter.Close()

// Naming a schema
adapter, err := postgres.NewAdapterWithSchema(
    "postgresql://...",
    "myschema",
)
Export (TODO)
// A full export
packets, err := adapter.ExportTable("Users")

// With a filter
translator := tdtql.NewTranslator()
query, _ := translator.Translate("SELECT * FROM Users WHERE active = true")
packets, err := adapter.ExportTableWithQuery("Users", query, "App", "Server")
Import (TODO)
// Import, replacing
err := adapter.ImportPacket(packet, postgres.StrategyReplace)

// Import, ignoring duplicates
err := adapter.ImportPacket(packet, postgres.StrategyIgnore)

// Several packets, imported atomically
err := adapter.ImportPackets(packets, postgres.StrategyReplace)

Installing dependencies

go get github.com/jackc/pgx/v5
go get github.com/jackc/pgx/v5/pgxpool

Testing

PostgreSQL in Docker (already running)
# Check the container
docker ps | grep postgres

# Connect, to confirm
docker exec -it tdtp-postgres psql -U tdtp_user -d tdtp_test

Connection string for tests:

postgresql://tdtp_user:tdtp_dev_pass_2025@localhost:5432/tdtp_test
Running the tests, once they exist
cd pkg/adapters/postgres
go test -v
go test -bench=. -benchmem

Implementation notes

Connection Pooling
  • MaxConns: 10 — the connection ceiling
  • MinConns: 2 — kept warm
  • Driver: pgx/v5 — current and fast
Working with schemas
// List every schema
schemas, _ := adapter.GetSchemas()

// Switch schema
adapter.SetSchema("custom_schema")

// Check for a table in the current schema
exists, _ := adapter.TableExists("Users")
Identifier handling

PostgreSQL is case-sensitive for quoted identifiers:

// Quoted for you
QuoteIdentifier("User")   → "User"
QuoteIdentifier("user")   → user
QuoteIdentifier("order")  → "order" (reserved word)

Roadmap

v0.9 (current)
  • export.go — read the schema through information_schema
  • export.go — export data, every type supported
  • import.go - COPY-based bulk insert
  • import.go — handle sequences
  • integration_test.go — full coverage
  • Array support (PostgreSQL-specific)
  • Benchmark PostgreSQL vs SQLite
v1.0 (planned)
  • Partitioned tables
  • Foreign keys and constraints
  • Schema migration (ALTER TABLE)
  • Incremental synchronisation

Worked examples

A migration involving PostgreSQL types
// A PostgreSQL table with UUID and JSONB
CREATE TABLE users (
    id UUID PRIMARY KEY,
    name VARCHAR(100),
    metadata JSONB,
    ip INET
);

// Export through TDTP
packets, _ := pgAdapter.ExportTable("users")

// In the TDTP packet:
<Schema>
  <Field name="id" type="TEXT" subtype="uuid" key="true"/>
  <Field name="name" type="TEXT" length="100"/>
  <Field name="metadata" type="TEXT" length="-1" subtype="jsonb"/>
  <Field name="ip" type="TEXT" subtype="inet"/>
</Schema>

// Import back into PostgreSQL
pgAdapter2.ImportPacket(packet, postgres.StrategyReplace)
// → UUID, JSONB and INET are restored
Cross-platform migration
// PostgreSQL (source)
pgSrc, _ := postgres.NewAdapter("postgresql://src/db")
packets, _ := pgSrc.ExportTable("products")

// SQLite as the target — types downgrade automatically
sqliteTgt, _ := sqlite.NewAdapter("products.db")
sqliteTgt.ImportPackets(packets, sqlite.StrategyReplace)
// UUID → TEXT, JSONB → TEXT, etc.

Known limitations

  1. Arrays: stored as TEXT with subtype="array"; the exact element type is not preserved
  2. Composite types: unsupported (TODO v1.1)
  3. Domains: treated as their base type
  4. Enums: stored as TEXT (TODO v1.1)

The PostgreSQL adapter is the workhorse for enterprise migrations.

Part of TDTP Framework v0.9

Documentation

Overview

Package postgres provides functionality for the TDTP framework.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BuildFieldFromPGColumn

func BuildFieldFromPGColumn(name, dataType string, isNullable, isPK bool, defaultValue string) (packet.Field, error)

BuildFieldFromPGColumn создает TDTP Field из информации о столбце PostgreSQL

func IsPostgreSQLReservedWord

func IsPostgreSQLReservedWord(word string) bool

IsPostgreSQLReservedWord проверяет является ли слово зарезервированным в PostgreSQL

func ParsePostgreSQLType

func ParsePostgreSQLType(pgType string) (baseType string, length, precision, scale int)

ParsePostgreSQLType парсит PostgreSQL тип и извлекает параметры

func PostgreSQLToTDTP

func PostgreSQLToTDTP(pgType string) (schema.DataType, string, error)

PostgreSQLToTDTP конвертирует PostgreSQL тип в TDTP тип

func QuoteIdentifier

func QuoteIdentifier(identifier string) string

QuoteIdentifier всегда заключает идентификатор в двойные кавычки, дублируя внутренние кавычки по правилам PostgreSQL/SQLite (RFC-совместимо). Условное квотирование было уязвимо: нижнерегистровый незарезервированный идентификатор возвращался без кавычек, допуская SQL-инъекцию через TableName.

func TDTPToPostgreSQL

func TDTPToPostgreSQL(field packet.Field) string

TDTPToPostgreSQL конвертирует TDTP тип в PostgreSQL CREATE TABLE тип. Текстовые поля без специального subtype всегда становятся TEXT — см. TDTPToPostgreSQLStrict, если нужно восстановить заявленную длину.

func TDTPToPostgreSQLStrict

func TDTPToPostgreSQLStrict(field packet.Field, strict bool) string

TDTPToPostgreSQLStrict — как TDTPToPostgreSQL, но при strict=true восстанавливает VARCHAR(field.Length) для TEXT/VARCHAR/CHAR/String без специального subtype, вместо безусловного TEXT.

strict=false (по умолчанию, и то же что TDTPToPostgreSQL) — CREATE TABLE не может обрезать данные при импорте: 7887de4 сначала восстанавливал VARCHAR(N) отсюда и был отменён именно потому, что это резало данные на интеграционных тестах ("Данные обрезались при импорте из-за ограничения длины"). Регрессия закрыта не тестом, а удалением кода — strict=true возвращает то же поведение осознанно, по флагу, а не по умолчанию.

strict=true — колонка получает то же ограничение длины, что было на источнике; вставка более длинного значения падает с ошибкой БД сразу, а не расширяется молча до TEXT. Точное совпадение длины гарантировано только при импорте назад в ту же СУБД, откуда шёл экспорт — единица длины (символы/байты) и правила усечения различаются между движками.

Types

type Adapter

type Adapter struct {
	// contains filtered or unexported fields
}

Adapter представляет адаптер для работы с PostgreSQL Реализует интерфейс adapters.Adapter

func NewAdapter deprecated

func NewAdapter(connString string) (*Adapter, error)

NewAdapter создает новый адаптер для PostgreSQL (legacy)

Deprecated: используйте adapters.New() с фабрикой

func NewAdapterWithSchema deprecated

func NewAdapterWithSchema(connString, schema string) (*Adapter, error)

NewAdapterWithSchema создает адаптер с указанной схемой (legacy)

Deprecated: используйте adapters.New() с фабрикой

func (*Adapter) BeginTx

func (a *Adapter) BeginTx(ctx context.Context) (adapters.Tx, error)

BeginTx начинает транзакцию Реализует интерфейс adapters.Adapter

func (*Adapter) Close

func (a *Adapter) Close(ctx context.Context) error

Close закрывает connection pool Реализует интерфейс adapters.Adapter

func (*Adapter) Connect

func (a *Adapter) Connect(ctx context.Context, cfg adapters.Config) error

Connect устанавливает подключение к PostgreSQL Реализует интерфейс adapters.Adapter

func (*Adapter) CreateTable

func (a *Adapter) CreateTable(ctx context.Context, tableName string, pktSchema packet.Schema) error

CreateTable implements base.TableManager interface

func (*Adapter) DropTable

func (a *Adapter) DropTable(ctx context.Context, tableName string) error

DropTable implements base.TableManager interface

func (*Adapter) Exec

func (a *Adapter) Exec(ctx context.Context, sql string, args ...any) error

Exec выполняет SQL команду (helper метод)

func (*Adapter) ExecuteRawQuery

func (a *Adapter) ExecuteRawQuery(ctx context.Context, query string) (*packet.DataPacket, error)

ExecuteRawQuery выполняет произвольный SQL SELECT запрос и возвращает результат как DataPacket. Используется ETL pipeline для загрузки данных из источников. Использует тот же путь что и ExportTable: ReadRowsWithSQL → convertValueToTDTP → RowsToData.

func (*Adapter) ExportTable

func (a *Adapter) ExportTable(ctx context.Context, tableName string) ([]*packet.DataPacket, error)

ExportTable экспортирует таблицу в TDTP reference пакеты Делегирует в base.ExportHelper для устранения дублирования кода

func (*Adapter) ExportTableIncremental

func (a *Adapter) ExportTableIncremental(ctx context.Context, tableName string, incrementalConfig adapters.IncrementalConfig) ([]*packet.DataPacket, string, error)

ExportTableIncremental экспортирует только измененные записи с момента последней синхронизации Реализует интерфейс adapters.Adapter

func (*Adapter) ExportTableWithQuery

func (a *Adapter) ExportTableWithQuery(ctx context.Context, tableName string, query *packet.Query, sender, recipient string) ([]*packet.DataPacket, error)

ExportTableWithQuery экспортирует таблицу с фильтрацией через TDTQL Делегирует в base.ExportHelper для устранения дублирования кода

func (*Adapter) GetDatabaseType

func (a *Adapter) GetDatabaseType() string

GetDatabaseType возвращает тип СУБД Реализует интерфейс adapters.Adapter

func (*Adapter) GetDatabaseVersion

func (a *Adapter) GetDatabaseVersion(ctx context.Context) (string, error)

GetDatabaseVersion возвращает версию PostgreSQL Реализует интерфейс adapters.Adapter

func (*Adapter) GetRowCount

func (a *Adapter) GetRowCount(ctx context.Context, tableName string) (int64, error)

GetRowCount implements base.DataReader interface Returns the number of rows in a table

func (*Adapter) GetSchemas

func (a *Adapter) GetSchemas(ctx context.Context) ([]string, error)

GetSchemas возвращает список всех схем в БД (helper метод)

func (*Adapter) GetTableNames

func (a *Adapter) GetTableNames(ctx context.Context) ([]string, error)

GetTableNames возвращает список всех таблиц в текущей схеме Реализует интерфейс adapters.Adapter

func (*Adapter) GetTableSchema

func (a *Adapter) GetTableSchema(ctx context.Context, tableName string) (packet.Schema, error)

GetTableSchema читает схему таблицы из PostgreSQL через information_schema Реализует интерфейс adapters.Adapter

func (*Adapter) GetViewNames

func (a *Adapter) GetViewNames(ctx context.Context) ([]adapters.ViewInfo, error)

GetViewNames возвращает список всех views в текущей схеме с информацией об updatable/read-only Реализует интерфейс adapters.Adapter

func (*Adapter) ImportPacket

func (a *Adapter) ImportPacket(ctx context.Context, pkt *packet.DataPacket, strategy adapters.ImportStrategy) error

ImportPacket импортирует один TDTP пакет в PostgreSQL. StrategyCopy: атомарная замена таблицы через временную (temp → rename). StrategyReplace/Ignore/Fail: прямой INSERT с ON CONFLICT в существующую таблицу. Реализует интерфейс adapters.Adapter

func (*Adapter) ImportPackets

func (a *Adapter) ImportPackets(ctx context.Context, packets []*packet.DataPacket, strategy adapters.ImportStrategy) error

ImportPackets импортирует множество пакетов атомарно через временную таблицу ImportPackets импортирует множество пакетов атомарно. StrategyCopy: атомарная замена таблицы через временную (temp → rename). StrategyReplace/Ignore/Fail: прямой INSERT с ON CONFLICT в существующую таблицу, что позволяет накапливать данные из нескольких источников/файлов без затирания. Реализует интерфейс adapters.Adapter

func (*Adapter) InsertRows

func (a *Adapter) InsertRows(ctx context.Context, tableName string, pktSchema packet.Schema, rows []packet.Row, strategy adapters.ImportStrategy) error

InsertRows implements base.DataInserter interface Uses COPY for bulk insert (PostgreSQL-specific fast path)

func (*Adapter) InspectTable

func (a *Adapter) InspectTable(ctx context.Context, tableName string) (*adapters.TableReport, error)

InspectTable returns extended metadata for a live PostgreSQL table. Implements adapters.Adapter.

func (*Adapter) ParseFailures

func (a *Adapter) ParseFailures() []base.ParseFailure

ParseFailures implements base.ParseFailureReporter: values this adapter's exports could not convert to their declared type, since the previous call.

func (*Adapter) Ping

func (a *Adapter) Ping(ctx context.Context) error

Ping проверяет доступность БД Реализует интерфейс adapters.Adapter

func (*Adapter) Pool

func (a *Adapter) Pool() *pgxpool.Pool

Pool возвращает *pgxpool.Pool для прямого доступа

func (*Adapter) Query

func (a *Adapter) Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error)

Query выполняет SQL запрос (helper метод)

func (*Adapter) QueryRow

func (a *Adapter) QueryRow(ctx context.Context, sql string, args ...any) pgx.Row

QueryRow выполняет SQL запрос возвращающий одну строку (helper метод)

func (*Adapter) ReadAllRows

func (a *Adapter) ReadAllRows(ctx context.Context, tableName string, pkgSchema packet.Schema) ([][]string, error)

ReadAllRows implements base.DataReader interface Reads all rows from a table using a direct SQL query. Note: must NOT call ExportTable (avoids circular call via exportHelper.ExportTable → ReadAllRows).

func (*Adapter) ReadAllRowsStream

func (a *Adapter) ReadAllRowsStream(ctx context.Context, tableName string, pkgSchema packet.Schema) (packet.Schema, <-chan []string, <-chan error, error)

ReadAllRowsStream читает таблицу потоком, реализуя base.StreamingDataReader.

Отдельная реализация, а не base.StreamSQLRows: тот работает с *sql.Rows, а этот адаптер живёт на pgx/pgxpool мимо database/sql. Общее у них — ячейка: значения идут через тот же pgCellToTDTP, что и readRowsWithSQL, поэтому потоковое и построчное чтение дают одни байты.

Запрос — тот же SELECT *, что у ReadAllRows, и по той же причине: схема сопоставляется с колонками ПО ПОЗИЦИИ (schema.Fields[i]), так что перечислять колонки в другом порядке нельзя.

Соединение берётся из пула на всю выгрузку. На большой таблице это минуты, и для боевой базы это заметнее, чем для файла: одно соединение из пула занято.

func (*Adapter) ReadRowsWithSQL

func (a *Adapter) ReadRowsWithSQL(ctx context.Context, sqlQuery string, pkgSchema packet.Schema) ([][]string, error)

ReadRowsWithSQL implements base.DataReader interface Executes a SQL query and returns rows

func (*Adapter) RenameTable

func (a *Adapter) RenameTable(ctx context.Context, oldName, newName string) error

RenameTable implements base.TableManager interface

func (*Adapter) Schema

func (a *Adapter) Schema() string

Schema возвращает текущую схему

func (*Adapter) SetColumnarLayout

func (a *Adapter) SetColumnarLayout(on bool)

SetColumnarLayout включает колоночную раскладку Data (--columnar).

func (*Adapter) SetMaxFallbackRows

func (a *Adapter) SetMaxFallbackRows(n int64)

SetMaxFallbackRows задаёт лимит строк для in-memory fallback при провале SQL pushdown.

func (*Adapter) SetMaxMessageSize

func (a *Adapter) SetMaxMessageSize(size int)

SetMaxMessageSize задаёт максимальный размер одного TDTP пакета (в байтах). Вызывается из CLI при указании --packet-size.

func (*Adapter) SetSchema

func (a *Adapter) SetSchema(schema string)

SetSchema устанавливает схему для операций

func (*Adapter) SetSkipSpecialValues

func (a *Adapter) SetSkipSpecialValues(skip bool)

SetSkipSpecialValues включает режим --fast: DetectAndApply пропускается.

func (*Adapter) TableExists

func (a *Adapter) TableExists(ctx context.Context, tableName string) (bool, error)

TableExists проверяет существование таблицы в текущей схеме Реализует интерфейс adapters.Adapter

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL