Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use lowercase sys.synonyms in mssql. #2225

Merged
merged 2 commits into from Dec 1, 2021
Merged

Conversation

nvoxland
Copy link
Contributor

@nvoxland nvoxland commented Nov 18, 2021

Description

The sys.synonyms view is actually lowercase in mssql. Case-insensitive database collations are fine with our use of SYS.SYNONYMS, but case sensitive ones like Latin1_General_BIN2 or any that end in BIN or BIN2 are not.

Fixes #2044


Dev Handoff Notes (Internal Use)

Links

Testing

Dev Verification

Started a database with a BIN collation: docker run ~~e ACCEPT_EULA=Y -e MSSQL_PID="Developer"~~ e MSSQL_SA_PASSWORD="LiquibasePass1" ~~e MSSQL_TCP_PORT=14333~~ e MSSQL_COLLATION=Latin1_General_BIN2 ~~p 14333:14333~~ d mcr.microsoft.com/mssql/server:20 19-GA-ubuntu-16.04

then ran liquibase generate-changelog.

Prior to the fix, it outputs a fine-level message of Cannot get object dependencies like [2021-11-18 10:52:55] FINE [liquibase.diff] Cannot get object dependencies: Error executing SQL select object_schema_name(referencing_id) as referencing_schema_name, object_name(referencing_id) as referencing_name, object_name(referenced_id) as referenced_name, object_schema_name(referenced_id) as referenced_schema_name from sys.sql_expression_dependencies depz where (object_schema_name(referenced_id)='dbo') UNION select object_schema_name(object_id) as referencing_schema_name, object_name(object_id) as referencing_name, object_name(parent_object_id) as referenced_name, object_schema_name(parent_object_id) as referenced_schema_name from sys.objects where parent_object_id > 0 and is_ms_shipped=0 and (object_schema_name(object_id)='dbo') UNION select object_schema_name(fk.object_id) as referencing_schema_name, fk.name as referencing_name, i.name as referenced_name, object_schema_name(i.object_id) as referenced_schema_name from sys.foreign_keys fk join sys.indexes i on fk.referenced_object_id=i.object_id and fk.key_index_id=i.index_id where fk.is_ms_shipped=0 and (object_schema_name(fk.object_id)='dbo') UNION select object_schema_name(i.object_id) as referencing_schema_name, object_name(i.object_id) as referencing_name, s.name as referenced_name, null as referenced_schema_name from sys.indexes i join sys.partition_schemes s on i.data_space_id = s.data_space_id UNION select null as referencing_schema_name, s.name as referencing_name, f.name as referenced_name, null as referenced_schema_name from sys.partition_functions f join sys.partition_schemes s on s.function_id=f.function_id UNION select null as referencing_schema_name, s.name as referencing_name, fg.name as referenced_name, null as referenced_schema_name from sys.partition_schemes s join sys.destination_data_spaces ds on s.data_space_id=ds.partition_scheme_id join sys.filegroups fg on ds.data_space_id=fg.data_space_id UNION select distinct null as referencing_schema_name, f.name as referencing_name, ds.name as referenced_name, null as referenced_schema_name from sys.database_files f join sys.data_spaces ds on f.data_space_id=ds.data_space_id where f.data_space_id > 1 UNION select object_schema_name(t.object_id) as referencing_schema_name, t.name as referencing_name, ds.name as referenced_name, null as referenced_schema_name from sys.tables t join sys.data_spaces ds on t.filestream_data_space_id=ds.data_space_id where t.filestream_data_space_id > 1 UNION select object_schema_name(t.object_id) as referencing_schema_name, t.name as referencing_name, ds.name as referenced_name, null as referenced_schema_name from sys.tables t join sys.data_spaces ds on t.lob_data_space_id=ds.data_space_id where t.lob_data_space_id > 1 UNION select object_schema_name(i.object_id) as referencing_schema_name, i.name as referencing_name, ds.name as referenced_name, null as referenced_schema_name from sys.indexes i join sys.data_spaces ds on i.data_space_id=ds.data_space_id where i.data_space_id > 1 UNION select object_schema_name(i.object_id) as referencing_schema_name, i.name as referencing_name, object_name(i.object_id) as referenced_name, object_schema_name(i.object_id) as referenced_schema_name from sys.indexes i where object_schema_name(i.object_id)='dbo' UNION SELECT SCHEMA_NAME(SCHEMA_ID) as referencing_schema_name, name as referencing_name, PARSENAME(BASE_OBJECT_NAME,1) AS referenced_name, (CASE WHEN PARSENAME(BASE_OBJECT_NAME,2) IS NULL THEN schema_name(schema_id) else PARSENAME(BASE_OBJECT_NAME,2) END) AS referenced_schema_name FROM SYS.SYNONYMS WHERE is_ms_shipped='false' AND SCHEMA_NAME(SCHEMA_ID)='dbo' UNION select object_schema_name(c.object_id) as referencing_schema_name, c.name as referencing_name, object_schema_name(nc.object_id) as referenced_schema_name, nc.name as referenced_name from sys.indexes c join sys.indexes nc on c.object_id=nc.object_id JOIN sys.objects o ON c.object_id = o.object_id where c.index_id != nc.index_id and c.type_desc='CLUSTERED' and c.is_unique='true' and (not(nc.type_desc='CLUSTERED') OR nc.is_unique='false') AND o.type_desc='VIEW' AND o.name='AR_DETAIL_OPEN': Invalid object name 'SYS.SYNONYMS'. .

After the fix, there is no "cannot get object dependencies" log messages

Test Requirements (Liquibase Internal QA)

This test requires:

  • a SQL Server 2019 Docker with a case-sensitive collation enabled
  • a SQL Server Docker with default collation (any SQL Server version; the container you typically use for testing SQL Server should have a default, case-insensitive collation.
  • a Pro License (synonyms are a Pro-only object for snapshotting commands (generate/diff-changelog).

Start the MSSQL Docker container with case-sensitive collation by running:

docker run --name mssql_19 -e ACCEPT_EULA=Y -e MSSQL_PID="Developer" -e MSSQL_SA_PASSWORD="LiquibasePass1" -e MSSQL_TCP_PORT=14333 -e MSSQL_COLLATION=Latin1_General_BIN2 -p 14333:14333 -d mcr.microsoft.com/mssql/server:2019-GA-ubuntu-16.04

Connect to the database as sa with password LiquibasePass1; the port is 14333.

Create a user and a database to use for testing. You can use the default master database but if you run drop-all on that master, you drop a lot of dependencies SQL Server itself needs. It is best practice not to use the master database for testing.

To create a SQL Server login datical_login and database proCatalog:

/****** Object:  Database [proCatalog]    Script Date: 10/11/2019 4:50:57 PM ******/
CREATE DATABASE [proCatalog]
GO

ALTER DATABASE [proCatalog] SET  READ_WRITE;
GO

USE [proCatalog]
GO
/* For security reasons the login is created disabled and with a random password. */
/****** Object:  Login [datical_login]    Script Date: 10/11/2019 5:13:54 PM ******/
CREATE LOGIN [datical_login] WITH PASSWORD='D4tic4l01', DEFAULT_DATABASE=[proCatalog], DEFAULT_LANGUAGE=[us_english], CHECK_EXPIRATION=OFF, CHECK_POLICY=ON
GO

ALTER LOGIN [datical_login] ENABLE
GO

/****** Object:  User [dvone4145]    Script Date: 10/11/2019 5:43:19 PM ******/
CREATE USER [datical] FOR LOGIN [datical_login] WITH DEFAULT_SCHEMA=[dbo]
GO

USE [master]
GO
ALTER SERVER ROLE [dbcreator] ADD MEMBER [datical_login]
GO

GRANT VIEW ANY DEFINITION TO [datical_login]
GO
ALTER SERVER ROLE [dbcreator] ADD MEMBER [datical_login]
GO
EXEC master..sp_addsrvrolemember @loginame = N'datical_login', @rolename = N'sysadmin'
GO

USE [proCatalog]
GO

EXEC sp_addrolemember N'db_ddladmin', N'datical'
GO
EXEC sp_addrolemember N'db_datareader', N'datical'
GO
EXEC sp_addrolemember N'db_datawriter', N'datical'
GO
EXEC sp_addrolemember N'db_securityadmin', N'datical'
GO
GRANT VIEW DATABASE STATE to [datical]
GO

Create a liquibase.properties file that both url and referenceUrl defined:

### CONNECTION Properties ###
url: jdbc:sqlserver://localhost:14333;databaseName=proCatalog
username: datical_login
password: D4tic4l01
driver: com.microsoft.sqlserver.jdbc.SQLServerDriver

referenceUrl: jdbc:sqlserver://localhost:14333;databaseName=proCatalog
referenceUsername: sa
referencePassword: LiquibasePass1
referenceDriver: com.microsoft.sqlserver.jdbc.SQLServerDriver

### Liquibase Pro Key ###
liquibaseProKey: KEYHERE

Use the attached changelogs and run a liquibase update to create a private and a public synonym on the database.

liquibase update --changelog-file lb2175-changelog.xml --labels=setup,createPrivateSynonym,createPublicSynonym

Query to Determine SQL Server Collation (run directly on database)

SELECT CONVERT (varchar(256), SERVERPROPERTY('collation'));

  • Latin1_General_BIN2 is a case-sensitive collation setting
  • SQL_Latin1_General_CP1_CI_AS is a case-insensitive collation setting.

Manual Tests

Verify generatechangelog does not throw a database error when connecting to MSSQL with a casesensitive collation type.

  • liquibase --log-level INFO generate-changelog --changelog-file gcl.mssql.sql
  • generate-changelog is successful.
  • There is no error that SYS.SYNONYMS is an invalid object name.
  • The generated changelog has three changesets: a create table and two create synonyms.

Verify diffchangelog does not throw a database error when connecting to MSSQL with a casesensitive collation type.

  • liquibase --log-level INFO diff-changelog --changelog-file dcl.mssql.sql
  • diff-changelog is successful.
  • There is no error that SYS.SYNONYMS is an invalid object name.
  • The generated changelog has three changesets: a create table and two create synonyms.

Verify that generatechangelog does not throw a database error when there are no synonyms present when connecting to MSSQL with a casesensitive collation type.

  • SETUP: Drop the synonyms from proCatalog (make sure the jdbc url is configured to use proCatalog, not master!)
  • liquibase --log-level FINE generate-changelog --changelog-file gclEmpty.mssql.sql
  • There is no log output that SYS.SYNONYMS is an invalid object name.

Verify generate-changelog does not throw an error when connecting to a MSSQL with default collation.

  • SETUP: Your “normal” SQL Sever Docker will not have case-sensitive collation; you can use whatever Docker container you usually use to test for this. Don’t forget to update your liquibase.properties file to connect to this MSSQL instance.
  • liquibase --log-level FINE generate-changelog --changelog-file gclEmpty2.mssql.sql
  • There is no log output that SYS.SYNONYMS is an invalid object name.

Automated Tests

  • No new automated tests are required for this fix.

┆Issue is synchronized with this Jira Bug by Unito

@nvoxland nvoxland added this to To Do in Conditioning++ via automation Nov 18, 2021
@nvoxland nvoxland moved this from To Do to Code Review in Conditioning++ Nov 18, 2021
@suryaaki2 suryaaki2 moved this from Code Review to Ready for Handoff (In JIRA) in Conditioning++ Nov 24, 2021
@suryaaki2 suryaaki2 merged commit c5ea511 into master Dec 1, 2021
Conditioning++ automation moved this from Ready for Handoff (In JIRA) to Done Dec 1, 2021
@suryaaki2 suryaaki2 deleted the mssql-lowercase-sys-synonyms branch December 1, 2021 18:58
@nvoxland nvoxland added this to the v4.6.2 milestone Dec 1, 2021
@nvoxland nvoxland removed this from Done in Conditioning++ Dec 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Generating changelog fails for MSSQL 2019 when sys.synonyms exist
3 participants