Base64-codering in SQL: een complete gids
Van tijd tot tijd moet de database met de buitenwereld praten, en de buitenwereld spreekt niet altijd bytes. Een API wil je logo binnen een JSON-string. Een config-export wil een geheim dat op één regel YAML past zonder quotes of backslashes. Een onderhoudsscript wil een bestand versturen via een systeem dat alleen tekst kan dragen. Dat is het moment waarop je data een kostuum van letters aantrekt, en het kostuum heet base64.
Het formaat zelf is al behandeld op de homepage (64 drukbare tekens, elk viertal staat voor drie invoerbytes, en tot twee =-tekens halen de laatste groep op), dus dit artikel slaat die uitleg over en gaat direct over op het machine-werk. Twee dingen om vast te houden: coderen is de richting waarin data grooter wordt, zodat kolombreedtes en pakketlimieten elke byte ervan voelen, en de encoders in deze SQL-familie zijn het niet eens over de twee dingen die je later het lastigst ongedaan kunt maken: welke bytes ze lezen wanneer je kolom tekst bevat, en waar ze de regeleinden plaatsen in wat ze schrijven.
De encoder-snelreferentie
Wie er dienst heeft, wat ze eten, en waar ze hun uitvoer mee kapot maken. De laatste twee kolommen zijn degenen die bijten, want een string vol onuitgenodigde regeleinden en een string met een ander alfabet zijn beide perfect geldige base64-strings die je consument toch zal weigeren:
| Dialect | De aanroep | Inputtype | Wikkelt om bij 76? | URL-veilige optie | Sinds wanneer |
|---|---|---|---|---|---|
| MySQL 8.x / MariaDB 10.x | TO_BASE64(str) |
string (tekenset is van toepassing) | ja | geen | MySQL 5.6 (2013) |
| PostgreSQL | encode(bytea, 'base64') |
bytea |
ja, alleen LF | geen | 7.2 (2002) |
| SQLite (CLI 3.41+) | base64(blob) |
BLOB |
ja, bij 72 | geen | 3.41.0 (2023) |
| DuckDB | to_base64(blob) |
BLOB |
nee | geen | moderne releases |
| ClickHouse 18.16+ | base64Encode(x) |
wat dan ook, gecast naar String | nee | base64URLEncode() |
18.16 (2018) |
| SQL Server 2025+ | BASE64_ENCODE(bin [, url_safe]) |
varbinary |
nee | tweede argument | 2025 |
| Oracle | UTL_ENCODE.BASE64_ENCODE(raw) |
RAW |
nee | geen | 9i-tijdperk |
| Snowflake | BASE64_ENCODE(binary) |
BINARY |
nee | geen | actuele releases |
Lees de tabel van links naar rechts en het werk valt uiteen in twee beslissingen. Ten eerste, hoe je bytes er komen: de inputtype-kolom is waar tekenset-verrassingen worden geboren, want "dezelfde tekst" is verschillende bytes onder verschillende collaties. Ten tweede, wat er aan de overkant uitkomt: de omwikkeling-kolom beslist of je resultaat één platte regel is of een gedicht met om de 76 tekens een regeleinde, en de URL-veilige kolom beslist of je het resultaat überhaupt in een link kunt zetten.
Beslis eerst welke bytes je bedoelt
Een encoder pakt bytes, maar je kolom bevat doorgaans letters, en letters zijn alleen bytes als je zegt welk alfabet van bytes. TO_BASE64() van MySQL leest zijn argument in de verbindingstekenset, wat een gemak is tot het dat niet is: dezelfde 'héllo' reist als andere base64 onder een latin1-client dan onder een utf8mb4-client. Bedoel je de exacte bytes zoals ze bewaard staan, dan bevries je ze eerst met een binaire cast:
SELECT TO_BASE64('hello') AS from_text;
SELECT TO_BASE64(CAST('héllo' AS BINARY)) AS utf8_bytes;
De tweede regel komt terug als aMOpbGxv, waarbij de twee hex-bytes C3 A9 in het midden de manier van UTF-8 zijn om é te spellen. PostgreSQL is strenger bij voorbaat: encode() weigert te kijken naar iets dat geen bytea is, dus een tekstwaarde moet eerst zijn codering noemen, terwijl kale bytes als een hex-literal kunnen aankomen:
SELECT encode(convert_to('héllo', 'UTF8'), 'base64') AS text_as_utf8;
SELECT encode('\x68656c6c6f'::bytea, 'base64') AS raw_bytes;
Elk ander dialect heeft zijn eigen voordeur naar hetzelfde idee, en ze komen allemaal neer op "krijg de bytes, pak ze dan in":
| Dialect | Van tekst naar bytes | De encoder-aanroep |
|---|---|---|
| T-SQL | CAST('héllo' AS VARBINARY(8000)) via de collatie van de kolom |
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') geeft een BLOB |
to_base64(blob) |
| SQLite CLI | een BLOB-literal zoals X'68656C6C6F' |
base64(blob) |
De praktische regel is dezelfde als aan de decoderingskant: beslis de tekenset vóór je encodeert, schrijf hem in de query als een letterlijke waarde, en laat één geaccentueerde lading door de hele pipeline lopen vóór je de kolom vertrouwt. Eén héllo vangt elke verkeerde collatie, en het kost niets.
Kijk dan wat er uitkomt
Zodra de bytes zijn ingepakt, gaan de encoders uiteen over regeleinden. Drie van hen wikkelen de uitvoer om: MySQL en PostgreSQL bij 76 tekens, de e-mail-gewoonte, en de SQLite CLI bij 72; de rest geeft één platte regel terug, hoe lang die ook wordt. Het verschil is makkelijk over het hoofd te zien en duur om te ontdekken, want een base64-veld met verborgen regeleinden is een veld dat een JSON-parser halverwege kapotmaakt:
SELECT LENGTH(TO_BASE64(REPEAT('x', 300))) AS wrapped,
LENGTH(REPLACE(TO_BASE64(REPEAT('x', 300)), '\n', '')) AS flat;
Driehonderd invoerbytes komen terug als 400 base64-tekens, en dezelfde aanroep meet 405, omdat vijf regeleinden mee zijn gereden. De rekenkunde erachter is klein genoeg om in je hoofd te houden: de platte lengte is de invoerlengte gedeeld door drie, afgerond naar boven, maal vier. Wanneer je encoder omwikkelt, komen er regeleinden tussen regels van 76 tekens, en dat aantal is de platte lengte gedeeld door 76, afgerond naar boven, minus één. Driehonderd bytes: 400 plat, 405 na het omwikken. Eenhonderdelftien bytes: 148 plat, 149 na het omwikken. Eén regeleinde te veel dan je had ingecalculeerd is hoe een VARCHAR(500)-kolom begint een VARCHAR(480)-lading in stilte af te kappen.
Twee consequenties die het opschrijven waard zijn. Maatvoet tekstkolommen op de platte lengte plus wat marge, als de schrijver eventueel omwikkelt, of verbied het omwikken bij de schrijver en maatvoet op plat. En onthoud dat de limiet waar je resultaat tegen vecht, de limiet van de string is, niet van de bytes: in MySQL meet de omwikkelde tekst tegen max_allowed_packet (standaard 64 MB in MySQL 8), dus een foto van 50 megabyte, gecodeerd naar zo'n 67 megabyte letters, past niet in het standaardpakket, al past het kale bestand er wel in.
URL-veilige Base64: het reizende alfabet
Sectie 5 van RFC 4648 definieerde een tweede alfabet voor base64, omdat het oorspronkelijke alfabet twee tekens heeft met taken in URL-syntaxis. Het plus-teken voegt query parameters toe, de slash scheidt padsegmenten, en het equals-teken van de opvulling wordt percent-gecodeerd in het moment dat het een query string tegenkomt. De URL-veilige variant wisselt + in voor - en / in voor _, en de JWT-specificatie laat er bovenop de opvulling helemaal uit, zodat een token kan zitten in een link, een padsegment of een bestandsnaam zonder één percent-teken.
Alleen één dialect in deze familie levert de schakelaar natief. BASE64_ENCODE() van SQL Server 2025 neemt een optioneel tweede argument, en met die aan, gebruikt het resultaat - en _ en slaat de opvulling over:
SELECT BASE64_ENCODE(0xCAFECAFE) AS standard;
SELECT BASE64_ENCODE(0xCAFECAFE, TRUE) AS url_safe;
Dezelfde vier bytes komen terug als yv7K/g== en yv7K_g. ClickHouse houdt de varianten als aparte functies, en zijn URL-veilige vorm laat ook de opvulling uit:
SELECT base64URLEncode('https://clickhouse.com') AS url_safe;
die aankomt als aHR0cHM6Ly9jbGlja2hvdXNlLmNvbQ, met de twee opvultekens van de standaardvorm eraf geknipt. Overal elders is het recept twee tekenvertalingen en een trim, en het loont de moeite om het één keer als databasefunctie te schrijven, omdat elke token-pipeline het nodig heeft. In PostgreSQL leest het zo:
SELECT rtrim(replace(replace(
encode(convert_to('https://clickhouse.com', 'UTF8'), 'base64'),
'+', '-'),
'/', '_'),
'=') AS url_safe;
Vertaal + naar -, vertaal / naar _, trim de opvulling achteraan, klaar. Eén waarschuwing voor de SQL Server-club: de url_safe-uitvoer is niet wat de eigen XML- en JSON-base64-decoders van de server verwachten, dus een kolom die in URL-veilige vorm voor de buitenwereld wordt ingepakt, wordt binnen de database met de ingebouwden niet uitgepakt. Houd de lezers in gedachten vóór je het alfabet kiest.
JWTs: tokens slaan vanuit de database
Het interessantste dat je met de encoder kunt bouwen is een JSON Web Token, want een JWT is niets anders dan drie base64-stukken in een rij: een header en een payload, beide JSON-objecten verpakt URL-veilig zonder opvulling, en een handtekening berekend over de eerste twee. Heeft een batch-job tokens nodig (een testomgeving bezaaien, verlopen API-credentials regenereren, een audit-feed bouwen), dan past de hele ceremonie in één PostgreSQL-query als je pgcrypto accepteert voor de HMAC (schakel hem een keer in met 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;
Elke stap is één van de zetten die dit artikel al heeft getoond: het JSON verpakken als base64, het omvormen naar het URL-veilige alfabet zonder opvulling, en dan de eerste twee stukken ondertekenen en de handtekening op dezelfde manier omvormen. Voor het JSON hierboven en het geheim sql-secret-key is het resultaat eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkRldiBVc2VyIn0.7_3VdmM8vH0L2bRBNqXXQXzZIR1l8T4_S4QxPPNBEFM, een token dat elke HS256-inspecteur accepteert. De kanttekeningen verdienen net zo veel aandacht als de truc: zij dekt alleen HMAC-algoritmen (HS256, HS384, HS512), steekt een gedeeld geheim in een database-statement, en is gebouwd voor batch- en auditwerk, niet voor een productie-token-service. De verificatiekant, waar je dat token aan zijn geheim bewijst, is een taak voor de applicatielaag of voor de handtekeningcontrole van het decoderingsartikel.
Afbeeldingen en bestanden in een tekstkolom
De meest voorkomende reden om in SQL te coderen is een bestand dat als tekst moet reizen: een API die de afbeelding inlijnt in plaats van ernaar te verwijzen, een export voor een systeem dat geen binair draagt, een seedscript dat een database op een nieuwe server herbouwt. DuckDB maakt de rondreis bijna triviaal, omdat het bestanden via een tafelfunctie die glob-patronen accepteert in BLOBs leest, en de encoder platst alles wat aankomt:
SELECT filename, to_base64(content) AS b64
FROM read_blob('/data/pics/*.png');
Eén rij per bestand, een platte base64-string per rij, geen omwikkeling om te verwijderen, al rijden de gebruikelijke één of twee opvultekens mee achteraan. Schrijf het resultaat in een tekstkolom en de afbeeldingen zijn draagbaar door elk kanaal dat tekst verplaatst. En dan komt het eerlijke kostenoverleg: een foto van 1 megabyte arriveert als zo'n 1,33 megabyte letters, en vanaf dan betaalt elke scan, sortering en indexitem die prijs. Bestuur je het schema, dan is het betere ontwerp een BLOB-kolom plus een codering aan de API-grens, waar alleen de bytes die écht het gebouw verlaten zich uitdresseren.
HTTP, JSON en API-verkeer
Headers en ladingen zijn waar base64 zijn stille alledaagse werk doet. Een Basic auth-header is de letterlijke prefix Basic gevolgd door de base64 van username:password, en het samenstellen ervan in SQL is een concatenatie plus één codering:
SELECT 'Basic ' || encode(convert_to('alice' || ':' || 's3cret', 'UTF8'), 'base64') AS header;
die Basic YWxpY2U6czNjcmV0 bouwt, de exacte header die een client zou versturen. Gebruik hem om de fixtures te genereren waarmee je integratietests vergelijken, of om een kolom bewaarde headers te normaleren voordat je ze auditeert. Aan de JSON-kant kan MySQL een veld inpakken en in een document inbedden in één enkele expressie, zonder applicatiecode in de lus:
SELECT JSON_OBJECT('img', TO_BASE64(CAST('hello file' AS BINARY))) AS doc;
Het resultaat is {"img": "aGVsbG8gZmlsZQ=="}, een verzendklaar payload. Dezelfde vorm werkt voor certificaten, open sleutels en elk ander bestand dat je API besloot in te lijnen, en het is de richting die uitmaakt wanneer jij degene bent die het verkeer produceert, niet de decoder van dat van iemand anders.
Configbestanden, geheimen en omgevingsvariabelen
Eén exportgewoonte verdient zijn eigen alinea, omdat ze overal is: het geheim dat als base64 in een config-tabel wordt bewaard. Kubernetes hield de gewoonte in leven, waar geheimen in rust base64 zijn zodat ze op één regel YAML passen zonder quotes, zonder regeleinden en zonder backslashes, en elk eigen config-systeem dat ooit een Kubernetes-pipeline tegenkwam nam hem over. De inpakrichting is één codering per waarde, met de binaire cast die het tekensetwerk doet, zodat de geëxporteerde tekst exact de bewaarde bytes is:
SELECT name, TO_BASE64(CAST(value AS BINARY)) AS for_config
FROM app_config
WHERE name LIKE '%_secret%';
Elke waarde tot 57 bytes komt uit als een platte, plakklare string; langer dan dat heeft één REPLACE() nodig om de omwikkelregels te verwijderen vóór ze in YAML gaat, en de nieuwe omgeving decodeert hem aan de overkant terug. Behandel het resultaat met de zorg die het verdient: je hebt zojuist een kolom bewaarde geheimen veranderd in een kolom bewaarde geheimen die elke mens in zo'n tien seconden kan lezen, en de base64-in-config-gewoonte krijgt een tweede blik op het moment dat je zo dicht bij de open text staat. Base64 is een vervoer, geen kluis. Heeft de omgeving een échte geheimenbewaarder, dan is de base64-kolom één migratie van die af.
E-mail en de 76-tekens-gewoonte
De 76-tekens-omwikkeling is ouder dan elke database op deze pagina. MIME, het stel normen dat e-mail binaire bijlagen laat dragen (RFC 2045, sectie 6.8, 1996), wikkelt base64-uitvoer om bij 76 tekens en eindigt elke regel met een retourteken en een regeleinde, omdat het oude e-mailnetwerk niet te vertrouwen was met langere regels. Drie encoders hier erfden de omwikkeling als hun standaard (MySQL, PostgreSQL, de SQLite CLI), wat een geschenk is voor alles dat in een e-mail belandde en een val voor alles dat dat niet deed. En ze erfden het halfklaar: PostgreSQL eindigt zijn regels met een eenzame regeleinde, niet met het retourteken en de regeleinde die de MIME-norm voorschrijft, dus uitvoer die in een echte e-mailbijlage geplakt zou moeten worden, heeft nog één ronde nodig:
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;
Verwijder de regeleinden die PostgreSQL al had toegevoegd, wikkel daarna opnieuw om bij 76 met een volledige CRLF na elke regel, en de string is MIME-correct in één expressie. Laat driehonderd bytes doorlopen en de 400 tekens die je inpakte worden 412: zes omwikkelde regels, zes CRLF-paren, twaalf tekens vervoersceremonie. Dezelfde vorm van probleem toont zich bij PEM-blokken, die omwikken bij 64 in plaats van 76, en bij de API's die helemaal geen omwikkeling willen omdat hun JSON-parser geen regeleinde in het midden van een veld wil tegenkomen. De regel voor al die gevallen: ontdek welk contract de consument ondertekende vóór je encodeert, want opnieuw omwikken van een kolom bewaarde base64 is een migratie, geen query.
Valkuilen: waar encoders je tegen praten
Elke val op deze lijst is er één die een specifiek dialect zet, niet één die base64 zet, en elke val heeft minstens één codebase die hem in productie vond:
- De niet-gevraagde omwikkeling. MySQL en PostgreSQL wikkelen hun uitvoer standaard om bij 76, de SQLite CLI bij 72, en niemand in je query vroeg ernaar. Het base64-veld in je JSON bevat nu regeleinden, en de consument die het formaat in de specificatie accepteert, wijst het in de praktijk af. De platte vorm is één
REPLACE()over het regeleinde-teken, toegepast aan de schrijfkant zodat de kolom bewaart wat de lezer wil. - De tekenset-misstap.
TO_BASE64('héllo')zonder de binaire cast codeert wat de verbindingstekenset gelooft dat de letters zijn, en eenlatin1-client en eenutf8mb4-client geloven verschillende dingen. Dezelfde query-tekst, twee verschillende base64-resultaten, en de verkeerde decodeert naar mojibake dat niemand terugtraceert naar de encoder. De binaire cast, of een explicieteconvert_to(), is de enige eerlijke versie van de query. - De BLOB-deur.
to_base64()van DuckDB wil eenBLOB: geef hem tekst en hij cast impliciet, zodat eenvarchar-kolom met een niet-UTF-8-codering in stilte de verkeerde bytes kan coderen. De eerlijke route isto_base64(encode(...)), en daarom tonen de voorbeelden op deze pagina voor tekst-invoer altijd het paar. - De 26.7-witruimteverandering.
base64Decode()enbase64URLDecode()van ClickHouse waren jarenlang strikt over invoer, maar vanaf 26.7 negeren ze witruimte (spatie, tab, line feed, retourteken, form feed) in plaats van die te weigeren, zodat een script dat vroeger op een omwikkelde kolom een fout gaf nu in stilte slaagt, wat op zich een soort regressie is. Controleer de versie van de server vóór je de decodering vertrouwt. - De 6000-byte-grens.
BASE64_ENCODE()van SQL Server geeftvarchar(8000)terug wanneer de invoer eenvarbinary(n)is met n van 6000 of minder, envarchar(max)daarboven; de toewijzing hangt af van de gedeclareerde grootte, niet van de waarde, zodat eenvarbinary(8000)-kolom zelfs bij drie bytesvarchar(max)teruggeeft. Deurl_safe-uitvoer van dezelfde functie is intussen niet leesbaar voor de eigen XML- en JSON-base64-decoders van de server, die het standaard alfabet met opvulling verwachten. Kies de variant voor het publiek dat hem zal lezen. - De 2000-byte RAW. In Oracle toppt een
RAW-waarde in een puur SQL-statement af op 2000 bytes. Omdat de encoderRAWneemt enRAWteruggeeft, kan een codering in één statement slechts zo'n 1500 bytes invoer accepteren (de 2000 tekens uitvoer zouden passen, meer niet), en kan een decodering in één statement slechts 2000 tekens base64 accepteren. Grotere ladingen verhuizen naar PL/SQL, waar eenRAW-variabele 32767 bytes bevat en één aanroep het grootste deel draagt, en alleen ladingen wier base64-uitvoer die grens zou overschrijden, hebben de blokloop nodig, die dateert van de jaren-negentig-limiet die nooit is verschoven. - De pakketbelasting. MySQL meet de gecodeerde string tegen
max_allowed_packet, niet de kale bytes. Een foto die met ruime marge in de tabel past, kan het pakket overstromen zodra hij 33 procent groter is en omgebroken, en de faalmode is een afgekapt ofNULL-waarde die eruitziet als datacorruptie. Controleer de limiet in één adem met de kolombreedte. - Het regeleinde achteraan.
base64()van de SQLite CLI eindigt zijn laatste regel met een regeleinde, de regel-eind-gewoonte toegepast op de allerlaatste regel. Plak de shell-uitvoer in een JSON-veld en je hebt een base64-string verstuurd met een regeleinde erin, de niet-gevraagde omwikkeling in een ander hoedje. - De alfabet-aanname. Een consument gebouwd voor het standaard alfabet ontmoet je URL-veilige uitvoer (of andersom) en ziet tekens die hij niet kent. De meeste decoders falen luidruchtig op de liggende streep; enkele falen in stilte door hem te slaan. Documenteer het alfabet van elke base64-kolom in de schema-commentaar, want de volgende ontwikkelaar zal zich niet herinneren welke token-pipeline de rij schreef.
Wanneer grootte écht uitmaakt
De groottemath is de platte lengte plus welke omwikkeling je encoder er ook bijvoegt, en de homepage doet de volledige afleiding van de verhouding. Wat hier de moeite waard is, is doornemen waar het getal stopt met nieuwsgierigheid te zijn. Een VARCHAR-kolom, gemaatvoet op het aantal bytes van de invoer, kappt de uitvoer in stilte af de eerste keer dat de lading lang genoeg is om extra ruimte nodig te hebben, want drie invoerbytes kosten vier tekens. Een index over een base64-tekstkolom betaalt de belasting twee keer: één keer in de opslag en opnieuw bij elke vergelijking, want de indexitems zijn de omwikkelde letters, niet de bytes. max_allowed_packet van MySQL en het 1-GB bytea-plafond van PostgreSQL zijn de twee muren die de meeste mensen eerst raken, en beide worden gecheckt tegen de tekst, die de grotere kant van de ruil is. Het ontwerpanywoord gaat zelden over het kiezen van een andere encoder (er is maar één base64); het gaat over het kiezen van waar de codering plaatsvindt. BLOB-kolom, hash-kolom voor opzoeken, coderen aan de grens: de base64 bestaat alleen in het verkeer, waar hij thuishoort.
Beveiliging: wat base64 niet is
Base64 is geen versleuteling, en de gewoonte die hardop gezegd moet worden is die uit de config-tabel van hierboven: een geheim dat als base64 wordt bewaard, is een geheim dat in een ander lettertype wordt bewaard. De transformatie is een bijectie zonder sleutel, in één functie-aanroep omkeerbaar door elke programmeertaal op aarde, en haar enige echte effect is dat de waarde op één regel YAML blijft. Bevat het dreigingsmodel nog een andere gebruiker van deze database, een andere service die de export leest, of een log dat de rij ving, dan levert base64 exact nul op voor de verdediging. Het ontdoekt de waarde voor het menselijk oog een paar seconden, en daarom voelt het als bescherming in een code review en faalt het in een incident. Versleutel wat geheim moet blijven, versleutel het met een sleutel die iemand écht geheim kan houden, en laat base64 het werk doen dat het goed kan: bytes verplaatsen door een kanaal dat alleen tekst draagt.
Wanneer elk dialect omwikken leerde
De release notes vertellen hetzelfde verhaal als de decoderingskant deed, alleen met de letters die de andere kant op gaan, en het schema zegt iets over elke engine:
2002. PostgreSQL 7.2 noemt base64 als eerste-klasse formaat van encode() en decode(), gelijktijdig met UTL_ENCODE van Oracle uit het 9i-tijdperk, en de oudste base64-machine van deze familie met een nauwe marge. Een database met een echt binair type en een formaatargument kwam er vroeg, omdat het antwoord één enum-waarde ver weg was.
Begin 2000s. Het UTL_ENCODE-pakket van Oracle komt in het 9i-tijdperk met BASE64_ENCODE() naast zijn MIME-header, quoted-printable- en uuecode-zusters. RAW erin, RAW eraf, en een kwarteeuw later heeft het pakket zijn mening niet gewijzigd.
2013. MySQL 5.6 voegt TO_BASE64() en FROM_BASE64() toe als een gematcht paar, en MariaDB 10.0 erft beide. Het contract van het paar is sindsdien niet verschoven: regels van 76 tekens op weg naar buiten, witruimtetolerantie op weg naar binnen.
2018. ClickHouse 18.16 (december 2018) brengt base64Encode() en base64Decode() samen met de MySQL-stijl alias, omdat de kolomaire wereld workloads importeerde wier log-schemas base64 al in zich droegen.
2023. SQLite 3.41.0 voegt base64() toe aan de commandoregel-shell als applicatie-gedefinieerde functie. De kernbibliotheek krijgt niets, zoals haar de gewoonte is; de shell, waar mensen écht aan SQLite-bestanden prikkelen, krijgt het gereedschap.
2025. SQL Server 2025, dat in november 2025 algemeen beschikbaar werd, brengt eindelijk BASE64_ENCODE() en BASE64_DECODE(), zesendertig jaar na de lancering van het product en een generatie nadat de gebruikers de XML-omweg bij de les hadden.
Het patroon is hetzelfde waarmee het decoderingsartikel eindigt, gespiegeld: de engines met een echt binair type en een formaatargument kregen base64 de dag dat de behoefte duidelijk was, en de engines waar alles een string is, planden het voor later in.
Kinkerige dingen die het weten waard zijn
- De
base64()van de SQLite CLI is de vormveranderaar van de familie, en in de coderingsrichting toont hij de truc het best: geef hem eenBLOBen hij geeft omwikkelde tekst terug met een regeleinde achteraan, geef hem tekst en hij geeft eenBLOBterug. Eén naam, twee banen, gekozen door het type van het argument, en geen andere encoder in deze familie kan het. - De
base64Decode()-familie van ClickHouse werd in 26.7 soepel: witruimte in de invoer wordt nu genegeerd in plaats van geweigerd, zodat dezelfde query op een omwikkelde kolom faalt op de oude server en in stilte een waarde teruggeeft op de nieuwe. De decoder is niet kapot; hij ontspant, en dat is hoe dan ook lastiger te debuggen. - PostgreSQL wikkelt om bij 76 tekens, exact zoals de MIME-norm van 1996, behalve dat het de regels eindigt met een eenzame regeleinde in plaats van het retourteken en de regeleinde van de norm. Twintig-plus jaar na de specificatie, per regel één teken minder, en de opstand is onzichtbaar tenzij je de bytes diff't.
- In de
mysql-client drukken de bytes die je codeert prima af als base64-tekst, maar het moment dat je metCAST(... AS BINARY)naar de kale kolom kijkt, schakelt de client over op hex-weergave (binary-as-hex), en een volledig goedhelloarriveert op het scherm als0x68656C6C6F. De instelling heeft duizenden ontwikkelaars ervan overtuigd dat hun encoder kapot is. - Snowflake toont
BINARY-waarden als hex in elke resultaatset, zodat eenTO_BINARY()-invoer-kolom in je codering-query eruitziet als een checksum, al werkte alles. Twee dialecten, twee hex-weergaven, één identiek gevoel van ongemak. - Het SQL-niveau
RAWvan Oracle is beperkt tot 2000 bytes, dus een certificaat van 3 kilobyte kan helemaal niet in een SQL-statement worden geplakt als eenRAW-literal. De codering moet in PL/SQL gebeuren, waar eenRAW-variabele 32767 bytes bevat, zodat het certificaat van 3 kilobyte één aanroep is, en alleen ladingen wier base64-uitvoer 32 kilobytes zou overschrijden de blokloop nodig hebben, die dateert uit de jaren negentig. - Eén regel MIME-base64 is 76 tekens, wat 57 kale bytes is, omdat vier tekens drie dragen. Het getal 76 dat in de standaardinstellingen van drie encoders verschijnt, is minder een limiet dan een pakdichtheid: elke omwikkelde regel die je in een oude e-mailbijlage ziet, droeg exact 57 bytes van jouw data.
De andere richting
Dit artikel ging over het aantrekken van het kostuum: beslissen welke bytes je bedoelt, kijken wat er uitkomt, het alfabet kiezen voor de lezers, en de groottemath doen vóórdat de kolom afkapte. Het kostuum afzetten is een volstrekt ander temperament, met stille NULLs waar het ene dialect zijn schouders ophaalt, harde fouten waar een ander zijn stem verheft, en een URL-veilig alfabet dat de helft van de familie helemaal niet kent. Alles samen, van FROM_BASE64() tot decode() tot BASE64_DECODE(), staat gedetailleerd in het gerelateerde artikel over Base64-decoderen in SQL, gelinkt rechtsonder. Codeer hier, decodeer daar, en de hele rondreis past in één middag.
Laatst bijgewerkt: 2026-10-06
Gerelateerd artikel: Base64-decodering in SQL: een complete gids