Skip to content

Commit 0d8f090

Browse files
kyleconroyclaude
andauthored
Add MSSQL engine for parse and analyze via teesql (#4571)
* Add MSSQL engine for parse and analyze via teesql Add a new mssql engine backed by github.com/sqlc-dev/teesql, a T-SQL parser producing a SqlScriptDOM-compatible AST. The engine converts teesql's AST into sqlc's internal AST and analyzes queries through the analysis core, seeded with SQL Server's type system. - internal/engine/mssql: parser, AST converter, reserved keywords and dialect seed (types, functions, operator rules) - Wire the engine into the compiler's core-analysis path and the parse and analyze commands as the mssql (alias sqlserver) dialect - Named @parameters share a number across repeated uses; OUTPUT clauses map to returning lists with INSERTED/DELETED qualifiers stripped; the dbo schema maps to the catalog's default namespace - End-to-end coverage under parse_basic, analyze_basic and analyze_params Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FrnQ4EoZ3WCWWrSsm8bhCP * mssql: fix DML alias targets, byte offsets, dead keyword Address review findings on the initial engine: - DELETE dropped its FROM clause, so the T-SQL join form (DELETE b FROM books b JOIN ...) failed to resolve. UPDATE and DELETE now resolve an alias target against the FROM clause: the matching relation becomes the statement's target, and the ON conditions of any inner join dissolved by pulling it out move to the WHERE clause. Relations under outer joins are left alone. - AST Location fields carried teesql's UTF-16 code-unit offsets while StmtLocation/StmtLen are byte offsets; the converter now maps locations through the same byte-offset table as statement spans. - Drop the unreachable "within group" reserved-keyword case: keywords are checked one identifier token at a time. - Cover INSERT ... OUTPUT and the UPDATE/DELETE alias forms end to end under analyze_dml/mssql. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FrnQ4EoZ3WCWWrSsm8bhCP --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent d72f916 commit 0d8f090

31 files changed

Lines changed: 2109 additions & 11 deletions

File tree

docs/howto/analyze.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ provided. The schema is always read from the `--schema` file.
2626
## Flags
2727

2828
- `--dialect`, `-d` - The SQL dialect to use. One of `postgresql`, `mysql`,
29-
`sqlite`, `clickhouse`, or `googlesql`. Required.
29+
`sqlite`, `clickhouse`, `googlesql`, or `mssql`. Required.
3030
- `--schema`, `-s` - Path to the schema (DDL) file. Required.
3131
- `--ast` - Include each statement's AST in the output. Defaults to `false`.
3232

docs/howto/parse.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ provided.
2020
## Flags
2121

2222
- `--dialect`, `-d` - The SQL dialect to use. One of `postgresql`, `mysql`,
23-
`sqlite`, `clickhouse`, or `googlesql`. Required.
23+
`sqlite`, `clickhouse`, `googlesql`, or `mssql`. Required.
2424

2525
## Examples
2626

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ require (
2222
github.com/sqlc-dev/marino v0.1.0
2323
github.com/sqlc-dev/meyer v0.1.1
2424
github.com/sqlc-dev/oliphant v0.1.0
25+
github.com/sqlc-dev/teesql v1.1.0
2526
github.com/sqlc-dev/zetajones v0.1.0
2627
github.com/tetratelabs/wazero v1.12.0
2728
github.com/xeipuuv/gojsonschema v1.2.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ github.com/sqlc-dev/meyer v0.1.1 h1:BAeZcfgLyTnk9f90DyGEKXPrHxtgvVD/DTM6awq2kUY=
7373
github.com/sqlc-dev/meyer v0.1.1/go.mod h1:pS4USCRf/SLjWtaMcnTo4YrEEFKBj8CyyqlxcVUJQH8=
7474
github.com/sqlc-dev/oliphant v0.1.0 h1:RAsO6BMitIzB2+swx/qzUR5nf6w4cQ1abgHIu+Fgppo=
7575
github.com/sqlc-dev/oliphant v0.1.0/go.mod h1:fRM/t4FutRddTIq2YCuS4O9o+2rRwSwELRvLMqtPloo=
76+
github.com/sqlc-dev/teesql v1.1.0 h1:3sVYQ9FGxQVcqrqQOQ27bk0aF4c4yN1H1zLL79uaSxQ=
77+
github.com/sqlc-dev/teesql v1.1.0/go.mod h1:WwOp9UtnxG17+eNFT5KXu/AGBQ8ucdGc8wIOpnj4XCI=
7678
github.com/sqlc-dev/zetajones v0.1.0 h1:VeG0atx6lNABr9V2bSI5vL9DvOKTHX0XjMqWUE/rv40=
7779
github.com/sqlc-dev/zetajones v0.1.0/go.mod h1:dU1DxwqC6Cahbpnw16KpH1J2waWRDMdwyDSvovMZR4I=
7880
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

internal/cmd/analyze.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ Examples:
4343
# Analyze a GoogleSQL (BigQuery, Spanner) query
4444
sqlc analyze --dialect googlesql --schema schema.sql query.sql
4545
46+
# Analyze a SQL Server (T-SQL) query
47+
sqlc analyze --dialect mssql --schema schema.sql query.sql
48+
4649
# Analyze a query piped via stdin
4750
echo "-- name: GetAuthor :one
4851
SELECT * FROM authors WHERE id = $1;" | sqlc analyze --dialect postgresql --schema schema.sql
@@ -56,7 +59,7 @@ Examples:
5659
return err
5760
}
5861
if dialect == "" {
59-
return fmt.Errorf("--dialect flag is required (postgresql, mysql, sqlite, clickhouse, or googlesql)")
62+
return fmt.Errorf("--dialect flag is required (postgresql, mysql, sqlite, clickhouse, googlesql, or mssql)")
6063
}
6164

6265
schemaPath, err := cmd.Flags().GetString("schema")
@@ -117,8 +120,10 @@ Examples:
117120
engine = config.EngineClickHouse
118121
case "googlesql":
119122
engine = config.EngineGoogleSQL
123+
case "mssql", "sqlserver":
124+
engine = config.EngineMSSQL
120125
default:
121-
return fmt.Errorf("unsupported dialect: %s (use postgresql, mysql, sqlite, clickhouse, or googlesql)", dialect)
126+
return fmt.Errorf("unsupported dialect: %s (use postgresql, mysql, sqlite, clickhouse, googlesql, or mssql)", dialect)
122127
}
123128

124129
sql := config.SQL{
@@ -160,7 +165,7 @@ Examples:
160165
return nil
161166
},
162167
}
163-
cmd.Flags().StringP("dialect", "d", "", "SQL dialect to use (postgresql, mysql, sqlite, clickhouse, or googlesql)")
168+
cmd.Flags().StringP("dialect", "d", "", "SQL dialect to use (postgresql, mysql, sqlite, clickhouse, googlesql, or mssql)")
164169
cmd.Flags().StringP("schema", "s", "", "path to the schema file")
165170
cmd.Flags().BoolP("ast", "", false, "include the statement AST in the output")
166171
return cmd

internal/cmd/parse.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/sqlc-dev/sqlc/internal/engine/clickhouse"
1313
"github.com/sqlc-dev/sqlc/internal/engine/dolphin"
1414
"github.com/sqlc-dev/sqlc/internal/engine/googlesql"
15+
"github.com/sqlc-dev/sqlc/internal/engine/mssql"
1516
"github.com/sqlc-dev/sqlc/internal/engine/postgresql"
1617
"github.com/sqlc-dev/sqlc/internal/engine/sqlite"
1718
"github.com/sqlc-dev/sqlc/internal/metadata"
@@ -59,15 +60,18 @@ Examples:
5960
sqlc parse --dialect clickhouse queries.sql
6061
6162
# Parse GoogleSQL (BigQuery, Spanner)
62-
sqlc parse --dialect googlesql queries.sql`,
63+
sqlc parse --dialect googlesql queries.sql
64+
65+
# Parse SQL Server (T-SQL) SQL
66+
sqlc parse --dialect mssql queries.sql`,
6367
Args: cobra.MaximumNArgs(1),
6468
RunE: func(cmd *cobra.Command, args []string) error {
6569
dialect, err := cmd.Flags().GetString("dialect")
6670
if err != nil {
6771
return err
6872
}
6973
if dialect == "" {
70-
return fmt.Errorf("--dialect flag is required (postgresql, mysql, sqlite, clickhouse, or googlesql)")
74+
return fmt.Errorf("--dialect flag is required (postgresql, mysql, sqlite, clickhouse, googlesql, or mssql)")
7175
}
7276

7377
// Determine input source
@@ -104,8 +108,10 @@ Examples:
104108
parser = clickhouse.NewParser()
105109
case "googlesql":
106110
parser = googlesql.NewParser()
111+
case "mssql", "sqlserver":
112+
parser = mssql.NewParser()
107113
default:
108-
return fmt.Errorf("unsupported dialect: %s (use postgresql, mysql, sqlite, clickhouse, or googlesql)", dialect)
114+
return fmt.Errorf("unsupported dialect: %s (use postgresql, mysql, sqlite, clickhouse, googlesql, or mssql)", dialect)
109115
}
110116

111117
// Read the full source so each statement's name and command can be
@@ -149,6 +155,6 @@ Examples:
149155
return nil
150156
},
151157
}
152-
cmd.Flags().StringP("dialect", "d", "", "SQL dialect to use (postgresql, mysql, sqlite, clickhouse, or googlesql)")
158+
cmd.Flags().StringP("dialect", "d", "", "SQL dialect to use (postgresql, mysql, sqlite, clickhouse, googlesql, or mssql)")
153159
return cmd
154160
}

internal/compiler/engine.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/sqlc-dev/sqlc/internal/engine/clickhouse"
1212
"github.com/sqlc-dev/sqlc/internal/engine/dolphin"
1313
"github.com/sqlc-dev/sqlc/internal/engine/googlesql"
14+
"github.com/sqlc-dev/sqlc/internal/engine/mssql"
1415
"github.com/sqlc-dev/sqlc/internal/engine/postgresql"
1516
pganalyze "github.com/sqlc-dev/sqlc/internal/engine/postgresql/analyzer"
1617
"github.com/sqlc-dev/sqlc/internal/engine/sqlite"
@@ -57,9 +58,10 @@ func NewCompiler(conf config.SQL, combo config.CombinedSettings, parserOpts opts
5758
o(c)
5859
}
5960

60-
// ClickHouse and GoogleSQL have no legacy analysis path to fall back to.
61+
// ClickHouse, GoogleSQL and SQL Server have no legacy analysis path to
62+
// fall back to.
6163
switch conf.Engine {
62-
case config.EngineClickHouse, config.EngineGoogleSQL:
64+
case config.EngineClickHouse, config.EngineGoogleSQL, config.EngineMSSQL:
6365
c.coreAnalysis = true
6466
}
6567
if c.coreAnalysis {
@@ -138,6 +140,10 @@ func (c *Compiler) initCore() error {
138140
c.parser = googlesql.NewParser()
139141
c.selector = newDefaultSelector()
140142
dialect = googlesql.Dialect()
143+
case config.EngineMSSQL:
144+
c.parser = mssql.NewParser()
145+
c.selector = newDefaultSelector()
146+
dialect = mssql.Dialect()
141147
default:
142148
return fmt.Errorf("unknown engine: %s", c.conf.Engine)
143149
}

internal/config/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ const (
5656
EngineSQLite Engine = "sqlite"
5757
EngineClickHouse Engine = "clickhouse"
5858
EngineGoogleSQL Engine = "googlesql"
59+
EngineMSSQL Engine = "mssql"
5960
)
6061

6162
type Config struct {
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"command": "analyze",
3+
"args": ["--dialect", "mssql", "--schema", "schema.sql", "query.sql"],
4+
"contexts": ["base"]
5+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- name: ListAuthors :many
2+
SELECT id, name, bio, royalties, created FROM authors;

0 commit comments

Comments
 (0)