Build the loop every later stage plugs into: prompt, read one line, decide what it is, answer it, and keep going — including when the answer is an error.
Every other stage in this tutorial is reached through this loop. The lexer, the pager, the B+Tree, transactions — none of them has a user interface of its own. They are all things that happen between a line arriving and an answer being printed.
That makes the loop worth getting right once, because two of its properties are load-bearing for everything after it. The first: an error is an answer, not a stop. A database that exits when you type a bad query is a database you cannot use, and every later stage produces errors — a malformed statement, a full page, a duplicate key, a transaction that cannot commit. If the loop dies on the first one, none of those stages can be tested. The second: the exit status is a promise to whoever is running you. The tests for every later stage run your program as a subprocess and check that status.
There is a third reason, smaller but immediate: this stage is where you print your first error, so it is where the error format is settled. One line, a code that says what went wrong, and a message you are free to word however you like.
Notice what is not in the diagram: there is no path from an error to the end. Every answer, including an error, leads back to the prompt. The only two ways out are .exit and end of input, and both of them take the same exit.
A line beginning with . is a meta command: an instruction to the program itself rather than to the data. .exit and .help are the two you implement here; later stages add .mode. They are answered by the loop and never reach the query machinery.
Everything else is a statement, and eventually goes to the lexer and the parser. Not yet, though — there is nowhere to send it. So at this stage the loop recognises meta commands and rejects everything else, which is a real answer and not a placeholder: an unknown command is exactly what a line of SQL is to a program that has no SQL in it.
Three steps repeated until the input runs out. The order matters: the prompt belongs before the read, not after the answer, or a piped session shows its prompts in the wrong places.
>, with no space and no newline after it. Make sure it reaches the terminal before you block on the read — output with no newline can sit in a buffer, and a prompt nobody sees is a program that looks hung..exit does..exit..exit or end of input; nothing a user can type ends it any other way.Everything the loop can be given falls into four cases, and later stages only ever add to the last one.
.exit: end the session..help: print the three lines of help text, exactly as the contract gives them..: it is a meta command you do not have. Report error 00103.The same evaluator serves a session and a single command. What differs is the wrapping and the status.
-c "<command>", answer that one command and stop. Print no welcome line, no prompt, and no goodbye: the output is meant for a script reading it..exit or end of input, even if commands inside it failed. The session did its job.-c command printed an error, when an option is missing its argument, or when the option is not one you know.These are the exact lines the tests check. The full definition is on the output contract page, sections 2 and 3.
| Input | Output |
|---|---|
| a session starting | one line beginning Welcome to droid |
| before each read | the prompt >, no space, no newline after it |
.help | Available commands:, .exit - Exit the program, .help - Show this help message |
.exit or end of input | exiting.. good bye!, then exit 0 |
| an empty line | nothing |
.foo | [ERROR:00103] <message> |
| any other unrecognised line | [ERROR:00105] <message> |
-c with no argument | [ERROR:00102] <message>, exit 1 |
| an unknown option | [ERROR:00101] <message>, exit 1 |
An error is one line: [ERROR:SSCCC] message. The five digits are fixed by the contract and the tests assert them; the message after them is yours to word, and a second line starting HINT: may follow if you want to be helpful. Errors go to standard output, like everything else, so that a test reading one stream sees the session in the order it happened.
Why numbers rather than sentences? Because a message is for a person and a code is for everything else — a test, a log, a support question, a future you grepping the source. Sentences get reworded; 00103 does not. The first two digits say which part of the program noticed (01 is the command line and the loop), the last three say what it noticed.
Welcome to droid!>.helpAvailable commands:.exit - Exit the program.help - Show this help message>> .helpAvailable commands:.exit - Exit the program.help - Show this help message>.foo[ERROR:00103] Unknown meta command: .foo>select * from users;[ERROR:00105] Unknown command: select>.exitexiting.. good bye!
Three things to notice. The empty line printed nothing — the prompt after it is the only trace. The indented .help worked, because the line was trimmed before it was compared. And the session ended normally after two errors: they were answers, not accidents.
From a script, without a session around it:
$droid -c ".help"Available commands:.exit - Exit the program.help - Show this help message$echo $?0$droid -c "select * from users;"[ERROR:00105] Unknown command: select$echo $?1
.exit never matches. The line still carries its newline, or its leading spaces. Trim before comparing, and the trimmed-help-with-spaces test is the one that catches it..exit does. Piped input hits this every time.-c whose command failed exits 1. The status describes the request, not the crash.The page above is the same for every language. These are the only language-specific hints.
getline allocates and grows the buffer for you; fgets with a fixed array is where the 4096-byte case goes wrong. fflush(stdout) after the prompt.std::getline(std::cin, line) returns a falsy stream at end of input, which is the loop's exit condition. std::cout << std::flush after the prompt.BufRead::read_line returns Ok(0) at end of input — a zero-length read, not an error. io::stdout().flush() after the prompt.readUntilDelimiterOrEof returns null at end of input. Remember the allocator for the line, and flush the writer after the prompt.