2
0

Add *Conn. CopyFromTextual, CopyToTextual, which use textual format for copying data

This commit is contained in:
Murat Kabilov
2018-07-30 17:29:26 +02:00
parent 93ee40e691
commit 5315995dfa
7 changed files with 443 additions and 2 deletions
+169 -2
View File
@@ -2,6 +2,7 @@ package pgx_test
import (
"reflect"
"strings"
"testing"
"time"
@@ -25,10 +26,14 @@ func TestConnCopyFromSmall(t *testing.T) {
g timestamptz
)`)
tzedTime := time.Date(2010, 2, 3, 4, 5, 6, 0, time.Local)
inputRows := [][]interface{}{
{int16(0), int32(1), int64(2), "abc", "efg", time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2010, 2, 3, 4, 5, 6, 0, time.Local)},
{int16(0), int32(1), int64(2), "abc", "efg", time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), tzedTime},
{nil, nil, nil, nil, nil, nil, nil},
}
inputReader := strings.NewReader("0\t1\t2\tabc\tefg\t2000-01-01\t" + tzedTime.Format(time.RFC3339Nano) + "\n" +
"\\N\t\\N\t\\N\t\\N\t\\N\t\\N\t\\N\n")
copyCount, err := conn.CopyFrom(pgx.Identifier{"foo"}, []string{"a", "b", "c", "d", "e", "f", "g"}, pgx.CopyFromRows(inputRows))
if err != nil {
@@ -60,6 +65,34 @@ func TestConnCopyFromSmall(t *testing.T) {
t.Errorf("Input rows and output rows do not equal: %v -> %v", inputRows, outputRows)
}
mustExec(t, conn, "truncate foo")
if err := conn.CopyFromTextual(inputReader, "copy foo from stdin"); err != nil {
t.Errorf("Unexpected error for CopyFromTextual: %v", err)
}
rows, err = conn.Query("select * from foo")
if err != nil {
t.Errorf("Unexpected error for Query: %v", err)
}
outputRows = make([][]interface{}, 0)
for rows.Next() {
row, err := rows.Values()
if err != nil {
t.Errorf("Unexpected error for rows.Values(): %v", err)
}
outputRows = append(outputRows, row)
}
if rows.Err() != nil {
t.Errorf("Unexpected error for rows.Err(): %v", rows.Err())
}
if !reflect.DeepEqual(inputRows, outputRows) {
t.Errorf("Input rows and output rows do not equal: %v -> %v", inputRows, outputRows)
}
ensureConnValid(t, conn)
}
@@ -80,10 +113,14 @@ func TestConnCopyFromLarge(t *testing.T) {
h bytea
)`)
tzedTime := time.Date(2010, 2, 3, 4, 5, 6, 0, time.Local)
inputRows := [][]interface{}{}
inputStringRows := ""
for i := 0; i < 10000; i++ {
inputRows = append(inputRows, []interface{}{int16(0), int32(1), int64(2), "abc", "efg", time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2010, 2, 3, 4, 5, 6, 0, time.Local), []byte{111, 111, 111, 111}})
inputRows = append(inputRows, []interface{}{int16(0), int32(1), int64(2), "abc", "efg", time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), tzedTime, []byte{111, 111, 111, 111}})
inputStringRows += "0\t1\t2\tabc\tefg\t2000-01-01\t" + tzedTime.Format(time.RFC3339Nano) + "\toooo\n"
}
copyCount, err := conn.CopyFrom(pgx.Identifier{"foo"}, []string{"a", "b", "c", "d", "e", "f", "g", "h"}, pgx.CopyFromRows(inputRows))
@@ -116,6 +153,34 @@ func TestConnCopyFromLarge(t *testing.T) {
t.Errorf("Input rows and output rows do not equal")
}
mustExec(t, conn, "truncate foo")
if err := conn.CopyFromTextual(strings.NewReader(inputStringRows), "copy foo from stdin"); err != nil {
t.Errorf("Unexpected error for CopyFromTextual: %v", err)
}
rows, err = conn.Query("select * from foo")
if err != nil {
t.Errorf("Unexpected error for Query: %v", err)
}
outputRows = make([][]interface{}, 0)
for rows.Next() {
row, err := rows.Values()
if err != nil {
t.Errorf("Unexpected error for rows.Values(): %v", err)
}
outputRows = append(outputRows, row)
}
if rows.Err() != nil {
t.Errorf("Unexpected error for rows.Err(): %v", rows.Err())
}
if !reflect.DeepEqual(inputRows, outputRows) {
t.Errorf("Input rows and output rows do not equal")
}
ensureConnValid(t, conn)
}
@@ -140,6 +205,7 @@ func TestConnCopyFromJSON(t *testing.T) {
{map[string]interface{}{"foo": "bar"}, map[string]interface{}{"bar": "quz"}},
{nil, nil},
}
inputReader := strings.NewReader("{\"foo\":\"bar\"}\t{\"bar\":\"quz\"}\n\\N\t\\N\n")
copyCount, err := conn.CopyFrom(pgx.Identifier{"foo"}, []string{"a", "b"}, pgx.CopyFromRows(inputRows))
if err != nil {
@@ -171,6 +237,34 @@ func TestConnCopyFromJSON(t *testing.T) {
t.Errorf("Input rows and output rows do not equal: %v -> %v", inputRows, outputRows)
}
mustExec(t, conn, "truncate foo")
if err := conn.CopyFromTextual(inputReader, "copy foo from stdin"); err != nil {
t.Errorf("Unexpected error for CopyFrom: %v", err)
}
rows, err = conn.Query("select * from foo")
if err != nil {
t.Errorf("Unexpected error for Query: %v", err)
}
outputRows = make([][]interface{}, 0)
for rows.Next() {
row, err := rows.Values()
if err != nil {
t.Errorf("Unexpected error for rows.Values(): %v", err)
}
outputRows = append(outputRows, row)
}
if rows.Err() != nil {
t.Errorf("Unexpected error for rows.Err(): %v", rows.Err())
}
if !reflect.DeepEqual(inputRows, outputRows) {
t.Errorf("Input rows and output rows do not equal: %v -> %v", inputRows, outputRows)
}
ensureConnValid(t, conn)
}
@@ -212,6 +306,7 @@ func TestConnCopyFromFailServerSideMidway(t *testing.T) {
{int32(2), nil}, // this row should trigger a failure
{int32(3), "def"},
}
inputReader := strings.NewReader("1\tabc\n2\t\\N\n3\tdef\n")
copyCount, err := conn.CopyFrom(pgx.Identifier{"foo"}, []string{"a", "b"}, pgx.CopyFromRows(inputRows))
if err == nil {
@@ -246,6 +341,38 @@ func TestConnCopyFromFailServerSideMidway(t *testing.T) {
t.Errorf("Expected 0 rows, but got %v", outputRows)
}
mustExec(t, conn, "truncate foo")
err = conn.CopyFromTextual(inputReader, "copy foo from stdin")
if err == nil {
t.Errorf("Expected CopyFromTextual return error, but it did not")
}
if _, ok := err.(pgx.PgError); !ok {
t.Errorf("Expected CopyFromTextual return pgx.PgError, but instead it returned: %v", err)
}
rows, err = conn.Query("select * from foo")
if err != nil {
t.Errorf("Unexpected error for Query: %v", err)
}
outputRows = make([][]interface{}, 0)
for rows.Next() {
row, err := rows.Values()
if err != nil {
t.Errorf("Unexpected error for rows.Values(): %v", err)
}
outputRows = append(outputRows, row)
}
if rows.Err() != nil {
t.Errorf("Unexpected error for rows.Err(): %v", rows.Err())
}
if len(outputRows) != 0 {
t.Errorf("Expected 0 rows, but got %v", outputRows)
}
ensureConnValid(t, conn)
}
@@ -472,3 +599,43 @@ func TestConnCopyFromCopyFromSourceNextPanic(t *testing.T) {
t.Error("panic should have killed conn")
}
}
func TestConnCopyFromTextualQueryError(t *testing.T) {
t.Parallel()
conn := mustConnect(t, *defaultConnConfig)
defer closeConn(t, conn)
inputReader := strings.NewReader("")
err := conn.CopyFromTextual(inputReader, "cropy foo from stdin")
if err == nil {
t.Errorf("Expected CopyFromTextual return error, but it did not")
}
if _, ok := err.(pgx.PgError); !ok {
t.Errorf("Expected CopyFromTextual return pgx.PgError, but instead it returned: %v", err)
}
ensureConnValid(t, conn)
}
func TestConnCopyFromTextualNoTableError(t *testing.T) {
t.Parallel()
conn := mustConnect(t, *defaultConnConfig)
defer closeConn(t, conn)
inputReader := strings.NewReader("")
err := conn.CopyFromTextual(inputReader, "copy foo from stdin")
if err == nil {
t.Errorf("Expected CopyFromTextual return error, but it did not")
}
if _, ok := err.(pgx.PgError); !ok {
t.Errorf("Expected CopyFromTextual return pgx.PgError, but instead it returned: %v", err)
}
ensureConnValid(t, conn)
}