Codificação Base64 em SQL: um guia completo
De vez em quando o banco de dados tem que conversar com o mundo exterior, e o mundo exterior nem sempre fala bytes. Uma API quer o seu logotipo dentro de uma string JSON. Uma exportação de config quer um segredo que caiba numa linha de YAML sem aspas nem barras invertidas. Um script de manutenção quer enviar um arquivo por um sistema que só carrega texto. É nesse momento que os seus dados vestem um figurino de letras, e o nome do figurino é base64.
O formato em si já é coberto na página inicial (64 caracteres imprimíveis, cada quatro deles representando três bytes de entrada, até dois sinais = preenchendo o grupo final), então este artigo pula aquela palestra e vai direto ao trabalho mecânico. Duas coisas para segurar: codificar é a direção em que os dados ficam maiores, então larguras de coluna e limites de pacote sentem cada byte disso, e os codificadores desta família SQL discordam sobre as duas coisas que mais custam para desfazer depois: quais bytes eles leem quando a sua coluna guarda texto, e onde eles colocam as quebras de linha no que escrevem.
A tabela de consulta rápida dos codificadores
Quem está de plantão, o que eles comem e onde quebram a saída. As duas últimas colunas são as que mordem, porque uma string cheia de quebras de linha não convidadas e uma string com um alfabeto diferente são ambas strings base64 perfeitamente válidas que o seu consumidor ainda vai rejeitar:
| Dialeto | A chamada | Tipo de entrada | Quebra em 76? | Opção URL-safe | Desde quando |
|---|---|---|---|---|---|
| MySQL 8.x / MariaDB 10.x | TO_BASE64(str) |
string (conjunto de caracteres se aplica) | sim | nenhuma | MySQL 5.6 (2013) |
| PostgreSQL | encode(bytea, 'base64') |
bytea |
sim, só LF | nenhuma | 7.2 (2002) |
| SQLite (CLI 3.41+) | base64(blob) |
BLOB |
sim, em 72 | nenhuma | 3.41.0 (2023) |
| DuckDB | to_base64(blob) |
BLOB |
não | nenhuma | versões recentes |
| ClickHouse 18.16+ | base64Encode(x) |
qualquer coisa, cast para String | não | base64URLEncode() |
18.16 (2018) |
| SQL Server 2025+ | BASE64_ENCODE(bin [, url_safe]) |
varbinary |
não | segundo argumento | 2025 |
| Oracle | UTL_ENCODE.BASE64_ENCODE(raw) |
RAW |
não | nenhuma | era 9i |
| Snowflake | BASE64_ENCODE(binary) |
BINARY |
não | nenhuma | versões atuais |
Leia a tabela da esquerda para a direita e o trabalho cai em duas decisões. Primeira: como os seus bytes chegam lá; a coluna de tipo de entrada é onde nascem as surpresas de conjunto de caracteres, porque "o mesmo texto" é bytes diferentes sob colações diferentes. Segunda: o que sai do outro lado; a coluna de quebra decide se o resultado é uma linha única e plana ou um poema com uma quebra de linha a cada 76 caracteres, e a coluna URL-safe decide se o resultado pode até entrar num link.
Primeiro, decida quais bytes você quer dizer
O codificador empacota bytes, mas a sua coluna costuma guardar letras, e letras só são bytes se você disser qual alfabeto de bytes. O TO_BASE64() do MySQL lê o argumento no conjunto de caracteres da conexão, o que é uma conveniência até que não é: o mesmo 'héllo' viaja como base64 diferentes sob um cliente latin1 e um cliente utf8mb4. Quando você quer dizer os bytes exatos como guardados, congele-os primeiro com um cast binário:
SELECT TO_BASE64('hello') AS from_text;
SELECT TO_BASE64(CAST('héllo' AS BINARY)) AS utf8_bytes;
A segunda linha volta como aMOpbGxv, os dois bytes hex C3 A9 no meio sendo a forma do UTF-8 de grafar é. O PostgreSQL é mais estrito logo de cara: o encode() recusa olhar para qualquer coisa que não seja bytea, então um valor de texto precisa primeiro nomear a sua codificação, enquanto bytes crus podem chegar como literal hex:
SELECT encode(convert_to('héllo', 'UTF8'), 'base64') AS text_as_utf8;
SELECT encode('\x68656c6c6f'::bytea, 'base64') AS raw_bytes;
Todos os outros dialetos têm a sua própria porta da frente para a mesma ideia, e todos se reduzem a "pegue os bytes e então empacote-os":
| Dialeto | De texto para bytes | A chamada do codificador |
|---|---|---|
| T-SQL | CAST('héllo' AS VARBINARY(8000)) via a colação da coluna |
BASE64_ENCODE(bin) |
| Snowflake | TO_BINARY('héllo', 'UTF-8') |
BASE64_ENCODE(binary) |
| Oracle | UTL_RAW.CAST_TO_RAW('héllo') |
UTL_ENCODE.BASE64_ENCODE(raw) |
| DuckDB | encode('héllo') dá um BLOB |
to_base64(blob) |
| SQLite CLI | um literal BLOB como X'68656C6C6F' |
base64(blob) |
A regra prática é a mesma do lado da decodificação: decida o conjunto de caracteres antes de codificar, escreva-o na consulta como literal, e passe um payload acentuado pelo pipeline inteiro antes de confiar na coluna. Um único héllo pega toda colação errada, e não custa nada.
Depois, observe o que sai
Uma vez que os bytes estão empacotados, os codificadores se separam em relação às quebras de linha. Três deles quebram a saída: MySQL e PostgreSQL em 76 caracteres, o hábito de e-mail, e o CLI do SQLite em 72; os demais devolvem uma linha única e plana, não importa quanto cresça. A diferença é fácil de perder e cara de encontrar, porque um campo base64 com quebras de linha ocultas é um campo que quebra um parser JSON no meio do caminho:
SELECT LENGTH(TO_BASE64(REPEAT('x', 300))) AS wrapped,
LENGTH(REPLACE(TO_BASE64(REPEAT('x', 300)), '\n', '')) AS flat;
Trezentos bytes de entrada voltam como 400 caracteres base64, e a mesma chamada mede 405 porque cinco quebras de linha viajaram de carona. A aritmética por trás disso é pequena o bastante para caber na sua cabeça: o tamanho plano é o tamanho da entrada dividido por três, arredondado para cima, vezes quatro. Se o seu codificador quebra, some uma quebra de linha entre as linhas de 76 caracteres, que é o tamanho plano dividido por 76 e arredondado para cima, menos um. Trezentos bytes: 400 planos, 405 quebrados. Cento e onze bytes: 148 planos, 149 quebrados. É assim que, com uma quebra de linha a mais do que o orçamento, uma coluna VARCHAR(500) começa a truncar em silêncio um payload VARCHAR(480).
Duas consequências que valem a pena anotar. Dimensione colunas de texto para o tamanho plano mais uma folga, se o gravador pode quebrar linhas, ou proíba a quebra no gravador e dimensione para o plano. E lembre-se de que o limite contra o qual o seu resultado luta é o limite da string, não dos bytes: no MySQL, o texto quebrado conta contra o max_allowed_packet (padrão de 64 MB no MySQL 8), então uma foto de 50 megabytes codificada em uns 67 megabytes de letras não cabe no pacote padrão, mesmo que o arquivo bruto caberia.
Base64 URL-safe: o alfabeto viajante
A seção 5 do RFC 4648 definiu um segundo alfabeto para base64 porque o original tem dois caracteres com funções na sintaxe de URL. O sinal de mais soma parâmetros de query, a barra separa segmentos de caminho, e o sinal de igual do preenchimento é percent-encoded no momento em que encontra uma query string. A variante URL-safe troca + por - e / por _, e a especificação de JWT por cima disso ainda descarta o preenchimento inteiramente, então um token pode morar num link, num segmento de caminho ou num nome de arquivo sem um único sinal de percent.
Apenas um dialeto desta família traz o interruptor nativamente. O BASE64_ENCODE() do SQL Server 2025 aceita um segundo argumento opcional, e com ele ligado, o resultado usa - e _ e pula o preenchimento:
SELECT BASE64_ENCODE(0xCAFECAFE) AS standard;
SELECT BASE64_ENCODE(0xCAFECAFE, TRUE) AS url_safe;
Os mesmos quatro bytes voltam como yv7K/g== e yv7K_g. O ClickHouse mantém as variantes como funções separadas, e a sua forma URL-safe também descarta o preenchimento:
SELECT base64URLEncode('https://clickhouse.com') AS url_safe;
que chega como aHR0cHM6Ly9jbGlja2hvdXNlLmNvbQ, com os dois sinais de preenchimento da forma padrão aparados. Em todo o resto do mundo, a receita é duas traduções de caracteres e um aparar, e vale a pena escrever uma vez como função de banco de dados porque todo pipeline de token precisa dela. No PostgreSQL ela se lê assim:
SELECT rtrim(replace(replace(
encode(convert_to('https://clickhouse.com', 'UTF8'), 'base64'),
'+', '-'),
'/', '_'),
'=') AS url_safe;
Traduza + para -, traduza / para _, apare o preenchimento no final, pronto. Um aviso para a galera do SQL Server: a saída url_safe não é o que os próprios decodificadores de base64 XML e JSON do servidor esperam, então uma coluna empacotada em forma URL-safe para o mundo exterior não será desempacotada dentro do banco com as funções embutidas. Tenha o público em mente antes de escolher o alfabeto.
JWTs: carimbando tokens a partir do banco
A coisa mais interessante que dá para construir com o codificador é um JSON Web Token, porque um JWT é nada além de três pedaços base64 em fila: um cabeçalho e um payload, ambos objetos JSON empacotados URL-safe sem preenchimento, e uma assinatura calculada sobre os dois primeiros. Quando um job em lote precisa cunhar tokens (semeando um ambiente de teste, regenerando credenciais de API expiradas, construindo um feed de auditoria), a cerimônia inteira cabe numa consulta PostgreSQL se você aceitar o pgcrypto para o HMAC (ative uma vez com CREATE EXTENSION IF NOT EXISTS pgcrypto;):
WITH head AS (
SELECT encode(convert_to('{"alg":"HS256","typ":"JWT"}', 'UTF8'), 'base64') AS h
),
body AS (
SELECT encode(convert_to('{"sub":"1234567890","name":"Dev User"}', 'UTF8'), 'base64') AS p
),
joined AS (
SELECT rtrim(replace(replace(h, '+', '-'), '/', '_'), '=') AS h64u,
rtrim(replace(replace(p, '+', '-'), '/', '_'), '=') AS p64u
FROM head, body
)
SELECT h64u || '.' || p64u || '.' ||
rtrim(replace(replace(
encode(hmac((h64u || '.' || p64u)::bytea, 'sql-secret-key'::bytea, 'sha256'), 'base64'),
'+', '-'),
'/', '_'),
'=') AS token
FROM joined;
Cada passo é um dos movimentos que este artigo já mostrou: empacotar o JSON como base64, remodelá-lo para o alfabeto URL-safe sem preenchimento, depois assinar os dois primeiros pedaços e remodelar a assinatura da mesma forma. Para o JSON acima e o segredo sql-secret-key, o resultado é eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkRldiBVc2VyIn0.7_3VdmM8vH0L2bRBNqXXQXzZIR1l8T4_S4QxPPNBEFM, um token que qualquer inspetor HS256 aceita. Os avisos merecem tanto tempo de antena quanto o truque: ele só cobre algoritmos HMAC (HS256, HS384, HS512), coloca um segredo compartilhado dentro de uma instrução de banco de dados, e foi feito para trabalho em lote e auditoria, não para um serviço de token de produção. O lado da verificação, onde você prova aquele token contra o segredo dele, é um trabalho da camada de aplicação ou da verificação de assinatura do artigo de decodificação.
Imagens e arquivos numa coluna de texto
O motivo mais comum para codificar em SQL é um arquivo que precisa viajar como texto: uma API que embute a imagem em vez de referenciá-la, uma exportação para um sistema que não carrega binário, um script de seed que recria um banco de dados num servidor novo. O DuckDB torna a ida e volta quase trivial, porque lê arquivos para BLOBs através de uma função de tabela que aceita padrões glob, e o codificador achata o que chegar:
SELECT filename, to_base64(content) AS b64
FROM read_blob('/data/pics/*.png');
Uma linha por arquivo, uma string base64 plana por linha, nenhuma quebra para remover, embora os costumeiros um ou dois sinais de preenchimento viajem de carona no final. Grave o resultado numa coluna de texto e as imagens ficam portáveis por qualquer canal que mova texto. Depois tenha a conversa de custo com honestidade: uma foto de 1 megabyte chega como uns 1,33 megabytes de letras, e a partir de então cada scan, cada ordenação e cada entrada de índice pagam esse preço. Se você controla o schema, o desenho melhor é uma coluna BLOB mais uma codificação na fronteira da API, onde só os bytes que de fato saem do prédio ficam vestidos.
HTTP, JSON e tráfego de API
Cabeçalhos e payloads são onde o base64 faz o seu trabalho diário em silêncio. Um cabeçalho de auth Basic é o prefixo literal Basic seguido do base64 de username:password, e construir um em SQL é uma concatenação mais uma codificação:
SELECT 'Basic ' || encode(convert_to('alice' || ':' || 's3cret', 'UTF8'), 'base64') AS header;
que monta Basic YWxpY2U6czNjcmV0, o cabeçalho exato que um cliente enviaria. Use para gerar os fixtures que os seus testes de integração comparam, ou para normalizar uma coluna de cabeçalhos guardados antes de auditá-los. No lado do JSON, o MySQL pode empacotar um campo e embutir dentro de um documento numa única expressão, sem código de aplicação no meio:
SELECT JSON_OBJECT('img', TO_BASE64(CAST('hello file' AS BINARY))) AS doc;
O resultado é {"img": "aGVsbG8gZmlsZQ=="}, um payload pronto para envio. A mesma forma funciona para certificados, chaves públicas e qualquer outro arquivo que a sua API decidiu embutir, e é a direção que importa quando você é quem produz o tráfego, e não quem decodifica o dos outros.
Arquivos de config, segredos e variáveis de ambiente
Um hábito de exportação merece o seu próprio parágrafo, porque está em todo lugar: o segredo guardado como base64 numa tabela de config. O Kubernetes manteve o hábito vivo, onde valores de segredo ficam em base64 em repouso para caber numa linha de YAML sem aspas, sem quebras de linha e sem barras invertidas, e todo sistema de config caseiro que já conheceu um pipeline Kubernetes adotou o hábito. A direção de empacotar é uma codificação por valor, com o cast binário fazendo o trabalho do conjunto de caracteres, para que o texto exportado seja exatamente os bytes guardados:
SELECT name, TO_BASE64(CAST(value AS BINARY)) AS for_config
FROM app_config
WHERE name LIKE '%_secret%';
Cada valor de até 57 bytes sai como uma string plana e pronta para colar; qualquer coisa maior precisa de um REPLACE() para remover as linhas quebradas antes de ir para o YAML, e o novo ambiente decodifica de volta do outro lado. Trate o resultado com o cuidado que ele merece: você acabou de transformar uma coluna de segredos guardados numa coluna de segredos guardados que qualquer humano lê em uns dez segundos, e o hábito de base64-em-config ganha uma segunda olhada no momento em que você está tão perto do texto puro. Base64 é um transporte, não um cofre. Se o ambiente tem um cofre de segredos de verdade, a coluna base64 está a uma migração de distância dele.
E-mail e o hábito dos 76 caracteres
A quebra em 76 caracteres é mais velha do que todos os bancos de dados desta página. O MIME, o conjunto de padrões que permite que o e-mail carregue anexos binários (RFC 2045, seção 6.8, 1996), quebra a saída base64 em 76 caracteres e termina cada linha com um retorno de carro e um line feed, porque a rede de e-mail antiga não podia ser confiada com linhas mais longas. Três codificadores aqui herdaram a quebra como padrão (MySQL, PostgreSQL, o CLI do SQLite), o que é um presente para qualquer coisa que acabou num e-mail e uma armadilha para a que não acabou. E herdaram pela metade: o PostgreSQL termina as linhas com uma quebra de linha solta, não com o retorno de carro e quebra de linha que o padrão MIME especifica, então uma saída que deveria colar num anexo de e-mail de verdade precisa de mais uma passada:
WITH t AS (
SELECT replace(encode(attachment::bytea, 'base64'), chr(10), '') AS s
FROM email_outbox
)
SELECT regexp_replace(s, '(.{1,76})', '\1' || chr(13) || chr(10), 'g') AS mime_ready
FROM t;
Remova as quebras de linha que o PostgreSQL já adicionou, depois quebre de novo em 76 com um CRLF completo depois de cada linha, e a string fica MIME-correta numa única expressão. Passe trezentos bytes por ela e os 400 caracteres que você empacotou viram 412: seis linhas quebradas, seis pares CRLF, doze caracteres de cerimônia de transporte. O mesmo formato de problema aparece com blocos PEM, que quebram em 64 em vez de 76, e com as APIs que não querem nenhuma quebra porque o parser JSON delas não vai aceitar uma quebra de linha no meio de um campo. A regra para todos: descubra qual contrato o consumidor assinou antes de codificar, porque quebrar de novo uma coluna de base64 guardado é uma migração, não uma consulta.
Armadilhas: onde os codificadores mentem para você
Toda armadilha desta lista é uma que um dialeto específico arma, não uma que o base64 arma, e todas elas têm ao menos um codebase que a encontrou em produção:
- A quebra não pedida. MySQL e PostgreSQL quebram a saída em 76 por padrão, o CLI do SQLite em 72, e ninguém na sua consulta pediu. O campo base64 do seu JSON agora contém quebras de linha, e o consumidor que aceita o formato na especificação o rejeita no mundo real. A forma plana é um
REPLACE()sobre o caractere de quebra de linha, aplicado no lado do gravador, para que a coluna guarde o que o leitor quer. - O escorregão do conjunto de caracteres.
TO_BASE64('héllo')sem o cast binário codifica o que quer que o conjunto de caracteres da conexão acredite que as letras sejam, e um clientelatin1e um clienteutf8mb4acreditam em coisas diferentes. O mesmo texto de consulta, dois resultados base64 diferentes, e o errado decodifica para mojibake que ninguém rastreia de volta ao codificador. O cast binário, ou umconvert_to()explícito, é a única versão honesta da consulta. - A porta BLOB. O
to_base64()do DuckDB quer umBLOB: passe texto e ele faz o cast implicitamente, então uma colunavarcharcom codificação não-UTF-8 pode codificar em silêncio os bytes errados. O caminho honesto éto_base64(encode(...)), que é por isso que os exemplos desta página sempre mostram o par para entrada de texto. - A mudança de espaço em branco da 26.7. O
base64Decode()e obase64URLDecode()do ClickHouse foram estritos com a entrada por anos, mas a partir da 26.7 ignoram espaço em branco (espaço, tabulação, line feed, retorno de carro, form feed) em vez de rejeitá-lo, então um script que usava a dar erro numa coluna quebrada agora tem sucesso em silêncio, o que é o seu próprio tipo de regressão. Confira a versão do servidor antes de confiar na decodificação. - A fronteira de 6000 bytes. O
BASE64_ENCODE()do SQL Server devolvevarchar(8000)quando a entrada é umvarbinary(n)com n de 6000 ou menos, evarchar(max)acima disso; o mapeamento usa como chave o tamanho declarado, não o valor, então uma colunavarbinary(8000)devolvevarchar(max)mesmo com três bytes. A saídaurl_safeda mesma função, por outro lado, não é legível pelos próprios decodificadores de base64 XML e JSON do servidor, que esperam o alfabeto padrão com preenchimento. Escolha a variante para o público que vai lê-la. - O RAW de 2000 bytes. No Oracle, um valor
RAWnuma instrução SQL pura fica limitado a 2000 bytes. Como o codificador recebeRAWe devolveRAW, uma codificação de instrução única só aceita uns 1500 bytes de entrada (os 2000 caracteres de saída caberiam, mais do que isso não), e uma decodificação de instrução única só aceita 2000 caracteres de base64. Payloads maiores se movem para o PL/SQL, onde uma variávelRAWguarda 32767 bytes e uma chamada única carrega a maioria deles, e só payloads cuja saída base64 ultrapassaria esse teto precisam do loop de pedaços, que data do limite dos anos 1990 que nunca se moveu. - O imposto de pacote. O MySQL conta a string codificada contra o
max_allowed_packet, não os bytes crus. Uma foto que cabe na tabela de sobra pode estourar o pacote quando fica 33 por cento maior e quebrada, e o modo de falha é um valor truncado ou umNULLque parece corrupção de dados. Confira o limite no mesmo fôlego da largura da coluna. - A quebra de linha final. O
base64()do CLI do SQLite termina a sua última linha com uma quebra de linha, o hábito de fim de linha aplicado à linha final também. Cole a saída do shell num campo JSON e você enviou uma string base64 com uma quebra de linha dentro, a quebra não pedida vestindo um chapéu diferente. - A suposição do alfabeto. Um consumidor construído para o alfabeto padrão encontra a sua saída URL-safe (ou vice-versa) e vê caracteres que não conhece. A maioria dos decodificadores falha alto no sublinhado; alguns falham em silêncio pulando-o. Documente o alfabeto de toda coluna base64 no comentário do schema, porque o próximo desenvolvedor não vai lembrar qual pipeline de token escreveu a linha.
Quando o tamanho realmente importa
A conta de tamanho é o comprimento plano mais as quebras que o seu codificador soma, e a página inicial faz a dedução completa da razão. O que vale a pena fazer aqui é caminhar pelos lugares onde o número para de ser curiosidade. Uma coluna VARCHAR dimensionada para a contagem de bytes da entrada truncará a saída em silêncio pela primeira vez que o payload for longo o bastante para precisar de folga extra, porque três bytes de entrada custam quatro caracteres. Um índice sobre uma coluna de texto base64 paga o imposto duas vezes: uma no armazenamento e outra em cada comparação, porque as entradas do índice são as letras quebradas, não os bytes. O max_allowed_packet do MySQL e o teto de 1 GB de bytea do PostgreSQL são as duas paredes que a maioria das pessoas encontra primeiro, e ambos conferem contra o texto, que é o lado maior da troca. A resposta de desenho raramente é escolher um codificador diferente (só existe um base64); é escolher onde a codificação acontece. Coluna BLOB, coluna de hash para buscas, codificar na fronteira: o base64 existe só no tráfego, onde ele mora.
Segurança: o que o base64 não é
Base64 não é criptografia, e o único hábito que precisa ser dito em voz alta é o da tabela de config de antes: um segredo guardado como base64 é um segredo guardado numa fonte diferente. A transformação é uma bijeção sem chave, reversível por qualquer linguagem de programação da Terra numa chamada de função, e o seu único efeito real é manter o valor numa linha de YAML. Se o modelo de ameaça inclui outro usuário deste banco de dados, outro serviço que lê a exportação, ou um log que capturou a linha, o base64 contribui exatamente zero para a defesa. Ele ofusca o valor do olho humano por alguns segundos, que é por isso que parece proteção numa revisão de código e por isso que falha num incidente. Cifre o que precisa ser secreto, cifre com uma chave que alguém consiga de fato manter secreta, e deixe o base64 fazer o trabalho que ele é bom: mover bytes por um canal que só carrega texto.
Quando cada dialeto aprendeu a quebrar em linhas
As notas de release contam a mesma história que o lado da decodificação contou, só que com as letras indo na outra direção, e o cronograma diz algo sobre cada motor:
2002. O PostgreSQL 7.2 lista o base64 como formato de primeira classe do encode() e do decode(), contemporâneo do UTL_ENCODE do Oracle na era 9i, e o mecanismo de base64 mais antigo desta família por uma margem curta. Um banco de dados com um tipo binário de verdade e um argumento de formato chegou cedo, porque a resposta estava a um valor de enum de distância.
Princípio dos anos 2000. O pacote UTL_ENCODE do Oracle chega na era 9i com o BASE64_ENCODE() ao lado dos primos de cabeçalho MIME, quoted-printable e uudecode. RAW por dentro, RAW por fora, e um quarto de século depois o pacote não mudou de ideia.
2013. O MySQL 5.6 adiciona TO_BASE64() e FROM_BASE64() como um par feito um para o outro, e o MariaDB 10.0 herda os dois. O contrato do par não se moveu desde então: linhas de 76 caracteres na saída, tolerância a espaço em branco na entrada.
2018. O ClickHouse 18.16 (dezembro de 2018) lança base64Encode() e base64Decode() junto com o alias no estilo MySQL, porque o mundo colunar estava importando workloads cujos schemas de log já carregavam base64 dentro.
2023. O SQLite 3.41.0 adiciona base64() ao shell de linha de comando como uma função definida pela aplicação. A biblioteca núcleo não ganha nada, como é do seu jeito; o shell, onde os humanos de fato cutucam arquivos SQLite, ganha a ferramenta.
2025. O SQL Server 2025, que ficou disponível em geral em novembro de 2025, finalmente lança BASE64_ENCODE() e BASE64_DECODE(), trinta e seis anos depois do lançamento do produto e uma geração depois que os usuários decoraram a solução alternativa XML de cor.
O padrão é o mesmo com o qual o artigo de decodificação termina, espelhado: os motores com um tipo binário de verdade e um argumento de formato ganharam base64 no dia em que a necessidade ficou óbvia, e os motores onde tudo é string agendaram para depois.
Esquisitices que valem a pena conhecer
- O
base64()do CLI do SQLite é o camaleão da família, e na direção de codificação ele mostra o truque melhor: passe umBLOBe ele devolve texto quebrado com uma quebra de linha final, passe texto e ele devolve umBLOB. Um nome, dois trabalhos, escolhidos pelo tipo do argumento, e nenhum outro codificador desta família faz isso. - A família
base64Decode()do ClickHouse ficou tolerante na 26.7: espaço em branco na entrada agora é ignorado em vez de rejeitado, então a mesma consulta numa coluna quebrada falha no servidor antigo e devolve um valor em silêncio no novo. O decodificador não quebrou; ele relaxou, o que é de algum jeito mais difícil de depurar. - O PostgreSQL quebra em 76 caracteres exatamente como o padrão MIME de 1996, exceto que termina as linhas com uma quebra de linha solta em vez do retorno de carro e quebra de linha do padrão. Vinte e tantos anos depois da especificação, um caractere a menos por linha, e a revolução é invisível a menos que você faça diff dos bytes.
- No cliente
mysql, os bytes que você codifica imprimem bem como texto base64, mas no momento em que você olha a coluna bruta comCAST(... AS BINARY), o cliente muda para exibição hex (binary-as-hex), e umhelloperfeitamente bom chega na tela como0x68656C6C6F. A configuração já convenceu milhares de desenvolvedores de que o codificador deles está quebrado. - O Snowflake exibe valores
BINARYcomo hex em todos os conjuntos de resultado, então uma coluna de entrada deTO_BINARY()na sua consulta de codificação se lê como um checksum mesmo quando tudo funcionou. Dois dialetos, duas exibições hex, uma sensação de desconfiança idêntica. - O
RAWem nível de SQL do Oracle é limitado a 2000 bytes, então um certificado de 3 quilobytes não pode nem ser colado numa instrução SQL como literalRAW. A codificação tem que acontecer em PL/SQL, onde uma variávelRAWguarda 32767 bytes, então o certificado de 3 quilobytes é uma chamada única, e só payloads cuja saída base64 ultrapassaria 32 quilobytes precisam do loop de pedaços, que data dos anos 1990. - Uma linha de base64 MIME é 76 caracteres, que são 57 bytes crus, porque quatro caracteres carregam três. O número 76 que aparece nos padrões de três codificadores não é tanto um limite quanto uma densidade de empacotamento: cada linha quebrada que você vê num anexo de e-mail antigo carregava exatamente 57 bytes dos seus dados.
A outra direção
Este artigo tem sido sobre vestir o figurino: decidir quais bytes você quer dizer, observar o que sai, escolher o alfabeto para o público e fazer a conta de tamanho antes que a coluna trunque. Tirar o figurino é um temperamento completamente diferente, com NULLs silenciosos onde um dialeto dá de ombros, erros duros onde outro levanta a voz, e um alfabeto URL-safe que metade da família não conhece nem de vista. Tudo isso, do FROM_BASE64() ao decode() ao BASE64_DECODE(), está coberto em profundidade no artigo relacionado de decodificação Base64 para SQL, linkado logo abaixo. Codifique aqui, decodifique lá, e a ida e volta inteira cabe dentro de uma tarde.
Última atualização: 2026-09-08
Artigo relacionado: Decodificação Base64 em SQL: um guia completo