/
toddl.go
200 lines (195 loc) · 7.44 KB
/
toddl.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package postgres handles schema and data migrations from Postgres.
package postgres
import (
"github.com/GoogleCloudPlatform/spanner-migration-tool/common/constants"
"github.com/GoogleCloudPlatform/spanner-migration-tool/internal"
"github.com/GoogleCloudPlatform/spanner-migration-tool/schema"
"github.com/GoogleCloudPlatform/spanner-migration-tool/sources/common"
"github.com/GoogleCloudPlatform/spanner-migration-tool/spanner/ddl"
)
// ToDdlImpl Postgres specific implementation for ToDdl.
type ToDdlImpl struct {
}
// ToSpannerType maps a scalar source schema type (defined by id and
// mods) into a Spanner type. This is the core source-to-Spanner type
// mapping. toSpannerType returns the Spanner type and a list of type
// conversion issues encountered.
func (tdi ToDdlImpl) ToSpannerType(conv *internal.Conv, spType string, srcType schema.Type) (ddl.Type, []internal.SchemaIssue) {
ty, issues := toSpannerTypeInternal(srcType, spType)
if len(srcType.ArrayBounds) > 1 {
ty = ddl.Type{Name: ddl.String, Len: ddl.MaxLength}
issues = append(issues, internal.MultiDimensionalArray)
} else if len(srcType.ArrayBounds) == 1 {
// This check has been added because we don't support Array<primitive type> to string conversions
// and Array datatype is currently not supported in datastream.
ty = ddl.Type{Name: ddl.String, Len: ddl.MaxLength}
issues = append(issues, internal.ArrayTypeNotSupported)
}
if conv.SpDialect == constants.DIALECT_POSTGRESQL {
ty = common.ToPGDialectType(ty)
}
return ty, issues
}
// toSpannerTypeInternal defines the mapping of source types into Spanner
// types. Each source type has a default Spanner type, as well as other potential
// Spanner types it could map to. When calling toSpannerTypeInternal, you specify
// the source type name (along with any modifiers), and optionally you specify
// a target Spanner type name (empty string if you don't have one). If the target
// Spanner type name is specified and is a potential mapping for this source type,
// then it will be used to build the returned ddl.Type. If not, the default
// Spanner type for this source type will be used.
func toSpannerTypeInternal(srcType schema.Type, spType string) (ddl.Type, []internal.SchemaIssue) {
switch srcType.Name {
case "bool", "boolean":
switch spType {
case ddl.String:
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, []internal.SchemaIssue{internal.Widened}
case ddl.Int64:
return ddl.Type{Name: ddl.Int64}, []internal.SchemaIssue{internal.Widened}
default:
return ddl.Type{Name: ddl.Bool}, nil
}
case "bigserial":
switch spType {
case ddl.String:
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, []internal.SchemaIssue{internal.Widened, internal.Serial}
default:
return ddl.Type{Name: ddl.Int64}, []internal.SchemaIssue{internal.Serial}
}
case "bpchar", "character": // Note: Postgres internal name for char is bpchar (aka blank padded char).
switch spType {
case ddl.Bytes:
if len(srcType.Mods) > 0 {
return ddl.Type{Name: ddl.Bytes, Len: srcType.Mods[0]}, nil
}
return ddl.Type{Name: ddl.Bytes, Len: 1}, nil
default:
if len(srcType.Mods) > 0 {
return ddl.Type{Name: ddl.String, Len: srcType.Mods[0]}, nil
}
// Note: bpchar without length specifier is equivalent to bpchar(1)
return ddl.Type{Name: ddl.String, Len: 1}, nil
}
case "bytea":
switch spType {
case ddl.String:
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, nil
default:
return ddl.Type{Name: ddl.Bytes, Len: ddl.MaxLength}, nil
}
case "date":
switch spType {
case ddl.String:
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, []internal.SchemaIssue{internal.Widened}
default:
return ddl.Type{Name: ddl.Date}, nil
}
case "float8", "double precision":
switch spType {
case ddl.String:
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, []internal.SchemaIssue{internal.Widened}
default:
return ddl.Type{Name: ddl.Float64}, nil
}
case "float4", "real":
switch spType {
case ddl.String:
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, []internal.SchemaIssue{internal.Widened}
default:
return ddl.Type{Name: ddl.Float64}, []internal.SchemaIssue{internal.Widened}
}
case "int8", "bigint":
switch spType {
case ddl.String:
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, []internal.SchemaIssue{internal.Widened}
default:
return ddl.Type{Name: ddl.Int64}, nil
}
case "int4", "integer":
switch spType {
case ddl.String:
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, []internal.SchemaIssue{internal.Widened}
default:
return ddl.Type{Name: ddl.Int64}, []internal.SchemaIssue{internal.Widened}
}
case "int2", "smallint":
switch spType {
case ddl.String:
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, []internal.SchemaIssue{internal.Widened}
default:
return ddl.Type{Name: ddl.Int64}, []internal.SchemaIssue{internal.Widened}
}
case "numeric":
switch spType {
case ddl.String:
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, []internal.SchemaIssue{internal.Widened}
default:
// TODO: check mod[0] and mod[1] and generate a warning
// if this numeric won't fit in Spanner's NUMERIC.
return ddl.Type{Name: ddl.Numeric}, nil
}
case "serial":
switch spType {
case ddl.String:
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, []internal.SchemaIssue{internal.Widened, internal.Serial}
default:
return ddl.Type{Name: ddl.Int64}, []internal.SchemaIssue{internal.Serial}
}
case "text":
switch spType {
case ddl.Bytes:
return ddl.Type{Name: ddl.Bytes, Len: ddl.MaxLength}, nil
default:
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, nil
}
case "timestamptz", "timestamp with time zone":
switch spType {
case ddl.String:
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, []internal.SchemaIssue{internal.Widened}
default:
return ddl.Type{Name: ddl.Timestamp}, nil
}
case "timestamp", "timestamp without time zone":
// Map timestamp without timezone to Spanner timestamp.
switch spType {
case ddl.String:
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, []internal.SchemaIssue{internal.Widened}
default:
return ddl.Type{Name: ddl.Timestamp}, []internal.SchemaIssue{internal.Timestamp}
}
case "json", "jsonb":
switch spType {
case ddl.String:
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, nil
default:
return ddl.Type{Name: ddl.JSON}, nil
}
case "varchar", "character varying":
switch spType {
case ddl.Bytes:
if len(srcType.Mods) > 0 {
return ddl.Type{Name: ddl.Bytes, Len: srcType.Mods[0]}, nil
}
return ddl.Type{Name: ddl.Bytes, Len: ddl.MaxLength}, nil
default:
if len(srcType.Mods) > 0 {
return ddl.Type{Name: ddl.String, Len: srcType.Mods[0]}, nil
}
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, nil
}
}
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, []internal.SchemaIssue{internal.NoGoodType}
}