• Stars
    star
    656
  • Rank 66,068 (Top 2 %)
  • Language
    Go
  • License
    MIT License
  • Created about 13 years ago
  • Updated 9 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

The SQL Fan's Migrator

Tern - The SQL Fan's Migrator

Tern is a standalone migration tool for PostgreSQL. It includes traditional migrations as well as a separate optional workflow for managing database code such as functions and views.

Features

  • Multi-platform
  • Stand-alone binary
  • SSH tunnel support built-in
  • Data variable interpolation into migrations

Installation

go install github.com/jackc/tern/v2@latest

Creating a Tern Project

To create a new tern project in the current directory run:

tern init

Or to create the project somewhere else:

tern init path/to/project

Tern projects are composed of a directory of migrations and optionally a config file. See the sample directory for an example.

Configuration

Database connection settings can be specified via the standard PostgreSQL environment variables, via program arguments, or in a config file. By default tern will look in the current directory for the config file tern.conf and the migrations.

The tern.conf file is stored in the ini format with two sections, database and data. The database section contains settings for connection to the database server.

Values in the data section will be available for interpolation into migrations. This can help in scenarios where migrations are managing permissions and the user to which permissions are granted should be configurable.

If all database settings are supplied by PG* environment variables or program arguments the config file is not required. In particular, using the PGSERVICE can reduce or eliminate the need for a configuration file.

The entire tern.conf file is processed through the Go standard text/template package. Sprig functions are available.

Example tern.conf:

[database]
# host supports TCP addresses and Unix domain sockets
# host = /private/tmp
host = 127.0.0.1
# port = 5432
database = tern_test
user = jack
password = {{env "MIGRATOR_PASSWORD"}}
# version_table = public.schema_version
#
# sslmode generally matches the behavior described in:
# http://www.postgresql.org/docs/9.4/static/libpq-ssl.html#LIBPQ-SSL-PROTECTION
#
# There are only two modes that most users should use:
# prefer - on trusted networks where security is not required
# verify-full - require SSL connection
# sslmode = prefer
#
# "conn_string" accepts two formats; URI or DSN as described in:
# https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
#
# This property is lenient i.e., it does not throw error
# if values for both "conn_string" and "host/port/.." are
# provided. In this case, the individual properties will
# override the correspoding part in the "conn_string".
#
# URI format:
# conn_string = postgresql://other@localhost/otherdb?connect_timeout=10&application_name=myapp
# DSN format:
# conn_string = host=localhost port=5432 dbname=mydb connect_timeout=10

# Proxy the above database connection via SSH
# [ssh-tunnel]
# host =
# port = 22
# user defaults to OS user
# user =
# password is not required if using SSH agent authentication
# password =

[data]
prefix = foo
app_user = joe

This flexibility configuration style allows handling multiple environments such as test, development, and production in several ways.

  • Separate config file for each environment
  • Environment variables for database settings and optionally one config file for shared settings
  • Program arguments for database settings and optionally one config file for shared settings

In addition to program arguments, TERN_CONFIG and TERN_MIGRATIONS environment variables may be used to set the config path and migrations path respectively.

Migrations

To create a new migration:

tern new name_of_migration

This will create a migration file with the given name prefixed by the next available sequence number (e.g. 001, 002, 003). The -e flag can be used to automatically open the new file in EDITOR.

The migrations themselves have an extremely simple file format. They are simply the up and down SQL statements divided by a magic comment.

---- create above / drop below ----

Example:

create table t1(
  id serial primary key
);

---- create above / drop below ----

drop table t1;

If a migration is irreversible such as a drop table, simply delete the magic comment.

drop table widgets;

To interpolate a custom data value from the config file prefix the name with a dot and surround the whole with double curly braces.

create table {{.prefix}}config(
  id serial primary key
);

Migrations are read from files in the migration directory in the order of the numerical prefix. Each migration is run in a transaction.

Any SQL files in subdirectories of the migration directory, will be available for inclusion with the template command. This can be especially useful for definitions of views and functions that may have to be dropped and recreated when the underlying table(s) change.

// Include the file shared/v1_001.sql. Note the trailing dot.
// It is necessary if the shared file needs access to custom data values.
{{ template "shared/v1_001.sql" . }}
);

Tern uses the standard Go text/template package so conditionals and other advanced templating features are available if needed. See the package docs for details. Sprig functions are also available.

Migrations are wrapped in a transaction by default. Some SQL statements such as create index concurrently cannot be performed within a transaction. To disable the transaction include the magic comment:

---- tern: disable-tx ----

Migrating

To migrate up to the last version using migrations and config file located in the same directory simply run tern:

tern migrate

To migrate up or down to a specific version:

tern migrate --destination 42

To migrate up N versions:

tern migrate --destination +3

To migrate down N versions:

tern migrate --destination -3

To migrate down and rerun the previous N versions:

tern migrate --destination -+3

To use a different config file:

tern migrate --config path/to/tern.json

To use a different migrations directory:

tern migrate --migrations path/to/migrations

Renumbering Conflicting Migrations

When migrations are created on multiple branches the migrations need to be renumbered when the branches are merged. The tern renumber command can automatically do this. On the branch with the only migrations to keep at the lower numbers run tern renumber start. Merge the branches. Then run tern renumber finish.

$ git switch master
Switched to branch 'master'
$ ls
001_create_users.sql
002_add_last_login_to_users.sql

$ git switch feature
Switched to branch 'feature'
$ ls
001_create_users.sql
002_create_todos.sql

# Both branches have a migration number 2.

# Run tern renumber start on the branch with the migrations that should come first.

$ git switch master
Switched to branch 'master'
$ tern renumber start

# Then go to the branch with migrations that should come later and merge or rebase.

$ git switch feature
$ git rebase master
Successfully rebased and updated refs/heads/feature.
$ ls
001_create_users.sql
002_add_last_login_to_users.sql
002_create_todos.sql

# There are now two migrations with the same migration number.

$ tern renumber finish
$ ls
001_create_users.sql
002_add_last_login_to_users.sql
003_create_todos.sql

# The migrations are now renumbered in the correct order.

Code Packages

The migration paradigm works well for creating and altering tables, but it can be unwieldy when dealing with database code such as server side functions and views. For example, consider a schema where view c depends on view b which depends on view a. A change to a may require the following steps:

  1. Drop c
  2. Drop b
  3. Drop a
  4. Create a
  5. Create b
  6. Create c

In addition to the challenge of manually building such a migration it is difficult to use version control to see the changes in a particular database object over time when its definition is scattered through multiple migrations.

A solution to this is code packages. A code package is a directory with an install.sql file that contains the instructions to completely drop and recreate a set of database code. The command code install can be used to directly install a code package (especially useful during development) and the code snapshot command can be used to make a single migration that installs that code package.

For example given a directory code containing the following files:

-- install.sql
drop schema if exists code cascade;
create schema code;

{{ template "a.sql" . }}
{{ template "b.sql" . }}
{{ template "c.sql" . }}
-- a.sql
create view code.a as select ...;
-- b.sql
create view code.b as select * from code.a where ...;
-- c.sql
create view code.c as select * from code.b where ...;

Then this command would install the package into the database.

tern code install path/to/code --config path/to/tern.conf

And this command would create a migration from the current state of the code package.

tern code snapshot path/to/code --migrations path/to/migrations

Code packages have access to data variables defined in your configuration file as well as functions provided by Sprig.

It is recommended but not required for each code package to be installed into its own PostgreSQL schema. This schema could be determined by environment variable as part of a blue / green deployment process.

Template Tips

The env function can be used to read process environment variables.

drop schema if exists {{ env "CODE_SCHEMA" }} cascade;
create schema {{ env "CODE_SCHEMA" }};

The Sprig dictionary functions can be useful to call templates with extra parameters merged into the . value.

{{ template "_view_partial.sql" (merge (dict "view_name" "some_name" "where_clause" "some_extra_condition=true") . ) }}

SSH Tunnel

Tern includes SSH tunnel support. Simply supply the SSH host, and optionally port, user, and password in the config file or as program arguments and Tern will tunnel the database connection through that server. When using a SSH tunnel the database host should be from the context of the SSH server. For example, if your PostgreSQL server is pg.example.com, but you only have SSH access, then your SSH host would be pg.example.com and your database host would be localhost.

Tern will automatically use an SSH agent or ~/.ssh/id_rsa if available.

Embedding Tern

All the actual functionality of tern is in the github.com/jackc/tern/v2/migrate library. If you need to embed migrations into your own application this library can help.

Running the Tests

To run the tests tern requires two test databases to run migrations against.

  1. Create a new database for main tern program tests (e.g. tern_test).
  2. Open testdata/tern.conf.example
  3. Enter the connection information.
  4. Run tests with the connection string for the main tern program tests in the TERN_TEST_CONN_STRING environment variable.
  5. Save as testdata/tern.conf.
  6. Create another database for the migrate library tests (e.g. tern_migrate_test).
  7. Run tests with the connection string for the migrate library tests in the MIGRATE_TEST_CONN_STRING environment variable
TERN_TEST_CONN_STRING="host=/private/tmp database=tern_test" MIGRATE_TEST_CONN_STRING="host=/private/tmp database=tern_migrate_test" go test ./...

Prior Ruby Gem Version

The projects using the prior version of tern that was distributed as a Ruby Gem are incompatible with the version 1 release. However, that version of tern is still available through RubyGems and the source code is on the ruby branch.

Version History

2.1.0 (April 13, 2023)

  • Add print-connstring command to CLI (abs3ntdev)
  • Allow multiple config files on CLI (abs3ntdev)
  • Fix port being ignored in config file if sslmode is set (abs3ntdev)

2.0.1 (March 2, 2023)

  • Fix -e flag with terminal editors (abs3nt)

2.0.0 (February 23, 2023)

  • Remove deprecated env access syntax in config file
  • Replace MigratorFS interface with fs.FS
  • Upgrade to pgx v5
  • Upgrade to sprig v3
  • Add TERN_CONFIG and TERN_MIGRATIONS environment variables
  • Add -e flag to tern new to open file in EDITOR
  • Add per migration disable-tx option
  • Add renumber commands to help when merging branches that have conflicting migration numbers

1.13.0 (April 21, 2022)

  • Add conn string connection config option (vivek-shrikhande)
  • Add Filename to MigrationPgError (davidmdm)

1.12.5 (June 12, 2021)

  • Look for SSH keys in ~/.ssh/id_rsa (Miles Delahunty)

1.12.4 (February 27, 2021)

  • Use user's known_hosts file when connecting via SSH

1.12.3 (December 24, 2020)

  • Fix reported version number

1.12.2 (December 23, 2020)

  • Fix setting port from config file
  • Fix non-schema qualified version table not in public but in search path (Tynor Fujimoto)

1.12.1 (June 27, 2020)

  • Update to latest version of pgx.

1.12.0 (June 26, 2020)

  • Command code install no longer outputs compiled SQL.
  • Add code compile command to print compiled SQL code package.
  • Better error reporting for code install.

1.11.0 (April 10, 2020)

  • Add Sprig functions to configuration file and migrations.
  • Add SQL code management distinct from migrations.

1.10.2 (March 28, 2020)

  • CLI now handles SIGINT (ctrl+c) and attempts to cancel in-progress migration before quitting

1.10.1 (March 24, 2020)

  • Fix default CLI version-table argument overriding config value

1.10.0 (March 7, 2020)

  • Better locking to protect against multiple concurrent migrators on first run
  • Update pgx version to support PostgreSQL service files

1.9.1 (February 1, 2020)

  • Look for version table in all schemas in search_path instead of just the top schema

1.9.0 (February 1, 2020)

  • Default version table is explicitly in public schema
  • Update to pgx v4 (Alex Gaynor)

1.8.2 (July 19, 2019)

  • Show PostgreSQL error details
  • Rename internal error type

1.8.1 (April 5, 2019)

  • Issue reset all after every migration
  • Use go modules instead of Godep / vendoring

1.8.0 (February 26, 2018)

  • Update to latest version of pgx (PostgreSQL driver)
  • Support PGSSLROOTCERT
  • Fix typos and internal cleanup
  • Refactor internals for easier embedding (hsyed)

1.7.1 (January 30, 2016)

  • Simplify SSH tunnel code so it does not listen on localhost

1.7.0 (January 17, 2016)

  • Add SSH tunnel support

1.6.1 (January 16, 2016)

  • Fix version output
  • Evaluate config files through text/template with ENV

1.6.0 (January 15, 2016)

  • Optionally read database connection settings from environment
  • Accept database connection settings via program arguments
  • Make config file optional

1.5.0 (October 1, 2015)

  • Add status command
  • Add relative migration destinations
  • Add redo migration destinations

1.4.0 (May 15, 2015)

  • Add TLS support

1.3.3 (May 1, 2015)

  • Fix version output

1.3.2 (May 1, 2015)

  • Better error messages

1.3.1 (December 24, 2014)

  • Fix custom version table name

1.3.0 (December 23, 2014)

  • Prefer host config whether connecting with unix domain socket or TCP

1.2.2 (May 30, 2014)

  • Fix new migration short name

1.2.1 (May 18, 2014)

  • Support socket directory as well as socket file

1.2.0 (May 6, 2014)

  • Move to subcommand interface
  • Require migrations to begin with ascending, gapless numbers
  • Fix: migrations directory can contain other files
  • Fix: gracefully handle invalid current version
  • Fix: gracefully handle migrations with duplicate sequence number

1.1.1 (April 22, 2014)

  • Do not require user -- default to OS user

1.1.0 (April 22, 2014)

  • Add sub-template support
  • Switch to ini for config files
  • Add custom data merging

1.0.0 (April 19, 2014)

  • Total rewrite in Go

0.7.1

  • Print friendly error message when database error occurs instead of stack trace.

0.7.0

  • Added ERB processing to SQL files

License

Copyright (c) 2011-2014 Jack Christensen, released under the MIT license

More Repositories

1

pgx

PostgreSQL driver and toolkit for Go
Go
8,422
star
2

tod

Time of day and shift types for Ruby
Ruby
415
star
3

surus

PostgreSQL extensions for ActiveRecord
Ruby
395
star
4

sqlfmt

SQL Formatter
Yacc
335
star
5

pgtype

Go
295
star
6

pglogrepl

PostgreSQL logical replication library for Go.
Go
277
star
7

puddle

Generic resource pool for Go
Go
232
star
8

pgconn

Go
168
star
9

pgproto3

Go
151
star
10

pgerrcode

Package pgerrcode contains constants for PostgreSQL error codes.
Go
130
star
11

pgmock

Go
111
star
12

edge

Edge provides graph functionality to ActiveRecord.
Ruby
89
star
13

mat-view-strat-pg

Materialized View Strategies Using PostgreSQL
PLpgSQL
59
star
14

go_db_bench

Benchmarks for database/drivers from Go
Go
52
star
15

json_api_bench

Benchmark of Rails and PostgreSQL JSON generation techniques
Ruby
32
star
16

tpr

The Pithy Reader - a fast, simple, and lightweight feed aggregator and reader
Go
22
star
17

pgxlisten

pgxlisten provides higher level PostgreSQL LISTEN / NOTIFY tooling built on pgx.
Go
18
star
18

pg-custom-aggregate-grt

PostgreSQL custom aggregate greatest running total implemented in PL/pgSQL and C
PLpgSQL
17
star
19

pgxutil

Go
16
star
20

react2fs

Run command in reaction to file system events
Go
15
star
21

chunkreader

Go
14
star
22

overload

HTTP load tester
Go
14
star
23

pgxrecord

pgxrecord is a tiny framework for CRUD operations and data mapping.
Go
14
star
24

pgxjob

pgxjob provides a job queue implementation using PostgreSQL.
Go
13
star
25

pgx-top-to-bottom

HTML
13
star
26

pgio

Package pgio is a low-level toolkit building messages in the PostgreSQL wire protocol.
Go
12
star
27

command_model

CommandModel - when update_attributes isn't enough.
Ruby
12
star
28

pgx-zerolog

Go
10
star
29

writable-cte-examples

Examples of Writable Common Table Expressions in PostgreSQL
Ruby
9
star
30

pgxdata

Go
9
star
31

pgtocsv

pgtocsv executes a query on a PostgreSQL database and outputs the results in CSV.
Go
9
star
32

numfmt

Number formatting in Go
Go
9
star
33

pgservicefile

pgservicefile is a parser PostgreSQL for service files (e.g. .pg_service.conf)
Go
8
star
34

pgsql

Go
8
star
35

go_map_vs_switch

Benchmark for Go map of functions vs. switch
Go
7
star
36

pgx-shopspring-decimal

Go
7
star
37

pg-extid

Go
7
star
38

booklog

Go
7
star
39

go-extid

Go
7
star
40

pgundolog

pgundolog is a system for rolling back changes to a PostgreSQL database without using transactions.
PLpgSQL
7
star
41

pgx-zap

Go
6
star
42

pgx-gofrs-uuid

Go
6
star
43

hannibal

Experimental PostgreSQL web application server
Go
5
star
44

cavein

TCP tunnel that designed for testing applications with random connection losses
Go
5
star
45

quo_vadis

Really simple and fast HTTP Router
Go
5
star
46

pgxrepl

Replication client library for PostgreSQL in Go using the pgx driver
Go
5
star
47

text_tunnel

Use your local text editor to edit files on remote servers.
Ruby
5
star
48

pgpassfile

Go
4
star
49

doublecheck

Doublecheck your database before it is too late
Go
4
star
50

csvtopg

csvtopg copies a CSV to a PostgreSQL database.
Go
4
star
51

chicagolang-feb-2015

Slides for Building Command Line Applications in Go
Go
4
star
52

go_router_tutorial

Tutorial to build a simple HTTP router in Go
Go
4
star
53

why-pgx

Slides for lightning talk on pgx
4
star
54

gel

Go Compiled Templates
Go
3
star
55

ego

Experimental shorthand for Go error handling.
Go
3
star
56

pgx_notify_lost_connection

Test for PGX WaitForNotification when connection is lost
Go
3
star
57

pigeon-exp-xmlpp

Experimenting with pigeon PEG generator for Go
Go
3
star
58

doublecheck_view

Ruby gem for integrating doublecheck view pattern with database integration tests
Ruby
3
star
59

cachet

The cache for all your Ts
Go
2
star
60

pgxsql

Go
2
star
61

flex

Go
2
star
62

cgol-rust

Quick, experimental port of Go cgol to Rust
Rust
2
star
63

dailyprogrammer

Go
2
star
64

sadpath

sadpath provides a simple way to centralize error handling and reduce boilerplate.
Go
2
star
65

imperator

Imperator is an imperative normalization and validation library for Go.
Go
2
star
66

pg95-whats-new

What's new in PostgreSQL 9.5
PLpgSQL
2
star
67

go-intro

Quick introduction to Go -- sample project and speaker notes
Go
2
star
68

pgxbench

PLSQL
2
star
69

react2fs-deb

react2fs Debian package
Go
2
star
70

ppg

ppg runs a SQL file multiple times in parallel
Go
2
star
71

box

Go
2
star
72

tern-deb

Build scripts for debian package for tern
Go
2
star
73

sqlfmt-deb

sqlfmt Debian package
Go
2
star
74

ctxio

Go
2
star
75

jchat

Go
2
star
76

exp-go-repl

Experimenting with yacc
Go
2
star
77

webby-template-minimal-website

Minimal possible website template for Webby
2
star
78

mp

mp (map parser) provides a parser that parses maps into types defined at runtime.
Go
2
star
79

go-hello

Go program to practice packaging
Go
2
star
80

form

HTTP forms for Go
Go
2
star
81

tpr-deb

The Pithy Reader Debian Package
Go
2
star
82

go-server-closed-tcp-conn-os-inconsistencies

Example of differing server closed TCP connections on Linux and Windows
2
star
83

assert_db_rejects

Test database level constraints with ActiveRecord
Ruby
2
star
84

pgx_issues

Test and example cases for pgx issues
Go
1
star
85

context-rows-cancel

Test of Go database/sql context cancelation
Go
1
star
86

pgx845

This is a test application for https://github.com/jackc/pgx/issues/845.
Go
1
star
87

homebrew-formulas

Jack Christensen's Formulas
Ruby
1
star
88

markovbot

Go
1
star
89

reggie

Regular expression builder and tester for Ruby
Ruby
1
star
90

snake_case

Go
1
star
91

cbc-sinatra-crash

Test app demonstrating a crash
Ruby
1
star
92

hackers-delight

Hacker's Delight Exercises and Experiments
Go
1
star
93

pg_pl_bench

Go
1
star
94

booklogh

Experimental port of Booklog to Hannibal
PLpgSQL
1
star
95

alt_record

An alternative implementation of the active record pattern for Ruby
Ruby
1
star
96

structify

Go
1
star
97

form_model

Ruby
1
star
98

testdb

Test database manager
Go
1
star
99

go_database_sql_retry_bug

Test application to determine if database/sql auto-retry logic is safe
Go
1
star
100

go-tls-deadline-temporary-error

Go
1
star