Migrando Logins – SQL Server

Quando o assunto é ‘migração’, muitas possibilidades, mudanças e acima de tudo, problemas, surgem na cabeça de um DBA. Montar um banco do zero não é nada comparado ao processo de migrar um banco em funcionamento, com diversos processos executando e aplicações apontando.

Um dos grandes problemas que encontramos no SQL Server é justamente a migração de logins/senhas de usuários. E nesses problemas e dores de cabeça, encontramos a solução, vindo direto da microsoft, extremamente funcional!

Trata-se de uma procedure, que faz o link entre usuário e login, e caso o login não exista, ele trata de cria-lo, já com o hash que existia antes. Assim, ao migrar um database de servidor, ou criar um ambiente de testes, você não terá problemas em migrar os users. Segue a proc abaixo!

USE master
GO
IF OBJECT_ID (‘sp_hexadecimal’) IS NOT NULL
DROP PROCEDURE sp_hexadecimal
GO
CREATE PROCEDURE sp_hexadecimal
@binvalue varbinary(256),
@hexvalue varchar (514) OUTPUT
AS
DECLARE @charvalue varchar (514)
DECLARE @i int
DECLARE @length int
DECLARE @hexstring char(16)
SELECT @charvalue = ‘0x’
SELECT @i = 1
SELECT @length = DATALENGTH (@binvalue)
SELECT @hexstring = ‘0123456789ABCDEF’
WHILE (@i <= @length)
BEGIN
DECLARE @tempint int
DECLARE @firstint int
DECLARE @secondint int
SELECT @tempint = CONVERT(int, SUBSTRING(@binvalue,@i,1))
SELECT @firstint = FLOOR(@tempint/16)
SELECT @secondint = @tempint – (@firstint*16)
SELECT @charvalue = @charvalue +
SUBSTRING(@hexstring, @firstint+1, 1) +
SUBSTRING(@hexstring, @secondint+1, 1)
SELECT @i = @i + 1
END
SELECT @hexvalue = @charvalue
GOIF OBJECT_ID (‘sp_help_revlogin’) IS NOT NULL
DROP PROCEDURE sp_help_revlogin
GO
CREATE PROCEDURE sp_help_revlogin @login_name sysname = NULL
AS
DECLARE @name sysname
DECLARE @type varchar (1)
DECLARE @hasaccess int
DECLARE @denylogin int
DECLARE @is_disabled int
DECLARE @PWD_varbinary  varbinary (256)
DECLARE @PWD_string  varchar (514)
DECLARE @SID_varbinary varbinary (85)
DECLARE @SID_string varchar (514)
DECLARE @tmpstr  varchar (1024)
DECLARE @is_policy_checked varchar (3)
DECLARE @is_expiration_checked varchar (3)
DECLARE @defaultdb sysnameIF (@login_name IS NULL)
DECLARE login_curs CURSOR FORSELECT p.sid, p.name, p.type, p.is_disabled, p.default_database_name, l.hasaccess, l.denylogin FROM 
sys.server_principalsLEFT JOIN sys.syslogins l
ON ( l.name = p.name ) WHERE p.type IN ( ‘S’, ‘G’, ‘U’ ) AND p.name <> ‘sa’
ELSE

DECLARE login_curs CURSOR FOR

SELECT p.sid, p.name, p.type, p.is_disabled, p.default_database_name, l.hasaccess, l.denylogin FROM
sys.server_principalsLEFT JOIN sys.syslogins l
ON ( l.name = p.name ) WHERE p.type IN ( ‘S’, ‘G’, ‘U’ ) AND p.name = @login_name
OPEN login_curs

FETCH NEXT FROM login_curs INTO @SID_varbinary, @name, @type, @is_disabled, @defaultdb, @hasaccess, @denylogin
IF (@@fetch_status = -1)
BEGIN
  PRINT ‘No login(s) found.’
CLOSE login_curs
DEALLOCATE login_curs
RETURN -1
END
SET @tmpstr = ‘/* sp_help_revlogin script ‘
PRINT @tmpstr
SET @tmpstr = ‘** Generated ‘ + CONVERT (varchar, GETDATE()) + ‘ on ‘ + @@SERVERNAME + ‘ */’
PRINT @tmpstr
PRINT
WHILE (@@fetch_status <> -1)
BEGIN
IF (@@fetch_status <> -2)
BEGIN
PRINT
SET @tmpstr = ‘– Login: ‘ + @name
PRINT @tmpstr
IF (@type IN ( ‘G’, ‘U’))
BEGIN — NT authenticated account/group

SET @tmpstr = ‘CREATE LOGIN ‘ + QUOTENAME( @name ) + ‘ FROM WINDOWS WITH DEFAULT_DATABASE = [‘ + @defaultdb + ‘]’
END
ELSE BEGIN — SQL Server authentication
— obtain password and sid
SET @PWD_varbinary = CAST( LOGINPROPERTY( @name, ‘PasswordHash’ ) AS varbinary (256) )
EXEC sp_hexadecimal @PWD_varbinary, @PWD_string OUT
EXEC sp_hexadecimal @SID_varbinary,@SID_string OUT

— obtain password policy state
SELECT @is_policy_checked = CASE is_policy_checked WHEN 1 THEN ‘ON’ WHEN 0 THEN ‘OFF’ ELSE NULL END FROM sys.sql_logins WHERE name = @name
SELECT @is_expiration_checked = CASE is_expiration_checked WHEN 1 THEN ‘ON’ WHEN 0 THEN ‘OFF’ ELSE NULL END FROM sys.sql_logins WHERE name = @name

SET @tmpstr = ‘CREATE LOGIN ‘ + QUOTENAME( @name ) + ‘ WITH PASSWORD = ‘ + @PWD_string + ‘ HASHED, SID = ‘ + @SID_string + ‘, DEFAULT_DATABASE = [‘ + @defaultdb + ‘]’

IF ( @is_policy_checked IS NOT NULL )
BEGIN
SET @tmpstr = @tmpstr + ‘, CHECK_POLICY = ‘ + @is_policy_checked
END
IF ( @is_expiration_checked IS NOT NULL )
BEGIN
SET @tmpstr = @tmpstr + ‘, CHECK_EXPIRATION = ‘ + @is_expiration_checked
END
END
IF (@denylogin = 1)
BEGIN — login is denied a ccess
SET @tmpstr = @tmpstr + ‘; DENY CONNECT SQL TO ‘ + QUOTENAME( @name )
END
ELSE IF (@hasaccess = 0)
BEGIN  — login exists but does not have access
SET @tmpstr = @tmpstr + ‘; REVOKE CONNECT SQL TO ‘ + QUOTENAME( @name )
END
IF (@is_disabled = 1)
BEGIN — login is disabled
SET @tmpstr = @tmpstr + ‘; ALTER LOGIN ‘ + QUOTENAME( @name ) + ‘ DISABLE’
END
PRINT @tmpstr
END

FETCH NEXT FROM login_curs INTO @SID_varbinary, @name, @type, @is_disabled, @defaultdb, @hasaccess, @denylogin
END
CLOSE login_curs
DEALLOCATE login_curs
RETURN 0
GO

—- EXECUTAR —-
EXEC sp_help_revlogin
Espero ter ajudado!
Até mais pessoal =)

Backup apontando para outro servidor – SQL Server

Já falamos de backup, em SQL Server aqui , porém, recebi a pouco um e-mail sobre uma dúvida ao qual não foi colocado no post: como apontar o backup para um outro servidor?
Na verdade não tem segredo, são alguns detalhes que vão ajudar a fazer o backup apontar para outro servidor. A parte mais importante é: No servidor de destino (onde o backup será armazenado), deve ter o disco compartilhado, e preferencialmente uma pasta compartilhada. Aqui, nosssa pasta compartilhada será a bkp_databases, meu servidor ficticio de backup irá responder pelo ip 192.168.50.13 , sendo assim, nosso script de backup ficará:

BACKUP DATABASE [database]
TO DISK = N’\\192.168.50.13\bkp_databases\nome_do_arquivo.bkp’
WITH NOINIT, NOUNLOAD,
Name = N’meu backup’
COMPRESSION, NOSKIP,
STATS = 10, NOFORMAT
GO

E aqui o detalhe importante é: quando vamos direcionar o backup, colocamos no script o clássico \\ip . Note que eu não estou apontando a um disco e sim a uma pasta, pois eu deixei ela compartilhada. Caso tenha um disco compartilhado, ele pode ser adicionado no local da pasta.

See you amiguinhos.

Convertendo Engine de uma tabela – MySQL

Como já explicamos aqui no blog, o mysql possui vários tipos de engines para suas tabelas, cada qual para uma função e que aceita algumas funções que outras não. Por isso, as vezes ao criarmos uma tabela, não definimos sua engine e ela fica no padrão do mysql. Porém, as vezes a engine padrão não é a melhor para a utilização do bd, e precisamos altera-las. A idéia é explicar rapidamente, 2 modos de alterar a engine das tabelas. Um exeplo via Linux, restaurando a tabela, e o outro dando um alter direto no banco de dados.
O comando via MySQL é simples, um alter na engine resolve o problema, como no exemplo abaixo, alterando a engine da tabela ‘posts_dba’ para a engine InnoDB:

ALTER TABLE `blog`.`posts_dba`ENGINE = InnoDB;

O problema desta alteração: versões de mysql 5.0 não aceitam o alter table engine. Para isso, temos um outro modo de fazer: via backup/restore. No meu exemplo, estou alterando TODA a base para engine innoDB, ou seja, todas as tabelas. Porém, você pode executar backup de apenas uma tabela e restaurar utilizando o comando abaixo (comando de restore apenas):

[root@localhost ~]$ zcat blog.sql.gz |sed ‘s/ENGINE=MyISAM/ENGINE=Innodb/g’ | mysql -u root -p blog

Com isso, no restore, convertemos a engine de myisan para innodb.

Espero ter ajudado!
Até mais pessoal =)

Descobrindo Definer de Objetos – MySQL

Em alguns posts antigos, como o de erro com o root e delimiter citamos algumas vezes o ‘definer’. Descobri que poucos sabem o que é um definer, sua função e sua importância no mysql.

Definer funciona como um owner de objetos dentro do mysql. Ao criar alguns objetos, como triggers e views, um definer deve ser especificado, ou, será criado com o seu próprio user.
Por ‘boas práticas’ recomendamos que o definer dos objetos seja sempre o ‘root’@’localhost’. Por que? O root é um user ao qual você não vai excluir do MySQL, ao contrário do user daquele DBA que hoje está na sua empresa e amanhã sai. E ele sempre terá acesso local no MySQL. Uma vez o user sendo Definer de um objeto, ele não pode deixar de existir no banco. Se deixar de existir, o objeto para de funcionar. O Mysql dá um erro ao qual você nunca irá entender, e ao excluir o user, não é retornado nenhum aviso quanto aos seus definers.

Por isso, se atente aos seus objetos e na criação dos mesmos, trabalhe com padrão e tenha segurança.
Para descobrir definers de objetos, trabalhe na tabela ‘information_schema‘, onde você consegue as informações dos objetos.
Abaixo, exemplos de select para descobrir os definers.

SELECT `specific_name`, `routine_schema`, `routine_name`, `routine_type`, `definer` FROM `information_schema`.`routines` WHERE `definer` = ‘meu_user’;

SELECT `trigger_catalog`, `trigger_schema`, `trigger_name`, `definer` FROM `information_schema`.`triggers` WHERE`definer` = ‘meu_user’;

SELECT `table_catalog`, `table_schema`, `table_name`, `definer` FROM `information_schema`.`views` WHERE`definer` = ‘meu_user’;

Espero ter ajudado!
Até mais pessoal =)

Banco sem arquivo de data e log

Pessoal,

Mais um post da categoria “me descabelei mas resolvi!“, relatando um chamado do dia-a-dia, vou relatar um rescente problemas que tivemos em um servidor de BI.

Cenário -> SQLServer 2008 R2 / Windows Server 2008
Resumo do Ambiente -> BI/Alto volume de dados/Grandes databases direto no Storage

Problema -> Dois databases (os principais do sistema de BI) estavam inacessíveis. Ao entrarmos pelo Management Studio e visualizarmos os databases, os mesmos estavam online, porém, não era possível abri-los.
Ao tentar acessar o properties/files o mesmo estava inacessível.
Ao executar um:
sp_helpdb ‘database_BI’
tinhamos o retorno do nome dos seus arquivos de data e log, porém, sem um informações sobre os mesmos.

Análise -> Com isso, pesquisamos em diversos locais, como um database poderia perder refencia a seus arquivos, e localizamos uma referencia a este problema.

Solução -> Logado no Management Studio, colocar o database como offline (Tasks…/Take Offlinerezar 2 pai nossos, aguardar 10s,  colocar o database como online (Tasks…/ Bring Online) Neste momento, o database pode demorar um tempo para subir, pois o mesmo está buscando a referência aos arquivos. Ao subir, o mesmo se tornou acessível novamente.

Riscos -> Caso algum arquivo estivesse corrompido, o risco de o database não subir era altíssimo (de 150% pra mais). Por isso, vale reforçar que esta medida foi realizada após muita análise e nenhuma solução funcionar. Onde ‘já não tinhamos mais o que fazer’, apontamos para esta ação.

Causa -> Como dito no início, este servidor é de BI, e tem discos na barriga e no Storage. Os discos que se tornaram indisponíveis foram os do Storage. Posteriormente descobrimos que na noite anterior, houve um problema na infraestrutura ao qual indisponibilizou o Storage. O banco tentou visualizar o caminho, não localizou, com isso perdeu a referência.

Pessoal, vale reforçar que a ação de colocar o database offline foi tomada depois de muitas análises. Ao se deparar com um problema semelhante, antes de mais nada, verifique as informações do servidor para localizar algum LOG de erro de discos ou processos. Verifique também o errorlog do SQL Server, onde pode apontar algum problema lógico, que possa impedir o banco de subir e tenha outra solução!

Espero ter ajudado!
Até mais ! =)