All checks were successful
- Implement SQLite DDL writer to convert PostgreSQL schemas to SQLite-compatible SQL statements. - Include automatic schema flattening, type mapping, auto-increment detection, and function translation. - Add templates for creating tables, indexes, unique constraints, check constraints, and foreign keys. - Implement tests for writer functionality and data type mapping.
175 lines
4.9 KiB
Go
175 lines
4.9 KiB
Go
package sqlite
|
|
|
|
import (
|
|
"bytes"
|
|
"embed"
|
|
"fmt"
|
|
"text/template"
|
|
|
|
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
|
"git.warky.dev/wdevs/relspecgo/pkg/writers"
|
|
)
|
|
|
|
//go:embed templates/*.tmpl
|
|
var templateFS embed.FS
|
|
|
|
// TemplateExecutor manages and executes SQLite SQL templates
|
|
type TemplateExecutor struct {
|
|
templates *template.Template
|
|
options *writers.WriterOptions
|
|
}
|
|
|
|
// NewTemplateExecutor creates a new template executor for SQLite
|
|
func NewTemplateExecutor(opts *writers.WriterOptions) (*TemplateExecutor, error) {
|
|
// Create template with SQLite-specific functions
|
|
funcMap := GetTemplateFuncs(opts)
|
|
|
|
tmpl, err := template.New("").Funcs(funcMap).ParseFS(templateFS, "templates/*.tmpl")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse templates: %w", err)
|
|
}
|
|
|
|
return &TemplateExecutor{
|
|
templates: tmpl,
|
|
options: opts,
|
|
}, nil
|
|
}
|
|
|
|
// Template data structures
|
|
|
|
// TableTemplateData contains data for table template
|
|
type TableTemplateData struct {
|
|
Schema string
|
|
Name string
|
|
Columns []*models.Column
|
|
PrimaryKey *models.Constraint
|
|
}
|
|
|
|
// IndexTemplateData contains data for index template
|
|
type IndexTemplateData struct {
|
|
Schema string
|
|
Table string
|
|
Name string
|
|
Columns []string
|
|
}
|
|
|
|
// ConstraintTemplateData contains data for constraint templates
|
|
type ConstraintTemplateData struct {
|
|
Schema string
|
|
Table string
|
|
Name string
|
|
Columns []string
|
|
Expression string
|
|
ForeignSchema string
|
|
ForeignTable string
|
|
ForeignColumns []string
|
|
OnDelete string
|
|
OnUpdate string
|
|
}
|
|
|
|
// Execute methods
|
|
|
|
// ExecutePragmaForeignKeys executes the pragma foreign keys template
|
|
func (te *TemplateExecutor) ExecutePragmaForeignKeys() (string, error) {
|
|
var buf bytes.Buffer
|
|
err := te.templates.ExecuteTemplate(&buf, "pragma_foreign_keys.tmpl", nil)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to execute pragma_foreign_keys template: %w", err)
|
|
}
|
|
return buf.String(), nil
|
|
}
|
|
|
|
// ExecuteCreateTable executes the create table template
|
|
func (te *TemplateExecutor) ExecuteCreateTable(data TableTemplateData) (string, error) {
|
|
var buf bytes.Buffer
|
|
err := te.templates.ExecuteTemplate(&buf, "create_table.tmpl", data)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to execute create_table template: %w", err)
|
|
}
|
|
return buf.String(), nil
|
|
}
|
|
|
|
// ExecuteCreateIndex executes the create index template
|
|
func (te *TemplateExecutor) ExecuteCreateIndex(data IndexTemplateData) (string, error) {
|
|
var buf bytes.Buffer
|
|
err := te.templates.ExecuteTemplate(&buf, "create_index.tmpl", data)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to execute create_index template: %w", err)
|
|
}
|
|
return buf.String(), nil
|
|
}
|
|
|
|
// ExecuteCreateUniqueConstraint executes the create unique constraint template
|
|
func (te *TemplateExecutor) ExecuteCreateUniqueConstraint(data ConstraintTemplateData) (string, error) {
|
|
var buf bytes.Buffer
|
|
err := te.templates.ExecuteTemplate(&buf, "create_unique_constraint.tmpl", data)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to execute create_unique_constraint template: %w", err)
|
|
}
|
|
return buf.String(), nil
|
|
}
|
|
|
|
// ExecuteCreateCheckConstraint executes the create check constraint template
|
|
func (te *TemplateExecutor) ExecuteCreateCheckConstraint(data ConstraintTemplateData) (string, error) {
|
|
var buf bytes.Buffer
|
|
err := te.templates.ExecuteTemplate(&buf, "create_check_constraint.tmpl", data)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to execute create_check_constraint template: %w", err)
|
|
}
|
|
return buf.String(), nil
|
|
}
|
|
|
|
// ExecuteCreateForeignKey executes the create foreign key template
|
|
func (te *TemplateExecutor) ExecuteCreateForeignKey(data ConstraintTemplateData) (string, error) {
|
|
var buf bytes.Buffer
|
|
err := te.templates.ExecuteTemplate(&buf, "create_foreign_key.tmpl", data)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to execute create_foreign_key template: %w", err)
|
|
}
|
|
return buf.String(), nil
|
|
}
|
|
|
|
// Helper functions to build template data from models
|
|
|
|
// BuildTableTemplateData builds TableTemplateData from a models.Table
|
|
func BuildTableTemplateData(schema string, table *models.Table) TableTemplateData {
|
|
// Get sorted columns
|
|
columns := make([]*models.Column, 0, len(table.Columns))
|
|
for _, col := range table.Columns {
|
|
columns = append(columns, col)
|
|
}
|
|
|
|
// Find primary key constraint
|
|
var pk *models.Constraint
|
|
for _, constraint := range table.Constraints {
|
|
if constraint.Type == models.PrimaryKeyConstraint {
|
|
pk = constraint
|
|
break
|
|
}
|
|
}
|
|
|
|
// If no explicit primary key constraint, build one from columns with IsPrimaryKey=true
|
|
if pk == nil {
|
|
pkCols := []string{}
|
|
for _, col := range table.Columns {
|
|
if col.IsPrimaryKey {
|
|
pkCols = append(pkCols, col.Name)
|
|
}
|
|
}
|
|
if len(pkCols) > 0 {
|
|
pk = &models.Constraint{
|
|
Name: "pk_" + table.Name,
|
|
Type: models.PrimaryKeyConstraint,
|
|
Columns: pkCols,
|
|
}
|
|
}
|
|
}
|
|
|
|
return TableTemplateData{
|
|
Schema: schema,
|
|
Name: table.Name,
|
|
Columns: columns,
|
|
PrimaryKey: pk,
|
|
}
|
|
}
|