Check a WHERE clause before reading any row, then test each row the cursor gives against it and print only the rows that match, comparing ids as unsigned numbers and text byte by byte.
SELECT can read the whole table. Nobody wants the whole table. where id = 42, where name = 'ada', where id > 1000: every real query asks for some of the rows, and this stage gives SELECT the one tool it needs for that, a predicate, a yes-or-no question asked of each row.
The parser has been reading WHERE clauses since the parser stage and printing them in ast. Now they do something.
This stage does it the simple way: read every row, keep the ones that match. For where id = 42 in a table of a million rows that reads a million rows to return one, and the tree could have gone straight to it. That is on purpose. First make WHERE correct for every column and every operator. Later in this part, index-scan makes it fast for id, and the answers must not change when it does.
The scan does not change: the cursor still visits every row from the first leaf to the last. What changes is that each row passes through the predicate before it is printed.
A WHERE clause in this part is one condition: a column, an operator (=, < or >) and a value. There is no AND, no OR, no <=: the grammar has none of them yet.
id is a number, and compares as one: 9 is below 10, and 4294967295 is above everything, because ids are unsigned.
name and email are text, and compare byte by byte, the way they are stored. Compare the first bytes; if they differ, that decides it. If they are equal, move to the next byte. Here are 'Bob' and 'ada' as stored in the row:
Three consequences follow, and the tests check each of them:
'Bob' and 'bob' are different names, where name = 'BOB' does not find 'bob', and 'Bob' < 'ada'.'bo' and 'bob' agree for two bytes, then 'bo' ends. The shorter string is the smaller one: 'bo' < 'bob' < 'bobby'. The empty string is smaller than every other.'user4' and 'user38' differ at their fifth byte, 4 against 3, so 'user4' > 'user38', whatever the numbers say.Stored strings end at their first zero byte, and the zeros that pad the field are not part of the string. Compare the string, not the 28-byte field.
A WHERE can be wrong in three ways, and none of them depends on the rows: the column does not exist (00402), the value has the wrong type (00403: where id = 'one', where name = 5), or an id does not fit in 32 bits (00404). Check them all before reading anything. A statement with several things wrong reports the first, in this order: the table, the select list, the WHERE column, the value's type, its range. One line, and nothing else.
A string value longer than 27 bytes is not on that list. No stored string is that long, so = simply finds nothing, and < and > compare as usual.
Everything that can be wrong with a SELECT is wrong before any row is read, so it is checked first, in a fixed order.
The predicate compares one field of the row with the value, and answers yes or no.
SELECT does not change how it reads the table. It asks the predicate about each row before printing it.
These are the exact lines the tests check. The full definition is on the output contract page, sections 3 and 10.
| Input | Output |
|---|---|
select <columns> from users where <column> <op> <value>; | the header of the select list, the matching rows in ascending id order, then (<n> rows) counting them |
| the same, when no row matches | the header, then (0 rows) |
a WHERE column that users does not have | [ERROR:00402] <message> |
a string compared with id, a number with name or email | [ERROR:00403] <message> |
an id value past 4294967295 | [ERROR:00404] <message> |
$droid --db users.dbWelcome to droid!>insert into users (id, name, email) values (4, 'bob', 'bob@example.com');Insert statement executed successfully>insert into users (id, name, email) values (1, 'ada', 'ada@example.com');Insert statement executed successfully>insert into users (id, name, email) values (3, 'Bob', 'bob@work.example');Insert statement executed successfully>insert into users (id, name, email) values (2, 'bo', 'bo@example.com');Insert statement executed successfully>select * from users where id > 2;id | name | email3 | Bob | bob@work.example4 | bob | bob@example.com(2 rows)>select * from users where name < 'bob';id | name | email1 | ada | ada@example.com2 | bo | bo@example.com3 | Bob | bob@work.example(3 rows)>select email from users where name = 'bob';bob@example.com(1 rows)>select * from users where id = 7;id | name | email(0 rows)>select * from users where id = 'one';[ERROR:00403] Column id expects a number>select * from users where phone = 1;[ERROR:00402] Unknown column: phone>.exitexiting.. good bye!
name < 'bob' returns ada, bo and Bob, and not bob itself. The WHERE column, name, is not in the select list of the third query, and does not have to be.
where id > 5 misses 4294967295, which a signed 32-bit number reads as −1.'bo' equal to nothing and sorts strings by their padding. Compare up to the string's end.'bobby' < 'carol', though it is the longer string.'bob' for where name = 'BOB'. Bytes do not fold.< that includes the value. where id < 4 must not print row 4.(0 rows) is a correct answer. Print the header and the footer.where name > 'user38' compares text. Converting names to numbers, or sorting the output, gives the wrong rows.The page above is the same for every language. These are the only language-specific hints.
strcmp compares byte by byte as unsigned char, and stops at the zero byte, which is exactly the rule here. memcmp over the whole field is not: it compares the padding too.&[u8] slices with <, == or cmp is byte-by-byte comparison with the shorter-is-smaller rule. Take the slice up to the first zero byte first.std.mem.order(u8, a, b) gives the byte order of two slices; trim each at its first zero byte first.