diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d908da96..0e414db1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -151,7 +151,7 @@ included below. * pgtype has been spun off to a separate package (github.com/jackc/pgtype). * pgproto3 has been spun off to a separate package (github.com/jackc/pgproto3/v2). * Logical replication support has been spun off to a separate package (github.com/jackc/pglogrepl). -* Lower level PostgreSQL functionality is now implemented in a separate package (github.com/jackc/pgconn). +* Lower level PostgreSQL functionality is now implemented in a separate package (github.com/ethanpailes/pgconn). * Tests are now configured with environment variables. * Conn has an automatic statement cache by default. * Batch interface has been simplified. diff --git a/README.md b/README.md index 59e7c7919..ee5a28cd3 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,7 @@ pgx follows semantic versioning for the documented public API on stable releases pgx is the head of a family of PostgreSQL libraries. Many of these can be used independently. Many can also be accessed from pgx for lower-level control. -### [github.com/jackc/pgconn](https://github.com/jackc/pgconn) +### [github.com/ethanpailes/pgconn](https://github.com/ethanpailes/pgconn) `pgconn` is a lower-level PostgreSQL database driver that operates at nearly the same level as the C library `libpq`. diff --git a/batch.go b/batch.go index 9f787f996..c69224b83 100644 --- a/batch.go +++ b/batch.go @@ -3,7 +3,7 @@ package pgx import ( "context" - "github.com/jackc/pgconn" + "github.com/ethanpailes/pgconn" errors "golang.org/x/xerrors" ) diff --git a/batch_test.go b/batch_test.go index 7a25ba52f..2077a016f 100644 --- a/batch_test.go +++ b/batch_test.go @@ -5,8 +5,8 @@ import ( "os" "testing" - "github.com/jackc/pgconn" - "github.com/jackc/pgconn/stmtcache" + "github.com/ethanpailes/pgconn" + "github.com/ethanpailes/pgconn/stmtcache" "github.com/jackc/pgx/v4" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/bench_test.go b/bench_test.go index f2d98bab0..5429a2391 100644 --- a/bench_test.go +++ b/bench_test.go @@ -12,8 +12,8 @@ import ( "testing" "time" - "github.com/jackc/pgconn" - "github.com/jackc/pgconn/stmtcache" + "github.com/ethanpailes/pgconn" + "github.com/ethanpailes/pgconn/stmtcache" "github.com/jackc/pgtype" "github.com/jackc/pgx/v4" "github.com/stretchr/testify/require" diff --git a/conn.go b/conn.go index 28b0f87cd..81b01560d 100644 --- a/conn.go +++ b/conn.go @@ -8,8 +8,8 @@ import ( errors "golang.org/x/xerrors" - "github.com/jackc/pgconn" - "github.com/jackc/pgconn/stmtcache" + "github.com/ethanpailes/pgconn" + "github.com/ethanpailes/pgconn/stmtcache" "github.com/jackc/pgtype" "github.com/jackc/pgx/v4/internal/sanitize" ) @@ -74,6 +74,15 @@ type Conn struct { wbuf []byte preallocatedRows []connRows eqb extendedQueryBuilder + + // We track whether or not this connection has any outstanding un-`Close`d transactions + // because certain operations (like deallocating a prepared statement) are not guaranteed to + // succeed in the middle of a transaction. + inTx bool + // A queue of statements to clobber from the statement cache as soon as it is safe to do so + // (once we are not in a transaction or running batch sql). + stmtsToFlush []string + } // Identifier a PostgreSQL identifier or name. Identifiers can be composed of @@ -652,6 +661,16 @@ optionLoop: rows.resultReader = c.pgConn.ExecParams(ctx, sql, c.eqb.paramValues, sd.ParamOIDs, c.eqb.paramFormats, resultFormats) } else { rows.resultReader = c.pgConn.ExecPrepared(ctx, sd.Name, c.eqb.paramValues, c.eqb.paramFormats, resultFormats) + if IsInvalidCachedStatementPlanError(rows.resultReader.Err()) { + rows.fatal(rows.resultReader.Err()) + // If the statement has been invalidated out from under us, we won't retry, but we do + // clobber it out of the cache so that an application level retry has a prayer of + // succeeding. + err = c.invalidateStmt(ctx, sd.SQL) // already have an error to return + if err != nil { + rows.err = errors.Errorf("cleaning up from '%s': %s", rows.err.Error(), err.Error()) + } + } } return rows, rows.err @@ -790,3 +809,49 @@ func (c *Conn) sanitizeForSimpleQuery(sql string, args ...interface{}) (string, return sanitize.SanitizeSQL(sql, valueArgs...) } + +// invalidateStmt purges the given prepared statement from the stmtcache, or queues it up +// for flushing if the connection is not in a good state to flush a prepared statement. +func (c *Conn) invalidateStmt(ctx context.Context, sql string) error { + // TODO(ethan): for some reason I'm trying to invalidate a statement when the connection + // is locked. Wut do, I don't think I can explictly unlock, and I'm not even + // really sure what this lock is protecting anyway. I think I may need to test + // if the connection is busy here as well, though I'm not really sure how I + // can arrange to be notified when the lock is released. + if c.inTx { + c.stmtsToFlush = append(c.stmtsToFlush, sql) + return nil + } else { + return c.stmtcache.ClearStmt(ctx, sql) + } +} + +func (c *Conn) setInTx(ctx context.Context, value bool) error { + c.inTx = value + + if !c.inTx { + // flush any outstanding statements + for _, sql := range c.stmtsToFlush { + err := c.stmtcache.ClearStmt(ctx, sql) + if err != nil { + return err + } + } + } + + return nil +} + +// IsInvalidCachedStatementPlanError inspects the given error and returns true if the +// error is due to the fact that we just tried to execute a cached prepared statement +// that is now invalid due to a schema change. +func IsInvalidCachedStatementPlanError(err error) bool { + pgErr, ok := err.(*pgconn.PgError) + if !ok { + return false + } + + return pgErr.Severity == "ERROR" && + pgErr.Code == "0A000" && + pgErr.Message == "cached plan must not change result type" +} diff --git a/conn_test.go b/conn_test.go index ba4eda95a..681c7be37 100644 --- a/conn_test.go +++ b/conn_test.go @@ -8,8 +8,8 @@ import ( "testing" "time" - "github.com/jackc/pgconn" - "github.com/jackc/pgconn/stmtcache" + "github.com/ethanpailes/pgconn" + "github.com/ethanpailes/pgconn/stmtcache" "github.com/jackc/pgtype" "github.com/jackc/pgx/v4" "github.com/stretchr/testify/assert" @@ -879,3 +879,130 @@ func TestDomainType(t *testing.T) { } }) } + +func TestStmtCacheInvalidationConn(t *testing.T) { + ctx := context.Background() + + conn := mustConnectString(t, os.Getenv("PGX_TEST_DATABASE")) + defer closeConn(t, conn) + + // create a table and fill it with some data + _, err := conn.Exec(ctx, ` + DROP TABLE IF EXISTS drop_cols; + CREATE TABLE drop_cols ( + id SERIAL PRIMARY KEY NOT NULL, + f1 int NOT NULL, + f2 int NOT NULL + ); + `) + require.NoError(t, err) + _, err = conn.Exec(ctx, "INSERT INTO drop_cols (f1, f2) VALUES (1, 2)") + require.NoError(t, err) + + getSQL := "SELECT * FROM drop_cols WHERE id = $1" + + // This query will populate the statement cache. We don't care about the result. + rows, err := conn.Query(ctx, getSQL, 1) + require.NoError(t, err) + rows.Close() + + // Now, change the schema of the table out from under the statement, making it invalid. + _, err = conn.Exec(ctx, "ALTER TABLE drop_cols DROP COLUMN f1") + require.NoError(t, err) + + // We must get an error the first time we try to re-execute a bad statement. + // It is up to the application to determine if it wants to try again. We punt to + // the application because there is no clear recovery path in the case of failed transactions + // or batch operations and because automatic retry is tricky and we don't want to get + // it wrong at such an importaint layer of the stack. + rows, err = conn.Query(ctx, getSQL, 1) + if !pgx.IsInvalidCachedStatementPlanError(err) { + if err == nil { + t.Fatal("expected InvalidCachedStatementPlanError: no error") + } else { + t.Fatalf("expected InvalidCachedStatementPlanError, got: %s", err.Error()) + } + } + rows.Close() + + // On retry, the statement should have been flushed from the cache. + rows, err = conn.Query(ctx, getSQL, 1) + require.NoError(t, err) + rows.Next() + err = rows.Err() + require.NoError(t, err) + rows.Close() + + ensureConnValid(t, conn) +} + +func TestStmtCacheInvalidationTx(t *testing.T) { + ctx := context.Background() + + conn := mustConnectString(t, os.Getenv("PGX_TEST_DATABASE")) + defer closeConn(t, conn) + + // create a table and fill it with some data + _, err := conn.Exec(ctx, ` + DROP TABLE IF EXISTS drop_cols; + CREATE TABLE drop_cols ( + id SERIAL PRIMARY KEY NOT NULL, + f1 int NOT NULL, + f2 int NOT NULL + ); + `) + require.NoError(t, err) + _, err = conn.Exec(ctx, "INSERT INTO drop_cols (f1, f2) VALUES (1, 2)") + require.NoError(t, err) + + tx, err := conn.Begin(ctx) + require.NoError(t, err) + + getSQL := "SELECT * FROM drop_cols WHERE id = $1" + + // This query will populate the statement cache. We don't care about the result. + rows, err := tx.Query(ctx, getSQL, 1) + require.NoError(t, err) + rows.Close() + + // Now, change the schema of the table out from under the statement, making it invalid. + _, err = tx.Exec(ctx, "ALTER TABLE drop_cols DROP COLUMN f1") + require.NoError(t, err) + + // We must get an error the first time we try to re-execute a bad statement. + // It is up to the application to determine if it wants to try again. We punt to + // the application because there is no clear recovery path in the case of failed transactions + // or batch operations and because automatic retry is tricky and we don't want to get + // it wrong at such an importaint layer of the stack. + rows, err = tx.Query(ctx, getSQL, 1) + if !pgx.IsInvalidCachedStatementPlanError(err) { + if err == nil { + t.Fatal("expected InvalidCachedStatementPlanError: no error") + } else { + t.Fatalf("expected InvalidCachedStatementPlanError, got: %s", err.Error()) + } + } + rows.Close() + + rows, err = tx.Query(ctx, getSQL, 1) + require.NoError(t, err) // error does not pop up immediately + rows.Next() + err = rows.Err() + // Retries within the same transaction are errors (really anything except a rollbakc + // will be an error in this transaction). + require.Error(t, err) + rows.Close() + + err = tx.Rollback(ctx) + require.NoError(t, err) + + // once we've rolled back, retries will work + rows, err = conn.Query(ctx, getSQL, 1) + require.NoError(t, err) + rows.Next() + err = rows.Err() + require.NoError(t, err) + rows.Close() + + ensureConnValid(t, conn) +} diff --git a/copy_from.go b/copy_from.go index afa80a1d1..11efbb461 100644 --- a/copy_from.go +++ b/copy_from.go @@ -6,7 +6,7 @@ import ( "fmt" "io" - "github.com/jackc/pgconn" + "github.com/ethanpailes/pgconn" "github.com/jackc/pgio" errors "golang.org/x/xerrors" ) diff --git a/copy_from_test.go b/copy_from_test.go index 9eaca011a..e5b948a17 100644 --- a/copy_from_test.go +++ b/copy_from_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/jackc/pgconn" + "github.com/ethanpailes/pgconn" "github.com/jackc/pgx/v4" "github.com/stretchr/testify/require" errors "golang.org/x/xerrors" diff --git a/doc.go b/doc.go index 2a4ede487..3cfd4a8ac 100644 --- a/doc.go +++ b/doc.go @@ -285,7 +285,7 @@ go.uber.org/zap, github.com/rs/zerolog, and the testing log are provided in the Lower Level PostgreSQL Functionality -pgx is implemented on top of github.com/jackc/pgconn a lower level PostgreSQL driver. The Conn.PgConn() method can be +pgx is implemented on top of github.com/ethanpailes/pgconn a lower level PostgreSQL driver. The Conn.PgConn() method can be used to access this lower layer. PgBouncer diff --git a/go.mod b/go.mod index 5a7375c0a..3f9c34be2 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.12 require ( github.com/cockroachdb/apd v1.1.0 github.com/gofrs/uuid v3.2.0+incompatible - github.com/jackc/pgconn v1.7.1 + github.com/ethanpailes/pgconn v1.7.101 github.com/jackc/pgio v1.0.0 github.com/jackc/pgproto3/v2 v2.0.5 github.com/jackc/pgtype v1.6.1 diff --git a/go.sum b/go.sum index e9c184c3c..daead5dfa 100644 --- a/go.sum +++ b/go.sum @@ -9,6 +9,42 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/ethanpailes/pgconn v0.0.0-20190420214824-7e0022ef6ba3 h1:ZFYpB74Kq8xE9gmfxCmXD6QxZ27ja+j3HwGFc+YurhQ= +github.com/ethanpailes/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/ethanpailes/pgconn v0.0.0-20190824142844-760dd75542eb h1:d6GP9szHvXVopAOAnZ7WhRnF3Xdxrylmm/9jnfmW4Ag= +github.com/ethanpailes/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/ethanpailes/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/ethanpailes/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk= +github.com/ethanpailes/pgconn v1.5.0 h1:oFSOilzIZkyg787M1fEmyMfOUUvwj0daqYMfaWwNL4o= +github.com/ethanpailes/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= +github.com/ethanpailes/pgconn v1.5.1-0.20200601181101-fa742c524853 h1:LRlrfJW9S99uiOCY8F/qLvX1yEY1TVAaCBHFb79yHBQ= +github.com/ethanpailes/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= +github.com/ethanpailes/pgconn v1.6.0 h1:8FiBxMxS/Z0eQ9BeE1HhL6pzPL1R5x+ZuQ+T86WgZ4I= +github.com/ethanpailes/pgconn v1.6.0/go.mod h1:yeseQo4xhQbgyJs2c87RAXOH2i624N0Fh1KSPJya7qo= +github.com/ethanpailes/pgconn v1.6.1 h1:lwofaXKPbIx6qEaK8mNm7uZuOwxHw+PnAFGDsDFpkRI= +github.com/ethanpailes/pgconn v1.6.1/go.mod h1:g8mKMqmSUO6AzAvha7vy07g1rbGOlc7iF0nU0ei83hc= +github.com/ethanpailes/pgconn v1.6.2 h1:ifRs/oHByR6NfEXfusvjoTqX/KcSvDYNFASoK/wXKfs= +github.com/ethanpailes/pgconn v1.6.2/go.mod h1:w2pne1C2tZgP+TvjqLpOigGzNqjBgQW9dUw/4Chex78= +github.com/ethanpailes/pgconn v1.6.3 h1:4Ks3RKvSvKPolXZsnLQTDAsokDhgID14Cv4ehECmzlY= +github.com/ethanpailes/pgconn v1.6.3/go.mod h1:w2pne1C2tZgP+TvjqLpOigGzNqjBgQW9dUw/4Chex78= +github.com/ethanpailes/pgconn v1.6.4 h1:S7T6cx5o2OqmxdHaXLH1ZeD1SbI8jBznyYE9Ec0RCQ8= +github.com/ethanpailes/pgconn v1.6.4/go.mod h1:w2pne1C2tZgP+TvjqLpOigGzNqjBgQW9dUw/4Chex78= +github.com/ethanpailes/pgconn v1.6.5-0.20200821030021-3eb5432c4738 h1:t/IRFEw2da5v6DroUIYPbEIDWxGGyvVLItoO7gOUBZM= +github.com/ethanpailes/pgconn v1.6.5-0.20200821030021-3eb5432c4738/go.mod h1:gm9GeeZiC+Ja7JV4fB/MNDeaOqsCrzFiZlLVhAompxk= +github.com/ethanpailes/pgconn v1.6.5-0.20200821030840-fdfc783345f6 h1:7f1RmJO6KZI4cskLrNHBd5CCLmLpIQ0BD75hsorvdz8= +github.com/ethanpailes/pgconn v1.6.5-0.20200821030840-fdfc783345f6/go.mod h1:gm9GeeZiC+Ja7JV4fB/MNDeaOqsCrzFiZlLVhAompxk= +github.com/ethanpailes/pgconn v1.6.5-0.20200905181414-0d4f029683fc h1:9ThyBXKdyBFN2Y1NSCPGCA0kdWCNpd9u4SKWwtr6GfU= +github.com/ethanpailes/pgconn v1.6.5-0.20200905181414-0d4f029683fc/go.mod h1:gm9GeeZiC+Ja7JV4fB/MNDeaOqsCrzFiZlLVhAompxk= +github.com/ethanpailes/pgconn v1.7.0 h1:pwjzcYyfmz/HQOQlENvG1OcDqauTGaqlVahq934F0/U= +github.com/ethanpailes/pgconn v1.7.0/go.mod h1:sF/lPpNEMEOp+IYhyQGdAvrG20gWf6A1tKlr0v7JMeA= +github.com/ethanpailes/pgconn v1.7.1 h1:Ii3hORkg9yTX+8etl2LtfFnL+YzmnR6VSLeTflQBkaQ= +github.com/ethanpailes/pgconn v1.7.1/go.mod h1:sF/lPpNEMEOp+IYhyQGdAvrG20gWf6A1tKlr0v7JMeA= +github.com/ethanpailes/pgconn v1.7.99 h1:G6i7kr9cmEz5gYGcgU81GNtHPYpr12GR85adgLdUUds= +github.com/ethanpailes/pgconn v1.7.99/go.mod h1:XZ5iVWDYmjuk12KDpJSddYs5FzbHegMckxXNQDxH2qc= +github.com/ethanpailes/pgconn v1.7.100 h1:3A8l2O1wSwPX/4M/PlT5u6/kpMr5H54RwCaWVDZ88ZA= +github.com/ethanpailes/pgconn v1.7.100/go.mod h1:XZ5iVWDYmjuk12KDpJSddYs5FzbHegMckxXNQDxH2qc= +github.com/ethanpailes/pgconn v1.7.101 h1:sjV/WLWfQATxJKU4vZr/XRrR5tmf1dD//d7xzPSTauU= +github.com/ethanpailes/pgconn v1.7.101/go.mod h1:XZ5iVWDYmjuk12KDpJSddYs5FzbHegMckxXNQDxH2qc= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= @@ -20,36 +56,13 @@ github.com/jackc/chunkreader/v2 v2.0.0 h1:DUwgMQuuPnS0rhMXenUtZpqZqrR/30NWY+qQvT github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3 h1:ZFYpB74Kq8xE9gmfxCmXD6QxZ27ja+j3HwGFc+YurhQ= github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= -github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb h1:d6GP9szHvXVopAOAnZ7WhRnF3Xdxrylmm/9jnfmW4Ag= github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk= -github.com/jackc/pgconn v1.5.0 h1:oFSOilzIZkyg787M1fEmyMfOUUvwj0daqYMfaWwNL4o= github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853 h1:LRlrfJW9S99uiOCY8F/qLvX1yEY1TVAaCBHFb79yHBQ= github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= -github.com/jackc/pgconn v1.6.0 h1:8FiBxMxS/Z0eQ9BeE1HhL6pzPL1R5x+ZuQ+T86WgZ4I= -github.com/jackc/pgconn v1.6.0/go.mod h1:yeseQo4xhQbgyJs2c87RAXOH2i624N0Fh1KSPJya7qo= -github.com/jackc/pgconn v1.6.1 h1:lwofaXKPbIx6qEaK8mNm7uZuOwxHw+PnAFGDsDFpkRI= -github.com/jackc/pgconn v1.6.1/go.mod h1:g8mKMqmSUO6AzAvha7vy07g1rbGOlc7iF0nU0ei83hc= -github.com/jackc/pgconn v1.6.2 h1:ifRs/oHByR6NfEXfusvjoTqX/KcSvDYNFASoK/wXKfs= -github.com/jackc/pgconn v1.6.2/go.mod h1:w2pne1C2tZgP+TvjqLpOigGzNqjBgQW9dUw/4Chex78= -github.com/jackc/pgconn v1.6.3 h1:4Ks3RKvSvKPolXZsnLQTDAsokDhgID14Cv4ehECmzlY= -github.com/jackc/pgconn v1.6.3/go.mod h1:w2pne1C2tZgP+TvjqLpOigGzNqjBgQW9dUw/4Chex78= -github.com/jackc/pgconn v1.6.4 h1:S7T6cx5o2OqmxdHaXLH1ZeD1SbI8jBznyYE9Ec0RCQ8= -github.com/jackc/pgconn v1.6.4/go.mod h1:w2pne1C2tZgP+TvjqLpOigGzNqjBgQW9dUw/4Chex78= -github.com/jackc/pgconn v1.6.5-0.20200821030021-3eb5432c4738 h1:t/IRFEw2da5v6DroUIYPbEIDWxGGyvVLItoO7gOUBZM= -github.com/jackc/pgconn v1.6.5-0.20200821030021-3eb5432c4738/go.mod h1:gm9GeeZiC+Ja7JV4fB/MNDeaOqsCrzFiZlLVhAompxk= -github.com/jackc/pgconn v1.6.5-0.20200821030840-fdfc783345f6 h1:7f1RmJO6KZI4cskLrNHBd5CCLmLpIQ0BD75hsorvdz8= -github.com/jackc/pgconn v1.6.5-0.20200821030840-fdfc783345f6/go.mod h1:gm9GeeZiC+Ja7JV4fB/MNDeaOqsCrzFiZlLVhAompxk= -github.com/jackc/pgconn v1.6.5-0.20200905181414-0d4f029683fc h1:9ThyBXKdyBFN2Y1NSCPGCA0kdWCNpd9u4SKWwtr6GfU= -github.com/jackc/pgconn v1.6.5-0.20200905181414-0d4f029683fc/go.mod h1:gm9GeeZiC+Ja7JV4fB/MNDeaOqsCrzFiZlLVhAompxk= -github.com/jackc/pgconn v1.7.0 h1:pwjzcYyfmz/HQOQlENvG1OcDqauTGaqlVahq934F0/U= -github.com/jackc/pgconn v1.7.0/go.mod h1:sF/lPpNEMEOp+IYhyQGdAvrG20gWf6A1tKlr0v7JMeA= -github.com/jackc/pgconn v1.7.1 h1:Ii3hORkg9yTX+8etl2LtfFnL+YzmnR6VSLeTflQBkaQ= -github.com/jackc/pgconn v1.7.1/go.mod h1:sF/lPpNEMEOp+IYhyQGdAvrG20gWf6A1tKlr0v7JMeA= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2 h1:JVX6jT/XfzNqIjye4717ITLaNwV9mWbJx0dLCpcRzdA= diff --git a/helper_test.go b/helper_test.go index 6c5320351..a489ca2e4 100644 --- a/helper_test.go +++ b/helper_test.go @@ -6,7 +6,7 @@ import ( "os" "testing" - "github.com/jackc/pgconn" + "github.com/ethanpailes/pgconn" "github.com/jackc/pgx/v4" "github.com/stretchr/testify/require" ) diff --git a/large_objects_test.go b/large_objects_test.go index ed50f0541..2e64a2b34 100644 --- a/large_objects_test.go +++ b/large_objects_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/jackc/pgconn" + "github.com/ethanpailes/pgconn" "github.com/jackc/pgx/v4" ) diff --git a/pgbouncer_test.go b/pgbouncer_test.go index e3fa4d0c8..31e35d848 100644 --- a/pgbouncer_test.go +++ b/pgbouncer_test.go @@ -5,8 +5,8 @@ import ( "os" "testing" - "github.com/jackc/pgconn" - "github.com/jackc/pgconn/stmtcache" + "github.com/ethanpailes/pgconn" + "github.com/ethanpailes/pgconn/stmtcache" "github.com/jackc/pgx/v4" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/pgxpool/batch_results.go b/pgxpool/batch_results.go index ec393a8c7..9be2c27bc 100644 --- a/pgxpool/batch_results.go +++ b/pgxpool/batch_results.go @@ -1,7 +1,7 @@ package pgxpool import ( - "github.com/jackc/pgconn" + "github.com/ethanpailes/pgconn" "github.com/jackc/pgx/v4" ) diff --git a/pgxpool/common_test.go b/pgxpool/common_test.go index 68e13a776..886c95db2 100644 --- a/pgxpool/common_test.go +++ b/pgxpool/common_test.go @@ -7,7 +7,7 @@ import ( "github.com/jackc/pgx/v4/pgxpool" - "github.com/jackc/pgconn" + "github.com/ethanpailes/pgconn" "github.com/jackc/pgx/v4" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/pgxpool/conn.go b/pgxpool/conn.go index 1172fbcb3..08edfd47f 100644 --- a/pgxpool/conn.go +++ b/pgxpool/conn.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/jackc/pgconn" + "github.com/ethanpailes/pgconn" "github.com/jackc/pgx/v4" "github.com/jackc/puddle" ) diff --git a/pgxpool/pool.go b/pgxpool/pool.go index e288a86c5..e0dedc978 100644 --- a/pgxpool/pool.go +++ b/pgxpool/pool.go @@ -6,7 +6,7 @@ import ( "strconv" "time" - "github.com/jackc/pgconn" + "github.com/ethanpailes/pgconn" "github.com/jackc/pgx/v4" "github.com/jackc/puddle" errors "golang.org/x/xerrors" diff --git a/pgxpool/rows.go b/pgxpool/rows.go index 6dc0cc342..0f4208542 100644 --- a/pgxpool/rows.go +++ b/pgxpool/rows.go @@ -1,7 +1,7 @@ package pgxpool import ( - "github.com/jackc/pgconn" + "github.com/ethanpailes/pgconn" "github.com/jackc/pgproto3/v2" "github.com/jackc/pgx/v4" ) diff --git a/pgxpool/tx.go b/pgxpool/tx.go index 3ff5cb952..33b5630c6 100644 --- a/pgxpool/tx.go +++ b/pgxpool/tx.go @@ -3,7 +3,7 @@ package pgxpool import ( "context" - "github.com/jackc/pgconn" + "github.com/ethanpailes/pgconn" "github.com/jackc/pgx/v4" ) diff --git a/query_test.go b/query_test.go index c850b5fe3..2227864ce 100644 --- a/query_test.go +++ b/query_test.go @@ -14,8 +14,8 @@ import ( "github.com/cockroachdb/apd" "github.com/gofrs/uuid" - "github.com/jackc/pgconn" - "github.com/jackc/pgconn/stmtcache" + "github.com/ethanpailes/pgconn" + "github.com/ethanpailes/pgconn/stmtcache" "github.com/jackc/pgtype" gofrs "github.com/jackc/pgtype/ext/gofrs-uuid" "github.com/jackc/pgx/v4" diff --git a/rows.go b/rows.go index 957192f68..a12b67eb2 100644 --- a/rows.go +++ b/rows.go @@ -7,7 +7,7 @@ import ( errors "golang.org/x/xerrors" - "github.com/jackc/pgconn" + "github.com/ethanpailes/pgconn" "github.com/jackc/pgproto3/v2" "github.com/jackc/pgtype" ) diff --git a/stdlib/sql.go b/stdlib/sql.go index a1e966eb8..cbfa00c05 100644 --- a/stdlib/sql.go +++ b/stdlib/sql.go @@ -63,7 +63,7 @@ import ( errors "golang.org/x/xerrors" - "github.com/jackc/pgconn" + "github.com/ethanpailes/pgconn" "github.com/jackc/pgtype" "github.com/jackc/pgx/v4" ) diff --git a/stdlib/sql_test.go b/stdlib/sql_test.go index bceb54a08..405b3aaf0 100644 --- a/stdlib/sql_test.go +++ b/stdlib/sql_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - "github.com/jackc/pgconn" + "github.com/ethanpailes/pgconn" "github.com/jackc/pgx/v4" "github.com/jackc/pgx/v4/stdlib" "github.com/stretchr/testify/assert" diff --git a/tx.go b/tx.go index f19a65a1b..c7ec38371 100644 --- a/tx.go +++ b/tx.go @@ -6,7 +6,7 @@ import ( "fmt" "strconv" - "github.com/jackc/pgconn" + "github.com/ethanpailes/pgconn" errors "golang.org/x/xerrors" ) @@ -81,6 +81,10 @@ func (c *Conn) BeginTx(ctx context.Context, txOptions TxOptions) (Tx, error) { c.die(errors.New("failed to begin transaction")) return nil, err } + err = c.setInTx(ctx, true) + if err != nil { + return nil, err + } return &dbTx{conn: c}, nil } @@ -162,6 +166,11 @@ func (tx *dbTx) Commit(ctx context.Context) error { } return err } + // we don't have to worry about nested transactions because they are handled by another struct + err = tx.conn.setInTx(ctx, false) + if err != nil { + return err + } if string(commandTag) == "ROLLBACK" { return ErrTxCommitRollback } @@ -185,6 +194,11 @@ func (tx *dbTx) Rollback(ctx context.Context) error { tx.conn.die(errors.Errorf("rollback failed: %w", err)) return err } + // we don't have to worry about nested transactions because they are handled by another struct + err = tx.conn.setInTx(ctx, false) + if err != nil { + return err + } return nil } diff --git a/tx_test.go b/tx_test.go index e0928c1bc..4221221cb 100644 --- a/tx_test.go +++ b/tx_test.go @@ -5,7 +5,7 @@ import ( "os" "testing" - "github.com/jackc/pgconn" + "github.com/ethanpailes/pgconn" "github.com/jackc/pgx/v4" "github.com/stretchr/testify/require" )