Skip to content

Commit e95ebeb

Browse files
Merge branch 'main' into bplunkett/no-concurrent-index
2 parents 1e88c8a + 3efa9b9 commit e95ebeb

32 files changed

+2672
-319
lines changed

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,24 @@ assignees: ''
88
---
99

1010
### Describe the bug**
11-
[//]: # A clear and concise description of what the bug is.
11+
<!-- A clear and concise description of what the bug is. -->
1212

1313
### Expected behavior**
14-
[//]: # A clear and concise description of what you expected to happen.
14+
<!-- A clear and concise description of what you expected to happen. -->
1515

1616
### To Reproduce
17-
[//]: # Steps to reproduce the behavior:
18-
[//]: # 1. Go to '...'
19-
[//]: # 2. Click on '....'
20-
[//]: # 3. Scroll down to '....'
21-
[//]: # 4. See error
17+
<!-- Steps to reproduce the behavior:
18+
1. Go to '...'
19+
2. Click on '....'
20+
3. Scroll down to '....'
21+
4. See error -->
2222

2323
### Context
24-
[//]: # Fetch from "pg-schema-diff version" if using the CLI or go.mod if using the library
24+
<!-- Fetch from "pg-schema-diff version" if using the CLI or go.mod if using the library -->
2525
pg-schema-diff version: Some sha or version
26-
[//]: # If you are using the CLI or using the library programatically
26+
<!-- If you are using the CLI or using the library programatically -->
2727
pg-schema-diff usage: [CLI | LIBRARY]
2828
Postgres version: [14, 15, 16, 17]
29-
[//]: # Only if relevant
29+
<!-- Only if relevant -->
3030
pg_dump of database: Gist link
3131

.github/ISSUE_TEMPLATE/feature_request.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ assignees: ''
88
---
99

1010
### Describe the feature
11-
[//]: # Provide a thorough description of the feature. If possible, include examples of the feature and its behavior. For example, if it's a new command flag, provide examples of using the command flag and expected outputs.
11+
<!-- Provide a thorough description of the feature. If possible, include examples of the feature and its behavior. For example, if it's a new command flag, provide examples of using the command flag and expected outputs. -->
1212

1313
### Motivation
14-
[//]: # Describe why you want this feature. This is important for assessing the priority of the feature and if alternative solutions exist.
14+
<!-- Describe why you want this feature. This is important for assessing the priority of the feature and if alternative solutions exist. -->

.github/pull_request_template.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
[//]: # README: Ensure you've read the CONTRIBUTING.MD and that your commits are signed
1+
<!-- README: Ensure you've read the CONTRIBUTING.MD and that your commits are signed -->
22

33
### Description
4-
[//]: # A clear and concise description of the purpose of this Pull Request. What is being changed? Include any relevant background for this change.
4+
<!-- A clear and concise description of the purpose of this Pull Request. What is being changed? Include any relevant background for this change. -->
55

66
### Motivation
7-
[//]: # Why you made these changes. Link to any relevant issues.
7+
<!-- Why you made these changes. Link to any relevant issues. -->
88

99
### Testing
10-
[//]: # Describe how you tested these changes
10+
<!-- Describe how you tested these changes -->

.sqlfluff

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[sqlfluff]
22
dialect = postgres
33
templater = placeholder
4+
large_file_skip_byte_limit = 0
45

56
[sqlfluff:templater:placeholder]
67
param_regex = sqlc.arg\(.*\)

README.md

Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,28 @@ index cc3a14b..cf4b32d 100644
2929
The generated plan (*queries using `message_idx` will always have an index backing them, even while the new index is being built*):
3030
```
3131
$ pg-schema-diff plan --from-dsn "postgres://postgres:postgres@localhost:5432/postgres" --to-dir ./schema
32-
################################ Generated plan ################################
33-
1. ALTER INDEX "message_idx" RENAME TO "pgschemadiff_tmpidx_message_idx_IiaKzkvPQtyA7ob9piVqiQ";
34-
-- Statement Timeout: 3s
35-
36-
2. CREATE INDEX CONCURRENTLY message_idx ON public.foobar USING btree (message, created_at);
37-
-- Statement Timeout: 20m0s
38-
-- Lock Timeout: 3s
39-
-- Hazard INDEX_BUILD: This might affect database performance. Concurrent index builds require a non-trivial amount of CPU, potentially affecting database performance. They also can take a while but do not lock out writes.
40-
41-
3. DROP INDEX CONCURRENTLY "pgschemadiff_tmpidx_message_idx_IiaKzkvPQtyA7ob9piVqiQ";
42-
-- Statement Timeout: 20m0s
43-
-- Lock Timeout: 3s
44-
-- Hazard INDEX_DROPPED: Dropping this index means queries that use this index might perform worse because they will no longer will be able to leverage it.
32+
/*
33+
Statement 0
34+
*/
35+
SET SESSION statement_timeout = 3000;
36+
SET SESSION lock_timeout = 3000;
37+
ALTER INDEX "public"."message_idx" RENAME TO "pgschemadiff_tmpidx_message_idx_cWw18w4cQ_i7MKzfy7ek9g";
38+
39+
/*
40+
Statement 1
41+
- INDEX_BUILD: This might affect database performance. Concurrent index builds require a non-trivial amount of CPU, potentially affecting database performance. They also can take a while but do not lock out writes.
42+
*/
43+
SET SESSION statement_timeout = 1200000;
44+
SET SESSION lock_timeout = 3000;
45+
CREATE INDEX CONCURRENTLY message_idx ON public.foobar USING btree (message, created_at);
46+
47+
/*
48+
Statement 2
49+
- INDEX_DROPPED: Dropping this index means queries that use this index might perform worse because they will no longer will be able to leverage it.
50+
*/
51+
SET SESSION statement_timeout = 1200000;
52+
SET SESSION lock_timeout = 3000;
53+
DROP INDEX CONCURRENTLY "public"."pgschemadiff_tmpidx_message_idx_cWw18w4cQ_i7MKzfy7ek9g";
4554
```
4655
### Online `NOT NULL` constraint creation
4756
Your project's diff:
@@ -61,18 +70,33 @@ index cc3a14b..5a1cec2 100644
6170
The generated plan (*leverages check constraints to eliminate the need for a long-lived access-exclusive lock on the table*):
6271
```
6372
$ pg-schema-diff plan --from-dsn "postgres://postgres:postgres@localhost:5432/postgres" --to-dir ./schema
64-
################################ Generated plan ################################
65-
1. ALTER TABLE "public"."foobar" ADD CONSTRAINT "pgschemadiff_tmpnn_BCOxMXqAQwaXlKPCRXoMMg" CHECK("created_at" IS NOT NULL) NOT VALID;
66-
-- Statement Timeout: 3s
67-
68-
2. ALTER TABLE "public"."foobar" VALIDATE CONSTRAINT "pgschemadiff_tmpnn_BCOxMXqAQwaXlKPCRXoMMg";
69-
-- Statement Timeout: 3s
70-
71-
3. ALTER TABLE "public"."foobar" ALTER COLUMN "created_at" SET NOT NULL;
72-
-- Statement Timeout: 3s
73-
74-
4. ALTER TABLE "public"."foobar" DROP CONSTRAINT "pgschemadiff_tmpnn_BCOxMXqAQwaXlKPCRXoMMg";
75-
-- Statement Timeout: 3s
73+
/*
74+
Statement 0
75+
*/
76+
SET SESSION statement_timeout = 3000;
77+
SET SESSION lock_timeout = 3000;
78+
ALTER TABLE "public"."foobar" ADD CONSTRAINT "pgschemadiff_tmpnn_GimngG1rRkODhKvgjhGfNA" CHECK("created_at" IS NOT NULL) NOT VALID;
79+
80+
/*
81+
Statement 1
82+
*/
83+
SET SESSION statement_timeout = 3000;
84+
SET SESSION lock_timeout = 3000;
85+
ALTER TABLE "public"."foobar" VALIDATE CONSTRAINT "pgschemadiff_tmpnn_GimngG1rRkODhKvgjhGfNA";
86+
87+
/*
88+
Statement 2
89+
*/
90+
SET SESSION statement_timeout = 3000;
91+
SET SESSION lock_timeout = 3000;
92+
ALTER TABLE "public"."foobar" ALTER COLUMN "created_at" SET NOT NULL;
93+
94+
/*
95+
Statement 3
96+
*/
97+
SET SESSION statement_timeout = 3000;
98+
SET SESSION lock_timeout = 3000;
99+
ALTER TABLE "public"."foobar" DROP CONSTRAINT "pgschemadiff_tmpnn_GimngG1rRkODhKvgjhGfNA";
76100
```
77101

78102
# Key features
@@ -90,10 +114,14 @@ $ pg-schema-diff plan --from-dsn "postgres://postgres:postgres@localhost:5432/po
90114
* Strong support of partitions
91115
# Install
92116
## CLI
117+
Brew:
118+
```bash
119+
brew install pg-schema-diff
120+
```
121+
Go toolchain:
93122
```bash
94123
go install github.com/stripe/pg-schema-diff/cmd/pg-schema-diff@latest
95124
```
96-
97125
## Library
98126
```bash
99127
go get -u github.com/stripe/pg-schema-diff@latest
@@ -171,12 +199,11 @@ for _, stmt := range plan.Statements {
171199
```
172200

173201
# Supported Postgres versions
174-
Supported: 14, 15, 16, 17
202+
Supported: 14, 15, 16, 17
175203
Unsupported: <= 13 are not supported. Use at your own risk.
176204

177205
# Unsupported migrations
178206
An abridged list of unsupported migrations:
179-
- Views (Planned)
180207
- Privileges (Planned)
181208
- Types (Only enums are currently supported)
182209
- Renaming. The diffing library relies on names to identify the old and new versions of a table, index, etc. If you rename

cmd/pg-schema-diff/apply_cmd_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@ func (suite *cmdTestSuite) TestApplyCmd() {
7878
})
7979
// The migration should have been successful. Assert it was.
8080
expectedDb := tempDbWithSchema(suite.T(), suite.pgEngine, tc.expectedSchemaDDL)
81-
expectedDbDump, err := pgdump.GetDump(expectedDb, pgdump.WithSchemaOnly())
81+
expectedDbDump, err := pgdump.GetDump(expectedDb, pgdump.WithSchemaOnly(), pgdump.WithRestrictKey(pgdump.FixedRestrictKey))
8282
suite.Require().NoError(err)
83-
fromDbDump, err := pgdump.GetDump(fromDb, pgdump.WithSchemaOnly())
83+
fromDbDump, err := pgdump.GetDump(fromDb, pgdump.WithSchemaOnly(), pgdump.WithRestrictKey(pgdump.FixedRestrictKey))
8484
suite.Require().NoError(err)
8585

8686
suite.Equal(expectedDbDump, fromDbDump)

cmd/pg-schema-diff/datastructs.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package main
33
import (
44
"fmt"
55
"sort"
6+
7+
"github.com/stripe/pg-schema-diff/internal/util"
68
)
79

810
func mustGetAndDeleteKey(m map[string]string, key string) (string, error) {
@@ -15,10 +17,7 @@ func mustGetAndDeleteKey(m map[string]string, key string) (string, error) {
1517
}
1618

1719
func keys(m map[string]string) []string {
18-
var vals []string
19-
for k := range m {
20-
vals = append(vals, k)
21-
}
20+
vals := util.Keys(m)
2221
sort.Strings(vals)
2322
return vals
2423
}

cmd/pg-schema-diff/datastructs_test.go

Lines changed: 0 additions & 42 deletions
This file was deleted.

cmd/pg-schema-diff/main_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ type runCmdWithAssertionsParams struct {
3434
// saving schemas to a randomly generated temporary directory.
3535
dynamicArgs []dArgGenerator
3636

37+
// outputEquals is the exact string that stdout should equal.
38+
outputEquals string
3739
// outputContains is a list of substrings that are expected to be contained in the stdout output of the command.
3840
outputContains []string
3941
// expectErrContains is a list of substrings that are expected to be contained in the error returned by
@@ -72,6 +74,9 @@ func (suite *cmdTestSuite) runCmdWithAssertions(tc runCmdWithAssertionsParams) {
7274
suite.Contains(stdOutStr, o)
7375
}
7476
}
77+
if len(tc.outputEquals) > 0 {
78+
suite.Equal(tc.outputEquals, stdOutStr)
79+
}
7580
}
7681

7782
// dArgGenerator generates argument at the run-time of the test case...

cmd/pg-schema-diff/plan_cmd.go

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ func buildPlanCmd() *cobra.Command {
4242
toSchemaFlags := createSchemaSourceFlags(cmd, "to-")
4343
tempDbConnFlags := createConnectionFlags(cmd, "temp-db-", "The temporary database to use for schema extraction. This is optional if diffing to/from a Postgres instance")
4444
planOptsFlags := createPlanOptionsFlags(cmd)
45-
outputFmt := outputFormatPretty
45+
outputFmt := outputFormatSql
4646
cmd.Flags().Var(
4747
&outputFmt,
4848
"output-format",
49-
fmt.Sprintf("Change the output format for what is printed. Defaults to pretty-printed human-readable output. (options: %s)", strings.Join(outputFormatStrings(), ", ")),
49+
fmt.Sprintf("Change the output format for what is printed. Defaults to %v. (options: %s)", outputFmt.identifier, strings.Join(outputFormatStrings(), ", ")),
5050
)
5151
cmd.RunE = func(cmd *cobra.Command, args []string) error {
5252
logger := log.SimpleLogger()
@@ -163,17 +163,23 @@ type (
163163
)
164164

165165
var (
166-
outputFormatPretty = outputFormat{
167-
identifier: "pretty",
168-
convertToOutputString: planToPrettyS,
169-
}
170-
171166
outputFormatJson = outputFormat{
172167
identifier: "json",
173168
convertToOutputString: planToJsonS,
174169
}
175170

171+
outputFormatSql = outputFormat{
172+
identifier: "sql",
173+
convertToOutputString: planToSql,
174+
}
175+
176+
outputFormatPretty = outputFormat{
177+
identifier: "pretty",
178+
convertToOutputString: planToPrettyS,
179+
}
180+
176181
outputFormats = []outputFormat{
182+
outputFormatSql,
177183
outputFormatPretty,
178184
outputFormatJson,
179185
}
@@ -590,10 +596,33 @@ func hazardToPrettyS(hazard diff.MigrationHazard) string {
590596
}
591597
}
592598

599+
// planToJsonS converts the plan to JSON.
593600
func planToJsonS(plan diff.Plan) string {
594601
jsonData, err := json.MarshalIndent(plan, "", " ")
595602
if err != nil {
596603
panic(err)
597604
}
598605
return string(jsonData)
599606
}
607+
608+
// planToSql converts the plan to one large runnable SQL script.
609+
func planToSql(plan diff.Plan) string {
610+
sb := strings.Builder{}
611+
for i, stmt := range plan.Statements {
612+
sb.WriteString("/*\n")
613+
sb.WriteString(fmt.Sprintf("Statement %d\n", i))
614+
if len(stmt.Hazards) > 0 {
615+
for _, hazard := range stmt.Hazards {
616+
sb.WriteString(fmt.Sprintf(" - %s\n", hazardToPrettyS(hazard)))
617+
}
618+
}
619+
sb.WriteString("*/\n")
620+
sb.WriteString(fmt.Sprintf("SET SESSION statement_timeout = %d;\n", stmt.Timeout.Milliseconds()))
621+
sb.WriteString(fmt.Sprintf("SET SESSION lock_timeout = %d;\n", stmt.LockTimeout.Milliseconds()))
622+
sb.WriteString(fmt.Sprintf("%s;", stmt.DDL))
623+
if i < len(plan.Statements)-1 {
624+
sb.WriteString("\n\n")
625+
}
626+
}
627+
return sb.String()
628+
}

0 commit comments

Comments
 (0)