msdn12_capa.JPG

 

Clique aqui para ler todos os artigos desta edição

 

Implantação de Dados

Facilite o processo de definição do seu banco de dados com um instalador personalizado

por Alek Davis

Este artigo discute

Este artigo usa as seguintes tecnologias:

·         ?          Automatizando a instalação do database

·         ?          Automatizando upgrades

·         ?          Gerenciamento de versões

·         ?          Criando um script genérico

SQL, VBScript e ADO

 

Download:

CustomDatabaseInstaller.exe (135KB)

Chapéu

Database

 

 

Se você já precisou instalar, reparar ou fazer o upgrade de um grande banco de dados, deve saber que a instalação de um banco de dados pode ser um processo intimidador e especialmente sujeito a falhas humanas. Acidentes como saltar um script de upgrade ou esquecer de recompilar uma nova versão de um procedimento armazenado, podem ocorrer com facilidade, principalmente quando a instalação envolve operações manuais.

Uma maneira de reduzir as falhas humanas é por meio da implementação de um programa de instalação automatizada, como o arquivo Microsoft® Installer (MSI) ou o aplicativo InstallShield. Opcionalmente, você pode criar um aplicativo ou script de instalação de bancos de dados. (A Tabela 1 resume as vantagens e desvantagens das diversas opções de instalação de bancos de dados.)

Neste artigo, explicarei uma abordagem geral para a criação de instaladores de bancos de dados e ilustrarei com um exemplo real. O artigo é acompanhado por scripts de auxílio, que podem ser usados na configuração de bancos de dados e na compilação de scripts T-SQL.

 

Tabela 1 Opções de instalação de bancos de dados

Tipo de Instalador

Prós

Contras

Arquivo MSI

Tecnologia de instalação padrão do Windows. A configuração do banco de dados pode fazer parte da configuração do aplicativo.

A lógica da instalação do banco de dados deve ser implementada em um programa externo que permita maior complexidade. É preciso um maior esforço para programar notificações eficientes de andamento de configuração. Exige conhecimento profundo da tecnologia MSI.

Configuração do InstallShield

A configuração do banco de dados pode fazer parte da configuração do aplicativo. Não exige um instalador externo de banco de dados. O processo de instalação do banco de dados pode ser exibido na mesma janela de configuração que a configuração do aplicativo (sem janelas pop-up).

Não é uma tecnologia de instalação padrão do Windows (está sendo depreciado). Exige conhecimento da linguagem proprietária do InstallScript.

Aplicativos personalizados baseados no Windows

Pode ser chamado de uma configuração baseada no MSI ou como programa independente.

Não pode usar uma janela de configuração padrão para dar retorno ao usuário (quando chamado de uma configuração baseada em MSI). Implementação mais trabalhosa do que um script personalizado.

Script personalizado

Pode ser chamado de uma configuração baseada no MSI ou como programa independente. Implementação menos trabalhosa do que um aplicativo personalizado baseado no Windows.

Não pode usar uma janela de configuração padrão para dar retorno ao usuário (quando chamado de uma configuração baseada em MSI).

 

Como escolher uma tecnologia de instalação

Se você decidir criar um instalador de banco de dados, deverá antes escolher uma tecnologia na qual basear o instalador. No Windows®, os tipos mais comuns de instaladores de aplicativos são os arquivos MSI, que podem ser construídos com o Visual Studio® .NET, ou produtos de terceiros como o InstallShield Developer ou o Wise for Windows Installer.

Na maioria dos casos, as configurações baseadas em MSI tratam muito bem as instalações de aplicativos mas, infelizmente, o MSI não oferece suporte interno para as configurações de bancos de dados. Embora uma configuração típica baseada em Windows geralmente envolva a implantação de arquivos de aplicativo, o registro de objetos COM ou assemblies .NET, a alteração do Registro do Windows ou a criação de atalhos, nenhuma dessas etapas se aplica à instalação de bancos de dados. E, a menos que o banco de dados use stored procedures estendidos, nem mesmo precisa deixar arquivos físicos no sistema depois de concluída a instalação (com arquivos físicos refiro-me a scripts T-SQL usados para construir metadados, não o banco de dados ou arquivos de log de transações.) Com isso, tudo o que o instalador teria que fazer seria executar os scripts de instalação na ordem correta e, em seguida, removê-los ao final do processo. Isso pode ser feito via programa, mas a versão atual do MSI não oferece recursos de script T-SQL; ele baseia-se em programas externos para fazer isso.

Se você decidir usar o MSI, além do projeto de instalação, precisará criar um aplicativo externo para tratar a instalação do banco de dados. Contudo, a implementação de instaladores baseados no MSI está além dos objetivos deste artigo, por isso não vou abordá-la aqui. Em vez disso, explicarei como criar um script robusto capaz de lidar com a instalação de bancos de dados. Esse script pode ser chamado de um instalador baseado em MSI, linha de comando ou de um programa de instalação personalizado, e funcionará com a maioria das instalações de bancos de dados.

 

Criando o instalador de banco de dados

Partirei do pressuposto de que, a essas alturas, você já criou os scripts T-SQL responsáveis pela criação dos metadados do banco de dados e pelo preenchimento das tabelas. Agora você precisará organizar os scripts de uma forma coerente, para que o instalador possa localizá-los. Para permitir que o instalador localize e execute os scripts do banco de dados, é necessário implementa-los de acordo com as diretrizes que definirei mais tarde. Agora, deixe-me explicar rapidamente como deve funcionar um instalador típico de bancos de dados.

Não posso garantir que essa lógica será abrangente, mas a partir da minha experiência, as etapas definidas na Listagem 1 devem ser suficientes para a maioria dos bancos de dados. Alguns desenvolvedores de bancos de dados preferem combinar várias etapas lógicas em uma operação. Por exemplo, alguns podem escrever um script de criação de tabela que contenha definições de restrições assim como um índice, e código de triggers.

 

Listagem 1 Pseudocódigo da lógica de instalação de bancos de dados

Conecta com o SQL Server como administrador

Verifica se o banco de dados específico existe

If o banco de dados não existir

    Cria o banco de dados

    Cria os tipos de dados definidos pelo usuário

    Adiciona os papéis do banco de dados

    Cria os usuário assim como os devidos papéis

    Define as regras

    Constrói o banco de dados com as tabelas e consultas

    Define os índices

    Define os constraints

    Cria os triggers

    Preenche as tabelas com valores iniciais

Else (se o banco de dados existe)

    Verifica a versão do banco de dados

    Se a versão está autalizada

        Executa os scripts repairs (hotfixes)

    Else (se a versão do banco de dados estiver desatualizada)

        Executa os scripts de atualização

    End If

End If

Sempre recompila as stored procedures e UDFs

 

Muitos bancos de dados não exigem todas as etapas (por exemplo, nem todos os bancos de dados usam triggers), enquanto alguns podem exigir operações adicionais, como registrar stored procedures estendidos. Na maioria dos casos, tais diferenças não são essenciais. Apenas quero mostrar operações típicas na ordem em que devem ser executadas. Obviamente, a ordem das etapas é importante, já que não se pode preencher uma tabela antes de criá-la.

Este artigo é acompanhado por um script chamado dbsetup.vbs, que segue a lógica definida na Figura 2. Usarei este script como exemplo, para ilustrar o funcionamento de um instalador de banco de dados. Você pode usar esse script exatamente como está, modificar sua lógica e configurações padrão para atender às exigências do seu banco de dados ou usá-lo apenas como referência ao criar seu próprio instalador. Agora vamos analisar o funcionamento do script.

O Dbsetup pode ser executado a partir da linha de comandos. O script espera que sejam passados certos parâmetros, como as informações de conexão (nome do servidor, instância e porta) e os dados de autenticação (suporta tanto autenticação do SQL Server como autenticação integrada do Windows). Para ver a descrição dos parâmetros de linha de comando suportados, execute o script com a switch /?. Quando o dbsetup for iniciado, conectará ao servidor de banco de dados especificado e verificará o status de um determinado banco de dados. Para fazer isso, o script verifica primeiro a existência de um banco de dados e, dependendo do resultado, é executado em um dos três modos principais: criação, upgrade ou reparo (há três modos adicionais que não descreverei, mas você pode ler sobre eles nos comentários do script). Se o banco de dados não existir, o instalador criará e montará todos os metadados necessários, como tabelas, visualizadores, stored procedures e assim por diante. Se já existir, o script verificará sua versão. Se a versão estiver atualizada, o script aplicará o script de reparo (hotfixes). Se o banco de dados estiver desatualizado, o script fará o upgrade. Em ambos os casos, o script recompilará tanto as stored procedures como as funções definidas pelo usuário (UDFs).

 

Verificando a existência de bancos de dados e executando consultas

Você pode verificar a existência de um banco de dados, assim como realizar consultas e operações em outros bancos de dados, usando diversas tecnologias, como COM-based SQL Data Management Objects (SQL-DMO) ou ADO. A vantagem do SQL-DMO é que expõe os recursos de gerenciamento de bancos de dados de uma maneira mais lógica e baseada em objetos e não exige conhecimento do funcionamento interno do SQL Serverâ„¢. No SQL Server 2005, atualmente na versão beta, o SQL-DMO foi substituído pela nova tecnologia baseada no .NET chamada SQL Server Management Objects (SMO). Para verificar a existência do banco de dados usando SQL-DMO, o instalador pode obter o conjunto de bancos de dados da instância do SQL Server e procurar um item com o mesmo nome.

A desvantagem do SQL-DMO é que não pode ser instalado por padrão em um sistema sem o SQL Server. Isso poderá ser um problema se você tentar instalar o banco de dados em um computador remoto a partir de um sistema que não tiver SQL-DMO. Ao contrário do SQL-DMO, o ADO é distribuído com todas as versões dos Microsoft Data Access Components (MDAC) e estará disponível na maioria dos sistemas. Devido à sua ampla disponibilidade, recomendo o uso do ADO nas operações de bancos de dados, muito embora possa exigir modificações se as alterações nos metadados do sistema (que às vezes acompanham as novas versões do SQL Server) causarem quebras nas consultas ADO. Isto não acontece com muita freqüência, no entanto, mas se acontecer não deverá ser muito difícil de corrigir. Para verificar a existência do banco de dados usando ADO, o instalador executa a consulta T-SQL mostrada no código a seguir:

 

SELECT 1

FROM   master..sysdatabases

WHERE  name = 'databasename'

 

Se a consulta não retornar registros, o instalador considerará que o banco de dados não existe e tentará criá-lo.

 

Criando um banco de dados

A criação do banco de dados poderá depender de parâmetros, como a localização dos dados do banco de dados e dos arquivos de log de transações, que não podem ser conhecidos e codificados durante o projeto. Como o script não pode usar arquivos SQL existentes para a criação de bancos de dados, gerará todas as instruções T-SQL necessárias e as executará de forma programática em tempo de execução (consulte o método CreateDatabase). Se os parâmetros esperados em tempo de execução não forem especificados, o script usará os padrões.

 

Verificando a versão do banco de dados

Embora a existência do banco de dados possa ser verificada por meio de consultas aos metadados do sistema, a obtenção da versão exige alguma criatividade. Como a versão do banco de dados do SQL Server não pode ser determinada da mesma forma que os arquivos binários, você precisará implementar seu próprio esquema de versões. Uma maneira de fazer isso é criar uma stored procedure ou UDF simples que retorne uma string de versão incluída em hardcode. Para tornar esse método genérico, essa stored procedure ou função deverá ter o mesmo nome (como GetVersion) em todos os seus bancos de dados e deverá ser recompilado para retornar uma nova versão durante cada upgrade do banco de dados. Como alternativa, você pode armazenar a string de versão em uma tabela de banco de dados ou determiná-la usando algum outro critério. Apenas verifique se a interface de verificação da versão é coerente, para que o instalador possa usá-la em todos os bancos de dados que suportar. O Dbsetup parte do princípio de que o banco de dados implementa a GetVersion UDF (consulte o método GetDatabaseVersion).

Após obter a versão do banco de dados, o instalador deverá compará-la com a versão atual, mas de onde se obtém a versão atual? A resposta para esta pergunta depende de como o instalador for implementado. Se você o construir como um programa de instalação, como um arquivo MSI, uma instalação do InstallShield ou Wise, a opção lógica para a versão atual será a versão do produto, que é uma propriedade intrínseca tanto dos bancos de dados MSI como dos arquivos de instalação do InstallShield (criadas com o InstallShield Professional 6.x ou anterior). Se o seu instalador de bancos de dados for executado como um aplicativo independente ou utilitário de linha de comandos, você poderá pedir que o usuário informe a versão atual ou passar a versão ao instalador como um parâmetro de linha de comando. O Dbsetup obtém a versão atual da string a partir de um parâmetro de linha de comandos passado com a switch /ver.

Os scripts de criação de bancos de dados devem sempre refletir a versão mais recente do banco de dados. Sempre que o instalador criar um banco de dados, em vez de instalar uma versão mais antiga e depois fazer o upgrade, deverá construir a versão mais recente.

 

Padronizando arquivos e pastas de bancos de dados

Como os projetos de bancos de dados têm tantas semelhanças, não faz sentido reescrever o script ou o aplicativo de instalação para cada banco de dados. Para que seu instalador possa trabalhar com bancos de dados diferentes, ele deve ser capaz de localizar os arquivos dos bancos de dados, entender a ordem em que deverá executá-los e determinar que operação deverá ser realizada em cada arquivo. A única maneira de conseguir isso é padronizar a hierarquia de pastas, a ordem da execução do script, os nomes e as extensões dos arquivos.

Você deve agrupar logicamente todos os scripts usados para criar, atualizar ou reparar os bancos de dados. Isto permitirá que o instalador localize facilmente os scripts corretos para as operações corretas. Embora você possa implementar uma hierarquia alternativa, sugiro que use uma estrutura de pastas como a mostrada na Figura 1, que é o que o dbsetup espera encontrar. A Tabela 2 explica o objetivo de cada pasta e subpasta.

 

image001.gif

Figura 1 Pastas

 

Tabela 2 Objetivo de cada pasta da instalação

Pasta

Objetivo

Create

Agrupa scripts, que são executados somente durante a criação do banco de dados

First

Armazena os scripts, que são executados logo após a criação do banco de dados e antes da definição das tabelas e das visualizações.

Types

Armazena os scripts responsáveis pela criação de tipos de dados definidos pelo usuário

Roles

Armazena os scripts responsáveis pela criação dos papéis dos bancos de dados

Users

Armazena os scripts que criam usuários do banco de dados e os atribui funções adequadas

Rules

Armazena os scripts que definem as regras do banco de dados

Tables

Armazena os scripts de criação de tabelas

Indexes

Armazena os scripts que contêm as definições dos índices

Views

Armazena os scripts de definição de visualização

Triggers

Armazena os scripts usados para construir triggers

Constraints

Armazena os scripts que definem os relacionamentos e limitações de bancos de dados

Data

Armazena os scripts e arquivos de dados para preencher as tabelas

Last

Agrupa scripts, que serão executados depois que as tabelas tiverem sido preenchidas com dados

Common

Agrupa os scripts que são sempre executados (por exemplo, durante a criação, o upgrade e o reparo do banco de dados)

Procedures

Armazena os scripts que contêm o código fonte das stored procedures

Functions

Armazena os scripts que contêm os códigos fonte das funções definidas pelo usuário

Upgrade

Agrupa scripts de upgrade

xx

Armazena scripts de upgrade da versão xx (por exemplo, 1.4.2)

Repair

Armazena hotfixes

 

Tanto os caminhos relativos até as pastas de instalação como a ordem em que as pastas são processadas são incluídas por hardcode nas variáveis globais definidas no início do script. Se você achar que algumas dessas pastas não são necessárias, bastará removê-las das variáveis globais correspondentes. Criar pastas vazias de acordo com a definição do script também funciona. Se o script não localizar as pastas de instalação esperadas, ele emitirá uma mensagem de erro mas continuará o processamento independente disso.

 

Processando arquivos de bancos de dados

Além da hierarquia padrão de pastas que permite que o instalador associe as operações de configuração do banco de dados às pastas, o instalador deve também conhecer a ordem em que deverá executar os scripts dentro de cada pasta. Contudo, nem sempre a ordem de execução do script dentro de uma pasta é importante. Por exemplo, normalmente as stored procedures podem ser compiladas em qualquer ordem. Se uma determinada stored procedure depender de outra, mas for compilada antes dele, o SQL Server emitirá uma mensagem de aviso, mas a compilação será bem sucedida. Se a ordem da execução do script dentro de uma pasta for importante, o método mais direto será adicionar um arquivo de texto, como o usado pelo dbsetup (files.txt), que relacione os nomes dos arquivos de script na ordem em que deverão ser processados.

Se o instalador detectar esse arquivo em uma pasta, processará todos os arquivos relacionados e ignorará todos os demais arquivos. Se o instalador não localizar o arquivo files.txt, partirá do pressuposto de que a ordem de execução não é importante e processará todos os arquivos com extensões suportadas que se encontrarem na pasta; do contrário, processará os arquivos na mesma ordem em que estiverem relacionados no arquivo de texto.

Todos os arquivos devem ser especificados com caminhos relativos até à pastas imediata. Normalmente, isso seria apenas o nome do arquivo, mas os arquivos de outras pastas também poderão ser referenciados. Por exemplo, se um script que preencher uma tabela usar uma stored procedure para inserir os dados, essa stored procedure poderá ser compilada como parte da etapa de preenchimento de dados, já que, por padrão, as stored procedures são compiladas depois que as tabelas tiverem sido preenchidas. Nesse caso, o arquivo de stored procedure poderá ser referenciado com um caminho relativo à pasta Data, como:

 

..\..\Common\Procedures\AddUserAttribute.sql

PopulateUserAttribute.sql

 

Além da ordem de execução, o instalador deve conhecer que tipo de operação deverá realizar em um determinado arquivo. Por exemplo, os scripts que contiverem instruções SQL deverão ser processados de maneira diferente dos arquivos que contiverem grandes quantidades de dados de cópia ou programas executáveis. As extensões dos arquivos podem ajudar a atender a essa exigência. A Tabela 3 especifica como o dbsetup traduz extensões de arquivos em operações.

 

Tabela 3 O dbsetup traduz as extensões de arquivos em operações

Arquivos e extensões

Tipo e/ou objetivo

Engine

.sql, .tsql, .tab, .tbl, .udf, .sp, .trg

Contém instruções T-SQL

SQL Server

.dat, .bcp

Contém tabela ou dados em quantidade para cópia

SQL Server

.fmt

Contém o formato do arquivo de cópia de dados correspondente (opcional, usado implicitamente)

SQL Server

.exe, .bat, .cmd

Programas executáveis

cmd.exe

.vbs, .js, .ws

Arquivos WSH

cscript.exe

files.txt

Define a ordem de execução do script

Instalador

next.txt

Define o rótulo da versão do próximo upgrade

Instalador

todos os demais

Todos os demais arquivos não são processados

N/A

 

A associação dos tipos de arquivo às operações de instalação é bastante simples. No caso dos arquivos que contêm instruções T-SQL, como os que criam tabelas ou stored procedures, o instalador lê os comandos até localizar a instrução GO e executar esses comandos um lote de cada vez, por meio do objeto ADO Command. O mesmo resultado poderia ser obtido por meio da execução de scripts T-SQL, usando a ferramenta de linha de comandos isql.exe, mas o desempenho será baixo. Com o isql.exe a instalação será lenta, porque esse utilitário precisará estabelecer uma conexão com o banco de dados do SQL Server antes de executar cada script e, depois, fechar a conexão após a conclusão da operação. Para bancos de dados menores ou mais simples, é provável que o isql.exe será suficiente, mas para bancos de dados maiores e mais complexos, que vêm com muitos scripts, a execução de instruções SQL com ADO é uma opção muito melhor.

Não é provável que arquivos de programas executáveis, scripts de lote e Windows Script Host (WSH) sejam usados pela maioria dos instaladores de bancos de dados, mas adicionei suporte para esses casos, por precaução. É possível que você possa precisar deles em certas circunstâncias se, por exemplo, o upgrade do seu banco de dados exigir a criptografia de dados privativos, usando uma nova chave de criptografia. Para permitir que esse programa se conecte ao servidor do banco de dados, o dbsetup passa para ele todos os parâmetros de linha de comandos, incluindo as informações de conexão e as credenciais de usuário. O aplicativo invocado é responsável pela recuperação e o processamento desses parâmetros de linha de comandos.

Os arquivos de cópia em massa são um pouco mais complexos do que outros tipos de arquivos, porque podem apresentar diversas opções de processamento. O instalador deve conhecer que configurações de cópia em massa deverá usar com cada arquivo. Algumas das opções mais comuns que o instalador deve entender são o formato dos dados baseados em caracteres, o tratamento dos valores de identidade, a associação dos dados definidos no arquivo com as colunas da tabela e a associação entre arquivos e tabelas.

O Dbsetup implementa a associação arquivo-tabela por meio de convenções de nomes de arquivos. Para fazer com que o instalador saiba que tabela corresponde a que arquivo de cópia em massa, o nome do arquivo deve ser igual ao nome da tabela correspondente. Por exemplo, para preencher a tabela Employee, é preciso nomear o arquivo de cópia em massa como "Employee.bcp" ou "Employee.dat". Este exemplo parte do princípio de que todos os dados baseados em caracteres, presentes no arquivo Employee.bcp, são fornecidos no formato ASCII. Para indicar outros formatos de caracteres, adicione um indicador de codificação para o formato no início da extensão do arquivo. O Dbsetup suporta os seguintes indicadores: "u" para Unicode (como em Employee.ubcp), "n" para Native (como em Employee.nbcp), e "un" para Unicode Native (como em Employee.unbcp). Para indicar que os valores de identidade devem ser inseridos na tabela, a extensão do arquivo deve iniciar com a letra "i" (como em Employee.ibcp ou Employee.iunbcp).

Se o arquivo de cópia em massa exigir uma associação explícita dos dados do arquivo com as colunas da tabela, essa associação poderá ser fornecida por meio de um arquivo correspondente com a extensão .fmt (como Employee.fmt), que deve ser gerado por meio do utilitário BCP ou pelo comando BULK COPY. Com exceção do número máximo de erros que causam a falha da operação, que o dbsetup define em 1 em vez do padrão de 10, usa as configurações padrão para as outras opções de cópia em massa. Se o seu banco de dados exigir um tratamento mais apurado dos arquivos de cópia em massa, você poderá adicionar suas próprias opções.

Embora as exigências de nomeação para os arquivos de cópia em massa sejam bastante rigorosas, outros scripts de bancos de dados não seguem quaisquer regras particulares, desde que mantenham as extensões corretas. Ter um padrão de nomeação poderá ajudar a organizar melhor os arquivos e simplificar a manutenção. Considero úteis as seguintes convenções.

Os scripts SQL responsáveis pela criação e o preenchimento das tabelas e pela compilação das stored procedures e UDFs não devem tratar mais de um objeto (o script de criação de tabela não deve criar mais de uma tabela). Do contrário, será difícil rastrear o que for definido aqui. Os scripts que realizam outras operações, como criar tipos de dados definidos pelo usuário ou construir índices, normalmente contêm instruções SQL curtas que podem ser agrupadas de modo que possam encapsular todas as operações relacionadas em um arquivo ou apenas algumas.

Embora os nomes dos scripts de bancos de dados não tenham importância crucial, com a notável exceção dos arquivos de cópia em massa, dar aos scripts nomes significativos pode se mostrar bastante prático. Primeiro, facilitará bastante a manutenção. Segundo, o instalador poderá usar nomes significativos para oferecer uma resposta melhor. É por isso que os scripts responsáveis pela construção e pelo preenchimento das tabelas e pela compilação de stored procedures e UDFs deverão ser nomeados de acordo com os objetos de metadados que estiverem tratando.

 

Implementando upgrades em bancos de dados

Permitir que o instalador suporte upgrades a partir de diversas versões pode parecer uma exigência complexa, mas na verdade é bastante simples. A idéia é deixar que o instalador escolha a pasta correta que armazenará os scripts de upgrade do banco de dados e, em seguida, executar esses scripts na ordem correta.

Quando você lançar a versão 1.0 do seu aplicativo, a hierarquia de pastas de scripts do banco de dados deverá refletir a estrutura ilustrada na Figura 1 (sem as pastas de upgrade). Quando estiver pronto parar lançar a versão 1.1 do banco de dados, você precisará primeiro modificar todos os arquivos afetados nas pastas de criação do banco de dados, para que sempre reflitam a última versão. Isto cuidará da instalação do novo banco de dados. Para tratar o upgrade da versão 1.0 para a versão 1.1 em sistemas onde a versão anterior já se encontrar instalada, adicione uma nova pasta ao diretório Upgrade e dê a ela o mesmo nome da versão anterior, como "1.0". Essa pasta armazenará os scripts de upgrade da versão 1.0 para a 1.1. As pastas de upgrade devem ser sempre nomeadas com o nome da versão a partir da qual o upgrade for realizado.

Será um pouco mais complicado quando você passar para a versão 1.2, porque até lá o seu instalador deverá ser capaz de realizar dois tipos de upgrade: da versão 1.0 para a 1.2 e da versão 1.1 para a 1.2. Assim como no exemplo anterior, você precisará adicionar uma nova pasta de upgrade ("1.1") para os scripts de upgrade da versão 1.1 para a 1.2.

Há duas opções para os scripts de upgrade a partir da versão 1.0. Uma possibilidade é copiar todos os scripts de upgrade da versão 1.1 para a pasta 1.0 e adicioná-los à ordem de execução do script para que, depois que o instalador executar o upgrade a partir da versão 1.0, o banco de dados estará na versão 1.2. A desvantagem é que você precisará duplicar os scripts em todas as pastas de upgrade, de modo que, quando você chegar à versão 1.9, a pasta de upgrade 1.0 conterá todos os scripts das outras oito pastas (ou sejam, 1.1 a 1.8). Isto poderá ser facilitado se você usar um sistema de controle de fonte, que permita o compartilhamento de arquivos entre as pastas, mas se ficar muito complexo, como provavelmente ficará, você poderá tentar usar um método alternativo. Em vez de converter o banco de dados de qualquer versão mais antiga para a versão atual em uma só etapa, o instalador pode realizar upgrades escalonados, como da versão 1.0 para a 1.1 e, em seguida, da 1.1 para a 1.2, e assim por diante, até que chegar à versão atual, e é exatamente assim que o dbsetup funciona.

O Dbsetup parte do princípio de que todas as pastas de upgrade contêm um arquivo de texto (chamado "next.txt"), que contém a versão para a qual o banco de dados está sofrendo o upgrade. Antes de executar o primeiro script de uma pasta de upgrade, o dbsetup lê a string de versão do arquivo next.txt e a compara com a versão corrente da instalação. Se ambas as versões combinarem, ou se a versão vinda do arquivo de texto for mais recente do que a especificada pela instalação, o instalador partirá do princípio de que esta é a última etapa do upgrade; do contrário, após fazer o upgrade dessa versão, o dbsetup passará para a pasta de upgrade correspondente à versão nova mas ainda desatualizada e repetirá as mesmas etapas, até chegar à versão corrente.

O Dbsetup verifica também se a versão do próximo upgrade é mais recente do que a versão do upgrade anterior, para evitar condições de loop infinito. Depois de concluir um upgrade para cada versão, o dbsetup recompilará a função GetVersion para retornar a string da nova versão (para o caso de o programa de instalação encontrar um erro e abortar durante a próxima etapa de upgrade). O instalador fará cada etapa do upgrade dentro de um transação, mas esse comportamento pode ser substituído. Para ver como esse recurso é implementado, dê uma olhada na função UpgradeDatabase nos arquivos para download.

 

Reparo de bancos de dados

Poderá acontecer de você enviar o instalador de bancos de dados para uma equipe de testes ou para engenheiros de produção, e descobrir mais tarde que há um bug que precisa ser corrigido. Se a versão do banco de dados não rastrear números de build ou de revisão (e não acho que deveria, porque tornará a hierarquia de pastas de upgrade difícil de manter), não fará sentido atualizar o banco de dados. Nesse caso, você deverá aplicar um script de reparo ou um hotfix. Os scripts de reparo de bancos de dados são executados somente quando o instalador detecta que a versão do banco de dados está atualizada. Eles não devem ser chamados durante a criação inicial de bancos de dados ou de upgrades de uma versão anterior, porque todas as versões feitas pelos scripts de reparo devem ser duplicadas também na criação e no upgrade dos scripts. Como os scripts de reparo podem ser aplicados repetidas vezes, devem suportar redundância. Em outras palavras, todos os scripts de reparo não devem falhar ou provocar o corrompimento dos metadados ou dos dados, se forem executados mais de uma vez.

Seguindo a mesma idéia, lembre-se que você deve sempre fazer um backup do banco de dados antes de fazer uma instalação, para o caso de algo sair errado. Na verdade, você pode pensar na possibilidade de modificar os scripts de instalação para fazer os backups automaticamente, embora no caso de um banco de dados grande seja melhor deixar essa operação para o administrador tratá-la em separado.

 

Execução de operações comuns

Como você deve ter notado, as stored procedures e as UDFs são sempre recompiladas. Na verdade, não é preciso recompilar todos durante upgrades ou reparos mas, em vez disso, definir explicitamene os procedimentos que devem ser alterados em relação à versão anterior. Contudo, isso introduzirá uma possibilidade de erro, então, por que complicar sem necessidade? Como as stored procedures e as UDFs não afetam os dados, você poderá recompilá-los quantas vezes quiser sem qualquer efeito negativo. Não se esqueça de obter as versões mais recentes dos arquivos fonte do sistema de controle de fonte, antes de fazer o build. De modo a recompilar todas as stored procedures em uma pasta, sem executar a instalação, você deve experimentar o script isqlexec.bat que acompanha este artigo.

 

Programação defensiva

É uma boa idéia implementar os scripts SQL de tal maneira que possam ser executados repetidas vezes. Por exemplo, um script que crie um procedimento armazenado deve primeiro verificar se essa stored procedure já existe e, se for o caso, o script deverá livrar-se da stored procedure antes de criá-la. Os scripts de criação de tabelas não devem fazer nada se as tabelas que tentarem criar já estiverem definidas. Para evitar entradas em duplicidade, os scripts de preenchimento de tabelas (com exceção dos scripts de inserção em massa) devem verificar a existência de registros antes de inseri-los nas tabelas.

O Query Analyzer oferece diversos modelos para montar stored procedures e outros objetos de metadados. Basta selecionar a opção de menu File | New (que não deve ser confundida com o botão da barra de ferramentas New Query, que apresenta o mesmo ícone), e você verá todas as categorias de modelos disponíveis. Normalmente cada categoria de modelo oferece diversas versões de um modelo.

Um programa de instalação robusto deve desfazer as alterações caso ocorra algum erro durante a instalação. A mesma regra deve ser aplicada aos instaladores de bancos de dados, de modo que se o instalador encontrar um erro durante a instalação inicial do banco de dados, cancelará a instalação e descartará o banco de dados, que é como o dbsetup funciona. Os erros gerados durante os upgrades podem causar mais danos do que os erros ocorridos durante a instalação original, porque podem deixar o banco de dados em um es      tado inconsistente. Se ocorrer um erro durante o upgrade do banco de dados, o instalador não poderá simplesmente descartar o banco de dados. Se isso for feito causará a perda de dados, já que o banco de dados já estava lá antes de o instalador ser executado. Uma opção ideal seria voltar todas as operações executadas durante o upgrade, antes da ocorrência do erro, mas isso exigirá que todas as operações de upgrade sejam executadas dentro de uma transação. Dependendo do número de etapas do upgrade e dos dados afetados, isso poderá causar muita sobrecarga. Uma alternativa menos robusta mas razoavelmente aceitável seria fazer o backup do banco de dados antes do upgrade (o que deve ser feito, de qualquer forma) e, se o upgrade falhar, simplesmente restaurar o banco de dados a partir do backup. Como os erros de upgrade de bancos de dados ocorrem principalmente devido a erros de programação, e devem ser detectados durante os testes, esta opção funciona na maioria do casos. Por padrão, o dbsetup usa upgrades transacionais, mas esse comportamento pode ser desativado pela opção de linha de comando /notran.

 

Um exemplo prático

Para ilustrar o uso das técnicas de instalação de bancos de dados que acabei de apresentar, deixe-me explicar como as empreguei para implementar um programa de instalação para um dos aplicativos corporativos que minha equipe suporta atualmente. O instalador trata diversos aplicativos, que fazem parte de um único produto. Esses aplicativos incluem doze serviços do Windows, um filtro ISAPI, três websites, sete objetos COM, quatro ferramentas GUI, diversos componentes misturados, como DLLs compartilhadas, e quatro bancos de dados. O banco de dados maior consiste em aproximadamente 70 tabelas e cerca de 400 stored procedures. Devido ao tamanho do produto e à complexidade dos métodos anteriores de instalação, o principal objetivo do novo instalador era simplificar o processo, tornando-o mais rápido e livre de problemas. O procedimento de instalação do banco de dados segue as mesmas etapas que o dbsetup, mas ao contrário deste último, ele é implementado como parte de um programa de instalação maior, escrito no InstallShield Professional 6.x.

Agora vamos analisar o funcionamento do instalador. Primeiro, o usuário especifica que banco de dados irá instalar, selecionando uma caixa de opção correspondente na árvore componente (ou a árvore "feature" na linguagem do MSI) da caixa de diálogo de instalação personalizada. Quando o programa de instalação detectar que um banco de dados deverá ser instalado, copiará todos os scripts do banco de dados para um diretório temporário, criando uma hierarquia de pastas semelhante à que descrevi anteriormente. O instalador usa um diretório independente para armazenar os arquivos de cada banco de dados selecionado. Como os scripts de banco de dados são necessários somente durante o processo de instalação, o instalador os exclui da pasta temporária no final da instalação.

Depois que os arquivos do banco de dados forem copiados para uma pasta temporária, o programa de instalação disparará um evento específico da instalação, definido pelo programa de instalação para esse banco de dados. Quando o evento for acionado, chamará um handler de evento implementado no InstallScript. O script contém oito handlers de evento específicos de bancos de dados: um para instalar e outro para desinstalar cada um dos quatro bancos de dados. Todos os eventos de instalação definidos para cada um dos quatro bancos de dados seguem a mesma lógica. A única diferença é que usam pastas raiz distintas, contendo scripts de bancos de dados como pontos de partida. Quando for chamado um handler de evento de instalação de banco de dados, ele primeiro verificará as variáveis globais para ver se o InstallScript já conta com uma conexão com o banco de dados. Se não houver, o InstallScript pedirá ao usuário para definir o nome do servidor do banco de dados e as credenciais do administrador do SQL. Se o script não conseguir conectar-se ao servidor de SQL com as credenciais especificadas e as informações de conexão, pedirá ao usuário que as insira novamente. Com a conexão bem-sucedida, o InstallScript salva o objeto da conexão em uma variável global, para o caso de um outro banco de dados ser selecionado para uma operação de instalação.

Após conectar-se com o servidor do banco de dados, o instalador verificará a existência do banco de dados especificado. Se não existir, pedirá ao usuário que selecione o local do banco de dados e dos arquivos de log de transação, e criará o dispositivo do banco de dados usando valores padrão razoáveis para outros parâmetros do banco de dados, como o crescimento do banco de dados. Decidi não pedir ao usuário qualquer informação além da localização dos arquivos do banco de dados, porque outras configurações do banco de dados podem ser facilmente alteradas pelo administrador depois que o banco de dados tiver sido instalado, e poderão ser ajustados posteriormente, de qualquer forma. O instalador dá ao usuário uma opção de deixar os bancos de dados intactos durante a desinstalação, ou descartá-los do sistema. Ao realizar a instalação, o upgrade ou o reparo, o programa de instalação segue as mesmas etapas que descrevi aqui.

Certas etapas da instalação, como a compilação de 400 stored procedures podem demorar um pouco. Para informar o usuário sobre o andamento da instalação, o instalador apresenta uma mensagem de status para cada operação realizada, como "Compilando a stored procedure GetUserList…" Nomear os scripts SQL com os mesmos nomes dos objetos de metadados que criarem me permitirá apresentar informações corretas nas mensagens de informação. Se ocorrer um erro durante a execução dos scripts SQL, o instalador apresentará uma caixa de diálogo com uma mensagem de erro detalhada, um trecho do lote T-SQL malsucedido, o nome do arquivo e a linha onde o erro ocorreu. Quaisquer erros que ocorrerem durante a instalação do SQL fará com que o processo de instalação seja cancelado.

Uma operação que não mencionei anteriormente, que este instalador deverá executar, é registrar uma stored procedure estendida. Essa operação é mais complicada do que outras etapas da instalação, porque requer a coordenação entre o instalador e o script de registro. Vamos analisar agora o funcionamento do registro da stored procedure estendida.

Primeiramente, uma DLL que implementar a stored procedure estendida deverá ser copiada para o host do SQL Server. O registro propriamente dito pode ser executado em um script SQL estático por meio da stored procedure sp_addextendedproc, mas o principal problema com este método é que a instrução T-SQL que chama o sp_addextendedproc deve especificar o caminho completo até o arquivo da DLL, que pode se alterar dependendo do ambiente em que o banco de dados for instalado (geralmente os desenvolvedores de bancos de dados não sabem onde os arquivos do aplicativo serão colocados nos ambientes de teste, pré-produção ou produção). Para resolver esse problema, em vez de especificar o caminho completo até o arquivo da DLL, referenciei o script pelo nome, como mostrado aqui:

 

exec sp_addextendedproc 'xp_myproc', 'XPMyProc.dll'

 

Se o arquivo da DLL não contiver a informação completa do caminho, o SQL Server partirá do princípio de que encontra-se na pasta BINN do diretório de programa do SQL Server. A próxima etapa será colocar esse arquivo lá; isso é uma tarefa para o InstallShield. Eu associei a DLL do procedimento armazenado estendido aos componentes do banco de dados, usando um grupo de arquivos especial. Ao contrário do grupo de arquivos comuns do banco de dados que contém os scripts de instalação que serão copiados para uma pasta temporária e removidos no fim da instalação, o grupo de arquivos do procedimento armazenado estendido não será excluído. O programa de instalação associa esse arquivo à pasta BINN subfolder de um diretório, cuja localização é determinada no momento da instalação, utilizando o valor de SQLPath sob a chave de registro HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\Setup. Essa chave armazena o caminho até à pasta raiz dos arquivos de programa do SQL Server. Como a capacidade de instalar stored procedures estendidas baseia-se na tecnologia da instalação, não me importei em implementá-la no dbsetup.

Basear o instalador na tecnologia mais antiga do InstallShield proporcionou diversas vantagens importantes. Primeiramente, como o InstallScript oferece todos os recursos necessários para executar as operações relevantes para a instalação do banco de dados, como chamar objetos COM, obter informações de erro, pedir informações ao usuário e exibir caixas de diálogo Selecionar arquivo ou pasta, me foi possível implementar todas as operações da instalação do banco em um projeto do InstallShield, e não precisei manter quaisquer arquivos de instalação ou aplicativos de ajuda adicionais. Outra vantagem foi ser capaz de controlar o processo de instalação a partir de uma única fonte. Ter todas as operações definidas em alguns arquivos do InstallScript files dentro do mesmo projeto, onde todas as funções e handlers de eventos pudessem acessar facilmente variáveis globais e configurações de instalação, ajudou a reduzir o trabalho de programação. Isto também contribuiu para uma melhor experiência do usuário. Como o instalador informa o usuário sobre o andamento do processo na mesma caixa de diálogo que o status da instalação, o processo parece mais simples do que seria se o instalador fosse projetado para iniciar algumas operações em janelas pop-up irritantes.

Outra vantagem da instalação do InstallShield é a facilidade de implementação de upgrades. O instalador usa o mesmo código de produto, permitindo que todos os upgrades sejam realizados sem que o usuário tenha que desinstalar e reinstalar o aplicativo. Não é preciso que o usuário memorize quaisquer parâmetros de linha de comandos para iniciar o programa de instalação no modo de upgrade ou reparo. Depois que o usuário der um duplo clique no arquivo setup.exe, o InstallShield detectará o modo em que precisará ser executado e não confundirá o usuário com mensagens de erro reclamando que o produto já está instalado.

 

Conclusão

Embora o método geral que descrevi e o dbsetup de exemplo que acompanha este artigo devem ser suficientes para a maioria das instalações de bancos de dados, há algumas limitações de que você deve estar ciente, uma das quais sendo a incapacidade de criar arquivos múltiplos de dados ou de log de transações, durante a criação de bancos de dados. Somente as configurações padrão para as opções de criação da base de dados são suportadas, como o tamanho máximo, o crescimento dos arquivos de dados ou de logs de transações e operações de cópia em massa.

Além disso, não há suporte a XML, o que é uma pena, já que provavelmente o XML deverá ganhar maior popularidade com o lançamento do SQL Server 2005. Finalmente, uma pessoa que executar a instalação deverá certificar-se de que não há qualquer aplicativo ou usuário conectado ao banco de dados durante a instalação. Contudo, essas limitações não deverão ser difíceis de resolver.

Se você quiser evitar ter que lidar com problemas de instalação de bancos de dados, considere a possibilidade de implementar um instalador genérico. Eliminar operações manuais do procedimento de instalação do banco de dados ajudará a evitar erros e a melhorar a produtividade. Se o banco de dados exigir um método não ortodoxo, talvez seja preciso você passar um pouco mais de tempo implementando o programa de instalação, mas a boa notícia é que se você fizer certo uma vez, poderá reutilizá-lo em outros projetos.

 

Download disponível em www.neoficio.com.br/msdn CustomDatabaseInstaller.exe (135KB)