Desculpa
Em primeiro lugar, peço desculpas pela extensão - este é um problema estranho, então estou tentando fornecer detalhes suficientes para torná-lo reproduzível.
dr.
Acho que encontrei um problema ao exportar um *.bacpac
aplicativo de camada de dados entre dois servidores MS SQL para migrar uma instância de um banco de dados Indeo ProGet que cria um bacpac inconsistente. Tentar importar este bacpac resulta em um erro, e eu apreciaria uma segunda olhada no problema para ter certeza de que não estou fazendo nada estúpido...
Acho que a causa raiz é um possível bug no processo "Exportar aplicativo da camada de dados" quando há várias colunas de esquema usando o mesmo tipo definido pelo usuário e o UDT está vinculado a uma regra de validação - o resultado parece ser um *.bacpac
arquivo corrompido que não pode ser importado sem primeiro fazer alguns ajustes manuais.
Minha pergunta é:
- Parte 1 : Estou fazendo algo errado ou isso é um bug conhecido (ou novo)?
- Parte 2 : Se for um bug, você tem alguma ideia de onde posso reportá-lo?
Esquema
A seguir, recriamos uma pequena parte do esquema do banco de dados Inedo ProGet que demonstra o problema. (FWIW, atualmente estou usando o SQL Server 2022 v16.0.1105.1, mas tenho quase certeza de que isso também ocorre em outras versões).
Basicamente faz isso:
- Cria um tipo definido pelo usuário
YNINDICATOR
- Vincula-o a uma regra que restringe os valores
Y
eN
- Cria duas tabelas, cada uma com uma coluna do tipo
YNINDICATOR
(veja[dbo].[CustomLanguage].[Active_Indicator]
e[dbo].[ClusterNodes].[Primary_Indicator]
)
CREATE TYPE [dbo].[YNINDICATOR] FROM [char](1) NULL
GO
CREATE RULE [dbo].[YNINDICATOR_Domain]
AS
@Ind COLLATE Latin1_General_BIN IN ('Y', 'N');
EXEC sp_bindrule 'YNINDICATOR_Domain', 'YNINDICATOR'
GO
CREATE TABLE [dbo].[CustomLanguages](
[CustomLanguage_Id] [int] IDENTITY(1,1) NOT NULL,
[Culture_Name] [varchar](50) NOT NULL,
[Language_Name] [nvarchar](100) NOT NULL,
[Active_Indicator] [dbo].[YNINDICATOR] NOT NULL,
[CustomLanguage_Xml] [xml] NOT NULL,
CONSTRAINT [PK__CustomLanguages] PRIMARY KEY CLUSTERED
(
[CustomLanguage_Id] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE TABLE [dbo].[ClusterNodes](
[Server_Name] [nvarchar](64) NOT NULL,
[NodeType_Code] [char](1) NOT NULL,
[LastUpdated_Date] [datetime] NOT NULL,
[Primary_Indicator] [dbo].[YNINDICATOR] NOT NULL,
[Node_Configuration] [xml] NOT NULL,
CONSTRAINT [PK__ClusterNodes] PRIMARY KEY CLUSTERED
(
[Server_Name] ASC,
[NodeType_Code] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[ClusterNodes] WITH CHECK ADD CONSTRAINT [CK__ClusterNodes__NodeType_Code] CHECK (([NodeType_Code]='S' OR [NodeType_Code]='W'))
GO
ALTER TABLE [dbo].[ClusterNodes] CHECK CONSTRAINT [CK__ClusterNodes__NodeType_Code]
GO
Erro
A exportação do esquema acima para um arquivo bacpac do aplicativo da camada de dados é concluída sem erros, mas a tentativa de reimportá-lo como um novo banco de dados gera o seguinte erro:
Estúdio de gerenciamento do Microsoft SQL Server
Não foi possível importar o pacote.
Erro SQL72014: Framework Microsoft SqlDataClient Data Provider: MSG 2714, Nível 16, Estado 3, Procedimento YNINDICATOR_Domain, Linha 1 Já existe um objeto chamado 'YNINDICATOR_Domain' no banco de dados.
Erro SQL72045: Erro de execução de script. O script executado:
CRIAR REGRA [dbo].[YINDICATOR_Domain]
AS @Ind COLLATE Latin1_General_BIN IN ('Y', 'N')
(Microsoft.SqlServer.Dac)
Investigação
Descompactei o bacpac e descobri que quando há mais de uma coluna definida usando o YNINDICATOR
UDT, o model.xml
arquivo dentro do bacpac contém algo assim, com dois SqlRule
elementos definidos para [dbo].[YNINDICATOR_Domain]
- o primeiro contém as referências da coluna e o segundo contém o UDT referência:
Bacpac quebrado – duas referências de coluna e uma referência UDT -> dois elementos SqlRule
... snip ...
<Element Type="SqlRule" Name="[dbo].[YNINDICATOR_Domain]">
<Property Name="ExpressionScript">
<Value><![CDATA[@Ind COLLATE Latin1_General_BIN IN ('Y', 'N')]]></Value>
</Property>
<Relationship Name="BoundTargets">
<Entry>
<References Name="[dbo].[CustomLanguages].[Active_Indicator]" />
</Entry>
<Entry>
<References Name="[dbo].[ClusterNodes].[Primary_Indicator]" />
</Entry>
</Relationship>
<Relationship Name="Schema">
<Entry>
<References ExternalSource="BuiltIns" Name="[dbo]" />
</Entry>
</Relationship>
</Element>
<Element Type="SqlRule" Name="[dbo].[YNINDICATOR_Domain]">
<Property Name="ExpressionScript">
<Value><![CDATA[@Ind COLLATE Latin1_General_BIN IN ('Y', 'N')]]></Value>
</Property>
<Relationship Name="BoundTargets">
<Entry>
<References Name="[dbo].[YNINDICATOR]" />
</Entry>
</Relationship>
<Relationship Name="Schema">
<Entry>
<References ExternalSource="BuiltIns" Name="[dbo]" />
</Entry>
</Relationship>
</Element>
... snip ...
Esta é provavelmente a causa do already an object named 'YINDICATOR_Domain'
erro porque ele tenta criar o SqlRule duas vezes – uma vez para cada um Element
.
Se eu descartar qualquer uma das colunas usando o YNINDICATOR
UDT e reexportar um aplicativo da camada de dados, o bacpac conterá um único Element Type="SqlRule"
nó que contém a coluna e as referências do UDT:
Trabalhando bacpac - uma referência de coluna e uma referência UDT -> um elemento SqlRule
... snip ...
<Element Type="SqlRule" Name="[dbo].[YNINDICATOR_Domain]">
<Property Name="ExpressionScript">
<Value><![CDATA[@Ind COLLATE Latin1_General_BIN IN ('Y', 'N')]]></Value>
</Property>
<Relationship Name="BoundTargets">
<Entry>
<References Name="[dbo].[CustomLanguages].[Active_Indicator]" />
</Entry>
<Entry>
<References Name="[dbo].[YNINDICATOR]" />
</Entry>
</Relationship>
<Relationship Name="Schema">
<Entry>
<References ExternalSource="BuiltIns" Name="[dbo]" />
</Entry>
</Relationship>
</Element>
... snip ...
e se eu usar sp_unbindrule
para remover a ligação, o bacpac contém apenas uma Element
- ele não cria mais uma segunda para a ligação UDT, pois obviamente não existe mais:
Trabalhando bacpac - duas referências de coluna e nenhuma referência UDT -> um elemento SqlRule
<Element Type="SqlRule" Name="[dbo].[YNINDICATOR_Domain]">
<Property Name="ExpressionScript">
<Value><![CDATA[@Ind COLLATE Latin1_General_BIN IN ('Y', 'N')]]></Value>
</Property>
<Relationship Name="BoundTargets">
<Entry>
<References Name="[dbo].[CustomLanguages].[Active_Indicator]" />
</Entry>
<Entry>
<References Name="[dbo].[ClusterNodes].[Primary_Indicator]" />
</Entry>
</Relationship>
<Relationship Name="Schema">
<Entry>
<References ExternalSource="BuiltIns" Name="[dbo]" />
</Entry>
</Relationship>
</Element>
Na verdade, não posso usar nenhuma dessas opções, pois ela não corresponde ao esquema de banco de dados real do aplicativo ProGet, mas é interessante observar como a coluna e a ligação UDT são serializadas dependendo se há uma ou mais de uma coluna usando o UDT.
Gambiarra
Se eu hackear manualmentemodel.xml
o arquivo bacpac original e mesclar os dois SqlRule Element
s para que haja apenas um que contenha as colunas e a vinculação de regras, posso importá-lo corretamente e ele criará todos os objetos de esquema sem erros:
Isso não é muito útil, pois não quero continuar fazendo isso toda vez que migrar o banco de dados (não farei isso muitas vezes, mas o suficiente para que seja uma solução alternativa dolorosa).
Bacpac funcionando - duas referências de coluna e uma referência UDT hackeadas em um elemento SqlRule
<Element Type="SqlRule" Name="[dbo].[YNINDICATOR_Domain]">
<Property Name="ExpressionScript">
<Value><![CDATA[@Ind COLLATE Latin1_General_BIN IN ('Y', 'N')]]></Value>
</Property>
<Relationship Name="BoundTargets">
<Entry>
<References Name="[dbo].[CustomLanguages].[Active_Indicator]" />
</Entry>
<Entry>
<References Name="[dbo].[ClusterNodes].[Primary_Indicator]" />
</Entry>
<Entry>
<References Name="[dbo].[YNINDICATOR]" />
</Entry>
</Relationship>
<Relationship Name="Schema">
<Entry>
<References ExternalSource="BuiltIns" Name="[dbo]" />
</Entry>
</Relationship>
</Element>
Observação lateral: se eu reexportar esse banco de dados, ele voltará à forma original de 2 elementos SqlRule - um que contém as colunas e outro que contém a vinculação da regra.
Atualizar
O problema também acontece com o Az CLI:
PS> az version
{
"azure-cli": "2.56.0",
"azure-cli-core": "2.56.0",
"azure-cli-telemetry": "1.1.0",
"extensions": {
"storage-preview": "1.0.0b1"
}
}
PS> az sql db export `
--subscription "My Subscription" `
--resource-group "my-resource-group" `
--server "my-sql-server" `
--name "ProGet" `
--admin-user "my-admin-user" `
--admin-password "my-admin-password" `
--storage-uri "https://mystorageaccount.blob.core.windows.net/proget-db-backups/proget.bacpac" ``
--storage-key-type "StorageAccessKey" `
--storage-key "my storage key"
Baixar o blob e descompactá-lo mostra o problema com vários SqlRule Element
s.
Recapitular
Se você chegou até aqui, obrigado pela leitura, e só para recapitular, minha pergunta no "tl; dr" no topo foi:
- Parte 1 : Estou fazendo algo errado ou isso é um bug conhecido (ou novo)?
- Parte 2 : Se for um bug, você tem alguma ideia de onde posso reportá-lo?
Qualquer ajuda será muito apreciada.
dr.
Baixe e use o
SqlPackage.exe
utilitário de linha de comando mais recente em vez de SSMS ouaz
CLI - ele possui uma versão posterior da biblioteca DacFx que inclui uma correção para esse problema específico.Referências
Então, depois de um dia inteiro de escavação, parece que este é realmente um problema conhecido:
É um código de erro diferente - SQL72018 em vez de SQL72014, mas exatamente o mesmo problema.
Também parece que foi corrigido nesta versão:
I don't know how to check the exact version of the SqlPackage / DacFx components being used by SSMS, but I'm seeing the issue in SSMS version 19.3.4.0 which is the latest version as the time of writing.
Workaround
One workaround is to download and install the latest standalone SqlPackage tool and use that on the command-line instead:
On Windows this installs it to
%USERPROFILE%\.dotnet\tools\sqlpackage.exe
which is in the%PATH%
variable (at least it is on my machine) so it can be invoked with something like this to export the bacpac:Esta versão inclui uma correção para o problema do GitHub, portanto gera um arquivo backpac válido com o esquema da pergunta original:
modelo.xml
As referências de coluna e UDT agora são todas geradas em um único
<Element Type="SqlRule" ...
nó, que é importado com sucesso.