Add files via upload

This commit is contained in:
Ivan Nikolskiy 2025-01-26 01:24:56 +00:00 committed by GitHub
parent 4d82bb82a8
commit dc95d79945
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 1997 additions and 2 deletions

25
Makefile Normal file
View File

@ -0,0 +1,25 @@
CC = gcc
CFLAGS = -Wall -Wextra -Iinclude
SRC_DIR = src
BUILD_DIR = build
BIN_DIR = bin
SRC_FILES = $(wildcard $(SRC_DIR)/*.c)
OBJ_FILES = $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(SRC_FILES))
TARGET = $(BIN_DIR)/main
all: $(TARGET)
$(TARGET): $(OBJ_FILES)
@mkdir -p $(BIN_DIR)
$(CC) $(OBJ_FILES) -o $@
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
@mkdir -p $(BUILD_DIR)
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -rf $(BUILD_DIR) $(BIN_DIR)
.PHONY: all clean

View File

@ -1,2 +1,47 @@
# SimpleDB
SimpleDB
## Simple DB
Simple DB proof of concept that utilizes SQL syntax for REPL.
### Examples
**Loading DB from file**
```
Simple SQL-like Database
Copyright (c) 2025 Ivan Nikolskiy, All Rights Reserved.
Supported commands: CREATE TABLE, INSERT INTO, SELECT * FROM, SAVE, LOAD
Enter SQL query: LOAD
Database loaded from 'database.db'.
Enter SQL query: SELECT * FROM Students
Table: Students
Name Age Major
Alice 20 CS
Bob 20 CS
Enter SQL query: EXIT
```
**Writing your DB to file**
```
Simple SQL-like Database
Copyright (c) 2025 Ivan Nikolskiy, All Rights Reserved.
Supported commands: CREATE TABLE, INSERT INTO, SELECT * FROM, SAVE, LOAD
Enter SQL query: CREATE TABLE Students (Name, Age, Major)
Table 'Students' with 3 columns created successfully.
Enter SQL query: INSERT INTO Students VALUES (Alice, 20, CS)
Row inserted into table 'Students'.
Enter SQL query: INSERT INTO Students VALUES (Bob, 20, CS)
Row inserted into table 'Students'.
Enter SQL query: SELECT * FROM Students
Table: Students
Name Age Major
Alice 20 CS
Bob 20 CS
Enter SQL query: SAVE
Database saved to 'database.db'.
Enter SQL query: EXIT
```

BIN
database.db Normal file

Binary file not shown.

43
include/db.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef _DB_H_
#define _DB_H_
#define MAX_TABLES 10
#define MAX_COLUMNS 10
#define MAX_ROWS 100
#define MAX_QUERY_LENGTH 256
#define MAX_NAME_LENGTH 50
#define DB_FILE "database.db"
typedef struct
{
char name[MAX_NAME_LENGTH];
char data[MAX_ROWS][MAX_NAME_LENGTH];
} Column;
typedef struct
{
char name[MAX_NAME_LENGTH];
Column columns[MAX_COLUMNS];
int column_count;
int row_count;
} Table;
typedef struct
{
Table tables[MAX_TABLES];
int table_count;
} Database;
Database db;
void create_table(const char *table_name, char *columns);
void insert_into_table(const char *table_name, char *values);
void select_from_table(const char *table_name);
void parse_query(const char *query);
Table *find_table(const char *table_name);
void save_database_to_file(const char *filename);
void load_database_from_file(const char *filename);
int validate_ipv4_address(const char *ip);
void trim_whitespace(char *str);
#endif

113
include/linenoise.h Normal file
View File

@ -0,0 +1,113 @@
/* linenoise.h -- VERSION 1.0
*
* Guerrilla line editing library against the idea that a line editing lib
* needs to be 20,000 lines of C code.
*
* See linenoise.c for more information.
*
* ------------------------------------------------------------------------
*
* Copyright (c) 2010-2023, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __LINENOISE_H
#define __LINENOISE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h> /* For size_t. */
extern char *linenoiseEditMore;
/* The linenoiseState structure represents the state during line editing.
* We pass this state to functions implementing specific editing
* functionalities. */
struct linenoiseState {
int in_completion; /* The user pressed TAB and we are now in completion
* mode, so input is handled by completeLine(). */
size_t completion_idx; /* Index of next completion to propose. */
int ifd; /* Terminal stdin file descriptor. */
int ofd; /* Terminal stdout file descriptor. */
char *buf; /* Edited line buffer. */
size_t buflen; /* Edited line buffer size. */
const char *prompt; /* Prompt to display. */
size_t plen; /* Prompt length. */
size_t pos; /* Current cursor position. */
size_t oldpos; /* Previous refresh cursor position. */
size_t len; /* Current edited line length. */
size_t cols; /* Number of columns in terminal. */
size_t oldrows; /* Rows used by last refrehsed line (multiline mode) */
int history_index; /* The history index we are currently editing. */
};
typedef struct linenoiseCompletions {
size_t len;
char **cvec;
} linenoiseCompletions;
/* Non blocking API. */
int linenoiseEditStart(struct linenoiseState *l, int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt);
char *linenoiseEditFeed(struct linenoiseState *l);
void linenoiseEditStop(struct linenoiseState *l);
void linenoiseHide(struct linenoiseState *l);
void linenoiseShow(struct linenoiseState *l);
/* Blocking API. */
char *linenoise(const char *prompt);
void linenoiseFree(void *ptr);
/* Completion API. */
typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold);
typedef void(linenoiseFreeHintsCallback)(void *);
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
void linenoiseSetHintsCallback(linenoiseHintsCallback *);
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
/* History API. */
int linenoiseHistoryAdd(const char *line);
int linenoiseHistorySetMaxLen(int len);
int linenoiseHistorySave(const char *filename);
int linenoiseHistoryLoad(const char *filename);
/* Other utilities. */
void linenoiseClearScreen(void);
void linenoiseSetMultiLine(int ml);
void linenoisePrintKeyCodes(void);
void linenoiseMaskModeEnable(void);
void linenoiseMaskModeDisable(void);
#ifdef __cplusplus
}
#endif
#endif /* __LINENOISE_H */

376
src/db.c Normal file
View File

@ -0,0 +1,376 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "db.h"
void trim_whitespace(char *str)
{
char *end;
while (isspace((unsigned char)*str))
{
str++;
}
if (*str == 0)
{
return;
}
end = str + strlen(str) - 1;
while (end > str && isspace((unsigned char)*end))
{
end--;
}
end[1] = '\0';
}
int validate_ipv4_address(const char *ip)
{
int segments;
int ch_count;
const char *ptr;
segments = 0;
ch_count = 0;
ptr = ip;
while (*ptr)
{
if (*ptr == '.')
{
if (ch_count == 0 || ch_count > 3)
{
return 0;
}
segments++;
ch_count = 0;
}
else if (!isdigit((unsigned char)*ptr))
{
return 0;
}
else
{
ch_count++;
}
ptr++;
}
if (segments != 3 || ch_count == 0 || ch_count > 3)
{
return 0;
}
return 1;
}
Table *find_table(const char *table_name)
{
int i;
for (i = 0; i < db.table_count; i++)
{
if (strcmp(db.tables[i].name, table_name) == 0)
{
return &db.tables[i];
}
}
return NULL;
}
void create_table(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(table_name))
{
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)
{
trim_whitespace(token);
strcpy(table->columns[table->column_count++].name, token);
token = strtok(NULL, ",");
}
if (table->column_count == 0)
{
printf("Error: No columns defined for table '%s'.\n", table_name);
db.table_count--;
return;
}
printf("Table '%s' with %d columns created successfully.\n", table_name, table->column_count);
}
void insert_into_table(const char *table_name, char *values)
{
Table *table;
char *token;
int col_index;
table = find_table(table_name);
if (!table)
{
printf("Error: Table '%s' does not exist.\n", table_name);
return;
}
if (table->column_count == 0)
{
printf("Error: Table '%s' has no columns defined.\n", table_name);
return;
}
if (table->row_count >= MAX_ROWS)
{
printf("Error: Maximum row limit reached for table '%s'.\n", table_name);
return;
}
token = strtok(values, ",");
col_index = 0;
while (token && col_index < table->column_count)
{
trim_whitespace(token);
if (strcmp(table->columns[col_index].name, "IPv4") == 0)
{
if (!validate_ipv4_address(token))
{
printf("Error: Invalid IPv4 address '%s'.\n", token);
return;
}
}
strcpy(table->columns[col_index].data[table->row_count], token);
token = strtok(NULL, ",");
col_index++;
}
if (col_index != table->column_count)
{
printf("Error: Column count mismatch for table '%s'.\n", table_name);
return;
}
table->row_count++;
printf("Row inserted into table '%s'.\n", table_name);
}
void select_from_table(const char *table_name)
{
Table *table;
int i;
int j;
table = find_table(table_name);
if (!table)
{
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++)
{
printf("%s\t", table->columns[i].name);
}
printf("\n");
for (i = 0; i < table->row_count; i++)
{
for (j = 0; j < table->column_count; j++)
{
printf("%s\t", table->columns[j].data[i]);
}
printf("\n");
}
}
void save_database_to_file(const char *filename)
{
FILE *file;
file = fopen(filename, "wb");
if (!file)
{
printf("Error: Could not open file '%s' for writing.\n", filename);
return;
}
fwrite(&db, sizeof(Database), 1, file);
fclose(file);
printf("Database saved to '%s'.\n", filename);
}
void load_database_from_file(const char *filename)
{
FILE *file;
file = fopen(filename, "rb");
if (!file)
{
printf("Error: Could not open file '%s' for reading.\n", filename);
return;
}
fread(&db, sizeof(Database), 1, file);
fclose(file);
printf("Database loaded from '%s'.\n", filename);
}
void parse_query(const char *query)
{
char query_copy[MAX_QUERY_LENGTH];
char *command;
char *table_name;
char *columns;
strcpy(query_copy, query);
command = strtok(query_copy, " ");
if (strcmp(command, "CREATE") == 0)
{
char *next_token = strtok(NULL, " ");
if (next_token == NULL || strcmp(next_token, "TABLE") != 0)
{
printf("Error: Invalid CREATE TABLE syntax.\n");
return;
}
table_name = strtok(NULL, " ");
if (table_name == NULL)
{
printf("Error: Table name is missing.\n");
return;
}
columns = strchr(query, '(');
if (columns == NULL)
{
printf("Error: Missing column definitions.\n");
return;
}
columns++;
char *closing_paren = strchr(columns, ')');
if (closing_paren == NULL)
{
printf("Error: Missing closing parenthesis in column definitions.\n");
return;
}
*closing_paren = '\0';
trim_whitespace(columns);
if (strlen(columns) == 0)
{
printf("Error: No columns defined for table '%s'.\n", table_name);
return;
}
create_table(table_name, columns);
}
else if (strcmp(command, "INSERT") == 0)
{
char *next_token = strtok(NULL, " ");
if (next_token == NULL || strcmp(next_token, "INTO") != 0)
{
printf("Error: Invalid INSERT INTO syntax.\n");
return;
}
table_name = strtok(NULL, " ");
if (table_name == NULL)
{
printf("Error: Table name is missing.\n");
return;
}
columns = strchr(query, '(');
if (columns == NULL)
{
printf("Error: Missing values.\n");
return;
}
columns++;
char *closing_paren = strchr(columns, ')');
if (closing_paren == NULL)
{
printf("Error: Missing closing parenthesis in values.\n");
return;
}
*closing_paren = '\0';
trim_whitespace(columns);
if (strlen(columns) == 0)
{
printf("Error: No values provided for table '%s'.\n", table_name);
return;
}
insert_into_table(table_name, columns);
}
else if (strcmp(command, "SELECT") == 0)
{
strtok(NULL, " ");
strtok(NULL, " ");
table_name = strtok(NULL, " ");
if (table_name == NULL)
{
printf("Error: Table name is missing in SELECT query.\n");
return;
}
select_from_table(table_name);
}
else if (strcmp(command, "SAVE") == 0)
{
save_database_to_file(DB_FILE);
}
else if (strcmp(command, "LOAD") == 0)
{
load_database_from_file(DB_FILE);
}
else
{
printf("Error: Unsupported query.\n");
}
}

1349
src/linenoise.c Normal file

File diff suppressed because it is too large Load Diff

44
src/main.c Normal file
View File

@ -0,0 +1,44 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "linenoise.h"
#include "db.h"
int main()
{
char *query;
db.table_count = 0;
printf("Simple SQL-like Database\n");
printf("Copyright (c) 2025 Ivan Nikolskiy, All Rights Reserved.\n\n");
printf("Supported commands: CREATE TABLE, INSERT INTO, SELECT * FROM, SAVE, LOAD\n\n");
while (1)
{
query = linenoise("Enter SQL query: ");
if (!query)
{
break;
}
trim_whitespace(query);
if (strcmp(query, "EXIT") == 0)
{
free(query);
break;
}
if (strlen(query) > 0)
{
parse_query(query);
linenoiseHistoryAdd(query);
}
free(query);
}
return 0;
}