Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

clisqlshell: implement COPY ... FROM STDIN for CLI #79629

Merged
merged 1 commit into from
Apr 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions DEPS.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -5059,10 +5059,10 @@ def go_deps():
name = "com_github_lib_pq",
build_file_proto_mode = "disable_global",
importpath = "github.com/lib/pq",
sha256 = "0f50cfc8d4ed4bbb39767aacc04d6b23e1105d2fa50dcb8e4ae204b2c90018f0",
strip_prefix = "github.com/lib/[email protected].2",
sha256 = "5bca281c55dd8918e49a7e68d562eefb37f2cf17f7d45e1f3bd77e8eae49eb6e",
strip_prefix = "github.com/lib/[email protected].6-0.20220412200556-b3b833258663",
urls = [
"https://storage.googleapis.com/cockroach-godeps/gomod/github.com/lib/pq/com_github_lib_pq-v1.10.2.zip",
"https://storage.googleapis.com/cockroach-godeps/gomod/github.com/lib/pq/com_github_lib_pq-v1.10.6-0.20220412200556-b3b833258663.zip",
],
)
go_repository(
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ require (
github.com/kr/text v0.2.0
github.com/kylelemons/godebug v1.1.0
github.com/leanovate/gopter v0.2.5-0.20190402064358-634a59d12406
github.com/lib/pq v1.10.2
github.com/lib/pq v1.10.6-0.20220412200556-b3b833258663
github.com/lib/pq/auth/kerberos v0.0.0-20200720160335-984a6aa1ca46
github.com/linkedin/goavro/v2 v2.10.0
github.com/lufia/iostat v1.2.1
Expand Down
3 changes: 2 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1482,8 +1482,9 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.6-0.20220412200556-b3b833258663 h1:zQ4V0s+y+YrjamtmcRoERUVfsN/4jEb/pVdxrpL6zjU=
github.com/lib/pq v1.10.6-0.20220412200556-b3b833258663/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq/auth/kerberos v0.0.0-20200720160335-984a6aa1ca46 h1:q7hY+WNJTcSqJNGwJzXZYL++nWBaoKlKdgZOyY6jxz4=
github.com/lib/pq/auth/kerberos v0.0.0-20200720160335-984a6aa1ca46/go.mod h1:jydegJvs5JvVcuFD/YAT8JRmRVeOoRhtnGEgRnAoPpE=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
Expand Down
1 change: 1 addition & 0 deletions pkg/cli/clisqlclient/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ go_library(
"api.go",
"conn.go",
"context.go",
"copy.go",
"doc.go",
"init_conn_error.go",
"make_query.go",
Expand Down
116 changes: 116 additions & 0 deletions pkg/cli/clisqlclient/copy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright 2022 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

package clisqlclient

import (
"context"
"database/sql/driver"
"io"
"reflect"
"strings"

"github.com/cockroachdb/errors"
)

type copyFromer interface {
CopyData(ctx context.Context, line string) (r driver.Result, err error)
Exec(v []driver.Value) (r driver.Result, err error)
Close() error
}

// CopyFromState represents an in progress COPY FROM.
type CopyFromState struct {
driver.Tx
copyFromer
}

// BeginCopyFrom starts a COPY FROM query.
func BeginCopyFrom(ctx context.Context, conn Conn, query string) (*CopyFromState, error) {
txn, err := conn.(*sqlConn).conn.(driver.ConnBeginTx).BeginTx(ctx, driver.TxOptions{})
if err != nil {
return nil, err
}
stmt, err := txn.(driver.Conn).Prepare(query)
if err != nil {
return nil, errors.CombineErrors(err, txn.Rollback())
}
return &CopyFromState{Tx: txn, copyFromer: stmt.(copyFromer)}, nil
}

// copyFromRows is a mock Rows interface for COPY results.
type copyFromRows struct {
r driver.Result
}

func (c copyFromRows) Close() error {
return nil
}

func (c copyFromRows) Columns() []string {
return nil
}

func (c copyFromRows) ColumnTypeScanType(index int) reflect.Type {
return nil
}

func (c copyFromRows) ColumnTypeDatabaseTypeName(index int) string {
return ""
}

func (c copyFromRows) ColumnTypeNames() []string {
return nil
}

func (c copyFromRows) Result() driver.Result {
return c.r
}

func (c copyFromRows) Tag() string {
return "COPY"
}

func (c copyFromRows) Next(values []driver.Value) error {
return io.EOF
}

func (c copyFromRows) NextResultSet() (bool, error) {
return false, nil
}

// Cancel cancels a COPY FROM query from completing.
func (c *CopyFromState) Cancel() error {
return errors.CombineErrors(c.copyFromer.Close(), c.Tx.Rollback())
}

// Commit completes a COPY FROM query by committing lines to the database.
func (c *CopyFromState) Commit(ctx context.Context, cleanupFunc func(), lines string) QueryFn {
return func(ctx context.Context, conn Conn) (Rows, bool, error) {
defer cleanupFunc()
rows, isMulti, err := func() (Rows, bool, error) {
for _, l := range strings.Split(lines, "\n") {
_, err := c.copyFromer.CopyData(ctx, l)
if err != nil {
return nil, false, err
}
}
r, err := c.copyFromer.Exec(nil)
if err != nil {
return nil, false, err
}
return copyFromRows{r: r}, false, c.Tx.Commit()
}()
if err != nil {
return rows, isMulti, errors.CombineErrors(err, errors.CombineErrors(c.copyFromer.Close(), c.Tx.Rollback()))
}
return rows, isMulti, err
}
}
22 changes: 0 additions & 22 deletions pkg/cli/clisqlclient/make_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@ package clisqlclient
import (
"context"
"database/sql/driver"
"strings"

"github.com/cockroachdb/cockroach/pkg/sql/scanner"
"github.com/cockroachdb/errors"
)

// QueryFn is the type of functions produced by MakeQuery.
Expand All @@ -28,7 +26,6 @@ func MakeQuery(query string, parameters ...interface{}) QueryFn {
return func(ctx context.Context, conn Conn) (Rows, bool, error) {
isMultiStatementQuery, _ := scanner.HasMultipleStatements(query)
rows, err := conn.Query(ctx, query, parameters...)
err = handleCopyError(conn.(*sqlConn), err)
return rows, isMultiStatementQuery, err
}
}
Expand All @@ -51,22 +48,3 @@ func convertArgs(parameters []interface{}) ([]driver.NamedValue, error) {
}
return dVals, nil
}

// handleCopyError ensures the user is properly informed when they issue
// a COPY statement somewhere in their input.
func handleCopyError(conn *sqlConn, err error) error {
if err == nil {
return nil
}

if !strings.HasPrefix(err.Error(), "pq: unknown response for simple query: 'G'") {
return err
}

// The COPY statement has hosed the connection by putting the
// protocol in a state that lib/pq cannot understand any more. Reset
// it.
_ = conn.Close()
conn.reconnecting = true
return errors.New("woops! COPY has confused this client! Suggestion: use 'psql' for COPY")
}
1 change: 1 addition & 0 deletions pkg/cli/clisqlexec/run_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ var tagsWithRowsAffected = map[string]struct{}{
"DELETE": {},
"MOVE": {},
"DROP USER": {},
"COPY": {},
// This one is used with e.g. CREATE TABLE AS (other SELECT
// statements have type Rows, not RowsAffected).
"SELECT": {},
Expand Down
1 change: 1 addition & 0 deletions pkg/cli/clisqlshell/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ go_library(
"//pkg/sql/scanner",
"//pkg/sql/sqlfsm",
"//pkg/util/envutil",
"//pkg/util/errorutil/unimplemented",
"//pkg/util/syncutil",
"@com_github_cockroachdb_errors//:errors",
"@com_github_knz_go_libedit//:go-libedit",
Expand Down
Loading