|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: "Base 64 Encoding and Decoding with DB2" |
| 4 | +date: 2023-10-12 |
| 5 | +--- |
| 6 | + |
| 7 | +## Base64 Encoding and Decoding on IBM i |
| 8 | + |
| 9 | +Base64 encoding is a method for converting binary data into ASCII text. It's designed to prevent communication errors when transferring binary information. |
| 10 | + |
| 11 | +The IBM i operating system provides two DB2 services that allow you to BASE64 encode and decode. |
| 12 | + |
| 13 | +In this post we will discuss how to use these DB2 services, and why CCSID is an important consideration. The examples given will utilize embedded SQL in an SQLRPGLE program. We will make use of a service program exported procedure named 'print_this' that will be discussed in future post about service programs. |
| 14 | + |
| 15 | +### The IBM i DB2 services for base64 encoding and decoding |
| 16 | + |
| 17 | +[BASE64_ENCODE](https://www.ibm.com/docs/en/i/7.5?topic=functions-base64-encode) - returns the Base64 encoded version of a binary value. |
| 18 | + |
| 19 | +[BASE64_DECODE](https://www.ibm.com/docs/en/i/7.5?topic=functions-base64-decode) - returns a character string that has been Base64 decoded |
| 20 | + |
| 21 | +### Trying it out |
| 22 | + |
| 23 | +First lets encode a string 'MyText' in base64 using a simple SQLRPGLE program. As mentioned above, we will make use of service program sub-procedure named 'print_this' that simply prints 2 strings passed as parameters to a spoolfile. |
| 24 | + |
| 25 | +``` text |
| 26 | +**free |
| 27 | +ctl-opt actgrp(*new) bnddir('BNDUTIL'); |
| 28 | +
|
| 29 | +// Start of Main Procedure |
| 30 | +// Declare some stand-alone fields |
| 31 | +Dcl-S PlainText VARCHAR(100) INZ('MyText'); |
| 32 | +Dcl-S EncodedText VARCHAR(100); |
| 33 | +
|
| 34 | +/copy ./qptypesrc/printer.rpgleinc |
| 35 | +
|
| 36 | +// Print headings |
| 37 | +print_this('Field Name' : 'Value'); |
| 38 | +print_this('---------------------' : '-----------------------------------------------------------'); |
| 39 | +
|
| 40 | +print_this('PlainText' : %Trim(PlainText)); |
| 41 | +
|
| 42 | +// Encode the text into a VARCHAR field |
| 43 | +Exec SQL Values QSYS2.BASE64_ENCODE(:PlainText) Into :EncodedText; |
| 44 | +print_this('EncodedText' : %trim(EncodedText)); |
| 45 | +
|
| 46 | +return; |
| 47 | +``` |
| 48 | + |
| 49 | +Spoolfile Output: |
| 50 | + |
| 51 | +``` text |
| 52 | +Field Name Value |
| 53 | +--------------------- -------------------------------------------------------- |
| 54 | +PlainText MyText |
| 55 | +EncodedText 1Kjjhaej |
| 56 | +``` |
| 57 | + |
| 58 | +Now, lets check that against a base64 encoder on the web: [https://www.base64encode.org/](https://www.base64encode.org/) |
| 59 | + |
| 60 | +>  |
| 61 | +
|
| 62 | +The encoded value from our RPG program doesn't match the encoded value from www.base64encode.org. The reason they don't match, is because of CCSID. CCSID, or Coded Character Set Identitifier, is what uniquely identifies the specific encoding of a code page. |
| 63 | + |
| 64 | +If you read the documentation for base64_encode, it gives examples of base64 encodeing for both the system default EBCDIC code page and UTF-8 (code page 1208) CCSID. ON IBM i, the default character set is EBCDIC, with the specific code page varying by region. |
| 65 | + |
| 66 | +> On IBM i, ebcdic is the default character set. |
| 67 | +
|
| 68 | +The code page for your machine is stored in a system value called QCCSID. When your IBM i was delivered, it came with value 65535 - which isn't a real code page. 65535 is something IBM i uses to specify all character data tagging support support is turned off. IBM recommends this value be changed. In North America, the standard ebcdic code page for IBM i is 37. For more information, read up on QCCSID [here](https://www.ibm.com/docs/en/i/7.5?topic=values-coded-character-set-identifier-qccsid-system-value). |
| 69 | + |
| 70 | +With that understanding, if you want to base64 encode / decode like the rest of the world, and you probably do, you need to make a CCSID adjustments to your code: |
| 71 | + |
| 72 | +``` text |
| 73 | +**free |
| 74 | +ctl-opt actgrp(*new) bnddir('BNDUTIL'); |
| 75 | +
|
| 76 | +// Start of Main Procedure |
| 77 | +// Declare some stand-alone fields |
| 78 | +Dcl-S PlainText VARCHAR(100) INZ('MyText') CCSID(1208); |
| 79 | +Dcl-S EncodedText VARCHAR(100); |
| 80 | +
|
| 81 | +/copy ./qptypesrc/printer.rpgleinc |
| 82 | +
|
| 83 | +// Print headings |
| 84 | +print_this('Field Name' : 'Value'); |
| 85 | +print_this('---------------------' : '-----------------------------------------------------------'); |
| 86 | +
|
| 87 | +print_this('PlainText' : %Trim(PlainText)); |
| 88 | +
|
| 89 | +// Encode the text into a VARCHAR field |
| 90 | +Exec SQL Values QSYS2.BASE64_ENCODE(:PlainText) Into :EncodedText; |
| 91 | +print_this('EncodedText' : %trim(EncodedText)); |
| 92 | +
|
| 93 | +return; |
| 94 | +``` |
| 95 | + |
| 96 | +Spoolfile Output: |
| 97 | + |
| 98 | +``` text |
| 99 | +Field Name Value |
| 100 | +--------------------- -------------------------------------------------------- |
| 101 | +PlainText MyText |
| 102 | +EncodedText TXlUZXh0 |
| 103 | +``` |
| 104 | + |
| 105 | +In the code above, the only modification needed was to add the CCSID keyword to the definition for `PlainText`. I used the specific value of `1208`, but could also have used `*UTF8`. IBM's documentation suggests another method, which is to CAST the CCSID inline: |
| 106 | + |
| 107 | +``` |
| 108 | +VALUES QSYS2.BASE64_ENCODE (CAST(EncodedText AS VARCHAR(10) CCSID 1208)) Into :EncodedText; |
| 109 | +``` |
| 110 | + |
| 111 | +### And Now to Decode |
| 112 | + |
| 113 | +Lets add a few more lines to our example that decode the now encoded message. |
| 114 | + |
| 115 | +``` text |
| 116 | +**free |
| 117 | +ctl-opt actgrp(*new) bnddir('BNDUTIL'); |
| 118 | +
|
| 119 | +// Start of Main Procedure |
| 120 | +// Declare some stand-alone fields |
| 121 | +Dcl-S PlainText VARCHAR(100) INZ('MyText') CCSID(1208); |
| 122 | +Dcl-S EncodedText VARCHAR(100); |
| 123 | +Dcl-S DecodedTextVarBinary sqltype(VARBINARY:100); |
| 124 | +Dcl-S TranslatedTextVarChar VARCHAR(100) CCSID(1208); |
| 125 | +
|
| 126 | +/copy ./qptypesrc/printer.rpgleinc |
| 127 | +
|
| 128 | +// Print headings |
| 129 | +print_this('Field Name' : 'Value'); |
| 130 | +print_this('---------------------' : '-----------------------------------------------------------'); |
| 131 | +
|
| 132 | +print_this('PlainText' : %Trim(PlainText)); |
| 133 | +
|
| 134 | +// Encode the text into a VARCHAR field |
| 135 | +Exec SQL Values QSYS2.BASE64_ENCODE(:PlainText) Into :EncodedText; |
| 136 | +print_this('EncodedText' : %trim(EncodedText)); |
| 137 | +
|
| 138 | +// Decode the encoded text into an EBCDIC VarBinary field |
| 139 | +Exec SQL Values QSYS2.BASE64_DECODE(:EncodedText) Into :DecodedTextVarBinary; |
| 140 | +print_this('DecodedTextVarBinary' : %trim(DecodedTextVarBinary)); |
| 141 | +
|
| 142 | +// Translate binary data in EBCDIC to UTF-8 |
| 143 | +TranslatedTextVarChar = DecodedTextVarBinary; |
| 144 | +print_this('TranslatedTextVarChar' : %trim(TranslatedTextVarChar)); |
| 145 | +
|
| 146 | +return; |
| 147 | +``` |
| 148 | + |
| 149 | +Spoolfile Output: |
| 150 | + |
| 151 | +``` text |
| 152 | +Field Name Value |
| 153 | +--------------------- -------------------------------------------------------- |
| 154 | +PlainText MyText |
| 155 | +EncodedText TXlUZXh0 |
| 156 | +DecodedTextVarBinary (`èÁÌÈ |
| 157 | +TranslatedTextVarChar MyText |
| 158 | +``` |
| 159 | + |
| 160 | +In the code above, we first define a field of type `sqltype(VARBINARY:100)` to recieve the decoded text. We define another field of type `VARCHAR(100) CCSID(1208)` which allows us to translate the binary ebcdic data back to UTF-8 text. Unfortunately I haven't found a way to do this in one step. If you know of a better way, please mention it in the comments below. |
| 161 | + |
| 162 | +In summary, while care must be given when defining fields with the correct CCSID values and field types, the IBM i operating system does give us the features we need to encode and decode in base64 just like the rest of the world. |
| 163 | + |
0 commit comments