Repository: ariga/atlas
Pull request #1864 (2023-07-16 13:25:51)

sql/sqlx: quote Ident opening/closing


Patch:
diff --git a/sql/internal/sqlx/sqlx.go b/sql/internal/sqlx/sqlx.go
index 84e9dc91..5d556034 100644
--- a/sql/internal/sqlx/sqlx.go
+++ b/sql/internal/sqlx/sqlx.go
@@ -214,10 +214,11 @@ func ModeInspectRealm(o *schema.InspectRealmOption) schema.InspectMode {
 // A Builder provides a syntactic sugar API for writing SQL statements.
 type Builder struct {
 	bytes.Buffer
-	QuoteChar byte    // quoting identifiers
-	Schema    *string // schema qualifier
-	Indent    string  // indentation string
-	level     int     // current indentation level
+	QuoteOpening byte    // quoting identifiers
+	QuoteClosing byte    // quoting identifiers
+	Schema       *string // schema qualifier
+	Indent       string  // indentation string
+	level        int     // current indentation level
 }
 
 // P writes a list of phrases to the builder separated and
@@ -241,9 +242,9 @@ func (b *Builder) P(phrases ...string) *Builder {
 // Ident writes the given string quoted as an SQL identifier.
 func (b *Builder) Ident(s string) *Builder {
 	if s != "" {
-		b.WriteByte(b.QuoteChar)
+		b.WriteByte(b.QuoteOpening)
 		b.WriteString(s)
-		b.WriteByte(b.QuoteChar)
+		b.WriteByte(b.QuoteClosing)
 		b.WriteByte(' ')
 	}
 	return b
@@ -415,8 +416,9 @@ func (b *Builder) WrapIndent(f func(b *Builder)) *Builder {
 // Clone returns a duplicate of the builder.
 func (b *Builder) Clone() *Builder {
 	return &Builder{
-		QuoteChar: b.QuoteChar,
-		Buffer:    *bytes.NewBufferString(b.Buffer.String()),
+		QuoteOpening: b.QuoteOpening,
+		QuoteClosing: b.QuoteClosing,
+		Buffer:       *bytes.NewBufferString(b.Buffer.String()),
 	}
 }
 
diff --git a/sql/mysql/inspect.go b/sql/mysql/inspect.go
index f05ab4d0..95aaf13c 100644
--- a/sql/mysql/inspect.go
+++ b/sql/mysql/inspect.go
@@ -572,7 +572,7 @@ func (i *inspect) setAutoInc(s *showTable, t *schema.Table) error {
 // createStmt loads the CREATE TABLE statement for the table.
 func (i *inspect) createStmt(ctx context.Context, t *schema.Table) error {
 	c := &CreateStmt{}
-	b := &sqlx.Builder{QuoteChar: '`'}
+	b := &sqlx.Builder{QuoteOpening: '`', QuoteClosing: '`'}
 	rows, err := i.QueryContext(ctx, b.P("SHOW CREATE TABLE").Table(t).String())
 	if err != nil {
 		return fmt.Errorf("query CREATE TABLE %q: %w", t.Name, err)
diff --git a/sql/mysql/migrate.go b/sql/mysql/migrate.go
index bb2ca329..7d791f87 100644
--- a/sql/mysql/migrate.go
+++ b/sql/mysql/migrate.go
@@ -110,7 +110,7 @@ func (s *state) plan(changes []schema.Change) error {
 		case *schema.AddView:
 			err = s.addView(c)
 		case *schema.DropView:
-			err = s.dropView( c)
+			err = s.dropView(c)
 		case *schema.ModifyView:
 			err = s.modifyView(c)
 		case *schema.RenameView:
@@ -747,7 +747,7 @@ func (s *state) columnDefault(b *sqlx.Builder, c *schema.Column) {
 
 // Build instantiates a new builder and writes the given phrase to it.
 func (s *state) Build(phrases ...string) *sqlx.Builder {
-	b := &sqlx.Builder{QuoteChar: '`', Schema: s.SchemaQualifier, Indent: s.Indent}
+	b := &sqlx.Builder{QuoteOpening: '`', QuoteClosing: '`', Schema: s.SchemaQualifier, Indent: s.Indent}
 	return b.P(phrases...)
 }
 
diff --git a/sql/postgres/diff.go b/sql/postgres/diff.go
index cb35d9ab..c9b6a4fc 100644
--- a/sql/postgres/diff.go
+++ b/sql/postgres/diff.go
@@ -463,7 +463,7 @@ func identity(attrs []schema.Attr) (*Identity, bool) {
 // formatPartition returns the string representation of the
 // partition key according to the PostgreSQL format/grammar.
 func formatPartition(p Partition) (string, error) {
-	b := &sqlx.Builder{QuoteChar: '"'}
+	b := &sqlx.Builder{QuoteOpening: '"', QuoteClosing: '"'}
 	b.P("PARTITION BY")
 	switch t := strings.ToUpper(p.T); t {
 	case PartitionTypeRange, PartitionTypeList, PartitionTypeHash:
diff --git a/sql/postgres/migrate.go b/sql/postgres/migrate.go
index b0945035..1cac346a 100644
--- a/sql/postgres/migrate.go
+++ b/sql/postgres/migrate.go
@@ -1068,7 +1068,7 @@ func (s *state) append(c ...*migrate.Change) {
 
 // Build instantiates a new builder and writes the given phrase to it.
 func (s *state) Build(phrases ...string) *sqlx.Builder {
-	b := &sqlx.Builder{QuoteChar: '"', Schema: s.SchemaQualifier, Indent: s.Indent}
+	b := &sqlx.Builder{QuoteOpening: '"', QuoteClosing: '"', Schema: s.SchemaQualifier, Indent: s.Indent}
 	return b.P(phrases...)
 }
 
diff --git a/sql/sqlite/migrate.go b/sql/sqlite/migrate.go
index 157bba93..b6ac8b9a 100644
--- a/sql/sqlite/migrate.go
+++ b/sql/sqlite/migrate.go
@@ -563,7 +563,7 @@ func autoincPK(pk *schema.Index) bool {
 
 // Build instantiates a new builder and writes the given phrase to it.
 func (s *state) Build(phrases ...string) *sqlx.Builder {
-	b := &sqlx.Builder{QuoteChar: '`', Schema: s.SchemaQualifier, Indent: s.Indent}
+	b := &sqlx.Builder{QuoteOpening: '`', QuoteClosing: '`', Schema: s.SchemaQualifier, Indent: s.Indent}
 	return b.P(phrases...)
 }
 
@@ -584,7 +584,7 @@ func defaultValue(c *schema.Column) (string, error) {
 }
 
 func identComma(c []string) string {
-	b := &sqlx.Builder{QuoteChar: '`'}
+	b := &sqlx.Builder{QuoteOpening: '`', QuoteClosing: '`'}
 	b.MapComma(c, func(i int, b *sqlx.Builder) {
 		if strings.ContainsRune(c[i], '`') {
 			b.WriteString(c[i])


Test patch:
diff --git a/sql/internal/sqlx/sqlx_test.go b/sql/internal/sqlx/sqlx_test.go
index 6b51a988..3cc280a2 100644
--- a/sql/internal/sqlx/sqlx_test.go
+++ b/sql/internal/sqlx/sqlx_test.go
@@ -43,7 +43,7 @@ func TestModeInspectSchema(t *testing.T) {
 
 func TestBuilder(t *testing.T) {
 	var (
-		b       = &Builder{QuoteChar: '"'}
+		b       = &Builder{QuoteOpening: '"', QuoteClosing: '"'}
 		columns = []string{"a", "b", "c"}
 	)
 	b.P("CREATE TABLE").
@@ -64,7 +64,7 @@ func TestBuilder(t *testing.T) {
 func TestBuilder_Qualifier(t *testing.T) {
 	var (
 		s = "other"
-		b = &Builder{QuoteChar: '"', Schema: &s}
+		b = &Builder{QuoteOpening: '"', QuoteClosing: '"', Schema: &s}
 	)
 	b.P("CREATE TABLE").Table(schema.NewTable("users"))
 	require.Equal(t, `CREATE TABLE "other"."users"`, b.String())
@@ -84,7 +84,7 @@ func TestBuilder_Qualifier(t *testing.T) {
 func TestQuote(t *testing.T) {
 	var (
 		s = "s1"
-		b = &Builder{QuoteChar: '"', Schema: &s}
+		b = &Builder{QuoteOpening: '[', QuoteClosing: ']', Schema: &s}
 	)
 	b.P("EXECUTE sp_rename").
 		P("@newname = N'c2'").Comma().
@@ -92,7 +92,7 @@ func TestQuote(t *testing.T) {
 	b.P("@objname = ").Quote("N", func(b *Builder) {
 		b.TableResource(schema.NewTable("t1"), &schema.Column{Name: "c1"})
 	})
-	require.Equal(t, `EXECUTE sp_rename @newname = N'c2', @objtype = N'COLUMN', @objname = N'"s1"."t1"."c1"'`, b.String())
+	require.Equal(t, `EXECUTE sp_rename @newname = N'c2', @objtype = N'COLUMN', @objname = N'[s1].[t1].[c1]'`, b.String())
 }
 func TestMayWrap(t *testing.T) {
 	tests := []struct {