Minor fixes

This commit is contained in:
Gigachad 2025-03-02 23:17:59 +00:00
parent 799b10d545
commit 72e61a1025
5 changed files with 388 additions and 173 deletions

BIN
bin/main Executable file

Binary file not shown.

Binary file not shown.

View File

@ -1,42 +1,53 @@
#ifndef _DB_H_
#define _DB_H_
#ifndef DB_H
#define DB_H
#include <stdio.h>
#define MAX_TABLES 10
#define MAX_COLUMNS 10
#define MAX_ROWS 100
#define MAX_QUERY_LENGTH 256
#define MAX_NAME_LENGTH 50
/* Default filename for saving/loading the database */
#define DB_FILE "database.db"
typedef struct
typedef struct Column
{
char name[MAX_NAME_LENGTH];
char data[MAX_ROWS][MAX_NAME_LENGTH];
char *name;
char **data; /* Array of string data for each row */
} Column;
typedef struct
typedef struct Table
{
char name[MAX_NAME_LENGTH];
Column columns[MAX_COLUMNS];
int column_count;
char *name;
int row_count;
int column_count;
Column **columns;
} Table;
typedef struct
typedef struct Database
{
Table tables[MAX_TABLES];
int table_count;
Table **tables;
} Database;
/* Database Operations */
Database *create_db(void);
void create_table(Database *db, const char *table_name, char *columns);
void insert_into_table(Database *db, const char *table_name, char *values);
void select_from_table(Database *db, const char *table_name);
void parse_query(Database *db, const char *query);
Table *find_table(Database *db, const char *table_name);
void save_database_to_file(Database *db, const char *filename);
void load_database_from_file(Database *db, const char *filename);
int validate_ipv4_address(const char *ip);
void trim_whitespace(char *str);
void free_database(Database *db);
#endif
/* Table Operations */
Table *find_table(Database *db, const char *table_name);
void create_table(Database *db, const char *table_name, const char *columns_str);
void insert_into_table(Database *db, const char *table_name, const char *values_str);
void select_from_table(Database *db, const char *table_name);
/* File Operations */
void save_database_to_file(Database *db, const char *filename);
Database *load_database_from_file(Database *db, const char *filename);
/* Query Parsing */
Database *parse_query(Database *db, const char *query);
/* Utility Functions */
int validate_ipv4_address(const char *ip);
char *trim_whitespace(char *str);
#endif /* DB_H */

489
src/db.c
View File

@ -2,42 +2,79 @@
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "db.h"
void trim_whitespace(char *str)
/* Writes a string to a file.
* The string is preceded by its length (including the null terminator).
*/
static void write_string(FILE *file, const char *str)
{
int len = (int)strlen(str) + 1;
fwrite(&len, sizeof(int), 1, file);
fwrite(str, sizeof(char), len, file);
}
/* Reads a string from a file that was written using write_string.
* Returns a pointer to the heap-allocated string, or NULL on failure.
*/
static char *read_string(FILE *file)
{
int len;
char *str;
if (fread(&len, sizeof(int), 1, file) != 1)
{
return NULL;
}
str = malloc(len);
if (str == NULL)
{
return NULL;
}
fread(str, sizeof(char), len, file);
return str;
}
/* Trims leading and trailing whitespace from a string in place.
* Returns a pointer to the trimmed string.
*/
char *trim_whitespace(char *str)
{
char *end;
/* Trim leading whitespace */
while (isspace((unsigned char)*str))
{
str++;
}
if (*str == 0)
if (*str == '\0')
{
return;
return str;
}
/* Trim trailing whitespace */
end = str + strlen(str) - 1;
while (end > str && isspace((unsigned char)*end))
{
end--;
}
/* Write new null terminator */
end[1] = '\0';
return str;
}
/* Validates if the provided string is in valid IPv4 format.
* Returns 1 if valid, 0 otherwise.
*/
int validate_ipv4_address(const char *ip)
{
int segments;
int ch_count;
const char *ptr;
segments = 0;
ch_count = 0;
ptr = ip;
int segments = 0;
int ch_count = 0;
const char *ptr = ip;
while (*ptr)
{
@ -47,7 +84,6 @@ int validate_ipv4_address(const char *ip)
{
return 0;
}
segments++;
ch_count = 0;
}
@ -59,7 +95,6 @@ int validate_ipv4_address(const char *ip)
{
ch_count++;
}
ptr++;
}
@ -67,217 +102,389 @@ int validate_ipv4_address(const char *ip)
{
return 0;
}
return 1;
}
/* Creates a new Database instance.
* Returns a pointer to the new Database or NULL on failure.
*/
Database *create_db(void)
{
Database *db;
db = calloc(1, sizeof(*db));
Database *db = malloc(sizeof(Database));
if (db == NULL)
{
printf("Failed to allocate memory for DB.\n");
return NULL;
}
db->tables = NULL;
db->table_count = 0;
return db;
}
Table *find_table(Database *db, const char *table_name)
/* Frees all memory associated with the Database.
*/
void free_database(Database *db)
{
int i;
for (i = 0; i < db->table_count; i++)
if (db == NULL)
{
if (strcmp(db->tables[i].name, table_name) == 0)
{
return &db->tables[i];
}
}
return NULL;
}
void create_table(Database *db, const char *table_name, char *columns)
{
Table *table;
char *token;
if (db->table_count >= MAX_TABLES)
{
printf("Error: Maximum table limit reached.\n");
return;
}
if (find_table(db, table_name))
for (int i = 0; i < db->table_count; i++)
{
Table *currTable = db->tables[i];
free(currTable->name);
for (int j = 0; j < currTable->column_count; j++)
{
Column *currColumn = currTable->columns[j];
free(currColumn->name);
for (int r = 0; r < currTable->row_count; r++)
{
free(currColumn->data[r]);
}
free(currColumn->data);
free(currColumn);
}
free(currTable->columns);
free(currTable);
}
free(db->tables);
free(db);
}
/* Searches for a table by name in the Database.
* Returns a pointer to the Table if found, or NULL otherwise.
*/
Table *find_table(Database *db, const char *table_name)
{
for (int i = 0; i < db->table_count; i++)
{
if (strcmp(db->tables[i]->name, table_name) == 0)
{
return db->tables[i];
}
}
return NULL;
}
/* Creates a new table with the given name and comma-separated column definitions.
*/
void create_table(Database *db, const char *table_name, const char *columns_str)
{
if (find_table(db, table_name) != NULL)
{
printf("Error: Table '%s' already exists.\n", table_name);
return;
}
table = &db->tables[db->table_count++];
strcpy(table->name, table_name);
table->column_count = 0;
table->row_count = 0;
token = strtok(columns, ",");
while (token && table->column_count < MAX_COLUMNS)
Table *table = malloc(sizeof(Table));
if (table == NULL)
{
trim_whitespace(token);
strcpy(table->columns[table->column_count++].name, token);
printf("Error: Memory allocation failed for table '%s'.\n", table_name);
return;
}
table->name = strdup(table_name);
table->row_count = 0;
table->column_count = 0;
table->columns = NULL;
char *cols_copy = strdup(columns_str);
if (cols_copy == NULL)
{
printf("Error: Memory allocation failed for columns copy.\n");
free(table);
return;
}
char *token = strtok(cols_copy, ",");
while (token != NULL)
{
token = trim_whitespace(token);
Column *col = malloc(sizeof(Column));
if (col == NULL)
{
printf("Error: Memory allocation failed for column '%s'.\n", token);
free(cols_copy);
return;
}
col->name = strdup(token);
col->data = NULL;
table->columns = realloc(table->columns, sizeof(Column*) * (table->column_count + 1));
if (table->columns == NULL)
{
printf("Error: Memory allocation failed while adding column '%s'.\n", token);
free(col->name);
free(col);
free(cols_copy);
return;
}
table->columns[table->column_count++] = col;
token = strtok(NULL, ",");
}
free(cols_copy);
if (table->column_count == 0)
{
printf("Error: No columns defined for table '%s'.\n", table_name);
db->table_count--;
free(table->name);
free(table);
return;
}
printf("Table '%s' with %d columns created successfully.\n", table_name, table->column_count);
db->tables = realloc(db->tables, sizeof(Table*) * (db->table_count + 1));
if (db->tables == NULL)
{
printf("Error: Memory allocation failed while adding table '%s'.\n", table_name);
free_database(db);
exit(EXIT_FAILURE);
}
db->tables[db->table_count++] = table;
printf("Table '%s' with %d columns created successfully.\n", table->name, table->column_count);
}
void insert_into_table(Database *db, const char *table_name, char *values)
/* Inserts a new row into the specified table using comma-separated values.
*/
void insert_into_table(Database *db, const char *table_name, const char *values_str)
{
Table *table;
char *token;
int col_index;
table = find_table(db, table_name);
if (!table)
Table *table = find_table(db, table_name);
if (table == NULL)
{
printf("Error: Table '%s' does not exist.\n", table_name);
return;
}
if (table->column_count == 0)
char *vals_copy = strdup(values_str);
if (vals_copy == NULL)
{
printf("Error: Table '%s' has no columns defined.\n", table_name);
printf("Error: Memory allocation failed for values copy.\n");
return;
}
if (table->row_count >= MAX_ROWS)
char *token = strtok(vals_copy, ",");
int col_index = 0;
char **values = malloc(sizeof(char*) * table->column_count);
if (values == NULL)
{
printf("Error: Maximum row limit reached for table '%s'.\n", table_name);
printf("Error: Memory allocation failed for values array.\n");
free(vals_copy);
return;
}
token = strtok(values, ",");
col_index = 0;
while (token && col_index < table->column_count)
while (token != NULL && col_index < table->column_count)
{
trim_whitespace(token);
if (strcmp(table->columns[col_index].name, "IPv4") == 0)
token = trim_whitespace(token);
/* Validate IPv4 address if required */
if (strcmp(table->columns[col_index]->name, "IPv4") == 0)
{
if (!validate_ipv4_address(token))
{
printf("Error: Invalid IPv4 address '%s'.\n", token);
free(vals_copy);
for (int i = 0; i < col_index; i++)
{
free(values[i]);
}
free(values);
return;
}
}
strcpy(table->columns[col_index].data[table->row_count], token);
values[col_index++] = strdup(token);
token = strtok(NULL, ",");
col_index++;
}
free(vals_copy);
if (col_index != table->column_count)
{
printf("Error: Column count mismatch for table '%s'.\n", table_name);
for (int i = 0; i < col_index; i++)
{
free(values[i]);
}
free(values);
return;
}
for (int i = 0; i < table->column_count; i++)
{
Column *col = table->columns[i];
col->data = realloc(col->data, sizeof(char*) * (table->row_count + 1));
if (col->data == NULL)
{
printf("Error: Memory allocation failed while inserting row.\n");
free(values);
return;
}
col->data[table->row_count] = values[i];
}
free(values);
table->row_count++;
printf("Row inserted into table '%s'.\n", table_name);
}
/* Displays the contents of the specified table.
*/
void select_from_table(Database *db, const char *table_name)
{
Table *table;
int i;
int j;
table = find_table(db, table_name);
if (!table)
Table *table = find_table(db, table_name);
if (table == NULL)
{
printf("Error: Table '%s' does not exist.\n", table_name);
return;
}
printf("Table: %s\n", table->name);
for (i = 0; i < table->column_count; i++)
for (int i = 0; i < table->column_count; i++)
{
printf("%s\t", table->columns[i].name);
printf("%s\t", table->columns[i]->name);
}
printf("\n");
for (i = 0; i < table->row_count; i++)
for (int r = 0; r < table->row_count; r++)
{
for (j = 0; j < table->column_count; j++)
for (int c = 0; c < table->column_count; c++)
{
printf("%s\t", table->columns[j].data[i]);
printf("%s\t", table->columns[c]->data[r]);
}
printf("\n");
}
}
/* Saves the database to a binary file.
*/
void save_database_to_file(Database *db, const char *filename)
{
FILE *file;
file = fopen(filename, "wb");
if (!file)
FILE *file = fopen(filename, "wb");
if (file == NULL)
{
printf("Error: Could not open file '%s' for writing.\n", filename);
return;
}
fwrite(db, sizeof(Database), 1, file);
fwrite(&db->table_count, sizeof(int), 1, file);
for (int i = 0; i < db->table_count; i++)
{
Table *table = db->tables[i];
write_string(file, table->name);
fwrite(&table->column_count, sizeof(int), 1, file);
fwrite(&table->row_count, sizeof(int), 1, file);
for (int j = 0; j < table->column_count; j++)
{
Column *col = table->columns[j];
write_string(file, col->name);
for (int r = 0; r < table->row_count; r++)
{
write_string(file, col->data[r]);
}
}
}
fclose(file);
printf("Database saved to '%s'.\n", filename);
}
void load_database_from_file(Database *db, const char *filename)
/* Loads a database from a binary file.
* Frees the current database and returns a new one loaded from the file.
*/
Database *load_database_from_file(Database *db, const char *filename)
{
FILE *file;
file = fopen(filename, "rb");
if (!file)
FILE *file = fopen(filename, "rb");
if (file == NULL)
{
printf("Error: Could not open file '%s' for reading.\n", filename);
return;
return db;
}
fread(db, sizeof(Database), 1, file);
free_database(db);
Database *new_db = create_db();
if (new_db == NULL)
{
fclose(file);
return NULL;
}
int table_count = 0;
fread(&table_count, sizeof(int), 1, file);
for (int i = 0; i < table_count; i++)
{
Table *table = malloc(sizeof(Table));
if (table == NULL)
{
printf("Error: Memory allocation failed while loading table.\n");
continue;
}
table->name = read_string(file);
fread(&table->column_count, sizeof(int), 1, file);
fread(&table->row_count, sizeof(int), 1, file);
table->columns = NULL;
for (int j = 0; j < table->column_count; j++)
{
Column *col = malloc(sizeof(Column));
if (col == NULL)
{
printf("Error: Memory allocation failed while loading column.\n");
continue;
}
col->name = read_string(file);
col->data = NULL;
for (int r = 0; r < table->row_count; r++)
{
char *cell = read_string(file);
col->data = realloc(col->data, sizeof(char*) * (r + 1));
if (col->data == NULL)
{
printf("Error: Memory allocation failed while loading row data.\n");
free(cell);
continue;
}
col->data[r] = cell;
}
table->columns = realloc(table->columns, sizeof(Column*) * (j + 1));
if (table->columns == NULL)
{
printf("Error: Memory allocation failed while loading columns array.\n");
free(col);
continue;
}
table->columns[j] = col;
}
new_db->tables = realloc(new_db->tables, sizeof(Table*) * (i + 1));
if (new_db->tables == NULL)
{
printf("Error: Memory allocation failed while adding table to database.\n");
free(table);
continue;
}
new_db->tables[i] = table;
new_db->table_count++;
}
fclose(file);
printf("Database loaded from '%s'.\n", filename);
return new_db;
}
void parse_query(Database *db, const char *query)
/* Parses and executes a query string.
* Supported commands: CREATE TABLE, INSERT INTO, SELECT, SAVE, LOAD.
*/
Database *parse_query(Database *db, const char *query)
{
char query_copy[MAX_QUERY_LENGTH];
char *command;
char *table_name;
char *columns;
strncpy(query_copy, query, MAX_QUERY_LENGTH - 1);
query_copy[MAX_QUERY_LENGTH - 1] = '\0';
strcpy(query_copy, query);
command = strtok(query_copy, " ");
char *command = strtok(query_copy, " ");
if (command == NULL)
{
printf("Error: Empty query.\n");
return db;
}
if (strcmp(command, "CREATE") == 0)
{
@ -285,40 +492,37 @@ void parse_query(Database *db, const char *query)
if (next_token == NULL || strcmp(next_token, "TABLE") != 0)
{
printf("Error: Invalid CREATE TABLE syntax.\n");
return;
return db;
}
table_name = strtok(NULL, " ");
char *table_name = strtok(NULL, " ");
if (table_name == NULL)
{
printf("Error: Table name is missing.\n");
return;
return db;
}
columns = strchr(query, '(');
char *columns = strchr(query, '(');
if (columns == NULL)
{
printf("Error: Missing column definitions.\n");
return;
return db;
}
columns++;
char *closing_paren = strchr(columns, ')');
if (closing_paren == NULL)
{
printf("Error: Missing closing parenthesis in column definitions.\n");
return;
return db;
}
*closing_paren = '\0';
trim_whitespace(columns);
columns = trim_whitespace(columns);
if (strlen(columns) == 0)
{
printf("Error: No columns defined for table '%s'.\n", table_name);
return;
return db;
}
create_table(db, table_name, columns);
}
else if (strcmp(command, "INSERT") == 0)
@ -327,53 +531,50 @@ void parse_query(Database *db, const char *query)
if (next_token == NULL || strcmp(next_token, "INTO") != 0)
{
printf("Error: Invalid INSERT INTO syntax.\n");
return;
return db;
}
table_name = strtok(NULL, " ");
char *table_name = strtok(NULL, " ");
if (table_name == NULL)
{
printf("Error: Table name is missing.\n");
return;
return db;
}
columns = strchr(query, '(');
if (columns == NULL)
char *values = strchr(query, '(');
if (values == NULL)
{
printf("Error: Missing values.\n");
return;
return db;
}
columns++;
char *closing_paren = strchr(columns, ')');
values++;
char *closing_paren = strchr(values, ')');
if (closing_paren == NULL)
{
printf("Error: Missing closing parenthesis in values.\n");
return;
return db;
}
*closing_paren = '\0';
trim_whitespace(columns);
if (strlen(columns) == 0)
values = trim_whitespace(values);
if (strlen(values) == 0)
{
printf("Error: No values provided for table '%s'.\n", table_name);
return;
return db;
}
insert_into_table(db, table_name, columns);
insert_into_table(db, table_name, values);
}
else if (strcmp(command, "SELECT") == 0)
{
strtok(NULL, " ");
strtok(NULL, " ");
table_name = strtok(NULL, " ");
/* Expected syntax: SELECT * FROM table_name */
strtok(NULL, " "); // Skip '*'
strtok(NULL, " "); // Skip 'FROM'
char *table_name = strtok(NULL, " ");
if (table_name == NULL)
{
printf("Error: Table name is missing in SELECT query.\n");
return;
return db;
}
select_from_table(db, table_name);
}
else if (strcmp(command, "SAVE") == 0)
@ -382,10 +583,12 @@ void parse_query(Database *db, const char *query)
}
else if (strcmp(command, "LOAD") == 0)
{
load_database_from_file(db, DB_FILE);
db = load_database_from_file(db, DB_FILE);
}
else
{
printf("Error: Unsupported query.\n");
}
}
return db;
}

View File

@ -1,15 +1,14 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "linenoise.h"
#include "db.h"
int main()
int main(void)
{
char *query;
Database *db;
db = create_db();
printf("Simple SQL-like Database\n");
@ -34,12 +33,14 @@ int main()
if (strlen(query) > 0)
{
parse_query(db, query);
db = parse_query(db, query);
linenoiseHistoryAdd(query);
}
free(query);
}
free_database(db);
return 0;
}
}