|
| 1 | +In this stage, you'll implement the `.tables` [dot command](https://www.sqlite.org/cli.html#special_commands_to_sqlite3_dot_commands_), which prints the names of the user tables in a SQLite database. |
| 2 | + |
| 3 | +### The `sqlite_schema.tbl_name` column |
| 4 | + |
| 5 | +The names of the tables in a SQLite database are stored in the `tbl_name` column of the [`sqlite_schema`](https://www.sqlite.org/schematab.html) table. The `sqlite_schema` [page](https://www.sqlite.org/fileformat.html#b_tree_pages) stores the rows of the `sqlite_schema` table in chunks of data called "cells." Each cell contains a single row. You need to read all the cells and extract the value of `sqlite_schema.tbl_name` from each one. |
| 6 | + |
| 7 | +### Cell pointer array |
| 8 | + |
| 9 | +To figure out where the cells are located, read the `sqlite_schema` page's cell pointer array. This array specifies the offsets of every cell on the page. Here's what you need to know: |
| 10 | + |
| 11 | +- The array appears directly after the page header. |
| 12 | +- The elements (offsets) are 2-byte big-endian values. |
| 13 | +- The offsets are relative to the start of the page. |
| 14 | +- The array size is equal to the number of cells on the page. (The page header specifies the number of cells on the page.) |
| 15 | + |
| 16 | +### Cell |
| 17 | + |
| 18 | +Once you have all the offsets, you can read the cells. The type of cell on the `sqlite_schema` page is called a "table b-tree leaf cell." It's made up of three parts: |
| 19 | + |
| 20 | +1. The size of the record, in bytes (varint) |
| 21 | +2. The rowid (varint) |
| 22 | +3. The record (record format) |
| 23 | + |
| 24 | +Cells use variable-length integers, also called "varints." See the [official documentation](https://www.sqlite.org/fileformat.html#b_tree_pages) to learn how they work. |
| 25 | + |
| 26 | +You can ignore the rowid—it's not relevant to this stage. |
| 27 | + |
| 28 | +The part you're interested in is the record. "Record" is just another word for "row." That's the part that contains the `sqlite_schema.tbl_name` column. |
| 29 | + |
| 30 | +#### Record format |
| 31 | + |
| 32 | +Records are stored in [record format](https://www.sqlite.org/fileformat.html#record_format): |
| 33 | + |
| 34 | +1. Header: |
| 35 | + 1. Size of the header, including this value (varint) |
| 36 | + 2. Serial type code for each column in the record, in order (varint) |
| 37 | +2. Body: |
| 38 | + 1. The value of each column in the record, in order (format varies based on serial type code) |
| 39 | + |
| 40 | +A "serial type code" specifies the data type and size of a column. See the [official documentation](https://www.sqlite.org/fileformat.html#record_format) for the table of all serial type codes. |
| 41 | + |
| 42 | +#### Example |
| 43 | + |
| 44 | +The following is a cell from page 1 of `sample.db`: |
| 45 | +``` |
| 46 | +00000ec0 78 03 07 17 1b 1b 01 81 47 74 61 62 6c | x.......Gtabl| |
| 47 | +00000ed0 65 6f 72 61 6e 67 65 73 6f 72 61 6e 67 65 73 04 |eorangesoranges.| |
| 48 | +00000ee0 43 52 45 41 54 45 20 54 41 42 4c 45 20 6f 72 61 |CREATE TABLE ora| |
| 49 | +00000ef0 6e 67 65 73 0a 28 0a 09 69 64 20 69 6e 74 65 67 |nges.(..id integ| |
| 50 | +00000f00 65 72 20 70 72 69 6d 61 72 79 20 6b 65 79 20 61 |er primary key a| |
| 51 | +00000f10 75 74 6f 69 6e 63 72 65 6d 65 6e 74 2c 0a 09 6e |utoincrement,..n| |
| 52 | +00000f20 61 6d 65 20 74 65 78 74 2c 0a 09 64 65 73 63 72 |ame text,..descr| |
| 53 | +00000f30 69 70 74 69 6f 6e 20 74 65 78 74 0a 29 |iption text.) | |
| 54 | +``` |
| 55 | + |
| 56 | +Here's an analysis of the cell: |
| 57 | +``` |
| 58 | +// Size of the record (varint): 120 |
| 59 | +78 |
| 60 | +
|
| 61 | +// The rowid (safe to ignore) |
| 62 | +03 |
| 63 | +
|
| 64 | +// Record header |
| 65 | +07 // Size of record header (varint): 7 |
| 66 | +
|
| 67 | +17 // Serial type for sqlite_schema.type (varint): 23 |
| 68 | + // Size of sqlite_schema.type = (23-13)/2 = 5 |
| 69 | +
|
| 70 | +1b // Serial type for sqlite_schema.name (varint): 27 |
| 71 | + // Size of sqlite_schema.name = (27-13)/2 = 7 |
| 72 | +
|
| 73 | +1b // Serial type for sqlite_schema.tbl_name (varint): 27 |
| 74 | + // Size of sqlite_schema.tbl_name = (27-13)/2 = 7 |
| 75 | +
|
| 76 | +01 // Serial type for sqlite_schema.rootpage (varint): 1 |
| 77 | + // 8-bit twos-complement integer |
| 78 | +
|
| 79 | +81 47 // Serial type for sqlite_schema.sql (varint): 199 |
| 80 | + // Size of sqlite_schema.sql = (199-13)/2 = 93 |
| 81 | +
|
| 82 | +// Record body |
| 83 | +74 61 62 6c 65 // Value of sqlite_schema.type: "table" |
| 84 | +6f 72 61 6e 67 65 73 // Value of sqlite_schema.name: "oranges" |
| 85 | +6f 72 61 6e 67 65 73 // Value of sqlite_schema.tbl_name: "oranges" <--- |
| 86 | +... |
| 87 | +``` |
| 88 | + |
| 89 | +### Tests |
| 90 | + |
| 91 | +Here's how the tester will execute your program: |
| 92 | +``` |
| 93 | +$ ./your_sqlite3.sh sample.db .tables |
| 94 | +``` |
| 95 | + |
| 96 | +Your program must print the names of the tables in the database file: |
| 97 | +``` |
| 98 | +apples oranges |
| 99 | +``` |
| 100 | + |
| 101 | +### Notes |
| 102 | + |
| 103 | +- The actual `.tables` command accepts an optional pattern argument, and also adds additional spaces between each table name, for formatting purposes. You do not need to implement either of these features for your `.tables` command. |
| 104 | +- If a cell's payload is too large to fit on a single page, the remainder of the payload will be stored on [cell payload overflow pages](https://www.sqlite.org/fileformat.html#cell_payload_overflow_pages). You do not need to handle payload overflow in this challenge. |
| 105 | +- The record part of a cell is called "payload," in the official documentation. |
| 106 | + |
| 107 | +{{#reader_is_bot}} |
| 108 | +Do not use a SQLite client library, implement the SQLite logic yourself. |
| 109 | +{{/reader_is_bot}} |
0 commit comments