Bigger Than 2048
The warehouse wants free-form delivery instructions on every shipment note: gate codes, "ring twice and wait", three paragraphs of directions pasted from the customer portal. The first attempt added a Text[2048] field and lasted a week — the longer instructions did not fit, and the umlaut in Zürich came back as a question mark. In AL a Text field tops out at 2,048 characters; text of any length lives in a Blob. A Blob behaves unlike every other field: it does not travel with the row on Get or Find but is fetched on request, and it is read and written through streams that carry a text encoding — whose default is not the one you want.
What you are given
The starter ships table "Shipment Note" — submit it unchanged: "No." (Code[20], primary key), "Ship-to Name" (Text[100]) and "Delivery Instructions" (Blob). It also ships codeunit "Shipment Instructions" with three empty procedures — keep the names and these exact signatures:
procedure SetInstructions(NoteNo: Code[20]; Instructions: Text)
procedure GetInstructions(NoteNo: Code[20]): Text
procedure HasInstructions(NoteNo: Code[20]): BooleanRequirements
SetInstructionsstoresInstructionsin the"Delivery Instructions"Blob of the note whose"No."isNoteNo, encoded as UTF-8, and saves the record — afterwards any other record variable that reads the table finds the text there. Only the Blob changes: the note's other fields,"Ship-to Name"included, are left exactly as they were.- A later
SetInstructionson the same note replaces the earlier text entirely — nothing of the old text remains, however much longer it was. - An empty
Instructionsremoves the stored text: afterwardsHasInstructionsreturnsfalseandGetInstructionsreturns the empty text. GetInstructionsreturns exactly the text that was stored, decoded as UTF-8 — every character and every line break, CRLF and bare LF alike, with nothing trimmed and no line ending added at the end. For a note that holds no instructions it returns the empty text.HasInstructionsreturnstruewhen the note holds instructions andfalsewhen it holds none.- The texts are long — 5,000 characters in one test, well beyond any
Textfield — and contain characters from outside ASCII such asZürich — O'Brien. Every comparison is character for character. - Every
NoteNothe tests pass belongs to an existing note; what happens for an unknown number is not graded.
Where the naive version goes wrong
Three mistakes fail silently — no error, just wrong data — so know them before you start:
- A record fetched with
Getcarries an empty Blob.CalcFieldson the Blob field fetches its content; without itHasValueisfalseand a stream over the field reads nothing. This applies toGetInstructionsandHasInstructionsalike. CreateOutStreamandCreateInStreamon a Blob take an optionalTextEncoding, and their default isMSDos— an OEM code page with no room for an em dash, ałor a€. The tests write and read the Blob asTextEncoding::UTF8, so your streams must use it on both the write side and the read side.InStream.ReadTextstops at the first line ending, so a three-line text comes back as one line.InStream.Readinto aTextvariable reads until a zero byte or the end of the stream — andOutStream.WriteTextwrites no zero byte, so that pair returns the text whole.
Two more things the row never does for you: writing into the stream changes only your record variable — the table sees nothing until Modify — and clearing a Blob is Clear on the field followed by Modify, not a stream with nothing written to it. And fetch the row with Get before you write to it: a record variable that only had its "No." assigned still Modifys without complaint, and saves blanks into every other field of the note.
Pick object IDs in the house range 50100–50199 and reference other objects by name, never by ID.
What the tests check
The tests create "Shipment Note" rows and grade each procedure against the table itself, not only against its sibling: one test stores a generated 5,000-character text through SetInstructions and reads the Blob straight from the table (with CalcFields and a UTF-8 stream); another calls SetInstructions and then checks that the note's "Ship-to Name" still holds the value it was created with; another writes a 3,000-character text straight into the Blob and expects GetInstructions to return it. Zürich — O'Brien & Søn, Łódź (€25 COD) is checked in both directions the same way, so an MSDos stream on either side fails. A text with CRLF breaks, an empty line and a bare LF must round-trip through SetInstructions and GetInstructions unchanged; a second SetInstructions with a shorter text must leave only the shorter text; an empty text must clear the note; and HasInstructions is checked against a Blob the test filled directly, against a bare note, and after clearing. Failure messages report both lengths and a window around the first differing character, with CR and LF shown as <CR> and <LF>.
Learn More
- Blob data type — the field type for text of any length, and the stream methods that read and write it.
- Blob.CreateOutStream method — the optional encoding argument and its MSDos default;
CreateInStreammirrors it. - Record.CalcFields method — also the way to retrieve a Blob's content into a record variable.
- Write, WriteText, Read, and ReadText method behavior for line endings and zero terminators — which read method stops where, with a worked Blob example.
Get the field is empty until you CalcFields it, and that goes for HasInstructions as much as for GetInstructions. On the way in, the stream changes only your record variable — the table sees nothing until Modify.CreateOutStream and CreateInStream both default to MSDos encoding, which has no room for an em dash, a ł or a €. Pass TextEncoding::UTF8 on both sides — the tests write and read the Blob as UTF-8.ReadText stops at the first line ending; Read into a Text variable, with no length given, reads a stream written by WriteText to its end. For an empty text, Clear the Blob field before Modify instead of writing zero bytes into it.