@@ -44,8 +44,8 @@ That means we need to upgrade our `prepare_statement` function to parse argument
44
44
We store those parsed arguments into a new ` Row ` data structure inside the statement object:
45
45
46
46
``` 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;
49
49
+ struct Row_t {
50
50
+ uint32_t id;
51
51
+ 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
115
115
+ const uint32_t TABLE_MAX_ROWS = ROWS_PER_PAGE * TABLE_MAX_PAGES;
116
116
+
117
117
+ struct Table_t {
118
- + void* pages[TABLE_MAX_PAGES];
119
118
+ uint32_t num_rows;
119
+ + void** pages;
120
120
+ };
121
121
+ typedef struct Table_t Table;
122
122
```
@@ -132,7 +132,7 @@ Speaking of which, here is how we figure out where to read/write in memory for a
132
132
+ void* row_slot(Table* table, uint32_t row_num) {
133
133
+ uint32_t page_num = row_num / ROWS_PER_PAGE;
134
134
+ void* page = table->pages[page_num];
135
- + if (! page) {
135
+ + if (page = NULL ) {
136
136
+ // Allocate memory only when we try to access page
137
137
+ page = table->pages[page_num] = malloc(PAGE_SIZE);
138
138
+ }
@@ -181,15 +181,26 @@ Now we can make `execute_statement` read/write from our table structure:
181
181
}
182
182
```
183
183
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:
185
186
186
187
``` diff
187
188
+ Table* new_table() {
188
- + Table* table = calloc (sizeof(Table));
189
+ + Table* table = malloc (sizeof(Table));
189
190
+ 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 *));
191
194
+ return table;
192
195
+ }
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
+ + }
193
204
```
194
205
``` diff
195
206
int main(int argc, char* argv[]) {
@@ -247,43 +258,52 @@ Now would be a great time to write some tests, for a couple reasons:
247
258
248
259
We'll address those issues in the next part. For now, here's the complete diff from this part:
249
260
``` 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
+ };
250
271
typedef struct InputBuffer_t InputBuffer;
251
-
272
+
252
273
+ enum ExecuteResult_t { EXECUTE_SUCCESS, EXECUTE_TABLE_FULL };
253
274
+ typedef enum ExecuteResult_t ExecuteResult;
254
275
+
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
+ +
262
282
+ enum PrepareResult_t {
263
283
+ PREPARE_SUCCESS,
264
284
+ PREPARE_SYNTAX_ERROR,
265
285
+ 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
274
294
+ 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];
278
298
+ };
279
299
+ typedef struct Row_t Row;
280
300
+
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
+ +
287
307
+ #define size_of_attribute(Struct, Attribute) sizeof(((Struct*)0)->Attribute)
288
308
+
289
309
+ 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
300
320
+ const uint32_t TABLE_MAX_ROWS = ROWS_PER_PAGE * TABLE_MAX_PAGES;
301
321
+
302
322
+ struct Table_t {
303
- + void* pages[TABLE_MAX_PAGES] ;
304
- + uint32_t num_rows ;
323
+ + uint32_t num_rows ;
324
+ + void** pages ;
305
325
+ };
306
326
+ typedef struct Table_t Table;
307
327
+
308
328
+ 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);
310
330
+ }
311
331
+
312
332
+ 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);
316
336
+ }
317
337
+
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);
322
342
+ }
323
343
+
324
344
+ 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;
334
354
+ }
335
355
+
336
356
+ 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
+ + }
339
364
+
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);
341
371
+ }
342
372
+
343
373
InputBuffer* new_input_buffer() {
344
374
InputBuffer* input_buffer = malloc(sizeof(InputBuffer));
345
375
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;
350
394
+ 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
+ + );
353
398
+ if (args_assigned < 3) {
354
- + return PREPARE_SYNTAX_ERROR;
399
+ + return PREPARE_SYNTAX_ERROR;
355
400
+ }
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;
367
406
+ }
368
407
+
369
- + Row* row_to_insert = &(statement->row_to_insert);
408
+ + return PREPARE_UNRECOGNIZED_STATEMENT;
409
+ + }
370
410
+
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
+ + }
373
415
+
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;
375
422
+ }
376
423
+
377
424
+ 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);
382
439
+ }
383
- + return EXECUTE_SUCCESS;
384
440
+ }
385
441
+
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
-
399
442
int main(int argc, char* argv[]) {
400
443
+ Table* table = new_table();
401
444
InputBuffer* input_buffer = new_input_buffer();
402
445
while (true) {
403
446
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;
408
468
+ 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);
410
474
+ 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;
426
475
+ }
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
+ }
427
485
}
428
486
}
429
487
```
0 commit comments