sources/sqlserver/toddl.go (141 lines of code) (raw):
// Copyright 2021 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 sqlserver handles schema and data migrations from sqlserver.
package sqlserver
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 sql server 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, isPk bool) (ddl.Type, []internal.SchemaIssue) {
ty, issues := toSpannerTypeInternal(srcType, spType)
if conv.SpDialect == constants.DIALECT_POSTGRESQL {
var pg_issues []internal.SchemaIssue
ty, pg_issues = common.ToPGDialectType(ty, isPk)
issues = append(issues, pg_issues...)
}
return ty, issues
}
func (tdi ToDdlImpl) GetColumnAutoGen(conv *internal.Conv, autoGenCol ddl.AutoGenCol, colId string, tableId string) (*ddl.AutoGenCol, error) {
return nil, nil
}
// 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 "bigint":
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.Int64}, nil
}
case "tinyint", "smallint", "int":
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.Int64}, []internal.SchemaIssue{internal.Widened}
}
case "real":
switch spType {
case ddl.String:
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, []internal.SchemaIssue{internal.Widened}
case ddl.Float64:
return ddl.Type{Name: ddl.Float64}, []internal.SchemaIssue{internal.Widened}
default:
return ddl.Type{Name: ddl.Float32}, nil
}
case "float":
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 "numeric", "decimal", "money", "smallmoney":
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 "bit":
switch spType {
case ddl.String:
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, nil
default:
return ddl.Type{Name: ddl.Bool}, nil
}
case "uniqueidentifier":
switch spType {
case ddl.Bytes:
if len(srcType.Mods) > 0 && srcType.Mods[0] > 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 && srcType.Mods[0] > 0 {
return ddl.Type{Name: ddl.String, Len: srcType.Mods[0]}, nil
}
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, nil
}
case "varchar", "char", "nvarchar", "nchar":
switch spType {
case ddl.Bytes:
if len(srcType.Mods) > 0 && srcType.Mods[0] > 0 {
return ddl.Type{Name: ddl.Bytes, Len: srcType.Mods[0]}, nil
}
return ddl.Type{Name: ddl.Bytes, Len: ddl.MaxLength}, nil
default:
// Sets the source length only if it falls within the allowed length range in Spanner.
if len(srcType.Mods) > 0 && srcType.Mods[0] > 0 && srcType.Mods[0] <= ddl.StringMaxLength {
return ddl.Type{Name: ddl.String, Len: srcType.Mods[0]}, nil
}
// Raises warning and sets length to MAX when -
// Source length is greater than maximum allowed length
// -OR-
// Source length is "-1" which represents MAX in SQL Server
if len(srcType.Mods) > 0 && (srcType.Mods[0] > ddl.StringMaxLength || srcType.Mods[0] < 0) {
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, []internal.SchemaIssue{internal.StringOverflow}
}
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, nil
}
case "ntext", "text", "xml":
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 "binary", "varbinary", "image":
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 "datetime2", "datetime", "datetimeoffset", "smalldatetime", "rowversion":
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 "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.Int64}, nil
}
case "time":
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, []internal.SchemaIssue{internal.Time}
}
return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, []internal.SchemaIssue{internal.NoGoodType}
}