Skip to content

Commit 4dd588b

Browse files
John Kraalpubkraal
authored andcommitted
feat: add mssql support
1 parent 531e542 commit 4dd588b

8 files changed

Lines changed: 232 additions & 11 deletions

File tree

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ func main() {
5252
}
5353
```
5454

55-
The `WithDialect()` option accepts: `schema.MySQL`, `schema.Postgres`, or
56-
`schema.SQLite`. These dialects all use only `database/sql` calls, so you may
57-
have success with other databases which are SQL-compatible with the above
58-
dialects.
55+
The `WithDialect()` option accepts: `schema.MySQL`, `schema.Postgres`,
56+
`schema.SQLite` or `schema.MSSQL`. These dialects all use only `database/sql`
57+
calls, so you may have success with other databases which are SQL-compatible
58+
with the above dialects.
5959

6060
You can also provide your own custom `Dialect`. See `dialect.go` for the
6161
definition of the `Dialect` interface, and the optional `Locker` interface. Note
@@ -114,7 +114,7 @@ additional databases or feature enhancements / bug fixes.
114114
- [x] PostgreSQL (database/sql driver only, see [adlio/pgxschema](https://github.com/adlio/pgxschema) if you use `jack/pgx`)
115115
- [x] SQLite (thanks [kalafut](https://github.com/kalafut)!)
116116
- [x] MySQL / MariaDB
117-
- [ ] SQL Server (open a Pull Request)
117+
- [x] SQL Server
118118
- [ ] CockroachDB, Redshift, Snowflake, etc (open a Pull Request)
119119

120120
## Package Opinions
@@ -174,7 +174,7 @@ there's a good chance a different schema migration tool is more appropriate.
174174
- [x] Enhancements and documentation to facilitate asset embedding via go:embed
175175
- [ ] Add a `Validate()` method to allow checking migration names for
176176
consistency and to detect problematic changes in the migrations list.
177-
- [ ] SQL Server support
177+
- [x] SQL Server support
178178

179179
## Version History
180180

go.mod

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,18 @@ require (
1616
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
1717
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
1818
github.com/containerd/continuity v0.2.1 // indirect
19+
github.com/denisenkom/go-mssqldb v0.12.0 // indirect
1920
github.com/docker/go-connections v0.4.0 // indirect
2021
github.com/docker/go-units v0.4.0 // indirect
22+
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
23+
github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 // indirect
2124
github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect
2225
github.com/opencontainers/go-digest v1.0.0 // indirect
2326
github.com/opencontainers/image-spec v1.0.2 // indirect
2427
github.com/opencontainers/runc v1.0.3 // indirect
2528
github.com/pkg/errors v0.9.1 // indirect
2629
github.com/sirupsen/logrus v1.8.1 // indirect
30+
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 // indirect
2731
golang.org/x/net v0.0.0-20211208012354-db4efeb81f4b // indirect
2832
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // indirect
2933
gotest.tools v2.2.0+incompatible // indirect

go.sum

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM=
22
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
3+
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
4+
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
5+
github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
36
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
47
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
58
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@@ -34,10 +37,14 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc
3437
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
3538
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
3639
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
40+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3741
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
3842
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
43+
github.com/denisenkom/go-mssqldb v0.12.0 h1:VtrkII767ttSPNRfFekePK3sctr+joXgO58stqQbtUA=
44+
github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU=
3945
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
4046
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
47+
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
4148
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
4249
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
4350
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
@@ -55,6 +62,10 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
5562
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
5663
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
5764
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
65+
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
66+
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
67+
github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 h1:+eHOFJl1BaXrQxKX+T06f78590z4qA2ZzBTqahsKSE4=
68+
github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+wXQnTPR4KqTKDgJukSZ6amVRtWMPEjE6sQoK8=
5869
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
5970
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
6071
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@@ -103,6 +114,7 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
103114
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
104115
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
105116
github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
117+
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
106118
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
107119
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
108120
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
@@ -117,6 +129,7 @@ github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xA
117129
github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA=
118130
github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs=
119131
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
132+
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
120133
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
121134
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
122135
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -147,9 +160,11 @@ github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHN
147160
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
148161
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
149162
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
163+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
150164
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
151165
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
152166
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
167+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
153168
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
154169
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
155170
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
@@ -165,14 +180,18 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
165180
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
166181
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
167182
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
183+
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
184+
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
168185
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
169186
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
170187
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
171188
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
172189
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
173190
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
191+
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
174192
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
175193
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
194+
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
176195
golang.org/x/net v0.0.0-20211208012354-db4efeb81f4b h1:MWaHNqZy3KTpuTMAGvv+Kw+ylsEpmyJZizz1dqxnu28=
177196
golang.org/x/net v0.0.0-20211208012354-db4efeb81f4b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
178197
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -185,6 +204,7 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h
185204
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
186205
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
187206
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
207+
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
188208
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
189209
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
190210
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -226,6 +246,10 @@ gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
226246
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
227247
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
228248
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
249+
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
250+
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
251+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
252+
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
229253
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
230254
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
231255
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

main_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func TestMain(m *testing.M) {
5555
}
5656

5757
func withEachDialect(t *testing.T, f func(t *testing.T, d Dialect)) {
58-
dialects := []Dialect{Postgres, MySQL, SQLite}
58+
dialects := []Dialect{Postgres, MySQL, SQLite, MSSQL}
5959
for _, dialect := range dialects {
6060
t.Run(fmt.Sprintf("%T", dialect), func(t *testing.T) {
6161
f(t, dialect)

mssql.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package schema
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strings"
7+
"time"
8+
"unicode"
9+
)
10+
11+
// MSSQL is the dialect for MS SQL-compatible databases
12+
var MSSQL = mssqlDialect{}
13+
14+
type mssqlDialect struct{}
15+
16+
func (s mssqlDialect) QuotedTableName(schemaName, tableName string) string {
17+
if schemaName == "" {
18+
return s.QuotedIdent(tableName)
19+
}
20+
return fmt.Sprintf("%s.%s", s.QuotedIdent(schemaName), s.QuotedIdent(tableName))
21+
}
22+
23+
func (s mssqlDialect) QuotedIdent(ident string) string {
24+
if ident == "" {
25+
return ""
26+
}
27+
28+
var sb strings.Builder
29+
sb.WriteRune('[')
30+
for _, r := range ident {
31+
switch {
32+
case unicode.IsSpace(r):
33+
continue
34+
case r == ';':
35+
continue
36+
case r == ']':
37+
sb.WriteRune(r)
38+
sb.WriteRune(r)
39+
default:
40+
sb.WriteRune(r)
41+
}
42+
}
43+
sb.WriteRune(']')
44+
45+
return sb.String()
46+
}
47+
48+
func (s mssqlDialect) CreateMigrationsTable(ctx context.Context, tx Queryer, tableName string) error {
49+
unquotedTableName := tableName[1 : len(tableName)-1]
50+
query := fmt.Sprintf(`
51+
IF NOT EXISTS (SELECT * FROM Sysobjects WHERE NAME='%s' AND XTYPE='U')
52+
CREATE TABLE %s (
53+
id VARCHAR(255) NOT NULL,
54+
checksum VARCHAR(32) NOT NULL DEFAULT '',
55+
execution_time_in_millis INTEGER NOT NULL DEFAULT 0,
56+
applied_at DATETIMEOFFSET
57+
)
58+
`, unquotedTableName, tableName)
59+
_, err := tx.ExecContext(ctx, query)
60+
return err
61+
}
62+
63+
func (s mssqlDialect) GetAppliedMigrations(ctx context.Context, tx Queryer, tableName string) (migrations []*AppliedMigration, err error) {
64+
migrations = make([]*AppliedMigration, 0)
65+
66+
query := fmt.Sprintf(`
67+
SELECT id, checksum, execution_time_in_millis, applied_at
68+
FROM %s ORDER BY id ASC
69+
`, tableName)
70+
71+
rows, err := tx.QueryContext(ctx, query)
72+
if err != nil {
73+
return migrations, err
74+
}
75+
defer rows.Close()
76+
77+
for rows.Next() {
78+
migration := AppliedMigration{}
79+
err = rows.Scan(&migration.ID, &migration.Checksum, &migration.ExecutionTimeInMillis, &migration.AppliedAt)
80+
if err != nil {
81+
err = fmt.Errorf("failed to GetAppliedMigrations. Did somebody change the structure of the %s table?: %w", tableName, err)
82+
return migrations, err
83+
}
84+
migration.AppliedAt = migration.AppliedAt.In(time.Local)
85+
migrations = append(migrations, &migration)
86+
}
87+
88+
return migrations, err
89+
}
90+
91+
func (s mssqlDialect) InsertAppliedMigration(ctx context.Context, tx Queryer, tableName string, am *AppliedMigration) error {
92+
query := fmt.Sprintf(`
93+
INSERT INTO %s
94+
( id, checksum, execution_time_in_millis, applied_at )
95+
VALUES
96+
( @p1, @p2, @p3, @p4 )`,
97+
tableName,
98+
)
99+
_, err := tx.ExecContext(ctx, query, am.ID, am.MD5(), am.ExecutionTimeInMillis, am.AppliedAt)
100+
return err
101+
}

mssql_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package schema
2+
3+
import (
4+
"testing"
5+
6+
// MSSQL Driver
7+
_ "github.com/denisenkom/go-mssqldb"
8+
)
9+
10+
// Interface verification that MSSQL is a valid Dialect
11+
var (
12+
_ Dialect = MSSQL
13+
)
14+
15+
func TestMSSQLQuotedTableName(t *testing.T) {
16+
type qtnTest struct {
17+
schema, table string
18+
expected string
19+
}
20+
21+
tests := []qtnTest{
22+
{"public", "ta[ble", `[public].[ta[ble]`},
23+
{"pu[b]lic", "ta[ble", `[pu[b]]lic].[ta[ble]`},
24+
{"tempdb", "users", `[tempdb].[users]`},
25+
{"schema.with.dot", "table.with.dot", `[schema.with.dot].[table.with.dot]`},
26+
{`public"`, `"; DROP TABLE users`, `[public"].["DROPTABLEusers]`},
27+
}
28+
29+
for _, tc := range tests {
30+
tc := tc
31+
t.Run(tc.expected, func(t *testing.T) {
32+
actual := MSSQL.QuotedTableName(tc.schema, tc.table)
33+
if actual != tc.expected {
34+
t.Errorf("Expected %s, got %s", tc.expected, actual)
35+
}
36+
})
37+
}
38+
39+
}
40+
41+
func TestMSSQLQuotedIdent(t *testing.T) {
42+
table := map[string]string{
43+
"": "",
44+
"MY_TABLE": "[MY_TABLE]",
45+
"users_roles": `[users_roles]`,
46+
"table.with.dot": `[table.with.dot]`,
47+
`table"with"quotes`: `[table"with"quotes]`,
48+
"table[with]brackets": "[table[with]]brackets]",
49+
}
50+
51+
for ident, expected := range table {
52+
t.Run(expected, func(t *testing.T) {
53+
actual := MSSQL.QuotedIdent(ident)
54+
if expected != actual {
55+
t.Errorf("Expected %s, got %s", expected, actual)
56+
}
57+
})
58+
}
59+
}

schema_test.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@ const (
2525
PostgresDriverName = "postgres"
2626
SQLiteDriverName = "sqlite3"
2727
MySQLDriverName = "mysql"
28+
MSSQLDriverName = "sqlserver"
2829
)
2930

3031
// TestDBs holds all of the specific database instances against which tests
31-
// will run. The connectDB test helper refere ces the keys of this map, and
32+
// will run. The connectDB test helper references the keys of this map, and
3233
// the withEachDB helper runs tests against every database defined here.
3334
var TestDBs map[string]*TestDB = map[string]*TestDB{
3435
"postgres:latest": {
@@ -53,4 +54,10 @@ var TestDBs map[string]*TestDB = map[string]*TestDB{
5354
DockerRepo: "mariadb",
5455
DockerTag: "latest",
5556
},
57+
"mssql:latest": {
58+
Dialect: MSSQL,
59+
Driver: MSSQLDriverName,
60+
DockerRepo: "mcr.microsoft.com/mssql/server",
61+
DockerTag: "2019-latest",
62+
},
5663
}

0 commit comments

Comments
 (0)