fix(assets): improve error reporting for asset failures
fix(embed): update embedDirectives to return result directly fix(executor): clean up import order and formatting fix(syntax_test): change file permission syntax to octal fix(mysql): update file permission syntax to octal fix(pgsql): update file permission syntax to octal fix(sqlex): simplify Walk function parameter handling fix(transform): adjust identifier comparison logic fix(template): clean up function formatting and error handling
This commit is contained in:
@@ -24,7 +24,7 @@ func TestStrictConversionPreflightsAllObjects(t *testing.T) {
|
||||
unsupported.Indexes[index.Name] = index
|
||||
db.Schemas[0].Tables = append(db.Schemas[0].Tables, unsupported)
|
||||
out := filepath.Join(t.TempDir(), "existing.sql")
|
||||
if err := os.WriteFile(out, []byte("keep existing contents"), 0600); err != nil {
|
||||
if err := os.WriteFile(out, []byte("keep existing contents"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
opts := &writers.WriterOptions{StrictConversion: true, OutputPath: out}
|
||||
|
||||
@@ -3,14 +3,16 @@ package mysql_test
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
||||
driver "github.com/go-sql-driver/mysql"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
driver "github.com/go-sql-driver/mysql"
|
||||
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
||||
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/readers"
|
||||
rmysql "git.warky.dev/wdevs/relspecgo/pkg/readers/mysql"
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/writers"
|
||||
@@ -50,7 +52,7 @@ func TestLiveUnsupportedObjectsDiagnosed(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
path := filepath.Join(t.TempDir(), "schema.sql")
|
||||
if err := os.WriteFile(path, []byte("original"), 0600); err != nil {
|
||||
if err := os.WriteFile(path, []byte("original"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = wmysql.NewWriter(&writers.WriterOptions{StrictConversion: true, OutputPath: path}).WriteDatabase(model)
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"gopkg.in/yaml.v3"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
@@ -12,12 +11,15 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
driver "github.com/go-sql-driver/mysql"
|
||||
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/readers"
|
||||
rmysql "git.warky.dev/wdevs/relspecgo/pkg/readers/mysql"
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/writers"
|
||||
wmysql "git.warky.dev/wdevs/relspecgo/pkg/writers/mysql"
|
||||
driver "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
func TestLiveSchemaRoundTrip(t *testing.T) {
|
||||
|
||||
@@ -10,11 +10,12 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/mariadb"
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/transform"
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/writers"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
type Writer struct {
|
||||
@@ -53,6 +54,7 @@ func (w *Writer) Statements(db *models.Database) ([]string, error) {
|
||||
}
|
||||
return databaseStatements(db), nil
|
||||
}
|
||||
|
||||
func (w *Writer) WriteDatabase(db *models.Database) error {
|
||||
statements, err := w.Statements(db)
|
||||
if err != nil {
|
||||
@@ -78,6 +80,7 @@ func (w *Writer) WriteDatabase(db *models.Database) error {
|
||||
}
|
||||
return closeErr
|
||||
}
|
||||
|
||||
func writeStatements(out io.Writer, statements []string, name string) error {
|
||||
name = strings.NewReplacer("\n", " ", "\r", " ").Replace(name)
|
||||
if _, err := fmt.Fprintf(out, "-- Database: %s\n", name); err != nil {
|
||||
@@ -93,9 +96,11 @@ func writeStatements(out io.Writer, statements []string, name string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *Writer) WriteSchema(s *models.Schema) error {
|
||||
return w.WriteDatabase(&models.Database{Schemas: []*models.Schema{s}})
|
||||
}
|
||||
|
||||
func (w *Writer) WriteTable(t *models.Table) error {
|
||||
return w.WriteSchema(&models.Schema{Name: "", Tables: []*models.Table{t}})
|
||||
}
|
||||
@@ -106,6 +111,7 @@ func qualified(schema, name string) string {
|
||||
}
|
||||
return quote(schema) + "." + quote(name)
|
||||
}
|
||||
|
||||
func keys[T any](m map[string]T) []string {
|
||||
out := make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
@@ -122,9 +128,11 @@ func metaBool(m map[string]any, key string, def bool) bool {
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
func signature(c *models.Column) string {
|
||||
return fmt.Sprintf("%s/%d/%d/%d", c.Type, c.Length, c.Precision, c.Scale)
|
||||
}
|
||||
|
||||
func columnType(c *models.Column) string {
|
||||
if native := metaString(c.Metadata, "mysql_type"); native != "" && metaString(c.Metadata, "mysql_type_signature") == signature(c) {
|
||||
return native
|
||||
@@ -152,6 +160,7 @@ func columnType(c *models.Column) string {
|
||||
func literal(s string) string {
|
||||
return "'" + strings.ReplaceAll(strings.ReplaceAll(s, "\\", "\\\\"), "'", "''") + "'"
|
||||
}
|
||||
|
||||
func defaultSQL(c *models.Column) string {
|
||||
value := fmt.Sprint(c.Default)
|
||||
if native := metaString(c.Metadata, "mysql_default_sql"); native != "" && metaString(c.Metadata, "mysql_default_value") == value {
|
||||
@@ -185,6 +194,7 @@ func defaultSQL(c *models.Column) string {
|
||||
}
|
||||
return literal(value)
|
||||
}
|
||||
|
||||
func indexParts(i *models.Index) string {
|
||||
if len(i.KeyParts) == 0 {
|
||||
return quoted(i.Columns)
|
||||
@@ -205,6 +215,7 @@ func indexParts(i *models.Index) string {
|
||||
}
|
||||
return strings.Join(parts, ", ")
|
||||
}
|
||||
|
||||
func tableStatement(s *models.Schema, t *models.Table) string {
|
||||
schema := ""
|
||||
if s != nil {
|
||||
@@ -346,6 +357,7 @@ func tableStatement(s *models.Schema, t *models.Table) string {
|
||||
}
|
||||
return "CREATE TABLE IF NOT EXISTS " + qualified(schema, t.Name) + " (\n" + strings.Join(defs, ",\n") + "\n)" + tail + ";\n\n"
|
||||
}
|
||||
|
||||
func databaseStatements(db *models.Database) []string {
|
||||
var statements []string
|
||||
for _, s := range db.Schemas {
|
||||
@@ -378,6 +390,7 @@ func databaseStatements(db *models.Database) []string {
|
||||
}
|
||||
return statements
|
||||
}
|
||||
|
||||
func (w *Writer) execute(statements []string, conn string) error {
|
||||
if w.options.AtomicExecution {
|
||||
return fmt.Errorf("MySQL DDL implicitly commits; atomic execution is unsupported")
|
||||
|
||||
@@ -848,7 +848,7 @@ func adoptableUniqueIndex(table *models.Table, constraint *models.Constraint) st
|
||||
return ""
|
||||
}
|
||||
for i, col := range index.Columns {
|
||||
if !(col == constraint.Columns[i]) {
|
||||
if col != constraint.Columns[i] {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
@@ -1239,21 +1239,21 @@ func constraintsEqual(c1, c2 *models.Constraint) bool {
|
||||
return false
|
||||
}
|
||||
for i, col := range c1.Columns {
|
||||
if !(col == c2.Columns[i]) {
|
||||
if col != c2.Columns[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// For foreign keys, also compare referenced table and columns
|
||||
if c1.Type == models.ForeignKeyConstraint {
|
||||
if !(c1.ReferencedTable == c2.ReferencedTable) {
|
||||
if c1.ReferencedTable != c2.ReferencedTable {
|
||||
return false
|
||||
}
|
||||
if len(c1.ReferencedColumns) != len(c2.ReferencedColumns) {
|
||||
return false
|
||||
}
|
||||
for i, col := range c1.ReferencedColumns {
|
||||
if !(col == c2.ReferencedColumns[i]) {
|
||||
if col != c2.ReferencedColumns[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -1283,7 +1283,7 @@ func indexesEqual(idx1, idx2 *models.Index) bool {
|
||||
return false
|
||||
}
|
||||
for i, col := range idx1.Columns {
|
||||
if !(col == idx2.Columns[i]) {
|
||||
if col != idx2.Columns[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -1365,7 +1365,7 @@ func primaryKeyColumnsMatch(modelTable *models.Table, current *models.Constraint
|
||||
return false
|
||||
}
|
||||
for i, col := range modelCols {
|
||||
if !(col == current.Columns[i]) {
|
||||
if col != current.Columns[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ func TestQuotedNamesAndLowercaseOption(t *testing.T) {
|
||||
t.Fatal("input changed")
|
||||
}
|
||||
path := filepath.Join(t.TempDir(), "out.sql")
|
||||
if err := os.WriteFile(path, []byte("keep"), 0600); err != nil {
|
||||
if err := os.WriteFile(path, []byte("keep"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
col.Name = "bad\x00name"
|
||||
|
||||
@@ -29,11 +29,13 @@ type Writer struct {
|
||||
}
|
||||
|
||||
// Shared execution results retain the PostgreSQL package's public type names.
|
||||
type ExecutionReport = writers.ExecutionReport
|
||||
type ExecutionFailure = writers.ExecutionFailure
|
||||
type SchemaReport = writers.SchemaReport
|
||||
type TableReport = writers.TableReport
|
||||
type ExecutionError = writers.ExecutionError
|
||||
type (
|
||||
ExecutionReport = writers.ExecutionReport
|
||||
ExecutionFailure = writers.ExecutionFailure
|
||||
SchemaReport = writers.SchemaReport
|
||||
TableReport = writers.TableReport
|
||||
ExecutionError = writers.ExecutionError
|
||||
)
|
||||
|
||||
// NewWriter creates a new PostgreSQL SQL writer
|
||||
func NewWriter(options *writers.WriterOptions) *Writer {
|
||||
@@ -2114,7 +2116,6 @@ func extractSequenceName(defaultExpr string) string {
|
||||
name.WriteByte(defaultExpr[i])
|
||||
}
|
||||
return ""
|
||||
|
||||
}
|
||||
|
||||
// executeDatabaseSQL applies db to a PostgreSQL database. By default it reads the live schema
|
||||
@@ -2497,7 +2498,6 @@ func parseQualifiedIdent(s string) (schema, name string) {
|
||||
}
|
||||
}
|
||||
return "", stripQuotes(token)
|
||||
|
||||
}
|
||||
|
||||
// firstBareIdent returns the first whitespace/delimiter-terminated token, stripping outer quotes.
|
||||
@@ -2520,7 +2520,6 @@ func firstBareIdent(s string) string {
|
||||
}
|
||||
}
|
||||
return s
|
||||
|
||||
}
|
||||
|
||||
// firstIdentAfterKeyword returns the first identifier token after keyword (matched case-insensitively via upperStmt).
|
||||
|
||||
@@ -46,9 +46,9 @@ func (w *Writer) WriteDatabase(db *models.Database) error {
|
||||
var scripts []*models.Script
|
||||
for _, schema := range db.Schemas {
|
||||
for _, script := range sortedScripts(schema.Scripts) {
|
||||
copy := *script
|
||||
copy.Name = schema.Name + "." + script.Name
|
||||
scripts = append(scripts, ©)
|
||||
scriptCopy := *script
|
||||
scriptCopy.Name = schema.Name + "." + script.Name
|
||||
scripts = append(scripts, &scriptCopy)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -137,6 +137,7 @@ func (w *Writer) executeSchemaMode(db *models.Database) error {
|
||||
}
|
||||
return w.executeFiles(data)
|
||||
}
|
||||
|
||||
func (w *Writer) executeDomainMode(db *models.Database) error {
|
||||
var data []*TemplateData
|
||||
for _, domain := range db.Domains {
|
||||
@@ -144,6 +145,7 @@ func (w *Writer) executeDomainMode(db *models.Database) error {
|
||||
}
|
||||
return w.executeFiles(data)
|
||||
}
|
||||
|
||||
func (w *Writer) executeScriptMode(db *models.Database) error {
|
||||
var data []*TemplateData
|
||||
for _, schema := range db.Schemas {
|
||||
@@ -153,6 +155,7 @@ func (w *Writer) executeScriptMode(db *models.Database) error {
|
||||
}
|
||||
return w.executeFiles(data)
|
||||
}
|
||||
|
||||
func (w *Writer) executeTableMode(db *models.Database) error {
|
||||
var data []*TemplateData
|
||||
for _, schema := range db.Schemas {
|
||||
@@ -246,7 +249,10 @@ func (w *Writer) writeArtifact(root *os.Root, file artifact) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer root.Remove(temporary)
|
||||
defer func() {
|
||||
// Best-effort cleanup; a successful rename already removes the staging path.
|
||||
_ = root.Remove(temporary)
|
||||
}()
|
||||
_, writeErr := staged.WriteString(file.content)
|
||||
if err := errors.Join(writeErr, staged.Close()); err != nil {
|
||||
return err
|
||||
|
||||
Reference in New Issue
Block a user