Skip to content

Commit

Permalink
roundtrip pq.Error values
Browse files Browse the repository at this point in the history
Previously, copyist was not preserving pq.Error values returned by
DB calls. This commit adds support for that type so that callers can
check error codes and other information only available on pq.Error.

Fixes #12
  • Loading branch information
andy-kimball committed Aug 15, 2021
1 parent bd52ae9 commit 2baa3f6
Show file tree
Hide file tree
Showing 18 changed files with 692 additions and 223 deletions.
15 changes: 10 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,19 @@
# permissions and limitations under the License.

# test re-records all copyist tests from a clean state and then re-runs them
# using copyist playback.
# Run this before submitting a PR.
# using copyist playback. Note that the drivertest/pqtestold test package has to
# be run separately because it has its own go.mod file (see comment in that file
# for reasons why).
#
# NOTE: Run this before submitting a PR.
#
.PHONY: test
test:
# Delete all copyist files and clear the test cache to get clean slate.
@find . -type f -name '*.copyist' -delete
# Re-record all tests.
@cd drivertest && COPYIST_RECORD=1 go test ./... -p=1 -count=1
@COPYIST_RECORD=1 go test ./... -p=1 -count=1
@cd drivertest/pqtestold && COPYIST_RECORD=1 go test ./... -p=1 -count=1
# Run all tests using playback.
@go test -count=1
@cd drivertest && go test ./... -count=1
@go test ./... -count=1
@cd drivertest/pqtestold && go test ./... -count=1
2 changes: 1 addition & 1 deletion drivertest/commontest/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func RunTestQuery(t *testing.T, driverName, dataSourceName string) {
})

t.Run("exec error", func(t *testing.T) {
_, err = db.Exec("SELECT * FROM nonexistent", 1)
_, err = db.Exec("SELECT * FROM \"bad\t table:name\"", 1)
require.Error(t, err)
})

Expand Down
2 changes: 1 addition & 1 deletion drivertest/commontest/testdata/big_test.copyist

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions drivertest/commontest/testdata/common_test.copyist
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
1=DriverOpen 1:nil
2=ConnQuery 2:"SELECT name FROM customers WHERE id=$1" 1:nil
3=RowsColumns 9:["name"]
4=RowsNext 11:[2:"Andy"] 1:nil
5=ConnQuery 2:"SHOW session_id" 1:nil
6=RowsColumns 9:["session_id"]
7=RowsNext 11:[2:"169b031ac1c71b280000000000000001"] 1:nil
8=RowsNext 11:[] 7:EOF
9=RowsNext 11:[2:"169b031ad2b390880000000000000001"] 1:nil
2=ConnQuery 2:"SHOW session_id" 1:nil
3=RowsColumns 9:["session_id"]
4=RowsNext 11:[2:"169b1595e2ee629c0000000000000001"] 1:nil
5=RowsNext 11:[] 7:"EOF"
6=ConnQuery 2:"SELECT name FROM customers WHERE id=$1" 1:nil
7=RowsColumns 9:["name"]
8=RowsNext 11:[2:"Andy"] 1:nil
9=RowsNext 11:[2:"169b1595eb56e4680000000000000001"] 1:nil

"TestIndirectOpen"=1,2,3,4
"TestPooling/ensure_connections_are_pooled_within_same_copyist_session"=1,5,6,7,8,5,6,7,8
"TestPooling/ensure_connections_are_*not*_pooled_across_copyist_sessions"=1,5,6,9
"TestPooling/ensure_connections_are_pooled_within_same_copyist_session"=1,2,3,4,5,2,3,4,5
"TestIndirectOpen"=1,6,7,8
"TestPooling/ensure_connections_are_*not*_pooled_across_copyist_sessions"=1,2,3,9
24 changes: 0 additions & 24 deletions drivertest/go.mod

This file was deleted.

81 changes: 35 additions & 46 deletions drivertest/go.sum

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion drivertest/multiple/testdata/multiple_test.copyist
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
2=ConnQuery 2:"SELECT name FROM customers WHERE id=$1" 1:nil
3=RowsColumns 9:["name"]
4=RowsNext 11:[2:"Andy"] 1:nil
5=RowsNext 11:[] 7:EOF
5=RowsNext 11:[] 7:"EOF"
6=RowsNext 11:[2:"Jay"] 1:nil

"TestMultipleDrivers"=1,2,3,4,5,1,2,3,4,5,2,3,6,5,2,3,6,5
72 changes: 36 additions & 36 deletions drivertest/pgxtest/testdata/pgxtest_test.copyist
Original file line number Diff line number Diff line change
@@ -1,38 +1,38 @@
1=DriverOpen 1:nil
2=ConnQuery 2:"SELECT 1::float, 1.1::float, 1e20::float" 1:nil
3=RowsColumns 9:["float8","float8","float8"]
4=RowsNext 11:[5:1,5:1.1,5:1e+20] 1:nil
5=ConnExec 2:"\n\t\tCREATE TABLE datatypes\n\t\t(i INT, s TEXT, tz TIMESTAMPTZ, t TIMESTAMP, b BOOL,\n\t\t by BYTES, f FLOAT, d DECIMAL, fa FLOAT[], u UUID)\n\t" 1:nil
6=ConnExec 2:"\n\t\tINSERT INTO datatypes VALUES\n\t\t\t(1, 'foo' || CHR(9) || CHR(10) || ' ,]', '2000-01-01T10:00:00Z', '2000-01-01T10:00:00Z',\n\t\t\t true, 'ABCD', 1.1, 100.1234, ARRAY(1.1, 1.2345678901234567),\n\t\t\t '8B78978B-7D8B-489E-8CA9-AC4BDC495A82'),\n\t\t\t(2, '', '2000-02-02T11:11:11-08:00', '2000-02-02T11:11:11-08:00', false,\n\t\t\t '', -1e10, -0.0, ARRAY(), '00000000-0000-0000-0000-000000000000')\n\t" 1:nil
7=ResultRowsAffected 4:0 1:nil
8=ConnQuery 2:"SELECT i, s, tz, t, b, by, f, d, fa, u FROM datatypes" 1:nil
9=RowsColumns 9:["i","s","tz","t","b","by","f","d","fa","u"]
10=RowsNext 11:[4:1,2:"foo\t\n ,]",8:2000-01-01T02:00:00-08:00,8:2000-01-01T10:00:00Z,6:true,10:QUJDRA,5:1.1,2:"100.1234",2:"{1.1,1.2345678901234567}",2:"8b78978b-7d8b-489e-8ca9-ac4bdc495a82"] 1:nil
11=RowsNext 11:[4:2,2:"",8:2000-02-02T11:11:11-08:00,8:2000-02-02T11:11:11Z,6:false,10:,5:-1e+10,2:"0.0",2:"{}",2:"00000000-0000-0000-0000-000000000000"] 1:nil
12=ConnQuery 2:"SELECT name FROM customers WHERE id=$1" 1:nil
13=RowsColumns 9:["name"]
14=RowsNext 11:[2:"Andy"] 1:nil
15=RowsNext 11:[] 7:EOF
16=ConnExec 2:"SELECT $1::int" 1:nil
17=ConnExec 2:"SELECT * FROM nonexistent" 7:ERROR: relation "nonexistent" does not exist (SQLSTATE 42P01)
18=ConnPrepare 2:"SELECT name FROM customers WHERE id=$1" 1:nil
19=StmtNumInput 3:1
20=StmtQuery 1:nil
21=ConnPrepare 2:"SELECT $1::int" 1:nil
22=StmtExec 1:nil
23=ConnExec 2:"INSERT INTO customers VALUES ($1, $2)" 1:nil
24=ResultRowsAffected 4:1 1:nil
25=ConnQuery 2:"SELECT COUNT(*) FROM customers" 1:nil
26=RowsColumns 9:["count"]
27=RowsNext 11:[4:4] 1:nil
28=ConnBegin 1:nil
29=TxCommit 1:nil
30=TxRollback 1:nil
2=ConnExec 2:"\n\t\tCREATE TABLE datatypes\n\t\t(i INT, s TEXT, tz TIMESTAMPTZ, t TIMESTAMP, b BOOL,\n\t\t by BYTES, f FLOAT, d DECIMAL, fa FLOAT[], u UUID)\n\t" 1:nil
3=ConnExec 2:"\n\t\tINSERT INTO datatypes VALUES\n\t\t\t(1, 'foo' || CHR(9) || CHR(10) || ' ,]', '2000-01-01T10:00:00Z', '2000-01-01T10:00:00Z',\n\t\t\t true, 'ABCD', 1.1, 100.1234, ARRAY(1.1, 1.2345678901234567),\n\t\t\t '8B78978B-7D8B-489E-8CA9-AC4BDC495A82'),\n\t\t\t(2, '', '2000-02-02T11:11:11-08:00', '2000-02-02T11:11:11-08:00', false,\n\t\t\t '', -1e10, -0.0, ARRAY(), '00000000-0000-0000-0000-000000000000')\n\t" 1:nil
4=ResultRowsAffected 4:0 1:nil
5=ConnQuery 2:"SELECT i, s, tz, t, b, by, f, d, fa, u FROM datatypes" 1:nil
6=RowsColumns 9:["i","s","tz","t","b","by","f","d","fa","u"]
7=RowsNext 11:[4:1,2:"foo\t\n ,]",8:2000-01-01T02:00:00-08:00,8:2000-01-01T10:00:00Z,6:true,10:QUJDRA,5:1.1,2:"100.1234",2:"{1.1,1.2345678901234567}",2:"8b78978b-7d8b-489e-8ca9-ac4bdc495a82"] 1:nil
8=RowsNext 11:[4:2,2:"",8:2000-02-02T11:11:11-08:00,8:2000-02-02T11:11:11Z,6:false,10:,5:-1e+10,2:"0.0",2:"{}",2:"00000000-0000-0000-0000-000000000000"] 1:nil
9=ConnQuery 2:"SELECT 1::float, 1.1::float, 1e20::float" 1:nil
10=RowsColumns 9:["float8","float8","float8"]
11=RowsNext 11:[5:1,5:1.1,5:1e+20] 1:nil
12=ConnBegin 1:nil
13=ConnExec 2:"INSERT INTO customers VALUES ($1, $2)" 1:nil
14=TxCommit 1:nil
15=TxRollback 1:nil
16=ConnQuery 2:"SELECT COUNT(*) FROM customers" 1:nil
17=RowsColumns 9:["count"]
18=RowsNext 11:[4:4] 1:nil
19=RowsNext 11:[] 7:"EOF"
20=ResultRowsAffected 4:1 1:nil
21=ConnQuery 2:"SELECT name FROM customers WHERE id=$1" 1:nil
22=RowsColumns 9:["name"]
23=RowsNext 11:[2:"Andy"] 1:nil
24=ConnExec 2:"SELECT $1::int" 1:nil
25=ConnExec 2:"SELECT * FROM \"bad\t table:name\"" 7:"ERROR: relation \"bad\\t table:name\" does not exist (SQLSTATE 42P01)"
26=ConnPrepare 2:"SELECT name FROM customers WHERE id=$1" 1:nil
27=StmtNumInput 3:1
28=StmtQuery 1:nil
29=ConnPrepare 2:"SELECT $1::int" 1:nil
30=StmtExec 1:nil

"TestInsert"=1,23,24,25,26,27,15
"TestTxns"=1,28,23,29,28,23,30,25,26,27,15
"TestSqlx"=1,28,12,13,14,15,29
"TestFloatLiterals/run_1"=1,2,3,4
"TestFloatLiterals/run_2"=1,2,3,4
"TestDataTypes"=1,5,6,7,8,9,10,11
"TestQuery"=1,12,13,14,15,16,17,18,18,19,20,13,14,15,21,21,19,22
"TestInsert"=1,13,20,16,17,18,19
"TestFloatLiterals/run_2"=1,9,10,11
"TestQuery"=1,21,22,23,19,24,25,26,26,27,28,22,23,19,29,29,27,30
"TestSqlx"=1,12,21,22,23,19,14
"TestDataTypes"=1,2,3,4,5,6,7,8
"TestFloatLiterals/run_1"=1,9,10,11
"TestTxns"=1,12,13,14,12,13,15,16,17,18,19
26 changes: 26 additions & 0 deletions drivertest/pqtest/pqtest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,16 @@
package pqtest

import (
"database/sql"
"testing"

"github.com/lib/pq"

"github.com/cockroachdb/copyist"
"github.com/cockroachdb/copyist/drivertest/commontest"
"github.com/fortytw2/leaktest"
"github.com/stretchr/testify/require"

_ "github.com/lib/pq"
)

Expand Down Expand Up @@ -76,3 +83,22 @@ func TestTxns(t *testing.T) {
func TestSqlx(t *testing.T) {
commontest.RunTestSqlx(t, "postgres", commontest.PostgresDataSourceName)
}

// TestPqError tests that pq.Error objects are round-tripped.
func TestPqError(t *testing.T) {
defer leaktest.Check(t)()
defer copyist.Open(t).Close()

// Open database.
db, err := sql.Open("copyist_postgres", commontest.PostgresDataSourceName)
require.NoError(t, err)
defer db.Close()

_, err = db.Exec("bad query")
pqErr, ok := err.(*pq.Error)
require.True(t, ok)
require.Equal(t, "ERROR", pqErr.Severity)
require.Equal(t, pq.ErrorCode("42601"), pqErr.Code)
require.Equal(t, "at or near \"bad\": syntax error", pqErr.Message)
require.Equal(t, "source SQL:\nbad query\n^", pqErr.Detail)
}
84 changes: 43 additions & 41 deletions drivertest/pqtest/testdata/pqtest_test.copyist
Original file line number Diff line number Diff line change
@@ -1,43 +1,45 @@
1=DriverOpen 1:nil
2=ConnExec 2:"INSERT INTO customers VALUES ($1, $2)" 1:nil
3=ResultRowsAffected 4:1 1:nil
4=ConnQuery 2:"SELECT COUNT(*) FROM customers" 1:nil
5=RowsColumns 9:["count"]
6=RowsNext 11:[4:4] 1:nil
7=RowsNext 11:[] 7:EOF
8=ConnExec 2:"\n\t\tCREATE TABLE datatypes\n\t\t(i INT, s TEXT, tz TIMESTAMPTZ, t TIMESTAMP, b BOOL,\n\t\t by BYTES, f FLOAT, d DECIMAL, fa FLOAT[], u UUID)\n\t" 1:nil
9=ConnExec 2:"\n\t\tINSERT INTO datatypes VALUES\n\t\t\t(1, 'foo' || CHR(9) || CHR(10) || ' ,]', '2000-01-01T10:00:00Z', '2000-01-01T10:00:00Z',\n\t\t\t true, 'ABCD', 1.1, 100.1234, ARRAY(1.1, 1.2345678901234567),\n\t\t\t '8B78978B-7D8B-489E-8CA9-AC4BDC495A82'),\n\t\t\t(2, '', '2000-02-02T11:11:11-08:00', '2000-02-02T11:11:11-08:00', false,\n\t\t\t '', -1e10, -0.0, ARRAY(), '00000000-0000-0000-0000-000000000000')\n\t" 1:nil
10=ResultRowsAffected 4:0 1:nil
11=ConnQuery 2:"SELECT i, s, tz, t, b, by, f, d, fa, u FROM datatypes" 1:nil
12=RowsColumns 9:["i","s","tz","t","b","by","f","d","fa","u"]
13=RowsNext 11:[4:1,2:"foo\t\n ,]",8:2000-01-01T10:00:00Z,8:2000-01-01T10:00:00+00:00,6:true,10:QUJDRA,5:1.1,10:MTAwLjEyMzQ,10:ezEuMSwxLjIzNDU2Nzg5MDEyMzQ1Njd9,10:OGI3ODk3OGItN2Q4Yi00ODllLThjYTktYWM0YmRjNDk1YTgy] 1:nil
14=RowsNext 11:[4:2,2:"",8:2000-02-02T19:11:11Z,8:2000-02-02T11:11:11+00:00,6:false,10:,5:-1e+10,10:MC4w,10:e30,10:MDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMDAw] 1:nil
15=ConnQuery 2:"SELECT 1::float, 1.1::float, 1e20::float" 1:nil
16=RowsColumns 9:["float8","float8","float8"]
17=RowsNext 11:[5:1,5:1.1,5:1e+20] 1:nil
18=ConnExec 2:"SELECT 1; SELECT 2;" 1:nil
19=ConnQuery 2:"SELECT 1; SELECT 2;" 1:nil
20=RowsColumns 9:["?column?"]
21=RowsNext 11:[4:1] 1:nil
22=ConnBegin 1:nil
23=TxCommit 1:nil
24=TxRollback 1:nil
25=ConnQuery 2:"SELECT name FROM customers WHERE id=$1" 1:nil
26=RowsColumns 9:["name"]
27=RowsNext 11:[2:"Andy"] 1:nil
28=ConnExec 2:"SELECT $1::int" 1:nil
29=ConnExec 2:"SELECT * FROM nonexistent" 7:pq: relation "nonexistent" does not exist
30=ConnPrepare 2:"SELECT name FROM customers WHERE id=$1" 1:nil
31=StmtNumInput 3:1
32=StmtQuery 1:nil
33=ConnPrepare 2:"SELECT $1::int" 1:nil
34=StmtExec 1:nil
2=ConnBegin 1:nil
3=ConnExec 2:"INSERT INTO customers VALUES ($1, $2)" 1:nil
4=TxCommit 1:nil
5=TxRollback 1:nil
6=ConnQuery 2:"SELECT COUNT(*) FROM customers" 1:nil
7=RowsColumns 9:["count"]
8=RowsNext 11:[4:4] 1:nil
9=RowsNext 11:[] 7:"EOF"
10=ConnQuery 2:"SELECT name FROM customers WHERE id=$1" 1:nil
11=RowsColumns 9:["name"]
12=RowsNext 11:[2:"Andy"] 1:nil
13=ConnExec 2:"SELECT $1::int" 1:nil
14=ConnExec 2:"SELECT * FROM \"bad\t table:name\"" 100:"SERROR\x00C42P01\x00Mrelation \"bad\\t table:name\" does not exist\x00Ferrors.go\x00L120\x00RNewUndefinedRelationError\x00\x00"
15=ConnPrepare 2:"SELECT name FROM customers WHERE id=$1" 1:nil
16=StmtNumInput 3:1
17=StmtQuery 1:nil
18=ConnPrepare 2:"SELECT $1::int" 1:nil
19=StmtExec 1:nil
20=ConnQuery 2:"SELECT 1::float, 1.1::float, 1e20::float" 1:nil
21=RowsColumns 9:["float8","float8","float8"]
22=RowsNext 11:[5:1,5:1.1,5:1e+20] 1:nil
23=ConnExec 2:"SELECT 1; SELECT 2;" 1:nil
24=ConnQuery 2:"SELECT 1; SELECT 2;" 1:nil
25=RowsColumns 9:["?column?"]
26=RowsNext 11:[4:1] 1:nil
27=ResultRowsAffected 4:1 1:nil
28=ConnExec 2:"\n\t\tCREATE TABLE datatypes\n\t\t(i INT, s TEXT, tz TIMESTAMPTZ, t TIMESTAMP, b BOOL,\n\t\t by BYTES, f FLOAT, d DECIMAL, fa FLOAT[], u UUID)\n\t" 1:nil
29=ConnExec 2:"\n\t\tINSERT INTO datatypes VALUES\n\t\t\t(1, 'foo' || CHR(9) || CHR(10) || ' ,]', '2000-01-01T10:00:00Z', '2000-01-01T10:00:00Z',\n\t\t\t true, 'ABCD', 1.1, 100.1234, ARRAY(1.1, 1.2345678901234567),\n\t\t\t '8B78978B-7D8B-489E-8CA9-AC4BDC495A82'),\n\t\t\t(2, '', '2000-02-02T11:11:11-08:00', '2000-02-02T11:11:11-08:00', false,\n\t\t\t '', -1e10, -0.0, ARRAY(), '00000000-0000-0000-0000-000000000000')\n\t" 1:nil
30=ResultRowsAffected 4:0 1:nil
31=ConnQuery 2:"SELECT i, s, tz, t, b, by, f, d, fa, u FROM datatypes" 1:nil
32=RowsColumns 9:["i","s","tz","t","b","by","f","d","fa","u"]
33=RowsNext 11:[4:1,2:"foo\t\n ,]",8:2000-01-01T10:00:00Z,8:2000-01-01T10:00:00+00:00,6:true,10:QUJDRA,5:1.1,10:MTAwLjEyMzQ,10:ezEuMSwxLjIzNDU2Nzg5MDEyMzQ1Njd9,10:OGI3ODk3OGItN2Q4Yi00ODllLThjYTktYWM0YmRjNDk1YTgy] 1:nil
34=RowsNext 11:[4:2,2:"",8:2000-02-02T19:11:11Z,8:2000-02-02T11:11:11+00:00,6:false,10:,5:-1e+10,10:MC4w,10:e30,10:MDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMDAw] 1:nil
35=ConnExec 2:"bad query" 100:"SERROR\x00C42601\x00Mat or near \"bad\": syntax error\x00Dsource SQL:\nbad query\n^\x00Flexer.go\x00L199\x00RError\x00\x00"

"TestTxns"=1,22,2,23,22,2,24,4,5,6,7
"TestQuery"=1,25,26,27,7,28,29,30,30,31,32,26,27,7,33,33,31,34
"TestFloatLiterals/run_2"=1,15,16,17
"TestSqlx"=1,22,25,26,27,7,23
"TestInsert"=1,2,3,4,5,6,7
"TestDataTypes"=1,8,9,10,11,12,13,14
"TestFloatLiterals/run_1"=1,15,16,17
"TestMultiStatement"=1,18,19,20,21,7
"TestTxns"=1,2,3,4,2,3,5,6,7,8,9
"TestQuery"=1,10,11,12,9,13,14,15,15,16,17,11,12,9,18,18,16,19
"TestFloatLiterals/run_1"=1,20,21,22
"TestMultiStatement"=1,23,24,25,26,9
"TestSqlx"=1,2,10,11,12,9,4
"TestFloatLiterals/run_2"=1,20,21,22
"TestInsert"=1,3,27,6,7,8,9
"TestDataTypes"=1,28,29,30,31,32,33,34
"TestPqError"=1,35
17 changes: 17 additions & 0 deletions drivertest/pqtestold/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module github.com/cockroachdb/copyist/drivertest/pqtestold

go 1.16

// Use separate go.mod file so that ancient version of PQ can be tested, before
// support was added for QueryContext, ExecContext, and BeginTx. This enables
// testing that copyist works even when drivers do not support those functions.
require (
github.com/cockroachdb/copyist v0.0.0-00010101000000-000000000000
github.com/lib/pq v1.10.2
)

// Reference copyist in the same repo.
replace github.com/cockroachdb/copyist => ./../..

// Override the latest version of lib/pq with an ancient version.
replace github.com/lib/pq => github.com/lib/pq v0.0.0-20170117202628-46f7bf5f8bd7
Loading

0 comments on commit 2baa3f6

Please sign in to comment.