Give a FROM table an alias, accept qualified column names such as u.name and u.*, and resolve every name in a SELECT to one entry of the FROM before planning, so that explain logical prints each column with the name of the entry it belongs to.
The next stage joins two tables, and the first thing a join needs is a way to say which id you mean. users and orders both have one. So before writing a single join, droid learns to name things: a table in the FROM can get a short alias, and a column can be qualified by it, u.id.
That sounds like parsing, and the parsing is the easy part. The real change is a step between parsing and planning that has existed quietly since the first SELECT: looking up every name and deciding what it means. Real engines call it the binder (or the analyser). Until now it had one table to look in. From here it has several, and later, with subqueries, it has nested scopes. This stage builds it while there is still only one table, so that nothing else changes at the same time.
Each table in a FROM is an entry, and an entry has a name: its alias if it has one, otherwise its table's name.
In from users u, the entry reads table users and is called u. A qualified name u.c looks for the entry called u, then for column c in its table.
Once a table has an alias, the alias is its only name: select users.id from users u is an error, 01701, as in the SQL standard. This is what will let the same table appear twice in one query later, users a join users b: two entries, one table, two names.
A bare name such as name is looked up in every entry's table. With one entry that is what has always happened.
Binding resolves each reference once, and stores the answer: entry and column. Everything after it works from that answer, never from the spelling. Two things follow.
Headers come from the column. select u.name from users u prints the header name, as select name from users always has. Only an expression keeps its typed form, u.id * 2.
Spellings do not matter. With from emp e, group by e.dept and a select list that says dept name the same column, so the select list may show it. The GROUP BY rule of the group-by stage compared expressions by how they print; now a column compares by what it is bound to.
explain logical is where you can see the binder's work. It prints every column with its entry's name in front, even the ones typed bare:
The physical plan of explain still prints expressions as typed, and a scan of an aliased entry adds alias=u. A bound written u.id = 1 is a key bound like id = 1.
This changes one earlier output: explain logical used to print columns as typed. The format case that checked it is retired in this stage.
Resolve every name once, before planning.
These are the exact lines the tests check. The full definition is on the output contract page, section 66.
| Input | Output |
|---|---|
select u.name from users u; | header name |
select u.*, u.id * 2 from users u ...; | header id | name | dept | u.id * 2 |
select users.id from users u; | [ERROR:01701] ... |
select u.nope from users u; | [ERROR:00402] ... |
explain logical select name from users where dept = 'eng'; | Project (cols=[users.name]), Filter (users.dept = 'eng'), Get (table=users) |
explain select u.name from users u where u.id = 2; | Project (cols=[u.name]), IndexScan (table=users, alias=u, key=2) |
tokenize u.name | [IDENTIFIER - u], [SYMBOL - .], [IDENTIFIER - name] |
ast select name from users u; | Statement: SELECT, Table: users AS u, Columns: [name] |
$droid --db names.dbWelcome to droid!>create table users (id int primary key, name varchar(20));Create table statement executed successfully>insert into users (id, name) values (1, 'ada');Insert statement executed successfully (1 rows)>select u.id, u.name from users as u where u.id = 1;id | name1 | ada(1 rows)[EXEC] rows_examined=1>select users.name from users u;[ERROR:01701] no FROM entry is called users>.exitexiting.. good bye!
u.name. The header is the column's name.e.dept and dept are one column once bound.u . name differently from u.name. They are the same three tokens.from users where ..., where starts the WHERE.The page above is the same for every language. These are the only language-specific hints.
{entry, column} stored in the expression node next to its typed name.