diff --git a/deployable.go b/deployable.go index 0969568..cf5f454 100644 --- a/deployable.go +++ b/deployable.go @@ -4,12 +4,14 @@ import ( "context" "database/sql" "fmt" - mssql "github.com/denisenkom/go-mssqldb" - "github.com/pkg/errors" - "github.com/vippsas/sqlcode/sqlparser" "io/fs" "strconv" "strings" + "time" + + mssql "github.com/denisenkom/go-mssqldb" + "github.com/pkg/errors" + "github.com/vippsas/sqlcode/sqlparser" ) type Deployable struct { @@ -137,6 +139,8 @@ func (d *Deployable) EnsureUploaded(ctx context.Context, dbc DB) error { lockResourceName := "sqlcode.EnsureUploaded/" + d.SchemaSuffix + // When a lock is opened with the Transaction lock owner, + // that lock is released when the transaction is committed or rolled back. var lockRetCode int err := dbc.QueryRowContext(ctx, ` declare @retcode int; @@ -261,3 +265,51 @@ func MustInclude(opts Options, fsys ...fs.FS) Deployable { } return result } + +type SchemaObject struct { + Name string + SchemaId int + Objects int + CreateDate time.Time + ModifyDate time.Time +} + +func (s *SchemaObject) Suffix() string { + return strings.Split(s.Name, "@")[1] +} + +// Return a list of sqlcode schemas that have been uploaded to the database. +// This includes all current and unused schemas. +func (d *Deployable) ListUploaded(ctx context.Context, dbc DB) []*SchemaObject { + objects := []*SchemaObject{} + impersonate(ctx, dbc, "sqlcode-deploy-sandbox-user", func(conn *sql.Conn) error { + rows, err := conn.QueryContext(ctx, ` + select + s.name + , s.schema_id + , o.objects + , o.create_date + , o.modify_date + from sys.schemas s + outer apply ( + select count(o.object_id) as objects + , min(o.create_date) as create_date + , max(o.modify_date) as modify_date + from sys.objects o + where o.schema_id = s.schema_id + ) as o + where s.name like 'code@%'`) + if err != nil { + return err + } + + for rows.Next() { + zero := &SchemaObject{} + rows.Scan(&zero.Name, &zero.Objects, &zero.SchemaId, &zero.CreateDate, &zero.ModifyDate) + objects = append(objects, zero) + } + + return nil + }) + return objects +} diff --git a/docker-compose.test.yml b/docker-compose.test.yml index c367863..94da3ec 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -3,7 +3,7 @@ services: # mssql # mssql: - image: mcr.microsoft.com/mssql/server:2022-CU18-ubuntu-22.04 + image: mcr.microsoft.com/mssql/server:latest hostname: mssql container_name: mssql diff --git a/migrations/0001.sqlcode.sql b/migrations/0001.sqlcode.sql index 60c5c2d..dd0d047 100644 --- a/migrations/0001.sqlcode.sql +++ b/migrations/0001.sqlcode.sql @@ -21,6 +21,7 @@ alter role [sqlcode-deploy-role] add member [sqlcode-deploy-sandbox-user]; -- seems fair... grant impersonate on user::[sqlcode-deploy-sandbox-user] to [sqlcode-deploy-role]; + go create schema sqlcode; @@ -63,7 +64,7 @@ as begin declare @msg varchar(max) declare @sql nvarchar(max) - if @@trancount = 0 throw 55001, 'You should run sqlcode.CreateCodeSchema within a transaction', 1; + if @@trancount = 0 throw 55001, 'You should run sqlcode.DropCodeSchema within a transaction', 1; declare @schemaname nvarchar(max) = concat('code@', @schemasuffix) declare @schemaid int = (select schema_id from sys.schemas where name = @schemaname); diff --git a/preprocess_test.go b/preprocess_test.go index 1f81e03..bf976e8 100644 --- a/preprocess_test.go +++ b/preprocess_test.go @@ -1,10 +1,24 @@ package sqlcode import ( - "github.com/stretchr/testify/assert" "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/vippsas/sqlcode/sqlparser" ) +func TestSchemaSuffixFromHash(t *testing.T) { + t.Run("returns a unique hash", func(t *testing.T) { + doc := sqlparser.Document{ + Declares: []sqlparser.Declare{}, + } + + value := SchemaSuffixFromHash(doc) + require.Equal(t, value, SchemaSuffixFromHash(doc)) + }) +} + func TestLineNumberInInput(t *testing.T) { // Scenario: diff --git a/sqltest/sqlcode_test.go b/sqltest/sqlcode_test.go index 0f209df..4352eac 100644 --- a/sqltest/sqlcode_test.go +++ b/sqltest/sqlcode_test.go @@ -23,4 +23,9 @@ func Test_RowsAffected(t *testing.T) { rowsAffected, err := res.RowsAffected() require.NoError(t, err) assert.Equal(t, int64(1), rowsAffected) + + schemas := SQL.ListUploaded(ctx, fixture.DB) + require.Len(t, schemas, 1) + require.Equal(t, 6, schemas[0].Objects) + require.Equal(t, "5420c0269aaf", schemas[0].Suffix()) }