Recuperando dados deletados do SQL Server sem Backup Full – Parte 5

Post 5/6. Este post é parte da série: Recuperando dados deletados sem Backup Full
Tempo de leitura estimado: 4 minutos

Olá! Hoje, dando continuidade a nossa série, vou responder a pergunta do último post: E se nos dados que estou recuperando tinham valores NULL?

E a resposta é muito simples: Isso está junto no meio desse binário todo! Como você estudou sobre a anatomia de um registro , com os vários links que eu coloquei nos posts anteriores (né?), então aprendeu que existe um negócio chamado NULL bitmap. Basicamente é uma sequencia de bytes que indicam quais colunas tem o valor nulo ou não. Recorra aos links que coloquei anteriormente para entender com mais detalhes sobre ele.

Sabendo isso, tudo que precisamos é identificar o NULL bitmap no meio desse binário todo, de cada registro, e checar se a coluna está nula ou não. Identificar o NULL bitmap envolve, além de conhecer a anatomia do registro, um pouco de mágica matemática:

Este trecho envolve basicamente achar alguns metadados que existem no registro (Veja o Post do Paul Randal) para encontrar o resto das informações. Primeiro, eu preciso encontrar os bytes que indicam a quantidade de colunas, por dois motivos:

  1. Achando a posição deles eu sei onde começa o NULL bitmap
  2. O tamanho do NULL bitmap, isto é, quantidade de bytes que formam ele, depende da quantidade de colunas
    A cada 8 colunas, ele precisa de um byte adicional. Por exemplo, uma tabela com 8 colunas, precisa de um NULL bitmap com 1 byte (8 bits, 1 pra cada coluna). Já com 10 colunas, ele vai gastar 2 bytes (16 bits, sendo apenas 10 usados). Com 16 colunas, continuamos com os 2 bytes (agora usando todos os 16 bits) e por aí vai…
Mas pera aí Rodrigo ✋
Se toda linha sempre tem a mesma quantidade de colunas, porque o SQL Server armazena esse dado para cada linha?

Pois é… corre elá nas fontes sobre a anatomia do registro e você entender os porquês disso…

Por essas razões, eu precisei gerar a coluna chamada ColCount, que me indica a quantidade de colunas. Ela começa no byte 27 porque a última coluna de tipo fixo é a DataCadastro, que começa no byte 19 e usa 8 bytes… Os bytes dela são do 19 ao 26… A quantidade de colunas sempre gasta 2 bytes (Neste caso, bytes 27 e 28).

Na subquery acima, a coluna NullBmp contém todo o NULL bitmap! Novamente, estou usando a função SUBSTRING com a coluna Registro ([RowLog Contents 0] de fn_dump_dblog) para extrair os bytes que compõe o NULL bitmap. Neste caso, o NULL bitmap começa no byte 29. E Pronto (como tenho 7 colunas, preciso de apenas 1 byte)! Agora que tenho o NULL bitmap completo, é so checar cada bit dele para saber se a respectiva coluna é nulo ou não:

  • O primeiro bit (o bit 0), indica se a primeira coluna é ou não nula (neste caso sempre vai ser zero, pois é a coluna Id, que é chave primária)
  • O bit 1 controla a da segunda
  • O bit 2 da coluna 3 e por aí vai.
Demonstração do NULL bitmap

No exemplo, o null bitmap possui o valor hexadecimal 48, e convertendo pra binário é 0100 1000. Começando do bit mais a direita, conseguimos mapear o NULL de cada coluna!

 

De posse do NULL bitmap, testar o valor de um binário no SQL Server, em uma posição específica, requer apenas um pouquinho de matemática (e talvez uma pesquisa no stack overflow):

Utilizando mais um pouco de matemática eu consigo testar cada bit do NULL bitmap e montar uma lista de colunas indicando qual se a respectiva coluna era NULL ou não

A lógica é: eu crio uma coluna para cada uma das colunas das tabelas. Basicamente o valor dessa coluna é o valor do bit respectivo no NULL bitmap: 1 indica que é NULL, 0 é o contrário. Eu resolvi colocar numa lista separada para que a expressão no SELECT principal não ficasse monstruosa. Então, lá no SELECT final, eu apenas faço uma condição mais simples: Se o valor da respectiva coluna for 1, então significa que ela é NULL, então retorno o valor NULL. Caso contrário, retorna o valor resultante daquela expressão que extrai o valor original:

Para as colunas que são NOT NULL, eu nem me dou o trabalho de fazer essa verificação, pois o bit sempre será 0, como, por exemplo, é o caso da coluna Id.

Com o NULL bitmap, além de determinar quais colunas tem o valor NULL, você consegue identificar onde iniciam as colunas de tipos variáveis. E no próximo, e último post, vamos entender como lidamos com essas colunas, que, na minha opinião, são as mais chatas de recuperar!

Até lá!

Navegue na série << Recuperando dados deletados do SQL Server sem Backup Full – Parte 4Recuperando dados deletados do SQL Server sem Backup Full – Parte 6 >>
Compartilhe este post!

Leave a Reply