diff --git a/utils/ybdbid/ybdbid.go b/utils/ybdbid/ybdbid.go index 1817bef..cca743a 100644 --- a/utils/ybdbid/ybdbid.go +++ b/utils/ybdbid/ybdbid.go @@ -3,19 +3,31 @@ package ybdbid import ( "encoding/base64" "fmt" + "regexp" "github.com/google/uuid" ) +// This ID type appears in the databasse when a snapshot with incorrect +// configuration is created via RPC. To trigger creating a snapshot +// with such ID, create a YSQL database where: +// - table namespace is not set +// - transaction aware is false +var nonUUIDIDTypeR *regexp.Regexp + +func init() { + r, _ := regexp.Compile("^[a-fA-F0-9]{32}$") + nonUUIDIDTypeR = r +} + +// YBDBID represents a parsed YugabyteDB. type YBDBID interface { Bytes() []byte - UUID() uuid.UUID String() string } type defaultYBDBID struct { bytes []byte - uuid uuid.UUID str string } @@ -23,10 +35,6 @@ func (id *defaultYBDBID) Bytes() []byte { return id.bytes } -func (id *defaultYBDBID) UUID() uuid.UUID { - return id.uuid -} - func (id *defaultYBDBID) String() string { return id.str } @@ -36,11 +44,21 @@ func (id *defaultYBDBID) String() string { func TryParseFromBytes(input []byte) (YBDBID, error) { aUUID := uuid.New() if err := aUUID.UnmarshalBinary(input); err != nil { + + // if it failed, it could be a third ID type: + if nonUUIDIDTypeR.Match(input) { + output := &defaultYBDBID{ + bytes: make([]byte, len(input)), + str: string(input), + } + copy(output.bytes, input) + return output, nil + } + return nil, err } output := &defaultYBDBID{ bytes: make([]byte, len(input)), - uuid: aUUID, str: aUUID.String(), } copy(output.bytes, input) @@ -52,6 +70,16 @@ func TryParseFromBytes(input []byte) (YBDBID, error) { // as originally returned by the protobuf API. func TryParseFromString(input string) (YBDBID, error) { + // support third type of ID: + if nonUUIDIDTypeR.Match([]byte(input)) { + output := &defaultYBDBID{ + bytes: make([]byte, len(input)), + str: string(input), + } + copy(output.bytes, input) + return output, nil + } + // try decoding as base64: maybeDecoded, err := base64.StdEncoding.DecodeString(input) if err == nil { @@ -74,7 +102,6 @@ func TryParseFromString(input string) (YBDBID, error) { output := &defaultYBDBID{ bytes: make([]byte, len(bys)), - uuid: aUUID, str: aUUID.String(), } copy(output.bytes, bys) diff --git a/utils/ybdbid/ybdbid_test.go b/utils/ybdbid/ybdbid_test.go index 49a9529..b9b0469 100644 --- a/utils/ybdbid/ybdbid_test.go +++ b/utils/ybdbid/ybdbid_test.go @@ -18,12 +18,10 @@ func TestYBDBIDParsing(t *testing.T) { parsedBackViaBytes, err := TryParseFromBytes(parsed.Bytes()) assert.Nil(tt, err) - assert.Equal(tt, parsed.UUID(), parsedBackViaBytes.UUID()) assert.Equal(tt, parsed.String(), parsedBackViaBytes.String()) parsedBackViaString, err := TryParseFromString(parsed.String()) assert.Nil(tt, err) - assert.Equal(tt, parsed.UUID(), parsedBackViaString.UUID()) assert.Equal(tt, parsed.String(), parsedBackViaString.String()) assert.Equal(tt, validBase64YBDBID, base64.StdEncoding.EncodeToString(parsedBackViaBytes.Bytes())) @@ -40,12 +38,10 @@ func TestYBDBIDParsing(t *testing.T) { parsedBackViaBytes, err := TryParseFromBytes(parsed.Bytes()) assert.Nil(tt, err) - assert.Equal(tt, parsed.UUID(), parsedBackViaBytes.UUID()) assert.Equal(tt, parsed.String(), parsedBackViaBytes.String()) parsedBackViaString, err := TryParseFromString(parsed.String()) assert.Nil(tt, err) - assert.Equal(tt, parsed.UUID(), parsedBackViaString.UUID()) assert.Equal(tt, parsed.String(), parsedBackViaString.String()) }) @@ -70,4 +66,16 @@ func TestYBDBIDParsing(t *testing.T) { assert.Nil(tt, parsed) }) + t.Run("it-supports non-UUID ID types", func(tt *testing.T) { + encodedRepresentation := "ODU4OWYyMzJjYWQxNGJiZWFmNGU4ZTA0NzEwMmYwNmI=" + bys, err := base64.StdEncoding.DecodeString(encodedRepresentation) + assert.Nil(tt, err) + parsed, err := TryParseFromBytes(bys) + assert.Nil(tt, err) + reparsed, err := TryParseFromString(parsed.String()) + assert.Nil(tt, err) + reencoded := base64.StdEncoding.EncodeToString([]byte(reparsed.String())) + assert.Equal(tt, encodedRepresentation, reencoded) + }) + }