Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bb7ceb37fe | |||
| 6a759ef3d1 | |||
| cb735f0754 | |||
| 80fb49bc5e | |||
| 9190df81dd |
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# Maintainer: Hein (Warky Devs) <hein@warky.dev>
|
# Maintainer: Hein (Warky Devs) <hein@warky.dev>
|
||||||
pkgname=relspec
|
pkgname=relspec
|
||||||
pkgver=1.0.57
|
pkgver=1.0.58
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="RelSpec is a comprehensive database relations management tool that reads, transforms, and writes database table specifications across multiple formats and ORMs."
|
pkgdesc="RelSpec is a comprehensive database relations management tool that reads, transforms, and writes database table specifications across multiple formats and ORMs."
|
||||||
arch=('x86_64' 'aarch64')
|
arch=('x86_64' 'aarch64')
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
Name: relspec
|
Name: relspec
|
||||||
Version: 1.0.57
|
Version: 1.0.58
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: RelSpec is a comprehensive database relations management tool that reads, transforms, and writes database table specifications across multiple formats and ORMs.
|
Summary: RelSpec is a comprehensive database relations management tool that reads, transforms, and writes database table specifications across multiple formats and ORMs.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,156 @@
|
|||||||
|
package mariadb
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// MariaDBToCanonicalTypes maps MariaDB/MySQL type names to canonical types.
|
||||||
|
var MariaDBToCanonicalTypes = map[string]string{
|
||||||
|
// Integer types
|
||||||
|
"tinyint": "int8",
|
||||||
|
"smallint": "int16",
|
||||||
|
"mediumint": "int",
|
||||||
|
"int": "int",
|
||||||
|
"integer": "int",
|
||||||
|
"int2": "int16",
|
||||||
|
"int4": "int",
|
||||||
|
"int8": "int64",
|
||||||
|
"bigint": "int64",
|
||||||
|
// Boolean (TINYINT(1) alias)
|
||||||
|
"boolean": "bool",
|
||||||
|
"bool": "bool",
|
||||||
|
"bit": "bool",
|
||||||
|
// Float types
|
||||||
|
"float": "float32",
|
||||||
|
"double": "float64",
|
||||||
|
"real": "float64",
|
||||||
|
"double precision": "float64",
|
||||||
|
// Decimal types
|
||||||
|
"decimal": "decimal",
|
||||||
|
"numeric": "decimal",
|
||||||
|
"dec": "decimal",
|
||||||
|
"fixed": "decimal",
|
||||||
|
// String types
|
||||||
|
"char": "string",
|
||||||
|
"character": "string",
|
||||||
|
"varchar": "string",
|
||||||
|
"nchar": "string",
|
||||||
|
"nvarchar": "string",
|
||||||
|
"tinytext": "text",
|
||||||
|
"text": "text",
|
||||||
|
"mediumtext": "text",
|
||||||
|
"longtext": "text",
|
||||||
|
// Binary/blob types
|
||||||
|
"binary": "bytea",
|
||||||
|
"varbinary": "bytea",
|
||||||
|
"tinyblob": "bytea",
|
||||||
|
"blob": "bytea",
|
||||||
|
"mediumblob": "bytea",
|
||||||
|
"longblob": "bytea",
|
||||||
|
// Date/time types
|
||||||
|
"date": "date",
|
||||||
|
"time": "time",
|
||||||
|
"datetime": "timestamp",
|
||||||
|
"timestamp": "timestamp",
|
||||||
|
"year": "int",
|
||||||
|
// Other types
|
||||||
|
"json": "json",
|
||||||
|
"enum": "string",
|
||||||
|
"set": "string",
|
||||||
|
"uuid": "uuid",
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanonicalToMariaDBTypes maps canonical types to MariaDB/MySQL types.
|
||||||
|
var CanonicalToMariaDBTypes = map[string]string{
|
||||||
|
"bool": "TINYINT(1)",
|
||||||
|
"int8": "TINYINT",
|
||||||
|
"int16": "SMALLINT",
|
||||||
|
"int": "INT",
|
||||||
|
"int32": "INT",
|
||||||
|
"int64": "BIGINT",
|
||||||
|
"uint": "INT UNSIGNED",
|
||||||
|
"uint8": "TINYINT UNSIGNED",
|
||||||
|
"uint16": "SMALLINT UNSIGNED",
|
||||||
|
"uint32": "INT UNSIGNED",
|
||||||
|
"uint64": "BIGINT UNSIGNED",
|
||||||
|
"float32": "FLOAT",
|
||||||
|
"float64": "DOUBLE",
|
||||||
|
"decimal": "DECIMAL",
|
||||||
|
"string": "VARCHAR(255)",
|
||||||
|
"text": "TEXT",
|
||||||
|
"date": "DATE",
|
||||||
|
"time": "TIME",
|
||||||
|
"timestamp": "DATETIME",
|
||||||
|
"timestamptz": "DATETIME",
|
||||||
|
"uuid": "CHAR(36)",
|
||||||
|
"json": "JSON",
|
||||||
|
"jsonb": "JSON",
|
||||||
|
"bytea": "BLOB",
|
||||||
|
}
|
||||||
|
|
||||||
|
// MariaDBTypeSynonyms maps MariaDB/MySQL type aliases to their canonical MariaDB name.
|
||||||
|
var MariaDBTypeSynonyms = map[string]string{
|
||||||
|
"integer": "int",
|
||||||
|
"int2": "smallint",
|
||||||
|
"int4": "int",
|
||||||
|
"int8": "bigint",
|
||||||
|
"double precision": "double",
|
||||||
|
"character": "char",
|
||||||
|
"dec": "decimal",
|
||||||
|
"fixed": "decimal",
|
||||||
|
"numeric": "decimal",
|
||||||
|
"boolean": "tinyint",
|
||||||
|
"bool": "tinyint",
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormalizeMariaDBType maps a MariaDB/MySQL base type (no dimension parameters)
|
||||||
|
// to its canonical MariaDB form. Unknown types are returned as-is (lowercased).
|
||||||
|
func NormalizeMariaDBType(baseType string) string {
|
||||||
|
lower := strings.ToLower(strings.TrimSpace(baseType))
|
||||||
|
if canonical, ok := MariaDBTypeSynonyms[lower]; ok {
|
||||||
|
return canonical
|
||||||
|
}
|
||||||
|
return lower
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertMariaDBToCanonical converts a MariaDB/MySQL type name to the canonical type.
|
||||||
|
// Strips dimension parameters and normalizes aliases. Defaults to "string".
|
||||||
|
func ConvertMariaDBToCanonical(mariadbType string) string {
|
||||||
|
base := strings.ToLower(strings.TrimSpace(mariadbType))
|
||||||
|
if idx := strings.Index(base, "("); idx >= 0 {
|
||||||
|
base = strings.TrimSpace(base[:idx])
|
||||||
|
}
|
||||||
|
|
||||||
|
if canonical, ok := MariaDBToCanonicalTypes[base]; ok {
|
||||||
|
return canonical
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefix match for composite types (e.g., "unsigned bigint")
|
||||||
|
for key, canonical := range MariaDBToCanonicalTypes {
|
||||||
|
if strings.HasPrefix(base, key) {
|
||||||
|
return canonical
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "string"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertCanonicalToMariaDB converts a canonical type to a MariaDB/MySQL type.
|
||||||
|
// Defaults to VARCHAR(255) for unrecognised types.
|
||||||
|
func ConvertCanonicalToMariaDB(canonicalType string) string {
|
||||||
|
lower := strings.ToLower(strings.TrimSpace(canonicalType))
|
||||||
|
if idx := strings.Index(lower, "("); idx >= 0 {
|
||||||
|
lower = strings.TrimSpace(lower[:idx])
|
||||||
|
}
|
||||||
|
|
||||||
|
if mariadbType, ok := CanonicalToMariaDBTypes[lower]; ok {
|
||||||
|
return mariadbType
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefix fallback
|
||||||
|
for canonical, mariadb := range CanonicalToMariaDBTypes {
|
||||||
|
if strings.HasPrefix(lower, canonical) {
|
||||||
|
return mariadb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "VARCHAR(255)"
|
||||||
|
}
|
||||||
+34
-6
@@ -5,9 +5,11 @@ package merge
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
||||||
|
"git.warky.dev/wdevs/relspecgo/pkg/pgsql"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MergeResult represents the result of a merge operation
|
// MergeResult represents the result of a merge operation
|
||||||
@@ -449,14 +451,40 @@ func columnTypeConflict(target, source *models.Column) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return normalizeType(target.Type) != normalizeType(source.Type) ||
|
tType, tLen, tPrec, tScale := extractTypeParts(target)
|
||||||
target.Length != source.Length ||
|
sType, sLen, sPrec, sScale := extractTypeParts(source)
|
||||||
target.Precision != source.Precision ||
|
|
||||||
target.Scale != source.Scale
|
return tType != sType || tLen != sLen || tPrec != sPrec || tScale != sScale
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizeType(value string) string {
|
// extractTypeParts returns the canonical base type and dimensions for a column,
|
||||||
return strings.ToLower(strings.TrimSpace(value))
|
// handling the case where dimensions are embedded in the type string (e.g. "char(2)")
|
||||||
|
// rather than stored in the separate Length/Precision/Scale fields.
|
||||||
|
func extractTypeParts(col *models.Column) (baseType string, length, precision, scale int) {
|
||||||
|
typeName := strings.ToLower(strings.TrimSpace(col.Type))
|
||||||
|
length, precision, scale = col.Length, col.Precision, col.Scale
|
||||||
|
|
||||||
|
if idx := strings.Index(typeName, "("); idx >= 0 {
|
||||||
|
inner := strings.TrimRight(strings.TrimSpace(typeName[idx+1:]), ")")
|
||||||
|
typeName = strings.TrimSpace(typeName[:idx])
|
||||||
|
parts := strings.Split(inner, ",")
|
||||||
|
if len(parts) == 2 {
|
||||||
|
if p, err := strconv.Atoi(strings.TrimSpace(parts[0])); err == nil && p > 0 && precision == 0 {
|
||||||
|
precision = p
|
||||||
|
}
|
||||||
|
if s, err := strconv.Atoi(strings.TrimSpace(parts[1])); err == nil && s > 0 && scale == 0 {
|
||||||
|
scale = s
|
||||||
|
}
|
||||||
|
} else if len(parts) == 1 {
|
||||||
|
if l, err := strconv.Atoi(strings.TrimSpace(parts[0])); err == nil && l > 0 && length == 0 && precision == 0 {
|
||||||
|
length = l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typeName = pgsql.NormalizePGType(typeName)
|
||||||
|
|
||||||
|
return typeName, length, precision, scale
|
||||||
}
|
}
|
||||||
|
|
||||||
func describeColumnType(col *models.Column) string {
|
func describeColumnType(col *models.Column) string {
|
||||||
|
|||||||
+76
-32
@@ -2,32 +2,73 @@ package mssql
|
|||||||
|
|
||||||
import "strings"
|
import "strings"
|
||||||
|
|
||||||
// CanonicalToMSSQLTypes maps canonical types to MSSQL types
|
// CanonicalToMSSQLTypes maps canonical types to MSSQL types.
|
||||||
|
// Accepts both Go canonical names ("int", "string") and SQL canonical names
|
||||||
|
// ("integer", "varchar") so the writer handles input from any reader.
|
||||||
var CanonicalToMSSQLTypes = map[string]string{
|
var CanonicalToMSSQLTypes = map[string]string{
|
||||||
|
// Boolean — Go and SQL canonical
|
||||||
"bool": "BIT",
|
"bool": "BIT",
|
||||||
|
"boolean": "BIT",
|
||||||
|
// Integer — Go canonical
|
||||||
"int8": "TINYINT",
|
"int8": "TINYINT",
|
||||||
"int16": "SMALLINT",
|
"int16": "SMALLINT",
|
||||||
"int": "INT",
|
"int": "INT",
|
||||||
"int32": "INT",
|
"int32": "INT",
|
||||||
"int64": "BIGINT",
|
"int64": "BIGINT",
|
||||||
"uint": "BIGINT",
|
"uint": "BIGINT",
|
||||||
"uint8": "SMALLINT",
|
"uint8": "TINYINT",
|
||||||
"uint16": "INT",
|
"uint16": "SMALLINT",
|
||||||
"uint32": "BIGINT",
|
"uint32": "BIGINT",
|
||||||
"uint64": "BIGINT",
|
"uint64": "BIGINT",
|
||||||
|
// Integer — SQL canonical (serial types map to base integer; IDENTITY is set via AutoIncrement)
|
||||||
|
"integer": "INT",
|
||||||
|
"smallint": "SMALLINT",
|
||||||
|
"bigint": "BIGINT",
|
||||||
|
"tinyint": "TINYINT",
|
||||||
|
"serial": "INT",
|
||||||
|
"smallserial": "SMALLINT",
|
||||||
|
"bigserial": "BIGINT",
|
||||||
|
// Float — Go canonical
|
||||||
"float32": "REAL",
|
"float32": "REAL",
|
||||||
"float64": "FLOAT",
|
"float64": "FLOAT",
|
||||||
|
// Float — SQL canonical
|
||||||
|
"real": "REAL",
|
||||||
|
"double precision": "FLOAT",
|
||||||
|
"double": "FLOAT",
|
||||||
|
// Decimal/numeric
|
||||||
"decimal": "NUMERIC",
|
"decimal": "NUMERIC",
|
||||||
|
"numeric": "NUMERIC",
|
||||||
|
"money": "MONEY",
|
||||||
|
// String — Go canonical
|
||||||
"string": "NVARCHAR(255)",
|
"string": "NVARCHAR(255)",
|
||||||
"text": "NVARCHAR(MAX)",
|
"text": "NVARCHAR(MAX)",
|
||||||
|
// String — SQL canonical
|
||||||
|
"varchar": "NVARCHAR(255)",
|
||||||
|
"char": "NCHAR",
|
||||||
|
"nvarchar": "NVARCHAR(255)",
|
||||||
|
"nchar": "NCHAR",
|
||||||
|
"citext": "NVARCHAR(MAX)",
|
||||||
|
// Date/time
|
||||||
"date": "DATE",
|
"date": "DATE",
|
||||||
"time": "TIME",
|
"time": "TIME",
|
||||||
|
"timetz": "DATETIMEOFFSET",
|
||||||
"timestamp": "DATETIME2",
|
"timestamp": "DATETIME2",
|
||||||
"timestamptz": "DATETIMEOFFSET",
|
"timestamptz": "DATETIMEOFFSET",
|
||||||
|
"datetime": "DATETIME2",
|
||||||
|
"interval": "NVARCHAR(50)",
|
||||||
|
// UUID
|
||||||
"uuid": "UNIQUEIDENTIFIER",
|
"uuid": "UNIQUEIDENTIFIER",
|
||||||
|
// JSON — MSSQL has no native JSON type; stored as NVARCHAR(MAX)
|
||||||
"json": "NVARCHAR(MAX)",
|
"json": "NVARCHAR(MAX)",
|
||||||
"jsonb": "NVARCHAR(MAX)",
|
"jsonb": "NVARCHAR(MAX)",
|
||||||
|
// Binary
|
||||||
"bytea": "VARBINARY(MAX)",
|
"bytea": "VARBINARY(MAX)",
|
||||||
|
"blob": "VARBINARY(MAX)",
|
||||||
|
// Network/geo types — no MSSQL native equivalent
|
||||||
|
"xml": "XML",
|
||||||
|
"inet": "NVARCHAR(45)",
|
||||||
|
"cidr": "NVARCHAR(43)",
|
||||||
|
"macaddr": "NVARCHAR(17)",
|
||||||
}
|
}
|
||||||
|
|
||||||
// MSSQLToCanonicalTypes maps MSSQL types to canonical types
|
// MSSQLToCanonicalTypes maps MSSQL types to canonical types
|
||||||
@@ -68,47 +109,50 @@ var MSSQLToCanonicalTypes = map[string]string{
|
|||||||
"geometry": "string",
|
"geometry": "string",
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConvertCanonicalToMSSQL converts a canonical type to MSSQL type
|
// MSSQLTypeSynonyms maps MSSQL type aliases to their canonical MSSQL name.
|
||||||
|
var MSSQLTypeSynonyms = map[string]string{
|
||||||
|
"integer": "int",
|
||||||
|
"dec": "decimal",
|
||||||
|
"float(n)": "float",
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormalizeMSSQLType maps an MSSQL base type (no dimension parameters) to its
|
||||||
|
// canonical MSSQL form. Unknown types are returned as-is (lowercased).
|
||||||
|
func NormalizeMSSQLType(baseType string) string {
|
||||||
|
lower := strings.ToLower(strings.TrimSpace(baseType))
|
||||||
|
if canonical, ok := MSSQLTypeSynonyms[lower]; ok {
|
||||||
|
return canonical
|
||||||
|
}
|
||||||
|
return lower
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertCanonicalToMSSQL converts a canonical type (Go or SQL) to an MSSQL type.
|
||||||
|
// Strips dimension parameters before lookup. Defaults to NVARCHAR(255) for unknown types.
|
||||||
func ConvertCanonicalToMSSQL(canonicalType string) string {
|
func ConvertCanonicalToMSSQL(canonicalType string) string {
|
||||||
// Check direct mapping
|
base := strings.ToLower(strings.TrimSpace(canonicalType))
|
||||||
if mssqlType, exists := CanonicalToMSSQLTypes[strings.ToLower(canonicalType)]; exists {
|
if idx := strings.Index(base, "("); idx >= 0 {
|
||||||
|
base = strings.TrimSpace(base[:idx])
|
||||||
|
}
|
||||||
|
base = strings.TrimSuffix(base, "[]")
|
||||||
|
|
||||||
|
if mssqlType, exists := CanonicalToMSSQLTypes[base]; exists {
|
||||||
return mssqlType
|
return mssqlType
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to find by prefix
|
|
||||||
lowerType := strings.ToLower(canonicalType)
|
|
||||||
for canonical, mssql := range CanonicalToMSSQLTypes {
|
|
||||||
if strings.HasPrefix(lowerType, canonical) {
|
|
||||||
return mssql
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default to NVARCHAR
|
|
||||||
return "NVARCHAR(255)"
|
return "NVARCHAR(255)"
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConvertMSSQLToCanonical converts an MSSQL type to canonical type
|
// ConvertMSSQLToCanonical converts an MSSQL type to the canonical type.
|
||||||
|
// Strips dimension parameters before lookup. Defaults to "string" for unknown types.
|
||||||
func ConvertMSSQLToCanonical(mssqlType string) string {
|
func ConvertMSSQLToCanonical(mssqlType string) string {
|
||||||
// Extract base type (remove parentheses and parameters)
|
base := strings.ToLower(strings.TrimSpace(mssqlType))
|
||||||
baseType := mssqlType
|
if idx := strings.Index(base, "("); idx >= 0 {
|
||||||
if idx := strings.Index(baseType, "("); idx != -1 {
|
base = strings.TrimSpace(base[:idx])
|
||||||
baseType = baseType[:idx]
|
|
||||||
}
|
}
|
||||||
baseType = strings.TrimSpace(baseType)
|
|
||||||
|
|
||||||
// Check direct mapping
|
if canonicalType, exists := MSSQLToCanonicalTypes[base]; exists {
|
||||||
if canonicalType, exists := MSSQLToCanonicalTypes[strings.ToLower(baseType)]; exists {
|
|
||||||
return canonicalType
|
return canonicalType
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to find by prefix
|
|
||||||
lowerType := strings.ToLower(baseType)
|
|
||||||
for mssql, canonical := range MSSQLToCanonicalTypes {
|
|
||||||
if strings.HasPrefix(lowerType, mssql) {
|
|
||||||
return canonical
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default to string
|
|
||||||
return "string"
|
return "string"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ var GoToStdTypes = map[string]string{
|
|||||||
"sqldate": "date",
|
"sqldate": "date",
|
||||||
"sqltime": "time",
|
"sqltime": "time",
|
||||||
"sqltimestamp": "timestamp",
|
"sqltimestamp": "timestamp",
|
||||||
|
"time.Time": "timestamp",
|
||||||
}
|
}
|
||||||
|
|
||||||
var GoToPGSQLTypes = map[string]string{
|
var GoToPGSQLTypes = map[string]string{
|
||||||
@@ -90,6 +91,7 @@ var GoToPGSQLTypes = map[string]string{
|
|||||||
"sqldate": "date",
|
"sqldate": "date",
|
||||||
"sqltime": "time",
|
"sqltime": "time",
|
||||||
"sqltimestamp": "timestamp",
|
"sqltimestamp": "timestamp",
|
||||||
|
"time.Time": "timestamp",
|
||||||
"citext": "citext",
|
"citext": "citext",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,6 +137,62 @@ func ConvertSQLType(anytype string) string {
|
|||||||
return anytype
|
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
|
||||||
|
}
|
||||||
|
|
||||||
func IsGoType(pTypeName string) bool {
|
func IsGoType(pTypeName string) bool {
|
||||||
for k := range GoToStdTypes {
|
for k := range GoToStdTypes {
|
||||||
if strings.EqualFold(pTypeName, k) {
|
if strings.EqualFold(pTypeName, k) {
|
||||||
|
|||||||
@@ -270,8 +270,16 @@ func (r *Reader) queryColumns(schemaName string) (map[string]map[string]*models.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if numPrecision != nil {
|
if numPrecision != nil {
|
||||||
|
// For integer and serial types, numeric_precision is a bit-width (32, 64, 16)
|
||||||
|
// not a user-visible column parameter. Only store precision for types where
|
||||||
|
// it represents actual decimal/scale precision (numeric, decimal, float).
|
||||||
|
switch column.Type {
|
||||||
|
case "integer", "bigint", "smallint", "serial", "bigserial", "smallserial":
|
||||||
|
// skip — bit-width, not a column parameter
|
||||||
|
default:
|
||||||
column.Precision = *numPrecision
|
column.Precision = *numPrecision
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if numScale != nil {
|
if numScale != nil {
|
||||||
column.Scale = *numScale
|
column.Scale = *numScale
|
||||||
|
|||||||
+28
-62
@@ -259,12 +259,14 @@ func (r *Reader) close() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mapDataType maps PostgreSQL data types while preserving exact type text when available.
|
// mapDataType maps a PostgreSQL data type to its canonical RelSpec name.
|
||||||
|
// For known built-in types, dimensions are stripped from the type string (they are
|
||||||
|
// stored separately in column.Length/Precision/Scale). For custom types (e.g.
|
||||||
|
// vector(1536), postgis geometries), the full formatted type is preserved.
|
||||||
func (r *Reader) mapDataType(pgType, udtName, formattedType string, hasNextval bool) string {
|
func (r *Reader) mapDataType(pgType, udtName, formattedType string, hasNextval bool) string {
|
||||||
normalizedPGType := strings.ToLower(strings.TrimSpace(pgType))
|
normalizedPGType := strings.ToLower(strings.TrimSpace(pgType))
|
||||||
|
|
||||||
// If the column has a nextval default, it's likely a serial type
|
// Detect serial types from nextval defaults before anything else.
|
||||||
// Map to the appropriate serial type instead of the base integer type
|
|
||||||
if hasNextval {
|
if hasNextval {
|
||||||
switch normalizedPGType {
|
switch normalizedPGType {
|
||||||
case "integer", "int", "int4":
|
case "integer", "int", "int4":
|
||||||
@@ -276,73 +278,38 @@ func (r *Reader) mapDataType(pgType, udtName, formattedType string, hasNextval b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the database-provided formatted type; this preserves arrays/custom
|
|
||||||
// types/modifiers like text[], vector(1536), numeric(10,2), etc.
|
|
||||||
if strings.TrimSpace(formattedType) != "" {
|
|
||||||
return formattedType
|
|
||||||
}
|
|
||||||
|
|
||||||
// information_schema reports arrays generically as "ARRAY" with udt_name like "_text".
|
// information_schema reports arrays generically as "ARRAY" with udt_name like "_text".
|
||||||
if strings.EqualFold(pgType, "ARRAY") && strings.HasPrefix(udtName, "_") && len(udtName) > 1 {
|
if strings.EqualFold(pgType, "ARRAY") && strings.HasPrefix(udtName, "_") && len(udtName) > 1 {
|
||||||
return udtName[1:] + "[]"
|
return udtName[1:] + "[]"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map common PostgreSQL types
|
// Use the database-formatted type when available. For known built-in types, strip
|
||||||
typeMap := map[string]string{
|
// embedded dimensions (they are stored in column.Length/Precision/Scale separately).
|
||||||
"integer": "integer",
|
// For unknown/custom types, keep the full formatted string (e.g. vector(1536)).
|
||||||
"bigint": "bigint",
|
if strings.TrimSpace(formattedType) != "" {
|
||||||
"smallint": "smallint",
|
lower := strings.ToLower(strings.TrimSpace(formattedType))
|
||||||
"int": "integer",
|
isArray := strings.HasSuffix(lower, "[]")
|
||||||
"int2": "smallint",
|
base := strings.TrimSuffix(lower, "[]")
|
||||||
"int4": "integer",
|
if idx := strings.Index(base, "("); idx >= 0 {
|
||||||
"int8": "bigint",
|
base = strings.TrimSpace(base[:idx])
|
||||||
"serial": "serial",
|
}
|
||||||
"bigserial": "bigserial",
|
canonical := pgsql.NormalizePGType(base)
|
||||||
"smallserial": "smallserial",
|
if pgsql.IsKnownPGBaseType(canonical) {
|
||||||
"numeric": "numeric",
|
if isArray {
|
||||||
"decimal": "decimal",
|
return canonical + "[]"
|
||||||
"real": "real",
|
}
|
||||||
"double precision": "double precision",
|
return canonical
|
||||||
"float4": "real",
|
}
|
||||||
"float8": "double precision",
|
return formattedType
|
||||||
"money": "money",
|
|
||||||
"character varying": "varchar",
|
|
||||||
"varchar": "varchar",
|
|
||||||
"character": "char",
|
|
||||||
"char": "char",
|
|
||||||
"text": "text",
|
|
||||||
"boolean": "boolean",
|
|
||||||
"bool": "boolean",
|
|
||||||
"date": "date",
|
|
||||||
"time": "time",
|
|
||||||
"time without time zone": "time",
|
|
||||||
"time with time zone": "timetz",
|
|
||||||
"timestamp": "timestamp",
|
|
||||||
"timestamp without time zone": "timestamp",
|
|
||||||
"timestamp with time zone": "timestamptz",
|
|
||||||
"timestamptz": "timestamptz",
|
|
||||||
"interval": "interval",
|
|
||||||
"uuid": "uuid",
|
|
||||||
"json": "json",
|
|
||||||
"jsonb": "jsonb",
|
|
||||||
"bytea": "bytea",
|
|
||||||
"inet": "inet",
|
|
||||||
"cidr": "cidr",
|
|
||||||
"macaddr": "macaddr",
|
|
||||||
"xml": "xml",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try mapped type first
|
// Fall back to normalizing the information_schema type name directly.
|
||||||
if mapped, exists := typeMap[normalizedPGType]; exists {
|
canonical := pgsql.NormalizePGType(normalizedPGType)
|
||||||
return mapped
|
if pgsql.IsKnownPGBaseType(canonical) {
|
||||||
|
return canonical
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use pgsql utilities if available
|
// Return UDT name for custom types.
|
||||||
if pgsql.ValidSQLType(pgType) {
|
|
||||||
return pgsql.GetSQLType(pgType)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return UDT name for custom types (including array fallback when needed)
|
|
||||||
if udtName != "" {
|
if udtName != "" {
|
||||||
if strings.HasPrefix(udtName, "_") && len(udtName) > 1 {
|
if strings.HasPrefix(udtName, "_") && len(udtName) > 1 {
|
||||||
return udtName[1:] + "[]"
|
return udtName[1:] + "[]"
|
||||||
@@ -350,7 +317,6 @@ func (r *Reader) mapDataType(pgType, udtName, formattedType string, hasNextval b
|
|||||||
return udtName
|
return udtName
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default to the original type
|
|
||||||
return pgType
|
return pgType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ func TestMapDataType(t *testing.T) {
|
|||||||
{"unknown_type", "custom", "", "custom"}, // Should return UDT name
|
{"unknown_type", "custom", "", "custom"}, // Should return UDT name
|
||||||
{"ARRAY", "_text", "", "text[]"},
|
{"ARRAY", "_text", "", "text[]"},
|
||||||
{"USER-DEFINED", "vector", "vector(1536)", "vector(1536)"},
|
{"USER-DEFINED", "vector", "vector(1536)", "vector(1536)"},
|
||||||
{"character varying", "varchar", "character varying(255)", "character varying(255)"},
|
{"character varying", "varchar", "character varying(255)", "varchar"},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
||||||
"git.warky.dev/wdevs/relspecgo/pkg/readers"
|
"git.warky.dev/wdevs/relspecgo/pkg/readers"
|
||||||
|
sqlitepkg "git.warky.dev/wdevs/relspecgo/pkg/sqlite"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Reader implements the readers.Reader interface for SQLite databases
|
// Reader implements the readers.Reader interface for SQLite databases
|
||||||
@@ -183,59 +184,9 @@ func (r *Reader) close() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mapDataType maps SQLite data types to canonical types
|
// mapDataType maps SQLite data types to canonical types.
|
||||||
func (r *Reader) mapDataType(sqliteType string) string {
|
func (r *Reader) mapDataType(sqliteType string) string {
|
||||||
// SQLite has a flexible type system, but we map common types
|
return sqlitepkg.ConvertSQLiteToCanonical(sqliteType)
|
||||||
typeMap := map[string]string{
|
|
||||||
"INTEGER": "int",
|
|
||||||
"INT": "int",
|
|
||||||
"TINYINT": "int8",
|
|
||||||
"SMALLINT": "int16",
|
|
||||||
"MEDIUMINT": "int",
|
|
||||||
"BIGINT": "int64",
|
|
||||||
"UNSIGNED BIG INT": "uint64",
|
|
||||||
"INT2": "int16",
|
|
||||||
"INT8": "int64",
|
|
||||||
"REAL": "float64",
|
|
||||||
"DOUBLE": "float64",
|
|
||||||
"DOUBLE PRECISION": "float64",
|
|
||||||
"FLOAT": "float32",
|
|
||||||
"NUMERIC": "decimal",
|
|
||||||
"DECIMAL": "decimal",
|
|
||||||
"BOOLEAN": "bool",
|
|
||||||
"BOOL": "bool",
|
|
||||||
"DATE": "date",
|
|
||||||
"DATETIME": "timestamp",
|
|
||||||
"TIMESTAMP": "timestamp",
|
|
||||||
"TEXT": "string",
|
|
||||||
"VARCHAR": "string",
|
|
||||||
"CHAR": "string",
|
|
||||||
"CHARACTER": "string",
|
|
||||||
"VARYING CHARACTER": "string",
|
|
||||||
"NCHAR": "string",
|
|
||||||
"NVARCHAR": "string",
|
|
||||||
"CLOB": "text",
|
|
||||||
"BLOB": "bytea",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try exact match first
|
|
||||||
if mapped, exists := typeMap[sqliteType]; exists {
|
|
||||||
return mapped
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try case-insensitive match for common types
|
|
||||||
sqliteTypeUpper := sqliteType
|
|
||||||
if len(sqliteType) > 0 {
|
|
||||||
// Extract base type (e.g., "VARCHAR(255)" -> "VARCHAR")
|
|
||||||
for baseType := range typeMap {
|
|
||||||
if len(sqliteTypeUpper) >= len(baseType) && sqliteTypeUpper[:len(baseType)] == baseType {
|
|
||||||
return typeMap[baseType]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default to string for unknown types
|
|
||||||
return "string"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// deriveRelationship creates a relationship from a foreign key constraint
|
// deriveRelationship creates a relationship from a foreign key constraint
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
package sqlite
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// SQLiteToCanonicalTypes maps SQLite type names to canonical types.
|
||||||
|
// SQLite has type affinity rules; this maps common type names including
|
||||||
|
// MySQL/PostgreSQL types that users write in SQLite schemas.
|
||||||
|
var SQLiteToCanonicalTypes = map[string]string{
|
||||||
|
// Integer affinity
|
||||||
|
"integer": "int",
|
||||||
|
"int": "int",
|
||||||
|
"tinyint": "int8",
|
||||||
|
"smallint": "int16",
|
||||||
|
"mediumint": "int",
|
||||||
|
"bigint": "int64",
|
||||||
|
"unsigned big int": "uint64",
|
||||||
|
"int2": "int16",
|
||||||
|
"int8": "int64",
|
||||||
|
// Real affinity
|
||||||
|
"real": "float64",
|
||||||
|
"double": "float64",
|
||||||
|
"double precision": "float64",
|
||||||
|
"float": "float32",
|
||||||
|
// Numeric affinity
|
||||||
|
"numeric": "decimal",
|
||||||
|
"decimal": "decimal",
|
||||||
|
// Boolean (stored as integer in SQLite)
|
||||||
|
"boolean": "bool",
|
||||||
|
"bool": "bool",
|
||||||
|
// Date/time (stored as text in SQLite)
|
||||||
|
"date": "date",
|
||||||
|
"datetime": "timestamp",
|
||||||
|
"timestamp": "timestamp",
|
||||||
|
// Text affinity
|
||||||
|
"text": "string",
|
||||||
|
"varchar": "string",
|
||||||
|
"char": "string",
|
||||||
|
"character": "string",
|
||||||
|
"varying character": "string",
|
||||||
|
"nchar": "string",
|
||||||
|
"nvarchar": "string",
|
||||||
|
"clob": "text",
|
||||||
|
// Blob affinity
|
||||||
|
"blob": "bytea",
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanonicalToSQLiteAffinity maps type names to SQLite type affinity names.
|
||||||
|
// Accepts both Go canonical names ("int", "string") and SQL canonical names
|
||||||
|
// ("integer", "varchar") so the writer handles input from any reader.
|
||||||
|
// The five SQLite type affinities are TEXT, INTEGER, REAL, NUMERIC, BLOB.
|
||||||
|
var CanonicalToSQLiteAffinity = map[string]string{
|
||||||
|
// INTEGER affinity — Go canonical
|
||||||
|
"int": "INTEGER",
|
||||||
|
"int8": "INTEGER",
|
||||||
|
"int16": "INTEGER",
|
||||||
|
"int32": "INTEGER",
|
||||||
|
"int64": "INTEGER",
|
||||||
|
"uint": "INTEGER",
|
||||||
|
"uint8": "INTEGER",
|
||||||
|
"uint16": "INTEGER",
|
||||||
|
"uint32": "INTEGER",
|
||||||
|
"uint64": "INTEGER",
|
||||||
|
"bool": "INTEGER",
|
||||||
|
// INTEGER affinity — SQL canonical
|
||||||
|
"integer": "INTEGER",
|
||||||
|
"smallint": "INTEGER",
|
||||||
|
"bigint": "INTEGER",
|
||||||
|
"serial": "INTEGER",
|
||||||
|
"smallserial": "INTEGER",
|
||||||
|
"bigserial": "INTEGER",
|
||||||
|
"boolean": "INTEGER",
|
||||||
|
"tinyint": "INTEGER",
|
||||||
|
"mediumint": "INTEGER",
|
||||||
|
// REAL affinity — Go canonical
|
||||||
|
"float32": "REAL",
|
||||||
|
"float64": "REAL",
|
||||||
|
// REAL affinity — SQL canonical
|
||||||
|
"real": "REAL",
|
||||||
|
"float": "REAL",
|
||||||
|
"double": "REAL",
|
||||||
|
"double precision": "REAL",
|
||||||
|
// NUMERIC affinity
|
||||||
|
"decimal": "NUMERIC",
|
||||||
|
"numeric": "NUMERIC",
|
||||||
|
"money": "NUMERIC",
|
||||||
|
"smallmoney": "NUMERIC",
|
||||||
|
// BLOB affinity
|
||||||
|
"bytea": "BLOB",
|
||||||
|
"blob": "BLOB",
|
||||||
|
// TEXT affinity — Go canonical
|
||||||
|
"string": "TEXT",
|
||||||
|
"text": "TEXT",
|
||||||
|
// TEXT affinity — SQL canonical
|
||||||
|
"varchar": "TEXT",
|
||||||
|
"char": "TEXT",
|
||||||
|
"nvarchar": "TEXT",
|
||||||
|
"nchar": "TEXT",
|
||||||
|
"citext": "TEXT",
|
||||||
|
"date": "TEXT",
|
||||||
|
"time": "TEXT",
|
||||||
|
"timetz": "TEXT",
|
||||||
|
"timestamp": "TEXT",
|
||||||
|
"timestamptz": "TEXT",
|
||||||
|
"datetime": "TEXT",
|
||||||
|
"uuid": "TEXT",
|
||||||
|
"json": "TEXT",
|
||||||
|
"jsonb": "TEXT",
|
||||||
|
"xml": "TEXT",
|
||||||
|
"inet": "TEXT",
|
||||||
|
"cidr": "TEXT",
|
||||||
|
"macaddr": "TEXT",
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertSQLiteToCanonical converts a SQLite type name to the canonical type.
|
||||||
|
// Strips dimension parameters (e.g. VARCHAR(255) → string) and handles
|
||||||
|
// SQLite's flexible affinity rules. Defaults to "string" for unknown types.
|
||||||
|
func ConvertSQLiteToCanonical(sqliteType string) string {
|
||||||
|
base := strings.ToUpper(strings.TrimSpace(sqliteType))
|
||||||
|
if idx := strings.Index(base, "("); idx >= 0 {
|
||||||
|
base = strings.TrimSpace(base[:idx])
|
||||||
|
}
|
||||||
|
lower := strings.ToLower(base)
|
||||||
|
|
||||||
|
if canonical, ok := SQLiteToCanonicalTypes[lower]; ok {
|
||||||
|
return canonical
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefix match for types like "VARYING CHARACTER(255)"
|
||||||
|
for key, canonical := range SQLiteToCanonicalTypes {
|
||||||
|
if strings.HasPrefix(lower, key) {
|
||||||
|
return canonical
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "string"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertCanonicalToSQLite converts a canonical type (or any SQL type) to its
|
||||||
|
// SQLite type affinity. Defaults to TEXT for unrecognised types.
|
||||||
|
func ConvertCanonicalToSQLite(canonicalType string) string {
|
||||||
|
normalized := strings.ToLower(strings.TrimSpace(canonicalType))
|
||||||
|
if idx := strings.Index(normalized, "("); idx >= 0 {
|
||||||
|
normalized = strings.TrimSpace(normalized[:idx])
|
||||||
|
}
|
||||||
|
normalized = strings.TrimSuffix(normalized, "[]")
|
||||||
|
|
||||||
|
if affinity, ok := CanonicalToSQLiteAffinity[normalized]; ok {
|
||||||
|
return affinity
|
||||||
|
}
|
||||||
|
|
||||||
|
return "TEXT"
|
||||||
|
}
|
||||||
@@ -5,6 +5,8 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
"git.warky.dev/wdevs/relspecgo/pkg/pgsql"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TemplateFunctions returns a map of custom template functions
|
// TemplateFunctions returns a map of custom template functions
|
||||||
@@ -162,24 +164,12 @@ func quoteIdent(s string) string {
|
|||||||
|
|
||||||
// Type conversion functions
|
// Type conversion functions
|
||||||
|
|
||||||
// goTypeToSQL converts Go type to PostgreSQL type
|
// goTypeToSQL converts Go type to PostgreSQL type using the shared pgsql type map.
|
||||||
func goTypeToSQL(goType string) string {
|
func goTypeToSQL(goType string) string {
|
||||||
typeMap := map[string]string{
|
if sqlType, ok := pgsql.GoToPGSQLTypes[goType]; ok {
|
||||||
"string": "text",
|
|
||||||
"int": "integer",
|
|
||||||
"int32": "integer",
|
|
||||||
"int64": "bigint",
|
|
||||||
"float32": "real",
|
|
||||||
"float64": "double precision",
|
|
||||||
"bool": "boolean",
|
|
||||||
"time.Time": "timestamp",
|
|
||||||
"[]byte": "bytea",
|
|
||||||
}
|
|
||||||
|
|
||||||
if sqlType, ok := typeMap[goType]; ok {
|
|
||||||
return sqlType
|
return sqlType
|
||||||
}
|
}
|
||||||
return "text" // Default
|
return "text"
|
||||||
}
|
}
|
||||||
|
|
||||||
// sqlTypeToGo converts PostgreSQL type to Go type
|
// sqlTypeToGo converts PostgreSQL type to Go type
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ package sqlite
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
sqlitepkg "git.warky.dev/wdevs/relspecgo/pkg/sqlite"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SQLite type affinities
|
// SQLite type affinity constants
|
||||||
const (
|
const (
|
||||||
TypeText = "TEXT"
|
TypeText = "TEXT"
|
||||||
TypeInteger = "INTEGER"
|
TypeInteger = "INTEGER"
|
||||||
@@ -13,72 +15,27 @@ const (
|
|||||||
TypeBlob = "BLOB"
|
TypeBlob = "BLOB"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MapPostgreSQLType maps PostgreSQL data types to SQLite type affinities
|
// MapTypeToSQLite maps any SQL or Go canonical type to a SQLite type affinity.
|
||||||
|
// Handles input from any reader (PostgreSQL, MSSQL, SQLite, Go canonical).
|
||||||
|
func MapTypeToSQLite(colType string) string {
|
||||||
|
return sqlitepkg.ConvertCanonicalToSQLite(colType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapPostgreSQLType is an alias for MapTypeToSQLite kept for compatibility.
|
||||||
|
//
|
||||||
|
// Deprecated: use MapTypeToSQLite.
|
||||||
func MapPostgreSQLType(pgType string) string {
|
func MapPostgreSQLType(pgType string) string {
|
||||||
// Normalize the type
|
return MapTypeToSQLite(pgType)
|
||||||
normalized := strings.ToLower(strings.TrimSpace(pgType))
|
|
||||||
|
|
||||||
// Remove array notation if present
|
|
||||||
normalized = strings.TrimSuffix(normalized, "[]")
|
|
||||||
|
|
||||||
// Remove precision/scale if present
|
|
||||||
if idx := strings.Index(normalized, "("); idx != -1 {
|
|
||||||
normalized = normalized[:idx]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map to SQLite type affinity
|
|
||||||
switch normalized {
|
|
||||||
// TEXT affinity
|
|
||||||
case "varchar", "character varying", "text", "char", "character",
|
|
||||||
"citext", "uuid", "timestamp", "timestamptz", "timestamp with time zone",
|
|
||||||
"timestamp without time zone", "date", "time", "timetz", "time with time zone",
|
|
||||||
"time without time zone", "json", "jsonb", "xml", "inet", "cidr", "macaddr":
|
|
||||||
return TypeText
|
|
||||||
|
|
||||||
// INTEGER affinity
|
|
||||||
case "int", "int2", "int4", "int8", "integer", "smallint", "bigint",
|
|
||||||
"serial", "smallserial", "bigserial", "boolean", "bool":
|
|
||||||
return TypeInteger
|
|
||||||
|
|
||||||
// REAL affinity
|
|
||||||
case "real", "float", "float4", "float8", "double precision":
|
|
||||||
return TypeReal
|
|
||||||
|
|
||||||
// NUMERIC affinity
|
|
||||||
case "numeric", "decimal", "money":
|
|
||||||
return TypeNumeric
|
|
||||||
|
|
||||||
// BLOB affinity
|
|
||||||
case "bytea", "blob":
|
|
||||||
return TypeBlob
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Default to TEXT for unknown types
|
|
||||||
return TypeText
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsIntegerType checks if a column type should be treated as integer
|
// IsIntegerType reports whether a column type maps to SQLite INTEGER affinity.
|
||||||
func IsIntegerType(colType string) bool {
|
func IsIntegerType(colType string) bool {
|
||||||
normalized := strings.ToLower(strings.TrimSpace(colType))
|
return MapTypeToSQLite(colType) == TypeInteger
|
||||||
normalized = strings.TrimSuffix(normalized, "[]")
|
|
||||||
if idx := strings.Index(normalized, "("); idx != -1 {
|
|
||||||
normalized = normalized[:idx]
|
|
||||||
}
|
|
||||||
|
|
||||||
switch normalized {
|
|
||||||
case "int", "int2", "int4", "int8", "integer", "smallint", "bigint",
|
|
||||||
"serial", "smallserial", "bigserial":
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapBooleanValue converts PostgreSQL boolean literals to SQLite (0/1)
|
// MapBooleanValue converts common boolean literals to SQLite integers (1/0).
|
||||||
func MapBooleanValue(value string) string {
|
func MapBooleanValue(value string) string {
|
||||||
normalized := strings.ToLower(strings.TrimSpace(value))
|
switch strings.ToLower(strings.TrimSpace(value)) {
|
||||||
switch normalized {
|
|
||||||
case "true", "t", "yes", "y", "1":
|
case "true", "t", "yes", "y", "1":
|
||||||
return "1"
|
return "1"
|
||||||
case "false", "f", "no", "n", "0":
|
case "false", "f", "no", "n", "0":
|
||||||
|
|||||||
Reference in New Issue
Block a user