Merge conflict detection compared bigserial (DBML) against bigint (live PostgreSQL read of an existing serial column) as incompatible types, since serial is sugar over an integer column plus a sequence default and PostgreSQL always reports back the underlying integer type. Add SerialUnderlyingType and use it when comparing column types for conflicts. QuoteDefaultValue also wrapped bare keyword expressions like CURRENT_DATE in string quotes because they contain no parentheses, unlike function-call defaults such as now(). Recognize known bare keyword defaults and leave them unquoted across CREATE TABLE, ALTER TABLE ADD COLUMN, and ALTER COLUMN SET DEFAULT generation.
235 lines
6.7 KiB
Go
235 lines
6.7 KiB
Go
package pgsql
|
|
|
|
import "strings"
|
|
|
|
var GoToStdTypes = map[string]string{
|
|
"bool": "boolean",
|
|
"int64": "bigint",
|
|
"int": "integer",
|
|
"int8": "smallint",
|
|
"int16": "smallint",
|
|
"int32": "integer",
|
|
"uint": "integer",
|
|
"uint8": "smallint",
|
|
"uint16": "smallint",
|
|
"uint32": "integer",
|
|
"uint64": "bigint",
|
|
"uintptr": "bigint",
|
|
"znullint64": "bigint",
|
|
"znullint32": "integer",
|
|
"znullbyte": "smallint",
|
|
"float64": "double",
|
|
"float32": "double",
|
|
"complex64": "double",
|
|
"complex128": "double",
|
|
"customfloat64": "double",
|
|
"string": "text",
|
|
"Pointer": "bigint",
|
|
"[]byte": "blob",
|
|
"customdate": "date",
|
|
"customtime": "time",
|
|
"customtimestamp": "timestamp",
|
|
"sqlfloat64": "double",
|
|
"sqlfloat16": "double",
|
|
"sqluuid": "uuid",
|
|
"sqljsonb": "jsonb",
|
|
"sqljson": "json",
|
|
"sqlint64": "bigint",
|
|
"sqlint32": "integer",
|
|
"sqlint16": "smallint",
|
|
"sqlbool": "boolean",
|
|
"sqlstring": "text",
|
|
"nullablejsonb": "jsonb",
|
|
"nullablejson": "json",
|
|
"nullableuuid": "uuid",
|
|
"sqldate": "date",
|
|
"sqltime": "time",
|
|
"sqltimestamp": "timestamp",
|
|
"time.Time": "timestamp",
|
|
}
|
|
|
|
var GoToPGSQLTypes = map[string]string{
|
|
"bool": "boolean",
|
|
"int64": "bigint",
|
|
"int": "integer",
|
|
"int8": "smallint",
|
|
"int16": "smallint",
|
|
"int32": "integer",
|
|
"uint": "integer",
|
|
"uint8": "smallint",
|
|
"uint16": "smallint",
|
|
"uint32": "integer",
|
|
"uint64": "bigint",
|
|
"uintptr": "bigint",
|
|
"znullint64": "bigint",
|
|
"znullint32": "integer",
|
|
"znullbyte": "integer",
|
|
"float64": "double precision",
|
|
"float32": "real",
|
|
"complex64": "double precision",
|
|
"complex128": "double precision",
|
|
"customfloat64": "double precision",
|
|
"string": "text",
|
|
"Pointer": "bigint",
|
|
"[]byte": "bytea",
|
|
"customdate": "date",
|
|
"customtime": "time",
|
|
"customtimestamp": "timestamp",
|
|
"sqlfloat64": "double precision",
|
|
"sqlfloat16": "double precision",
|
|
"sqluuid": "uuid",
|
|
"sqljsonb": "jsonb",
|
|
"sqljson": "json",
|
|
"sqlint64": "bigint",
|
|
"sqlint32": "integer",
|
|
"sqlint16": "smallint",
|
|
"sqlbool": "boolean",
|
|
"sqlstring": "text",
|
|
"nullablejsonb": "jsonb",
|
|
"nullablejson": "json",
|
|
"nullableuuid": "uuid",
|
|
"sqldate": "date",
|
|
"sqltime": "time",
|
|
"sqltimestamp": "timestamp",
|
|
"time.Time": "timestamp",
|
|
"citext": "citext",
|
|
}
|
|
|
|
func ValidSQLType(sqltype string) bool {
|
|
for _, sql := range GoToPGSQLTypes {
|
|
if strings.EqualFold(sql, sqltype) {
|
|
return true
|
|
}
|
|
}
|
|
for _, sql := range GoToStdTypes {
|
|
if strings.EqualFold(sql, sqltype) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func GetSQLType(anytype string) string {
|
|
for gotype, sql := range GoToPGSQLTypes {
|
|
if strings.EqualFold(gotype, anytype) || strings.EqualFold(sql, anytype) {
|
|
return sql
|
|
}
|
|
}
|
|
for gotype, sql := range GoToStdTypes {
|
|
if strings.EqualFold(gotype, anytype) || strings.EqualFold(sql, anytype) {
|
|
return sql
|
|
}
|
|
}
|
|
return "text"
|
|
}
|
|
|
|
func ConvertSQLType(anytype string) string {
|
|
for gotype, sql := range GoToPGSQLTypes {
|
|
if strings.EqualFold(gotype, anytype) || strings.EqualFold(sql, anytype) {
|
|
return sql
|
|
}
|
|
}
|
|
for gotype, sql := range GoToStdTypes {
|
|
if strings.EqualFold(gotype, anytype) || strings.EqualFold(sql, anytype) {
|
|
return sql
|
|
}
|
|
}
|
|
return anytype
|
|
}
|
|
|
|
// PGTypeCanonical maps PostgreSQL type aliases and synonyms to their canonical base name.
|
|
// Input should be a base type (no dimension parameters, lowercase).
|
|
var PGTypeCanonical = map[string]string{
|
|
// integer aliases
|
|
"int": "integer",
|
|
"int4": "integer",
|
|
"int2": "smallint",
|
|
"int8": "bigint",
|
|
// float aliases
|
|
"float4": "real",
|
|
"float8": "double precision",
|
|
// bool alias
|
|
"bool": "boolean",
|
|
// char aliases
|
|
"character": "char",
|
|
"character varying": "varchar",
|
|
"bpchar": "char",
|
|
// timestamp aliases
|
|
"timestamp without time zone": "timestamp",
|
|
"timestamp with time zone": "timestamptz",
|
|
// time aliases
|
|
"time without time zone": "time",
|
|
"time with time zone": "timetz",
|
|
// decimal alias
|
|
"decimal": "numeric",
|
|
}
|
|
|
|
// knownPGBaseTypes is the set of canonical PostgreSQL base types (no aliases).
|
|
var knownPGBaseTypes = map[string]struct{}{
|
|
"integer": {}, "bigint": {}, "smallint": {},
|
|
"serial": {}, "bigserial": {}, "smallserial": {},
|
|
"numeric": {}, "real": {}, "double precision": {}, "money": {},
|
|
"varchar": {}, "char": {}, "text": {}, "citext": {},
|
|
"boolean": {},
|
|
"date": {}, "time": {}, "timetz": {}, "timestamp": {}, "timestamptz": {}, "interval": {},
|
|
"uuid": {}, "json": {}, "jsonb": {}, "bytea": {},
|
|
"inet": {}, "cidr": {}, "macaddr": {}, "xml": {},
|
|
}
|
|
|
|
// NormalizePGType maps a PostgreSQL base type (no dimension parameters) to its
|
|
// canonical form. Unknown types are returned as-is (lowercased).
|
|
func NormalizePGType(baseType string) string {
|
|
lower := strings.ToLower(strings.TrimSpace(baseType))
|
|
if canonical, ok := PGTypeCanonical[lower]; ok {
|
|
return canonical
|
|
}
|
|
return lower
|
|
}
|
|
|
|
// IsKnownPGBaseType reports whether the given name (after NormalizePGType) is a
|
|
// recognized built-in PostgreSQL type. Custom types (e.g. vector, postgis) return false.
|
|
func IsKnownPGBaseType(baseType string) bool {
|
|
_, ok := knownPGBaseTypes[strings.ToLower(strings.TrimSpace(baseType))]
|
|
return ok
|
|
}
|
|
|
|
// serialUnderlyingType maps each serial pseudo-type to the integer type
|
|
// PostgreSQL actually stores the column as. serial/bigserial/smallserial are
|
|
// not real types: they are sugar for an integer column plus a sequence
|
|
// default, and pg_catalog (and information_schema) always reports the
|
|
// underlying integer type back for such columns.
|
|
var serialUnderlyingType = map[string]string{
|
|
"serial": "integer",
|
|
"bigserial": "bigint",
|
|
"smallserial": "smallint",
|
|
}
|
|
|
|
// SerialUnderlyingType returns the underlying integer type for a serial
|
|
// pseudo-type (e.g. "bigserial" -> "bigint"). If baseType (after
|
|
// NormalizePGType) is not a serial type, it is returned unchanged.
|
|
func SerialUnderlyingType(baseType string) string {
|
|
normalized := NormalizePGType(baseType)
|
|
if underlying, ok := serialUnderlyingType[normalized]; ok {
|
|
return underlying
|
|
}
|
|
return normalized
|
|
}
|
|
|
|
func IsGoType(pTypeName string) bool {
|
|
for k := range GoToStdTypes {
|
|
if strings.EqualFold(pTypeName, k) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func GetStdTypeFromGo(pTypeName string) string {
|
|
for k, s := range GoToStdTypes {
|
|
if strings.EqualFold(pTypeName, k) {
|
|
return s
|
|
}
|
|
}
|
|
return pTypeName
|
|
}
|