package sqlite import ( "fmt" "strings" "text/template" "git.warky.dev/wdevs/relspecgo/pkg/models" "git.warky.dev/wdevs/relspecgo/pkg/writers" ) // GetTemplateFuncs returns template functions for SQLite SQL generation func GetTemplateFuncs(opts *writers.WriterOptions) template.FuncMap { return template.FuncMap{ "quote_ident": QuoteIdentifier, "map_type": MapPostgreSQLType, "is_autoincrement": IsAutoIncrementCandidate, "qualified_table_name": func(schema, table string) string { return writers.QualifiedTableName(schema, table, opts.FlattenSchema) }, "format_default": FormatDefault, "format_constraint_name": func(schema, table, constraint string) string { return FormatConstraintName(schema, table, constraint, opts) }, "join": strings.Join, "lower": strings.ToLower, "upper": strings.ToUpper, } } // QuoteIdentifier quotes an identifier for SQLite (double quotes) func QuoteIdentifier(name string) string { // SQLite uses double quotes for identifiers // Escape any existing double quotes by doubling them escaped := strings.ReplaceAll(name, `"`, `""`) return fmt.Sprintf(`"%s"`, escaped) } // IsAutoIncrementCandidate checks if a column should use AUTOINCREMENT func IsAutoIncrementCandidate(col *models.Column) bool { // Must be a primary key if !col.IsPrimaryKey { return false } // Must be an integer type if !IsIntegerType(col.Type) { return false } // Check AutoIncrement field if col.AutoIncrement { return true } // Check if default suggests auto-increment if col.Default != nil { defaultStr, ok := col.Default.(string) if ok { defaultLower := strings.ToLower(defaultStr) if strings.Contains(defaultLower, "nextval") || strings.Contains(defaultLower, "autoincrement") || strings.Contains(defaultLower, "auto_increment") { return true } } } // Serial types are auto-increment typeLower := strings.ToLower(col.Type) return strings.Contains(typeLower, "serial") } // FormatDefault formats a default value for SQLite func FormatDefault(col *models.Column) string { if col.Default == nil { return "" } // Skip auto-increment defaults (handled by AUTOINCREMENT keyword) if IsAutoIncrementCandidate(col) { return "" } // Convert to string defaultStr, ok := col.Default.(string) if !ok { // If not a string, convert to string representation defaultStr = fmt.Sprintf("%v", col.Default) } if defaultStr == "" { return "" } // Convert PostgreSQL-specific functions to SQLite equivalents defaultLower := strings.ToLower(defaultStr) // Current timestamp functions if strings.Contains(defaultLower, "current_timestamp") || strings.Contains(defaultLower, "now()") { return "CURRENT_TIMESTAMP" } // Current date if strings.Contains(defaultLower, "current_date") { return "CURRENT_DATE" } // Current time if strings.Contains(defaultLower, "current_time") { return "CURRENT_TIME" } // Boolean values sqliteType := MapPostgreSQLType(col.Type) if sqliteType == TypeInteger { typeLower := strings.ToLower(col.Type) if strings.Contains(typeLower, "bool") { return MapBooleanValue(defaultStr) } } // UUID generation - SQLite doesn't have built-in UUID, comment it out if strings.Contains(defaultLower, "uuid") || strings.Contains(defaultLower, "gen_random_uuid") { return "" // Remove UUID defaults, users must handle this } // Remove PostgreSQL-specific casting defaultStr = strings.ReplaceAll(defaultStr, "::text", "") defaultStr = strings.ReplaceAll(defaultStr, "::integer", "") defaultStr = strings.ReplaceAll(defaultStr, "::bigint", "") defaultStr = strings.ReplaceAll(defaultStr, "::boolean", "") return defaultStr } // FormatConstraintName formats a constraint name with table prefix if flattening func FormatConstraintName(schema, table, constraint string, opts *writers.WriterOptions) string { if opts.FlattenSchema && schema != "" { // Prefix constraint with flattened table name flatTable := writers.QualifiedTableName(schema, table, opts.FlattenSchema) return fmt.Sprintf("%s_%s", flatTable, constraint) } return constraint }