Parse ALTER TABLE ADD COLUMN with NOT NULL and DEFAULT, change only the catalog, and read every row written before the change as if it had the new column, filled with its default.
Tables change. A product gains a discount column; a user table gains last_login. The obvious way to add a column is to read every row, add the new value, and write it back. On a table of a hundred million rows that takes hours, holds a transaction open the whole time, and doubles the log. Real databases avoid it, and so will you: ALTER TABLE ADD COLUMN changes the catalog and nothing else.
The stage closes Querying because it needs NULL from this part, as the value a new column reads as by default, and because every reader this part built, from the WHERE to GROUP BY, must see the new column.
That works because of a decision made back in Flexible Storage. Every row has stored its own column_count since varlen-rows, even though, until now, it always equalled the table's. Now it can be smaller.
After alter table t add column active int default 1, the catalog says t has three columns, while every row on disk still says it has two. The reader reconciles them: it decodes the columns the row has, and gives every column after them its default.
The bytes stay as they are until something writes the row. An UPDATE decodes it, defaults included, and writes it back with every column the table has, so the row grows to full width in place. Rows that are never updated stay short forever, and that is fine.
A default is the value a column takes when nothing gives it one. It arrives with ALTER because ALTER needs it most, but the same idea already existed in two other places, and now they agree:
create table p (id int, active int not null default 1).nulls stage made that NULL; NULL is simply the default of a column that declares none.A DEFAULT is checked like a value an INSERT would give the column: an INT takes a number, a VARCHAR(n) a string of at most n bytes. The first column, the key, cannot have one (01104): every row needs its own key.
alter table t add column x int not null on a table with rows would give every existing row a NULL in a column that promises none. That is 01104. With a DEFAULT, not null default 0, the old rows read 0 and the promise holds. On an empty table there is no row to break the promise, so it is allowed, and every later INSERT must give x a value (01503).
The test that this stage does no rewrite is the transaction counter from Transactions and Recovery. Inside a transaction, ALTER on a table of 300 wide rows, spread over dozens of leaves, changes exactly one page:
>begin;Begin statement executed successfully>alter table w add column x int default 7;Alter table statement executed successfully>tx status[TX] Status: IN_TRANSACTION pages=1>commit;Commit statement executed successfully
Because that page is a catalog page like any other, ROLLBACK takes the ALTER back with the rest of the transaction, and the table has its old columns again.
The whole statement is one change to one catalog row.
Since varlen-rows, every row has stored its column_count. Until now it always equalled the table's.
Old rows stay short until something writes them.
These are the exact lines the tests check. The full definition is on the output contract page, section 50.
| Input | Output |
|---|---|
alter table t add column active int default 1; | Alter table statement executed successfully |
catalog list after it | [CATALOG] t root=2 (id INT, name VARCHAR(20), active INT DEFAULT 1) |
btree find t 2 for a row written before | [BTREE] Row: id=2 name='b' active=1 |
ast alter table t add column active int not null default 1; | Statement: ALTER TABLE, Table: t, Add: active int NOT NULL DEFAULT 1 |
ast create table p (id int, note varchar(5) default 'hi'); | Columns: [id int, note varchar(5) DEFAULT 'hi'] |
explain alter table t add column x int; | [PLAN] alter table t add column x int, [PLAN] └── AlterTable (table=t) |
tx status after an ALTER in a transaction | [TX] Status: IN_TRANSACTION pages=1 |
| NOT NULL without DEFAULT on a table with rows; DEFAULT on the first column | [ERROR:01104] <message> |
not null default null | [ERROR:01503] <message> |
| a column the table already has | [ERROR:00408] <message> |
| a DEFAULT of the wrong type; out of 0 to 4294967295; longer than the column | [ERROR:00403], [ERROR:00404], [ERROR:00405] <message> |
alter, add, column and default are keywords. The catalog prints NOT NULL and then DEFAULT <v> after a column's type, the value written as a literal.
$droid --db shop.dbWelcome to droid!>select * from items;id | name1 | lamp2 | desk(2 rows)[EXEC] rows_examined=2>alter table items add column stock int not null default 0;Alter table statement executed successfully>alter table items add column note varchar(20);Alter table statement executed successfully>insert into items (id, name, stock) values (3, 'chair', 5);Insert statement executed successfully (1 rows)>update items set stock = 2 where id = 1;Update statement executed successfully (1 rows)[EXEC] rows_examined=1>select * from items;id | name | stock | note1 | lamp | 2 | NULL2 | desk | 0 | NULL3 | chair | 5 | NULL(3 rows)[EXEC] rows_examined=3>alter table items add column sku int not null;[ERROR:01104] Cannot add a NOT NULL column without a DEFAULT to a table with rows>.exitexiting.. good bye!
Row 2 was never touched: it is still two columns long on disk, and reads 0 and NULL from the catalog. Row 1 was updated, so it is now stored at full width. The messages are yours to word; the tests check only the codes.
tx status shows dozens of pages and a large table takes as long as copying it.column_count, then fill in.select * shows NULL for an old row whose column has DEFAULT 1.NOT NULL without a default.The page above is the same for every language. These are the only language-specific hints.
Option<Value> on each column for its DEFAULT; the decoder can extend the decoded row with the defaults of the remaining columns.column_count followed by a copy of the remaining defaults.