Skip to content

Commit

Permalink
tree: add FmtExport formatting for array types
Browse files Browse the repository at this point in the history
Part of work for cockroachdb#44322.

Fixes cockroachdb#33429.

Release note (enterprise change): This PR fixes a bug where
export/import could not round trip arrays correctly.
  • Loading branch information
rohany committed Jan 27, 2020
1 parent d5ffbf7 commit 2242cb0
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 11 deletions.
13 changes: 13 additions & 0 deletions pkg/ccl/importccl/import_stmt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1553,6 +1553,19 @@ func TestImportCSVStmt(t *testing.T) {
})
}

func TestExportImportRoundTrip(t *testing.T) {
defer leaktest.AfterTest(t)()
ctx := context.Background()
baseDir := filepath.Join("testdata", "csv")
tc := testcluster.StartTestCluster(
t, 1, base.TestClusterArgs{ServerArgs: base.TestServerArgs{ExternalIODir: baseDir}})
defer tc.Stopper().Stop(ctx)
conn := tc.Conns[0]
sqlDB := sqlutils.MakeSQLRunner(conn)
sqlDB.Exec(t, `EXPORT INTO CSV 'nodelocal:///foo.csv' FROM SELECT ARRAY['a', 'b', 'c']`)
sqlDB.Exec(t, `IMPORT TABLE zc(x TEXT[]) CSV DATA ('nodelocal:///foo.csv/n1.0.csv')`)
}

// TODO(adityamaru): Tests still need to be added incrementally as
// relevant IMPORT INTO logic is added. Some of them include:
// -> FK and constraint violation
Expand Down
2 changes: 1 addition & 1 deletion pkg/sql/sem/tree/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ const (
// FmtExport, if set, formats datums in a raw form suitable for
// EXPORT, e.g. suitable for output into a CSV file. The intended
// goal for this flag is to ensure values can be read back using the
// ParseDatumStringAs() / ParseStringas() functions (IMPORT).
// ParseDatumStringAs() / ParseStringAs() functions (IMPORT).
//
// We do not use FmtParsable for this purpose because FmtParsable
// intends to preserve all the information useful to CockroachDB
Expand Down
26 changes: 16 additions & 10 deletions pkg/sql/sem/tree/parse_array.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ import (
"github.com/cockroachdb/errors"
)

var enclosingError = pgerror.Newf(pgcode.InvalidTextRepresentation, "array must be enclosed in { and }")
var extraTextError = pgerror.Newf(pgcode.InvalidTextRepresentation, "extra text after closing right brace")
var enclosingError = pgerror.Newf(pgcode.InvalidTextRepresentation, "array must be enclosed in ARRAY[ and ]")
var extraTextError = pgerror.Newf(pgcode.InvalidTextRepresentation, "extra text after closing right bracket")
var nestedArraysNotSupportedError = unimplemented.NewWithIssueDetail(32552, "strcast", "nested arrays not supported")
var malformedError = pgerror.Newf(pgcode.InvalidTextRepresentation, "malformed array")

Expand All @@ -33,11 +33,11 @@ var isQuoteChar = func(ch byte) bool {
}

var isControlChar = func(ch byte) bool {
return ch == '{' || ch == '}' || ch == ',' || ch == '"'
return ch == '[' || ch == ']' || ch == ',' || ch == '"'
}

var isElementChar = func(r rune) bool {
return r != '{' && r != '}' && r != ','
return r != '[' && r != ']' && r != ','
}

// gobbleString advances the parser for the remainder of the current string
Expand Down Expand Up @@ -148,7 +148,7 @@ func (p *parseState) parseElement() error {
}

// ParseDArrayFromString parses the string-form of constructing arrays, handling
// cases such as `'{1,2,3}'::INT[]`. The input type t is the type of the
// cases such as `ARRAY[1, 2, 3]`. The input type t is the type of the
// parameter of the array to parse.
func ParseDArrayFromString(ctx ParseTimeContext, s string, t *types.T) (*DArray, error) {
ret, err := doParseDArrayFromString(ctx, s, t)
Expand All @@ -158,7 +158,7 @@ func ParseDArrayFromString(ctx ParseTimeContext, s string, t *types.T) (*DArray,
return ret, nil
}

// doParseDArraryFromString does most of the work of ParseDArrayFromString,
// doParseDArrayFromString does most of the work of ParseDArrayFromString,
// except the error it returns isn't prettified as a parsing error.
func doParseDArrayFromString(ctx ParseTimeContext, s string, t *types.T) (*DArray, error) {
parser := parseState{
Expand All @@ -169,12 +169,19 @@ func doParseDArrayFromString(ctx ParseTimeContext, s string, t *types.T) (*DArra
}

parser.eatWhitespace()
if parser.peek() != '{' {
out, err := parser.gobbleString(isControlChar)
if err != nil {
return nil, err
}
if out != "ARRAY" {
return nil, enclosingError
}
if parser.peek() != '[' {
return nil, enclosingError
}
parser.advance()
parser.eatWhitespace()
if parser.peek() != '}' {
if parser.peek() != ']' {
if err := parser.parseElement(); err != nil {
return nil, err
}
Expand All @@ -191,14 +198,13 @@ func doParseDArrayFromString(ctx ParseTimeContext, s string, t *types.T) (*DArra
if parser.eof() {
return nil, enclosingError
}
if parser.peek() != '}' {
if parser.peek() != ']' {
return nil, malformedError
}
parser.advance()
parser.eatWhitespace()
if !parser.eof() {
return nil, extraTextError
}

return parser.result, nil
}
11 changes: 11 additions & 0 deletions pkg/sql/sem/tree/parse_string_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ func TestParseDatumStringAs(t *testing.T) {
"true",
"false",
},
types.IntArray: {
"ARRAY[1]",
"ARRAY[1,2,3]",
},
types.StringArray: {
`ARRAY['hello','world']`,
`ARRAY['cockroach','database']`,
},
types.MakeArray(types.Decimal): {
`ARRAY[1.021,5.20503]`,
},
types.Bytes: {
`\x`,
`\x00`,
Expand Down

0 comments on commit 2242cb0

Please sign in to comment.