@@ -11,6 +11,9 @@ import (
1111 "time"
1212
1313 mssql "github.com/denisenkom/go-mssqldb"
14+ "github.com/jackc/pgx/v5"
15+ "github.com/jackc/pgx/v5/stdlib"
16+ pgxstdlib "github.com/jackc/pgx/v5/stdlib"
1417 "github.com/vippsas/sqlcode/sqlparser"
1518)
1619
@@ -77,21 +80,22 @@ func impersonate(ctx context.Context, dbc DB, username string, f func(conn *sql.
7780// Upload will create and upload the schema; resulting in an error
7881// if the schema already exists
7982func (d * Deployable ) Upload (ctx context.Context , dbc DB ) error {
80- // First, impersonate a user with minimal privileges to get at least
81- // some level of sandboxing so that migration scripts can't do anything
82- // the caller didn't expect them to.
83- return impersonate ( ctx , dbc , "sqlcode-deploy-sandbox-user" , func (conn * sql.Conn ) error {
83+ driver := dbc . Driver ()
84+ qs := make ( map [ string ][] interface {}, 1 )
85+
86+ var uploadFunc = func (conn * sql.Conn ) error {
8487 tx , err := conn .BeginTx (ctx , nil )
8588 if err != nil {
8689 return err
8790 }
8891
89- _ , err = tx .ExecContext (ctx , `sqlcode.CreateCodeSchema` ,
90- sql .Named ("schemasuffix" , d .SchemaSuffix ),
91- )
92- if err != nil {
93- _ = tx .Rollback ()
94- return err
92+ for q , args := range qs {
93+ _ , err = tx .ExecContext (ctx , q , args ... )
94+
95+ if err != nil {
96+ _ = tx .Rollback ()
97+ return fmt .Errorf ("failed to execute (%s) with arg(%s) in schema %s: %w" , q , args , d .SchemaSuffix , err )
98+ }
9599 }
96100
97101 preprocessed , err := Preprocess (d .CodeBase , d .SchemaSuffix )
@@ -123,8 +127,36 @@ func (d *Deployable) Upload(ctx context.Context, dbc DB) error {
123127
124128 return nil
125129
126- })
130+ }
131+
132+ if _ , ok := driver .(* mssql.Driver ); ok {
133+ // First, impersonate a user with minimal privileges to get at least
134+ // some level of sandboxing so that migration scripts can't do anything
135+ // the caller didn't expect them to.
136+ qs ["sqlcode.CreateCodeSchema" ] = []interface {
137+ }{
138+ sql .Named ("schemasuffix" , d .SchemaSuffix ),
139+ }
127140
141+ return impersonate (ctx , dbc , "sqlcode-deploy-sandbox-user" , uploadFunc )
142+ }
143+
144+ if _ , ok := driver .(* stdlib.Driver ); ok {
145+ qs [`set role "sqlcode-deploy-sandbox-user"` ] = nil
146+ qs [`call sqlcode.createcodeschema(@schemasuffix)` ] = []interface {}{
147+ pgx.NamedArgs {"schemasuffix" : d .SchemaSuffix },
148+ }
149+ conn , err := dbc .Conn (ctx )
150+ if err != nil {
151+ return err
152+ }
153+ defer func () {
154+ _ = conn .Close ()
155+ }()
156+ return uploadFunc (conn )
157+ }
158+
159+ return fmt .Errorf ("failed to determine sql driver to upload schema: %s" , d .SchemaSuffix )
128160}
129161
130162// EnsureUploaded checks that the schema with the suffix already exists,
@@ -137,37 +169,51 @@ func (d *Deployable) EnsureUploaded(ctx context.Context, dbc DB) error {
137169 return nil
138170 }
139171
172+ driver := dbc .Driver ()
140173 lockResourceName := "sqlcode.EnsureUploaded/" + d .SchemaSuffix
141174
175+ var lockRetCode int
176+ var lockQs string
177+ var unlockQs string
178+ var err error
179+
142180 // When a lock is opened with the Transaction lock owner,
143181 // that lock is released when the transaction is committed or rolled back.
144- var lockRetCode int
145- err := dbc .QueryRowContext (ctx , `
146- declare @retcode int;
147- exec @retcode = sp_getapplock @Resource = @resource, @LockMode = 'Shared', @LockOwner = 'Session', @LockTimeout = @timeoutMs;
148- select @retcode;
149- ` ,
150- sql .Named ("resource" , lockResourceName ),
151- sql .Named ("timeoutMs" , 20000 ),
152- ).Scan (& lockRetCode )
182+ if _ , ok := driver .(* pgxstdlib.Driver ); ok {
183+ lockQs = `select sqlcode.get_applock(@resource, @timeout)`
184+ unlockQs = `select sqlcode.release_applock(@resource)`
185+
186+ err = dbc .QueryRowContext (ctx , lockQs , pgx.NamedArgs {
187+ "resource" : lockResourceName ,
188+ "timeoutMs" : 20000 ,
189+ }).Scan (& lockRetCode )
190+
191+ defer func () {
192+ dbc .ExecContext (ctx , unlockQs , pgx.NamedArgs {"resource" : lockResourceName })
193+ }()
194+ }
195+
196+ if _ , ok := driver .(* mssql.Driver ); ok {
197+ // TODO
198+
199+ defer func () {
200+ // TODO: This returns an error if the lock is already released
201+ _ , _ = dbc .ExecContext (ctx , unlockQs ,
202+ sql .Named ("Resource" , lockResourceName ),
203+ sql .Named ("LockOwner" , "Session" ),
204+ )
205+ }()
206+ }
207+
153208 if err != nil {
154209 return err
155210 }
156211 if lockRetCode < 0 {
157212 return errors .New ("was not able to get lock before timeout" )
158213 }
159-
160- defer func () {
161- // TODO: This returns an error if the lock is already released
162- _ , _ = dbc .ExecContext (ctx , `sp_releaseapplock` ,
163- sql .Named ("Resource" , lockResourceName ),
164- sql .Named ("LockOwner" , "Session" ),
165- )
166- }()
167-
168214 exists , err := Exists (ctx , dbc , d .SchemaSuffix )
169215 if err != nil {
170- return err
216+ return fmt . Errorf ( "unable to determine if schema %s exists: %w" , d . SchemaSuffix , err )
171217 }
172218
173219 if exists {
0 commit comments