Vous avez à traiter le format Base64 ? Alors ce site est parfait pour vous ! Utilisez notre outil en ligne super pratique pour encoder ou décoder vos données.

Encodage Base64 en SQL : un guide complet

De temps en temps, la base de données doit parler au monde extérieur, et le monde extérieur ne parle pas toujours en octets. Une API veut votre logo dans une chaîne JSON. Un export de config veut un secret qui tienne sur une ligne de YAML sans guillemets ni backslashes. Un script de maintenance veut expédier un fichier à travers un système qui ne transporte que du texte. C'est le moment où vos données enfilent un costume de lettres, et le nom du costume, c'est base64.

Le format lui-même est déjà couvert sur la page d'accueil (64 caractères imprimables, chaque groupe de quatre représentant trois octets en entrée, au plus deux signes = de padding dans le groupe final), alors cet article saute ce cours et va droit au travail mécanique. Deux choses à retenir : l'encodage est la direction où les données deviennent plus grosses, donc les largeurs de colonne et les limites de paquet ressentent chacun de ses octets, et les encodeurs de cette famille SQL ne s'accordent pas sur les deux choses les plus difficiles à défaire plus tard : quels octets ils lisent quand votre colonne contient du texte, et où ils mettent les retours à la ligne dans ce qu'ils écrivent.

La fiche mémo des encodeurs

Qui est de service, ce qu'ils mangent, et où ils cassent leur sortie. Les deux dernières colonnes sont celles qui mordent, parce qu'une chaîne pleine de retours à la ligne non invités et une chaîne avec un autre alphabet sont toutes les deux des chaînes base64 parfaitement valides que votre consommateur va quand même rejeter :

Dialecte L'appel Type d'entrée Enveloppe à 76 ? Option URL-safe Depuis quand
MySQL 8.x / MariaDB 10.x TO_BASE64(str) chaîne (le jeu de caractères s'applique) oui aucune MySQL 5.6 (2013)
PostgreSQL encode(bytea, 'base64') bytea oui, LF uniquement aucune 7.2 (2002)
SQLite (CLI 3.41+) base64(blob) BLOB oui, à 72 aucune 3.41.0 (2023)
DuckDB to_base64(blob) BLOB non aucune versions modernes
ClickHouse 18.16+ base64Encode(x) n'importe quoi, cast en String non base64URLEncode() 18.16 (2018)
SQL Server 2025+ BASE64_ENCODE(bin [, url_safe]) varbinary non second argument 2025
Oracle UTL_ENCODE.BASE64_ENCODE(raw) RAW non aucune ère 9i
Snowflake BASE64_ENCODE(binary) BINARY non aucune versions actuelles

Lisez le tableau de gauche à droite et le travail se réduit à deux décisions. D'abord, comment vos octets y arrivent : la colonne type d'entrée est là où naissent les surprises du jeu de caractères, parce que « le même texte » est d'autres octets sous d'autres collations. Ensuite, ce qui sort de l'autre côté : la colonne d'enveloppe décide si votre résultat est une ligne plate ou un poème avec un retour à la ligne tous les 76 caractères, et la colonne URL-safe décide si vous pouvez mettre le résultat dans un lien du tout.

D'abord, décidez quels octets vous voulez dire

Un encodeur emballe des octets, mais votre colonne contient d'habitude des lettres, et les lettres ne sont des octets que si vous dites quel alphabet d'octets. Le TO_BASE64() de MySQL lit son argument dans le jeu de caractères de la connexion, ce qui est pratique jusqu'à ce que ça ne le soit plus : le même 'héllo' part en base64 différent sous un client latin1 et un client utf8mb4. Quand vous voulez dire les octets exacts tels que stockés, figez-les d'abord avec un cast binaire :

SELECT TO_BASE64('hello') AS from_text;
SELECT TO_BASE64(CAST('héllo' AS BINARY)) AS utf8_bytes;

La deuxième ligne revient sous la forme aMOpbGxv, les deux octets hexa C3 A9 au milieu étant la façon de UTF-8 d'épeller é. PostgreSQL est plus strict dès le départ : encode() refuse de regarder quoi que ce soit qui n'est pas du bytea, donc une valeur texte doit d'abord nommer son encodage, tandis que des octets bruts peuvent arriver en littéral hexa :

SELECT encode(convert_to('héllo', 'UTF8'), 'base64') AS text_as_utf8;
SELECT encode('\x68656c6c6f'::bytea, 'base64') AS raw_bytes;

Chaque autre dialecte a sa propre porte d'entrée vers la même idée, et tous se réduisent à « obtenez les octets, puis emballez-les » :

Dialecte Texte vers octets L'appel de l'encodeur
T-SQL CAST('héllo' AS VARBINARY(8000)) via la collation de la colonne 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') donne un BLOB to_base64(blob)
SQLite CLI un littéral BLOB comme X'68656C6C6F' base64(blob)

La règle pratique est la même que côté décodage : décidez le jeu de caractères avant d'encoder, écrivez-le dans la requête en tant que littéral, et passez un payload accentué à travers tout le pipeline avant de faire confiance à la colonne. Un seul héllo attrape chaque mauvaise collation, et ça ne coûte rien.

Ensuite, surveillez ce qui sort

Une fois les octets emballés, les encodeurs prennent des chemins différents sur les retours à la ligne. Trois d'entre eux enveloppent la sortie : MySQL et PostgreSQL à 76 caractères, l'habitude de l'e-mail, et la CLI SQLite à 72 ; le reste renvoie une ligne plate, quelle que soit sa longueur. La différence est facile à manquer et chère à trouver, parce qu'un champ base64 avec des retours à la ligne cachés est un champ qui casse un parseur JSON en pleine phrase :

SELECT LENGTH(TO_BASE64(REPEAT('x', 300))) AS wrapped,
      LENGTH(REPLACE(TO_BASE64(REPEAT('x', 300)), '\n', '')) AS flat;

Trois cents octets en entrée reviennent en 400 caractères base64, et le même appel mesure 405 parce que cinq retours à la ligne sont venus pour la route. L'arithmétique derrière est assez petite pour la garder en tête : la longueur plate est la longueur d'entrée divisée par trois, arrondie au-dessus, fois quatre. Si votre encodeur enveloppe, ajoutez des retours à la ligne entre les lignes de 76 caractères, soit la longueur plate divisée par 76, arrondie au-dessus, moins un. Trois cents octets : 400 en plat, 405 en enveloppé. Cent onze octets : 148 en plat, 149 en enveloppé. Un seul retour à la ligne de plus que votre budget, c'est comme ça qu'une colonne VARCHAR(500) commence à tronquer silencieusement un payload VARCHAR(480).

Deux conséquences à noter. Dimensionnez les colonnes texte pour la longueur plate plus un peu de marge si l'écrivain peut envelopper, ou interdisez l'enveloppe chez l'écrivain et dimensionnez pour le plat. Et souvenez-vous que la limite contre laquelle votre résultat se bat est la limite de la chaîne, pas des octets : dans MySQL, le texte enveloppé compte contre max_allowed_packet (64 Mo par défaut dans MySQL 8), donc une photo de 50 mégaoctets encodée en environ 67 mégaoctets de lettres ne rentre pas dans le paquet par défaut, alors que le fichier brut, si.

Base64 URL-safe : l'alphabet voyageur

La section 5 de la RFC 4648 a défini un second alphabet pour le base64, parce que l'original a deux caractères qui ont des rôles dans la syntaxe des URL. Le signe plus ajoute des paramètres de requête, la barre oblique sépare les segments de chemin, et le signe égal du padding est percent-encodé dès qu'il croise une chaîne de requête. La variante URL-safe échange + contre - et / contre _, et la spec JWT, par-dessus, jette le padding entièrement, donc un token peut se poser dans un lien, un segment de chemin ou un nom de fichier sans le moindre signe percent.

Un seul dialecte de cette famille livre le switch nativement. Le BASE64_ENCODE() de SQL Server 2025 prend un second argument optionnel, et quand il est activé, le résultat utilise - et _ et saute le padding :

SELECT BASE64_ENCODE(0xCAFECAFE) AS standard;
SELECT BASE64_ENCODE(0xCAFECAFE, TRUE) AS url_safe;

Les mêmes quatre octets reviennent sous la forme yv7K/g== et yv7K_g. ClickHouse garde les variantes comme fonctions séparées, et sa forme URL-safe jette aussi le padding :

SELECT base64URLEncode('https://clickhouse.com') AS url_safe;

qui arrive sous la forme aHR0cHM6Ly9jbGlja2hvdXNlLmNvbQ, les deux signes de padding de la forme standard rognés. Partout ailleurs, la recette est deux traductions de caractères et un rognage, et ça vaut le coup de l'écrire une fois en tant que fonction de base de données, parce que chaque pipeline de tokens en a besoin. Dans PostgreSQL, ça se lit comme ça :

SELECT rtrim(replace(replace(
        encode(convert_to('https://clickhouse.com', 'UTF8'), 'base64'),
        '+', '-'),
      '/', '_'),
      '=') AS url_safe;

Traduisez + en -, traduisez / en _, rognez le padding final, terminé. Une mise en garde pour les gens de SQL Server : la sortie url_safe n'est pas ce que les propres décodeurs base64 XML et JSON du serveur attendent, donc une colonne emballée en forme URL-safe pour le monde extérieur ne se déballera pas à l'intérieur de la base de données avec les fonctions intégrées. Gardez l'audience en tête avant de choisir l'alphabet.

JWT : frapper des tokens depuis la base de données

La chose la plus intéressante que vous puissiez construire avec l'encodeur, c'est un JSON Web Token, parce qu'un JWT n'est rien d'autre que trois morceaux base64 mis en ligne : un en-tête et un payload, tous deux des objets JSON emballés en URL-safe sans padding, et une signature calculée sur les deux premiers. Quand un job par lot doit frapper des tokens (ensemencer un environnement de test, régénérer des identifiants API expirés, construire un flux d'audit), tout le cérémonial tient dans une seule requête PostgreSQL, à condition d'accepter pgcrypto pour le HMAC (activez-la une fois avec 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;

Chaque étape est l'un des mouvements que cet article a déjà montrés : emballer le JSON en base64, le réformer dans l'alphabet URL-safe sans padding, puis signer les deux premiers morceaux et reformater la signature de la même façon. Pour le JSON ci-dessus et le secret sql-secret-key, le résultat est eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkRldiBVc2VyIn0.7_3VdmM8vH0L2bRBNqXXQXzZIR1l8T4_S4QxPPNBEFM, un token que n'importe quel inspecteur HS256 accepte. Les mises en garde méritent autant d'air que le tour : ça ne couvre que les algorithmes HMAC (HS256, HS384, HS512), ça place une clé partagée à l'intérieur d'un énoncé de base de données, et c'est fait pour le travail par lot et l'audit, pas pour un service de tokens de production. Le côté vérification, où vous prouvez ce token face à son secret, c'est un travail pour la couche applicative, ou pour le contrôle de signature de l'article de décodage.

Images et fichiers dans une colonne texte

La raison la plus courante d'encoder en SQL, c'est un fichier qui doit voyager en texte : une API qui inline l'image au lieu de la référencer, un export pour un système qui ne transporte pas de binaire, un script d'ensemencement qui recrée une base de données sur un nouveau serveur. DuckDB rend l'aller-retour presque trivial, parce qu'il lit les fichiers en BLOB à travers une fonction table qui accepte les motifs glob, et l'encodeur aplatit ce qui arrive :

SELECT filename, to_base64(content) AS b64
FROM read_blob('/data/pics/*.png');

Une ligne par fichier, une chaîne base64 plate par ligne, aucune enveloppe à enlever, bien que les habituels un ou deux signes de padding viennent pour la route à la fin. Écrivez le résultat dans une colonne texte et les images sont portables à travers n'importe quel canal qui transporte du texte. Ensuite, ayez la conversation du coût honnêtement : une photo de 1 mégaoctet arrive en environ 1,33 mégaoctets de lettres, et à partir de là, chaque scan, chaque tri et chaque entrée d'index paient ce prix. Si vous contrôlez le schéma, le meilleur design est une colonne BLOB plus un encodage à la frontière de l'API, où seuls les octets qui quittent vraiment le bâtiment s'habillent.

HTTP, JSON et trafic d'API

Les en-têtes et les payloads sont là où le base64 fait son travail quotidien discret. Un en-tête d'auth Basic est le préfixe littéral Basic suivi du base64 de username:password, et en construire un en SQL, c'est une concaténation plus un encodage :

SELECT 'Basic ' || encode(convert_to('alice' || ':' || 's3cret', 'UTF8'), 'base64') AS header;

qui construit Basic YWxpY2U6czNjcmV0, l'en-tête exact qu'un client enverrait. Utilisez-le pour générer les fixtures que vos tests d'intégration comparent, ou pour normaliser une colonne d'en-têtes stockés avant de les auditer. Côté JSON, MySQL peut emballer un champ et l'imbriquer dans un document en une seule expression, sans code applicatif dans la boucle :

SELECT JSON_OBJECT('img', TO_BASE64(CAST('hello file' AS BINARY))) AS doc;

Le résultat est {"img": "aGVsbG8gZmlsZQ=="}, un payload prêt à être envoyé. La même forme marche pour les certificats, les clés publiques et n'importe quel autre fichier que votre API a décidé d'inliner, et c'est la direction qui compte quand vous êtes celui qui produit le trafic, pas celui qui décode celui des autres.

Fichiers de config, secrets et variables d'environnement

Une habitude d'export mérite son propre paragraphe parce qu'elle est partout : le secret stocké en base64 dans une table de config. Kubernetes a maintenu l'habitude en vie : là, les valeurs de secret sont en base64 au repos afin qu'elles tiennent sur une ligne de YAML sans guillemets, sans retours à la ligne et sans backslashes, et chaque système de config maison qui a rencontré un pipeline Kubernetes l'a adopté. La direction d'emballage, c'est un encodage par valeur, le cast binaire faisant le travail du jeu de caractères pour que le texte exporté soit exactement les octets stockés :

SELECT name, TO_BASE64(CAST(value AS BINARY)) AS for_config
FROM app_config
WHERE name LIKE '%_secret%';

Chaque valeur d'au plus 57 octets sort en une chaîne plate, prête à coller ; tout ce qui est plus long a besoin d'un REPLACE() pour enlever les lignes enveloppées avant d'aller dans le YAML, et le nouvel environnement le décode de l'autre côté. Traitez le résultat avec les égards qu'il mérite : vous venez de transformer une colonne de secrets stockés en une colonne de secrets stockés que n'importe quel humain peut lire en une dizaine de secondes, et l'habitude du base64 dans la config reçoit un deuxième regard à l'instant même où vous êtes à deux doigts du texte brut. Le base64 est un transport, pas un coffre-fort. Si l'environnement a un vrai dépôt de secrets, passer de la colonne base64 à celui-là est juste une migration.

L'e-mail et l'habitude des 76 caractères

L'enveloppe de 76 caractères est plus vieille que toutes les bases de données de cette page. MIME, l'ensemble des standards qui permet à l'e-mail de porter des pièces jointes binaires (RFC 2045, section 6.8, 1996), enveloppe la sortie base64 à 76 caractères et termine chaque ligne avec un retour chariot et un retour à la ligne, parce que l'ancien réseau d'e-mail ne pouvait pas faire confiance à des lignes plus longues. Trois encodeurs ici ont hérité de l'enveloppe comme valeur par défaut (MySQL, PostgreSQL, la CLI SQLite), ce qui est un cadeau pour tout ce qui est finalement arrivé dans un e-mail et un piège pour tout ce qui ne l'a pas été. Et ils l'ont héritée à moitié finie : PostgreSQL termine ses lignes avec un retour à la ligne seul, pas le retour chariot et retour à la ligne que le standard MIME spécifie, donc une sortie qui devrait se coller dans une vraie pièce jointe d'e-mail a besoin d'un passage de plus :

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;

Enlevez les retours à la ligne que PostgreSQL a déjà ajoutés, puis ré-enveloppez à 76 avec un CRLF complet après chaque ligne, et la chaîne est MIME-correcte en une seule expression. Faites passer trois cents octets à travers et les 400 caractères que vous avez emballés deviennent 412 : six lignes enveloppées, six paires CRLF, douze caractères de cérémonie de transport. La même forme de problème apparaît avec les blocs PEM, qui enveloppent à 64 au lieu de 76, et avec les API qui veulent aucune enveloppe du tout parce que leur parseur JSON ne rencontrera pas un retour à la ligne au milieu d'un champ. La règle pour tous : découvrez quel contrat le consommateur a signé avant d'encoder, parce que ré-envelopper une colonne de base64 stockée est une migration, pas une requête.

Les pièges : là où les encodeurs vous mentent

Chaque piège de cette liste est un piège que pose un dialecte spécifique, pas un piège que pose le base64, et chacun d'eux a au moins une base de code qui l'a trouvé en production :

  • L'enveloppe non demandée. MySQL et PostgreSQL enveloppent leur sortie à 76 par défaut, la CLI SQLite à 72, et personne dans votre requête ne l'a demandé. Le champ base64 dans votre JSON contient maintenant des retours à la ligne, et le consommateur qui accepte le format dans la spec le rejette dans le monde réel. La forme plate, c'est un REPLACE() sur le caractère retour à la ligne, appliqué côté écrivain pour que la colonne stocke ce que le lecteur veut.
  • La glissade du jeu de caractères. TO_BASE64('héllo') sans le cast binaire encode ce que le jeu de caractères de la connexion croit que sont les lettres, et un client latin1 et un client utf8mb4 croient des choses différentes. Le même texte de requête, deux résultats base64 différents, et le mauvais se décode en mojibake que personne ne remonte jusqu'à l'encodeur. Le cast binaire, ou un convert_to() explicite, est la seule version honnête de la requête.
  • La porte BLOB. Le to_base64() de DuckDB veut un BLOB : donnez-lui du texte et il cast implicitement, donc une colonne varchar avec un encodage non UTF-8 peut encoder silencieusement les mauvais octets. La voie honnête est to_base64(encode(...)), c'est pourquoi les exemples de cette page montrent toujours le duo pour une entrée texte.
  • Le changement d'espaces en 26.7. base64Decode() et base64URLDecode() de ClickHouse étaient stricts sur l'entrée pendant des années, mais depuis 26.7 ils ignorent les espaces (espace, tabulation, retour à la ligne, retour chariot, saut de page) au lieu de les rejeter, donc un script qui levait une erreur sur une colonne enveloppée réussit maintenant silencieusement, ce qui est un autre genre de régression. Vérifiez la version du serveur avant de faire confiance au décodage.
  • La frontière des 6000 octets. Le BASE64_ENCODE() de SQL Server renvoie varchar(8000) quand l'entrée est un varbinary(n) avec n de 6000 ou moins, et varchar(max) au-dessus ; le mappage se fait sur la taille déclarée, pas sur la valeur, donc une colonne varbinary(8000) renvoie varchar(max) même à trois octets. La sortie url_safe de la même fonction, entre-temps, n'est pas lisible par les propres décodeurs base64 XML et JSON du serveur, qui attendent l'alphabet standard avec padding. Choisissez la variante pour l'audience qui va la lire.
  • Le RAW de 2000 octets. Dans Oracle, une valeur RAW dans un énoncé SQL simple plafonne à 2000 octets. Comme l'encodeur prend du RAW et rend du RAW, un encodage en un seul énoncé ne peut accepter qu'environ 1500 octets d'entrée (ses 2000 caractères de sortie rentreraient, plus, non), et un décodage en un seul énoncé ne peut accepter que 2000 caractères de base64. Les payloads plus grands s'échappent en PL/SQL, où une variable RAW tient 32767 octets et un seul appel en porte la plupart, et seuls les payloads dont la sortie base64 dépasserait ce plafond ont besoin de la boucle par morceaux, qui date de la limite des années 1990 qui n'a jamais bougé.
  • La taxe du paquet. MySQL compte la chaîne encodée contre max_allowed_packet, pas les octets bruts. Une photo qui rentre dans la table à des lieues peut déborder le paquet une fois qu'elle est 33 pour cent plus grosse et enveloppée, et le mode d'échec est une valeur tronquée ou un NULL qui a l'air d'une corruption de données. Vérifiez la limite dans le même souffle que la largeur de la colonne.
  • Le retour à la ligne final. Le base64() de la CLI SQLite termine sa dernière ligne avec un retour à la ligne, l'habitude de fin de ligne appliquée à la ligne finale aussi. Collez la sortie du shell dans un champ JSON et vous avez expédié une chaîne base64 avec un retour à la ligne dedans, l'enveloppe non demandée portant un autre chapeau.
  • L'hypothèse d'alphabet. Un consommateur construit pour l'alphabet standard rencontre votre sortie URL-safe (ou l'inverse) et voit des caractères qu'il ne connaît pas. La plupart des décodeurs échouent bruyamment sur le tiret bas ; quelques-uns échouent silencieusement en le sautant. Documentez l'alphabet de chaque colonne base64 dans le commentaire du schéma, parce que le prochain développeur ne se souviendra pas de quel pipeline de tokens a écrit la ligne.

Quand la taille compte vraiment

Le calcul de taille, c'est la longueur plate plus toute l'enveloppe que votre encodeur ajoute, et la page d'accueil fait la dérivation complète du ratio. Ce qui vaut le coup ici, c'est de parcourir là où le chiffre cesse d'être une curiosité. Une colonne VARCHAR dimensionnée au nombre d'octets de l'entrée tronque silencieusement la sortie la première fois que le payload est assez long pour avoir besoin de marge, parce que trois octets en entrée coûtent quatre caractères. Un index sur une colonne texte base64 paie la taxe deux fois : une fois dans le stockage et à nouveau dans chaque comparaison, parce que les entrées d'index sont les lettres enveloppées, pas les octets. Le max_allowed_packet de MySQL et le plafond bytea de 1 Go de PostgreSQL sont les deux murs que la plupart des gens heurtent en premier, et les deux sont vérifiés contre le texte, qui est le côté le plus gros de l'échange. La réponse de design est rarement de choisir un autre encodeur (il n'y a qu'un seul base64) ; c'est de choisir où l'encodage a lieu. Colonne BLOB, colonne de hash pour les recherches, encodage à la frontière : le base64 n'existe que dans le trafic, là où il a sa place.

Sécurité : ce que le base64 n'est pas

Le base64 n'est pas un chiffrement, et l'habitude qu'il faut dire à voix haute, c'est celle de la table de config de plus haut : un secret stocké en base64 est un secret stocké dans une autre police. La transformation est une bijection sans clé, inversible par chaque langage de programmation sur terre en un seul appel de fonction, et son seul effet réel est de garder la valeur sur une ligne de YAML. Si le modèle de menace inclut un autre utilisateur de cette base de données, un autre service qui lit l'export, ou un log qui a capturé la ligne, le base64 contribue exactement zéro à la défense. Il obfuscate la valeur pour l'œil humain pendant quelques secondes, c'est pour ça qu'il a l'air d'une protection dans une revue de code et qu'il échoue dans un incident. Chiffrez ce qui doit être secret, chiffrez-le avec une clé que quelqu'un peut vraiment garder secrète, et laissez le base64 faire le travail où il excelle : faire voyager des octets à travers un canal qui ne transporte que du texte.

Quand chaque dialecte a appris à envelopper

Les notes de version racontent la même histoire que le côté décodage, juste avec les lettres qui vont dans l'autre sens, et le calendrier dit quelque chose sur chaque moteur :

2002. PostgreSQL 7.2 liste base64 comme format de premier ordre de encode() et decode(), contemporain du UTL_ENCODE d'Oracle de l'ère 9i, et le mécanisme base64 le plus ancien de cette famille, de justesse. Une base de données avec un vrai type binaire et un argument de format y est arrivée tôt, parce que la réponse était à une valeur d'enum.

Début des années 2000. Le package UTL_ENCODE d'Oracle est livré à l'ère 9i avec BASE64_ENCODE() à côté de ses cousins d'en-tête MIME, quoted-printable et uuecode. RAW en entrée, RAW en sortie, et un quart de siècle plus tard, le package n'a pas changé d'avis.

2013. MySQL 5.6 ajoute TO_BASE64() et FROM_BASE64() comme duo assorti, et MariaDB 10.0 hérite des deux. Le contrat du duo n'a pas bougé depuis : lignes de 76 caractères en sortant, tolérance aux espaces en entrant.

2018. ClickHouse 18.16 (décembre 2018) livre base64Encode() et base64Decode() ensemble avec l'alias façon MySQL, parce que le monde columnar importait des workloads dont les schémas de logs portaient déjà du base64.

2023. SQLite 3.41.0 ajoute base64() au shell en ligne de commande en tant que fonction définie par l'application. La bibliothèque de base ne reçoit rien, comme c'est son habitude ; le shell, là où les humains piquent vraiment dans les fichiers SQLite, reçoit l'outil.

2025. SQL Server 2025, devenu disponible en général en novembre 2025, livre enfin BASE64_ENCODE() et BASE64_DECODE(), trente-six ans après le lancement du produit et une génération après que ses utilisateurs ont appris l'astuce XML par cœur.

Le motif est le même que celui dont l'article de décodage finit, en miroir : les moteurs avec un vrai type binaire et un argument de format ont eu le base64 le jour où le besoin était évident, et les moteurs où tout est une chaîne l'ont planifié pour plus tard.

Des curiosités à connaître

  • La base64() de la CLI SQLite est le polymorphe de la famille, et c'est dans la direction encodage qu'elle montre le mieux son tour : donnez-lui un BLOB et elle rend du texte enveloppé avec un retour à la ligne final, donnez-lui du texte et elle rend un BLOB. Un seul nom, deux métiers, choisis selon le type de l'argument, et aucun autre encodeur de cette famille ne le fera.
  • La famille base64Decode() de ClickHouse est devenue tolérante en 26.7 : les espaces dans l'entrée sont maintenant ignorés au lieu d'être rejetés, donc la même requête sur une colonne enveloppée échoue sur le vieux serveur et renvoie tranquillement une valeur sur le nouveau. Le décodeur ne s'est pas cassé ; il s'est détendu, ce qui est mystérieusement plus difficile à déboguer.
  • PostgreSQL enveloppe à 76 caractères exactement comme le standard MIME de 1996, sauf qu'il termine les lignes avec un retour à la ligne seul au lieu du retour chariot et retour à la ligne du standard. Plus de vingt ans après la spec, un caractère de moins par ligne, et la révolte est invisible à moins que vous ne diffiez les octets.
  • Dans le client mysql, les octets que vous encodez s'affichent bien en texte base64, mais dès que vous regardez la colonne brute avec CAST(... AS BINARY), le client bascule en affichage hexa (binary-as-hex), et un hello parfaitement bon arrive à l'écran sous la forme 0x68656C6C6F. Le réglage a convaincu des milliers de développeurs que leur encodeur est cassé.
  • Snowflake affiche les valeurs BINARY en hexa dans chaque jeu de résultats, donc une colonne d'entrée TO_BINARY() dans votre requête d'encodage se lit comme un checksum même quand tout a marché. Deux dialectes, deux affichages hexa, un même sentiment d'inquiétude.
  • Le RAW au niveau SQL d'Oracle plafonne à 2000 octets, donc un certificat de 3 kilo-octets ne peut tout simplement pas être collé dans un énoncé SQL en tant que littéral RAW. L'encodage doit se faire en PL/SQL, où une variable RAW tient 32767 octets, donc le certificat de 3 kilo-octets est un seul appel, et seuls les payloads dont la sortie base64 dépasserait 32 kilo-octets ont besoin de la boucle par morceaux, qui date des années 1990.
  • Une ligne de base64 MIME fait 76 caractères, ce qui est 57 octets bruts, parce que quatre caractères portent trois. Le chiffre 76 qui apparaît dans les valeurs par défaut de trois encodeurs est moins une limite qu'une densité d'emballage : chaque ligne enveloppée que vous voyez dans une vieille pièce jointe d'e-mail portait exactement 57 octets de vos données.

L'autre direction

Cet article a parlé d'enfiler le costume : décider quels octets vous voulez dire, surveiller ce qui sort, choisir l'alphabet pour l'audience, et faire le calcul de taille avant que la colonne ne tronque. Enlever le costume est un tempérament entièrement différent, avec des NULL silencieux là où un dialecte hausse les épaules, des erreurs sèches là où un autre lève la voix, et un alphabet URL-safe que la moitié de la famille ne connaît tout simplement pas. Tout cela, de FROM_BASE64() à decode() à BASE64_DECODE(), est couvert en profondeur dans l'article lié sur le décodage Base64 en SQL, lié juste en dessous. Encodez ici, décodez là-bas, et tout l'aller-retour tient dans une après-midi.

Dernière mise à jour : 2026-09-08

Article associé : Décodage Base64 en SQL : un guide complet