Skip to content

Commit 456d952

Browse files
committed
Merge branch 'part3-fixes' into walkthrough-revisions-fixes
2 parents afdd969 + 6a75735 commit 456d952

File tree

1 file changed

+172
-114
lines changed

1 file changed

+172
-114
lines changed

_parts/part3.md

Lines changed: 172 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ That means we need to upgrade our `prepare_statement` function to parse argument
4444
We store those parsed arguments into a new `Row` data structure inside the statement object:
4545

4646
```diff
47-
+const uint32_t COLUMN_USERNAME_SIZE = 32;
48-
+const uint32_t COLUMN_EMAIL_SIZE = 255;
47+
+#define COLUMN_USERNAME_SIZE = 32;
48+
+#define COLUMN_EMAIL_SIZE = 255;
4949
+struct Row_t {
5050
+ uint32_t id;
5151
+ char username[COLUMN_USERNAME_SIZE];
@@ -115,8 +115,8 @@ Next, a `Table` structure that points to pages of rows and keeps track of how ma
115115
+const uint32_t TABLE_MAX_ROWS = ROWS_PER_PAGE * TABLE_MAX_PAGES;
116116
+
117117
+struct Table_t {
118-
+ void* pages[TABLE_MAX_PAGES];
119118
+ uint32_t num_rows;
119+
+ void** pages;
120120
+};
121121
+typedef struct Table_t Table;
122122
```
@@ -132,7 +132,7 @@ Speaking of which, here is how we figure out where to read/write in memory for a
132132
+void* row_slot(Table* table, uint32_t row_num) {
133133
+ uint32_t page_num = row_num / ROWS_PER_PAGE;
134134
+ void* page = table->pages[page_num];
135-
+ if (!page) {
135+
+ if (page = NULL) {
136136
+ // Allocate memory only when we try to access page
137137
+ page = table->pages[page_num] = malloc(PAGE_SIZE);
138138
+ }
@@ -181,15 +181,26 @@ Now we can make `execute_statement` read/write from our table structure:
181181
}
182182
```
183183

184-
Lastly, we need to initialize the table and handle a few more error cases:
184+
Lastly, we need to initialize the table, create the respective
185+
memory release function and handle a few more error cases:
185186

186187
```diff
187188
+ Table* new_table() {
188-
+ Table* table = calloc(sizeof(Table));
189+
+ Table* table = malloc(sizeof(Table));
189190
+ table->num_rows = 0;
190-
+
191+
+ // Allocate space for the pointers to the pages
192+
+ // and initialize them to NULL
193+
+ table->pages = calloc(TABLE_MAX_PAGES, sizeof(void *));
191194
+ return table;
192195
+}
196+
+
197+
+void free_table(Table* table) {
198+
+ for (int i = 0; table->pages[i]; i++) {
199+
+ free(table->pages[i]);
200+
+ }
201+
+ free(table->pages);
202+
+ free(table);
203+
+}
193204
```
194205
```diff
195206
int main(int argc, char* argv[]) {
@@ -247,43 +258,52 @@ Now would be a great time to write some tests, for a couple reasons:
247258

248259
We'll address those issues in the next part. For now, here's the complete diff from this part:
249260
```diff
261+
@@ -2,6 +2,7 @@
262+
#include <stdio.h>
263+
#include <stdlib.h>
264+
#include <string.h>
265+
+#include <stdint.h>
266+
267+
struct InputBuffer_t {
268+
char* buffer;
269+
@@ -10,6 +11,106 @@ struct InputBuffer_t {
270+
};
250271
typedef struct InputBuffer_t InputBuffer;
251-
272+
252273
+enum ExecuteResult_t { EXECUTE_SUCCESS, EXECUTE_TABLE_FULL };
253274
+typedef enum ExecuteResult_t ExecuteResult;
254275
+
255-
enum MetaCommandResult_t {
256-
META_COMMAND_SUCCESS,
257-
META_COMMAND_UNRECOGNIZED_COMMAND
258-
};
259-
typedef enum MetaCommandResult_t MetaCommandResult;
260-
261-
-enum PrepareResult_t { PREPARE_SUCCESS, PREPARE_UNRECOGNIZED_STATEMENT };
276+
+enum MetaCommandResult_t {
277+
+ META_COMMAND_SUCCESS,
278+
+ META_COMMAND_UNRECOGNIZED_COMMAND
279+
+};
280+
+typedef enum MetaCommandResult_t MetaCommandResult;
281+
+
262282
+enum PrepareResult_t {
263283
+ PREPARE_SUCCESS,
264284
+ PREPARE_SYNTAX_ERROR,
265285
+ PREPARE_UNRECOGNIZED_STATEMENT
266-
+};
267-
typedef enum PrepareResult_t PrepareResult;
268-
269-
enum StatementType_t { STATEMENT_INSERT, STATEMENT_SELECT };
270-
typedef enum StatementType_t StatementType;
271-
272-
+const uint32_t COLUMN_USERNAME_SIZE = 32;
273-
+const uint32_t COLUMN_EMAIL_SIZE = 255;
286+
+ };
287+
+typedef enum PrepareResult_t PrepareResult;
288+
+
289+
+enum StatementType_t { STATEMENT_INSERT, STATEMENT_SELECT };
290+
+typedef enum StatementType_t StatementType;
291+
+
292+
+#define COLUMN_USERNAME_SIZE 32
293+
+#define COLUMN_EMAIL_SIZE 255
274294
+struct Row_t {
275-
+ uint32_t id;
276-
+ char username[COLUMN_USERNAME_SIZE];
277-
+ char email[COLUMN_EMAIL_SIZE];
295+
+ uint32_t id;
296+
+ char username[COLUMN_USERNAME_SIZE];
297+
+ char email[COLUMN_EMAIL_SIZE];
278298
+};
279299
+typedef struct Row_t Row;
280300
+
281-
struct Statement_t {
282-
StatementType type;
283-
+ Row row_to_insert; // only used by insert statement
284-
};
285-
typedef struct Statement_t Statement;
286-
301+
+struct Statement_t {
302+
+ StatementType type;
303+
+ Row row_to_insert; //only used by insert statement
304+
+};
305+
+typedef struct Statement_t Statement;
306+
+
287307
+#define size_of_attribute(Struct, Attribute) sizeof(((Struct*)0)->Attribute)
288308
+
289309
+const uint32_t ID_SIZE = size_of_attribute(Row, id);
@@ -300,130 +320,168 @@ We'll address those issues in the next part. For now, here's the complete diff f
300320
+const uint32_t TABLE_MAX_ROWS = ROWS_PER_PAGE * TABLE_MAX_PAGES;
301321
+
302322
+struct Table_t {
303-
+ void* pages[TABLE_MAX_PAGES];
304-
+ uint32_t num_rows;
323+
+ uint32_t num_rows;
324+
+ void** pages;
305325
+};
306326
+typedef struct Table_t Table;
307327
+
308328
+void print_row(Row* row) {
309-
+ printf("(%d, %s, %s)\n", row->id, row->username, row->email);
329+
+ printf("(%d, %s, %s)\n", row->id, row->username, row->email);
310330
+}
311331
+
312332
+void serialize_row(Row* source, void* destination) {
313-
+ memcpy(destination + ID_OFFSET, &(source->id), ID_SIZE);
314-
+ memcpy(destination + USERNAME_OFFSET, &(source->username), USERNAME_SIZE);
315-
+ memcpy(destination + EMAIL_OFFSET, &(source->email), EMAIL_SIZE);
333+
+ memcpy(destination + ID_OFFSET, &(source->id), ID_SIZE);
334+
+ memcpy(destination + USERNAME_OFFSET, &(source->username), USERNAME_SIZE);
335+
+ memcpy(destination + EMAIL_OFFSET, &(source->email), EMAIL_SIZE);
316336
+}
317337
+
318-
+void deserialize_row(void* source, Row* destination) {
319-
+ memcpy(&(destination->id), source + ID_OFFSET, ID_SIZE);
320-
+ memcpy(&(destination->username), source + USERNAME_OFFSET, USERNAME_SIZE);
321-
+ memcpy(&(destination->email), source + EMAIL_OFFSET, EMAIL_SIZE);
338+
+void deserialize_row(void *source, Row* destination) {
339+
+ memcpy(&(destination->id), source + ID_OFFSET, ID_SIZE);
340+
+ memcpy(&(destination->username), source + USERNAME_OFFSET, USERNAME_SIZE);
341+
+ memcpy(&(destination->email), source + EMAIL_OFFSET, EMAIL_SIZE);
322342
+}
323343
+
324344
+void* row_slot(Table* table, uint32_t row_num) {
325-
+ uint32_t page_num = row_num / ROWS_PER_PAGE;
326-
+ void* page = table->pages[page_num];
327-
+ if (!page) {
328-
+ // Allocate memory only when we try to access page
329-
+ page = table->pages[page_num] = malloc(PAGE_SIZE);
330-
+ }
331-
+ uint32_t row_offset = row_num % ROWS_PER_PAGE;
332-
+ uint32_t byte_offset = row_offset * ROW_SIZE;
333-
+ return page + byte_offset;
345+
+ uint32_t page_num = row_num / ROWS_PER_PAGE;
346+
+ void *page = table->pages[page_num];
347+
+ if (page == NULL) {
348+
+ // Allocate memory only when we try to access page
349+
+ page = table->pages[page_num] = malloc(PAGE_SIZE);
350+
+ }
351+
+ uint32_t row_offset = row_num % ROWS_PER_PAGE;
352+
+ uint32_t byte_offset = row_offset * ROW_SIZE;
353+
+ return page + byte_offset;
334354
+}
335355
+
336356
+Table* new_table() {
337-
+ Table* table = calloc(sizeof(Table));
338-
+ table->num_rows = 0;
357+
+ Table* table = malloc(sizeof(Table));
358+
+ table->num_rows = 0;
359+
+ // Allocate space for the pointers to the pages
360+
+ // and initialize them to NULL
361+
+ table->pages = calloc(TABLE_MAX_PAGES, sizeof(void *));
362+
+ return table;
363+
+}
339364
+
340-
+ return table;
365+
+void free_table(Table* table) {
366+
+ for (int i = 0; table->pages[i]; i++) {
367+
+ free(table->pages[i]);
368+
+ }
369+
+ free(table->pages);
370+
+ free(table);
341371
+}
342372
+
343373
InputBuffer* new_input_buffer() {
344374
InputBuffer* input_buffer = malloc(sizeof(InputBuffer));
345375
input_buffer->buffer = NULL;
346-
@@ -64,6 +137,12 @@ PrepareResult prepare_statement(InputBuffer* input_buffer,
347-
Statement* statement) {
348-
if (strncmp(input_buffer->buffer, "insert", 6) == 0) {
349-
statement->type = STATEMENT_INSERT;
376+
@@ -40,17 +141,105 @@ void close_input_buffer(InputBuffer* input_buffer) {
377+
free(input_buffer);
378+
}
379+
380+
+MetaCommandResult do_meta_command(InputBuffer* input_buffer, Table *table) {
381+
+ if (strcmp(input_buffer->buffer, ".exit") == 0) {
382+
+ close_input_buffer(input_buffer);
383+
+ free_table(table);
384+
+ exit(EXIT_SUCCESS);
385+
+ } else {
386+
+ return META_COMMAND_UNRECOGNIZED_COMMAND;
387+
+ }
388+
+}
389+
+
390+
+PrepareResult prepare_statement(InputBuffer* input_buffer,
391+
+ Statement* statement) {
392+
+ if (strncmp(input_buffer->buffer, "insert", 6) == 0) {
393+
+ statement->type = STATEMENT_INSERT;
350394
+ int args_assigned = sscanf(
351-
+ input_buffer->buffer, "insert %d %s %s", &(statement->row_to_insert.id),
352-
+ statement->row_to_insert.username, statement->row_to_insert.email);
395+
+ input_buffer->buffer, "insert %d %s %s", &(statement->row_to_insert.id),
396+
+ statement->row_to_insert.username, statement->row_to_insert.email
397+
+ );
353398
+ if (args_assigned < 3) {
354-
+ return PREPARE_SYNTAX_ERROR;
399+
+ return PREPARE_SYNTAX_ERROR;
355400
+ }
356-
return PREPARE_SUCCESS;
357-
}
358-
if (strcmp(input_buffer->buffer, "select") == 0) {
359-
@@ -74,18 +153,39 @@ PrepareResult prepare_statement(InputBuffer* input_buffer,
360-
return PREPARE_UNRECOGNIZED_STATEMENT;
361-
}
362-
363-
-void execute_statement(Statement* statement) {
364-
+ExecuteResult execute_insert(Statement* statement, Table* table) {
365-
+ if (table->num_rows >= TABLE_MAX_ROWS) {
366-
+ return EXECUTE_TABLE_FULL;
401+
+ return PREPARE_SUCCESS;
402+
+ }
403+
+ if (strcmp(input_buffer->buffer, "select") == 0) {
404+
+ statement->type = STATEMENT_SELECT;
405+
+ return PREPARE_SUCCESS;
367406
+ }
368407
+
369-
+ Row* row_to_insert = &(statement->row_to_insert);
408+
+ return PREPARE_UNRECOGNIZED_STATEMENT;
409+
+}
370410
+
371-
+ serialize_row(row_to_insert, row_slot(table, table->num_rows));
372-
+ table->num_rows += 1;
411+
+ExecuteResult execute_insert(Statement* statement, Table* table) {
412+
+ if (table->num_rows >= TABLE_MAX_ROWS) {
413+
+ return EXECUTE_TABLE_FULL;
414+
+ }
373415
+
374-
+ return EXECUTE_SUCCESS;
416+
+ Row* row_to_insert = &(statement->row_to_insert);
417+
+
418+
+ serialize_row(row_to_insert, row_slot(table, table->num_rows));
419+
+ table->num_rows += 1;
420+
+
421+
+ return EXECUTE_SUCCESS;
375422
+}
376423
+
377424
+ExecuteResult execute_select(Statement* statement, Table* table) {
378-
+ Row row;
379-
+ for (uint32_t i = 0; i < table->num_rows; i++) {
380-
+ deserialize_row(row_slot(table, i), &row);
381-
+ print_row(&row);
425+
+ Row row;
426+
+ for (uint32_t i = 0; i < table->num_rows; i++) {
427+
+ deserialize_row(row_slot(table, i), &row);
428+
+ print_row(&row);
429+
+ }
430+
+ return EXECUTE_SUCCESS;
431+
+}
432+
+
433+
+ExecuteResult execute_statement(Statement* statement, Table *table) {
434+
+ switch (statement->type) {
435+
+ case (STATEMENT_INSERT):
436+
+ return execute_insert(statement, table);
437+
+ case (STATEMENT_SELECT):
438+
+ return execute_select(statement, table);
382439
+ }
383-
+ return EXECUTE_SUCCESS;
384440
+}
385441
+
386-
+ExecuteResult execute_statement(Statement* statement, Table* table) {
387-
switch (statement->type) {
388-
case (STATEMENT_INSERT):
389-
- printf("This is where we would do an insert.\n");
390-
- break;
391-
+ return execute_insert(statement, table);
392-
case (STATEMENT_SELECT):
393-
- printf("This is where we would do a select.\n");
394-
- break;
395-
+ return execute_select(statement, table);
396-
}
397-
}
398-
399442
int main(int argc, char* argv[]) {
400443
+ Table* table = new_table();
401444
InputBuffer* input_buffer = new_input_buffer();
402445
while (true) {
403446
print_prompt();
404-
@@ -105,13 +205,22 @@ int main(int argc, char* argv[]) {
405-
switch (prepare_statement(input_buffer, &statement)) {
406-
case (PREPARE_SUCCESS):
407-
break;
447+
read_input(input_buffer);
448+
449+
- if (strcmp(input_buffer->buffer, ".exit") == 0) {
450+
- close_input_buffer(input_buffer);
451+
- exit(EXIT_SUCCESS);
452+
- } else {
453+
- printf("Unrecognized command '%s'.\n", input_buffer->buffer);
454+
+ if (input_buffer->buffer[0] == '.') {
455+
+ switch (do_meta_command(input_buffer, table)) {
456+
+ case (META_COMMAND_SUCCESS):
457+
+ continue;
458+
+ case (META_COMMAND_UNRECOGNIZED_COMMAND):
459+
+ printf("Unrecognized command '%s'\n", input_buffer->buffer);
460+
+ continue;
461+
+ }
462+
+ }
463+
+
464+
+ Statement statement;
465+
+ switch (prepare_statement(input_buffer, &statement)) {
466+
+ case (PREPARE_SUCCESS):
467+
+ break;
408468
+ case (PREPARE_SYNTAX_ERROR):
409-
+ printf("Syntax error. Could not parse statement.\n");
469+
+ printf("Syntax error. Could not parse statement.\n");
470+
+ continue;
471+
+ case (PREPARE_UNRECOGNIZED_STATEMENT):
472+
+ printf("Unrecognized keyword at start of '%s'.\n",
473+
+ input_buffer->buffer);
410474
+ continue;
411-
case (PREPARE_UNRECOGNIZED_STATEMENT):
412-
printf("Unrecognized keyword at start of '%s'.\n",
413-
input_buffer->buffer);
414-
continue;
415-
}
416-
417-
- execute_statement(&statement);
418-
- printf("Executed.\n");
419-
+ switch (execute_statement(&statement, table)) {
420-
+ case (EXECUTE_SUCCESS):
421-
+ printf("Executed.\n");
422-
+ break;
423-
+ case (EXECUTE_TABLE_FULL):
424-
+ printf("Error: Table full.\n");
425-
+ break;
426475
+ }
476+
+
477+
+ switch (execute_statement(&statement, table)) {
478+
+ case (EXECUTE_SUCCESS):
479+
+ printf("Executed.\n");
480+
+ break;
481+
+ case (EXECUTE_TABLE_FULL):
482+
+ printf("Error: Table full.\n");
483+
+ break;
484+
}
427485
}
428486
}
429487
```

0 commit comments

Comments
 (0)