Skip to content

Commit d6ba57f

Browse files
fix for handling special characters in connection fields (#1169)
* fix for handling special characters * removing print stmt
1 parent dce2c5b commit d6ba57f

File tree

3 files changed

+106
-87
lines changed

3 files changed

+106
-87
lines changed

profiles/common.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,20 +49,28 @@ func ParseMap(s string) (map[string]string, error) {
4949
}
5050

5151
for _, kv := range records[0] {
52-
s := strings.Split(strings.TrimSpace(kv), "=")
53-
if len(s) != 2 {
52+
equalSignIndex := strings.Index(kv, "=")
53+
if equalSignIndex == -1 {
5454
return params, fmt.Errorf("invalid key=value pair (expected format: key1=value1): %v", kv)
5555
}
56-
if _, ok := params[s[0]]; ok {
57-
return params, fmt.Errorf("duplicate key found: %v", s[0])
56+
57+
key := strings.TrimSpace(kv[:equalSignIndex])
58+
value := kv[equalSignIndex+1:]
59+
60+
if len(key) == 0 {
61+
return params, fmt.Errorf("empty key found in pair: %v", kv)
5862
}
59-
params[s[0]] = s[1]
63+
64+
if _, ok := params[key]; ok {
65+
return params, fmt.Errorf("duplicate key found: %v", key)
66+
}
67+
params[key] = value
6068
}
6169
return params, nil
6270
}
6371

64-
func ParseList(s string)([]string, error) {
65-
if (len(s) == 0) {
72+
func ParseList(s string) ([]string, error) {
73+
if len(s) == 0 {
6674
return nil, nil
6775
}
6876
r := csv.NewReader(strings.NewReader(s))

profiles/common_test.go

Lines changed: 88 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -24,40 +24,52 @@ import (
2424
func TestParseMap(t *testing.T) {
2525
// Avoid getting/setting env variables in the unit tests.
2626
testCases := []struct {
27-
name string
28-
inputString string
29-
expectedParams map[string]string
30-
errorExpected bool
27+
name string
28+
inputString string
29+
expectedParams map[string]string
30+
errorExpected bool
3131
}{
3232
{
33-
name: "empty params",
34-
inputString: "",
35-
expectedParams: map[string]string{},
36-
errorExpected: false,
33+
name: "empty params",
34+
inputString: "",
35+
expectedParams: map[string]string{},
36+
errorExpected: false,
3737
},
3838
{
39-
name: "valid params=",
40-
inputString: "instance=instance",
41-
expectedParams: map[string]string{"instance": "instance"},
42-
errorExpected: false,
39+
name: "valid params",
40+
inputString: "instance=instance",
41+
expectedParams: map[string]string{"instance": "instance"},
42+
errorExpected: false,
4343
},
4444
{
45-
name: "invalid params incorrect format",
46-
inputString: "uuwy",
47-
expectedParams: map[string]string{},
48-
errorExpected: true,
45+
name: "valid params with = in value",
46+
inputString: "password=pass=word",
47+
expectedParams: map[string]string{"password": "pass=word"},
48+
errorExpected: false,
4949
},
5050
{
51-
name: "invalid params new line char",
52-
inputString: "uuwy\n hjgse",
53-
expectedParams: map[string]string{},
54-
errorExpected: true,
51+
name: "valid params with special characters in value",
52+
inputString: "\"password=password`~!@#$%^&*()-_=+[]{}|;:<,>.?/'\\\"",
53+
expectedParams: map[string]string{"password": "password`~!@#$%^&*()-_=+[]{}|;:<,>.?/'\\"},
54+
errorExpected: false,
5555
},
5656
{
57-
name: "invalid params duplicates",
58-
inputString: "instance=instance, instance=instance",
59-
expectedParams: map[string]string{"instance": "instance"},
60-
errorExpected: true,
57+
name: "invalid params incorrect format",
58+
inputString: "uuwy",
59+
expectedParams: map[string]string{},
60+
errorExpected: true,
61+
},
62+
{
63+
name: "invalid params new line char",
64+
inputString: "uuwy\n hjgse",
65+
expectedParams: map[string]string{},
66+
errorExpected: true,
67+
},
68+
{
69+
name: "invalid params duplicates",
70+
inputString: "instance=instance, instance=instance",
71+
expectedParams: map[string]string{"instance": "instance"},
72+
errorExpected: true,
6173
},
6274
}
6375

@@ -68,33 +80,32 @@ func TestParseMap(t *testing.T) {
6880
}
6981
}
7082

71-
7283
// code for testing parse list
7384
func TestParseList(t *testing.T) {
7485
// Avoid getting/setting env variables in the unit tests.
7586
testCases := []struct {
76-
name string
77-
inputString string
78-
expectedParams []string
79-
errorExpected bool
87+
name string
88+
inputString string
89+
expectedParams []string
90+
errorExpected bool
8091
}{
8192
{
82-
name: "empty input string",
83-
inputString: "",
84-
expectedParams: nil,
85-
errorExpected: false,
93+
name: "empty input string",
94+
inputString: "",
95+
expectedParams: nil,
96+
errorExpected: false,
8697
},
8798
{
88-
name: "valid input string",
89-
inputString: "hello, world",
90-
expectedParams: []string{"hello","world"},
91-
errorExpected: false,
99+
name: "valid input string",
100+
inputString: "hello, world",
101+
expectedParams: []string{"hello", "world"},
102+
errorExpected: false,
92103
},
93104
{
94-
name: "invalid input string new line char",
95-
inputString: "hello, world\n, !",
96-
expectedParams: nil,
97-
errorExpected: true,
105+
name: "invalid input string new line char",
106+
inputString: "hello, world\n, !",
107+
expectedParams: nil,
108+
errorExpected: true,
98109
},
99110
}
100111

@@ -114,72 +125,72 @@ func TestGetSQLConnectionStr(t *testing.T) {
114125
pwd := "password"
115126
db := "database"
116127
testCases := []struct {
117-
name string
118-
inputSourceProfileConn SourceProfileConnection
119-
expectedOutput string
128+
name string
129+
inputSourceProfileConn SourceProfileConnection
130+
expectedOutput string
120131
}{
121132
{
122-
name: "source profile connection type mysql",
133+
name: "source profile connection type mysql",
123134
inputSourceProfileConn: SourceProfileConnection{Ty: SourceProfileConnectionTypeMySQL, Mysql: SourceProfileConnectionMySQL{Host: host, Port: port, User: user, Pwd: pwd, Db: db}},
124-
expectedOutput: "user:password@tcp(0.0.0.0:3306)/database",
135+
expectedOutput: "user:password@tcp(0.0.0.0:3306)/database",
125136
},
126137
{
127-
name: "source profile connection type postgres",
138+
name: "source profile connection type postgres",
128139
inputSourceProfileConn: SourceProfileConnection{Ty: SourceProfileConnectionTypePostgreSQL, Pg: SourceProfileConnectionPostgreSQL{Host: host, Port: port, User: user, Pwd: pwd, Db: db}},
129-
expectedOutput: "host=0.0.0.0 port=3306 user=user password=password dbname=database sslmode=disable",
140+
expectedOutput: "host=0.0.0.0 port=3306 user=user password=password dbname=database sslmode=disable",
130141
},
131142
{
132-
name: "source profile connection type dynamodb",
143+
name: "source profile connection type dynamodb",
133144
inputSourceProfileConn: SourceProfileConnection{Ty: SourceProfileConnectionTypeDynamoDB},
134-
expectedOutput: "",
145+
expectedOutput: "",
135146
},
136147
{
137-
name: "source profile connection type sql server",
148+
name: "source profile connection type sql server",
138149
inputSourceProfileConn: SourceProfileConnection{Ty: SourceProfileConnectionTypeSqlServer, SqlServer: SourceProfileConnectionSqlServer{Host: host, Port: port, User: user, Pwd: pwd, Db: db}},
139-
expectedOutput: "sqlserver://user:password@0.0.0.0:3306?database=database",
150+
expectedOutput: "sqlserver://user:password@0.0.0.0:3306?database=database",
140151
},
141152
{
142-
name: "source profile connection type oracle",
153+
name: "source profile connection type oracle",
143154
inputSourceProfileConn: SourceProfileConnection{Ty: SourceProfileConnectionTypeOracle, Oracle: SourceProfileConnectionOracle{Host: host, Port: port, User: user, Pwd: pwd, Db: db}},
144-
expectedOutput: "oracle://user:password@0.0.0.0:3306/database",
155+
expectedOutput: "oracle://user:password@0.0.0.0:3306/database",
145156
},
146157
}
147158

148159
for _, tc := range testCases {
149-
res:= GetSQLConnectionStr(SourceProfile{Ty: SourceProfileType(SourceProfileTypeConnection), Conn: tc.inputSourceProfileConn})
160+
res := GetSQLConnectionStr(SourceProfile{Ty: SourceProfileType(SourceProfileTypeConnection), Conn: tc.inputSourceProfileConn})
150161
assert.Equal(t, tc.expectedOutput, res, tc.name)
151162
}
152163
}
153164

154165
func TestGenerateConnectionStr(t *testing.T) {
155166
// Avoid getting/setting env variables in the unit tests.
156-
before := func(){
167+
before := func() {
157168
setEnvVariables()
158169
}
159170

160-
after := func(){
171+
after := func() {
161172
unsetEnvVariables()
162173
}
163174
testCases := []struct {
164-
name string
165-
expectedOutputPg string
166-
expectedOutputMysql string
167-
errorExpected bool
175+
name string
176+
expectedOutputPg string
177+
expectedOutputMysql string
178+
errorExpected bool
168179
}{
169180
{
170-
name: "valid get mysql and postgres conn string",
171-
expectedOutputPg: "host=0.0.0.0 port=3306 user=user password=password dbname=db sslmode=disable",
172-
expectedOutputMysql: "user:password@tcp(0.0.0.0:3306)/db",
173-
errorExpected: false,
181+
name: "valid get mysql and postgres conn string",
182+
expectedOutputPg: "host=0.0.0.0 port=3306 user=user password=password dbname=db sslmode=disable",
183+
expectedOutputMysql: "user:password@tcp(0.0.0.0:3306)/db",
184+
errorExpected: false,
174185
},
175186
}
176187

177188
for _, tc := range testCases {
178189
before()
179-
res, err:= GeneratePGSQLConnectionStr()
190+
res, err := GeneratePGSQLConnectionStr()
180191
assert.Equal(t, tc.expectedOutputPg, res, tc.name)
181192
assert.Equal(t, tc.errorExpected, err != nil, tc.name)
182-
res, err= GenerateMYSQLConnectionStr()
193+
res, err = GenerateMYSQLConnectionStr()
183194
assert.Equal(t, tc.expectedOutputMysql, res, tc.name)
184195
assert.Equal(t, tc.errorExpected, err != nil, tc.name)
185196
after()
@@ -189,24 +200,24 @@ func TestGenerateConnectionStr(t *testing.T) {
189200
func TestGetSchemaSampleSize(t *testing.T) {
190201
// Avoid getting/setting env variables in the unit tests.
191202
testCases := []struct {
192-
name string
193-
inputSourceProfile SourceProfile
194-
expectedOutput int64
203+
name string
204+
inputSourceProfile SourceProfile
205+
expectedOutput int64
195206
}{
196207
{
197-
name: "mysql source profile type",
198-
inputSourceProfile: SourceProfile{Ty: SourceProfileType(SourceProfileTypeConnection), Conn: SourceProfileConnection{Ty: SourceProfileConnectionTypeMySQL}},
199-
expectedOutput: int64(100000),
208+
name: "mysql source profile type",
209+
inputSourceProfile: SourceProfile{Ty: SourceProfileType(SourceProfileTypeConnection), Conn: SourceProfileConnection{Ty: SourceProfileConnectionTypeMySQL}},
210+
expectedOutput: int64(100000),
200211
},
201212
{
202-
name: "dynamo db source profile type",
203-
inputSourceProfile: SourceProfile{Ty: SourceProfileType(SourceProfileTypeConnection), Conn: SourceProfileConnection{Ty: SourceProfileConnectionTypeDynamoDB, Dydb: SourceProfileConnectionDynamoDB{SchemaSampleSize: int64(5000)}}},
204-
expectedOutput: int64(5000),
213+
name: "dynamo db source profile type",
214+
inputSourceProfile: SourceProfile{Ty: SourceProfileType(SourceProfileTypeConnection), Conn: SourceProfileConnection{Ty: SourceProfileConnectionTypeDynamoDB, Dydb: SourceProfileConnectionDynamoDB{SchemaSampleSize: int64(5000)}}},
215+
expectedOutput: int64(5000),
205216
},
206217
}
207218

208219
for _, tc := range testCases {
209220
res := GetSchemaSampleSize(tc.inputSourceProfile)
210221
assert.Equal(t, tc.expectedOutput, res, tc.name)
211222
}
212-
}
223+
}

webv2/web.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ import (
3333
"time"
3434

3535
instance "cloud.google.com/go/spanner/admin/instance/apiv1"
36+
cassandraaccessor "github.com/GoogleCloudPlatform/spanner-migration-tool/accessors/cassandra"
3637
cc "github.com/GoogleCloudPlatform/spanner-migration-tool/accessors/clients/cassandra"
3738
storageclient "github.com/GoogleCloudPlatform/spanner-migration-tool/accessors/clients/storage"
3839
storageaccessor "github.com/GoogleCloudPlatform/spanner-migration-tool/accessors/storage"
39-
"github.com/GoogleCloudPlatform/spanner-migration-tool/accessors/cassandra"
4040
"github.com/GoogleCloudPlatform/spanner-migration-tool/cmd"
4141
"github.com/GoogleCloudPlatform/spanner-migration-tool/common/constants"
4242
"github.com/GoogleCloudPlatform/spanner-migration-tool/common/utils"
@@ -830,15 +830,15 @@ func getSourceAndTargetProfiles(sessionState *session.SessionState, details type
830830
return profiles.SourceProfile{}, profiles.TargetProfile{}, utils.IOStreams{}, "", fmt.Errorf("error while creating config to initiate sharded migration:%v", err)
831831
}
832832
} else if sessionState.Driver == constants.CASSANDRA {
833-
sourceProfileString = fmt.Sprintf("host=%v,port=%v,user=%v,password=%v,keyspace=%v,datacenter=%v",
833+
sourceProfileString = fmt.Sprintf("\"host=%v\",\"port=%v\",\"user=%v\",\"password=%v\",\"keyspace=%v\",\"datacenter=%v\"",
834834
sourceDBConnectionDetails.Host,
835835
sourceDBConnectionDetails.Port,
836836
sourceDBConnectionDetails.User,
837837
sourceDBConnectionDetails.Password,
838838
sessionState.DbName,
839839
sourceDBConnectionDetails.DataCenter)
840840
} else {
841-
sourceProfileString = fmt.Sprintf("host=%v,port=%v,user=%v,password=%v,dbName=%v",
841+
sourceProfileString = fmt.Sprintf("\"host=%v\",\"port=%v\",\"user=%v\",\"password=%v\",\"dbName=%v\"",
842842
sourceDBConnectionDetails.Host, sourceDBConnectionDetails.Port, sourceDBConnectionDetails.User,
843843
sourceDBConnectionDetails.Password, sessionState.DbName)
844844
}

0 commit comments

Comments
 (0)