commit 7b6b2a1cbfd312b779dca512c2038d6b28926773 Author: Gerben Aaltink Date: Fri Nov 22 13:40:39 2024 +0100 Initial commit diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..de7da58 --- /dev/null +++ b/.clang-format @@ -0,0 +1,192 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveMacros: None +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: None +AlignConsecutiveDeclarations: None +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortEnumsOnASingleLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: true +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 140 +CommentPragmas: '^ IWYU pragma:' +QualifierAlignment: Leave +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +PackConstructorInitializers: BinPack +BasedOnStyle: '' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +AllowAllConstructorInitializersOnNextLine: true +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseLabels: false +IndentCaseBlocks: false +IndentGotoLabels: true +IndentPPDirectives: None +IndentExternBlock: AfterExternBlock +IndentRequires: false +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertTrailingCommas: None +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +LambdaBodyIndentation: Signature +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PenaltyIndentedWhitespace: 0 +PointerAlignment: Right +PPIndentWidth: -1 +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + BeforeNonEmptyParentheses: false +SpaceAroundPointerQualifiers: Default +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +BitFieldColonSpacing: Both +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseCRLF: false +UseTab: Never +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE + - NS_SWIFT_NAME + - CF_SWIFT_NAME +... + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fc597c9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.history +.sorm* +.vscode +a.out +db.sqlite3 + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..dfc73df --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +all: build run + +build: + gcc main.c -lsqlite3 -lreadline -o sorm + gcc -shared -o sorm.so -fPIC main.c -lsqlite3 -lreadline + + +run: + ./sorm \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/cli.h b/cli.h new file mode 100644 index 0000000..3497f1f --- /dev/null +++ b/cli.h @@ -0,0 +1,184 @@ +#ifndef SORM_CLI_H +#define SORM_CLI_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sorm.h" + +const char * history_filename = ".sorm_history"; + + int _sorm_readline_accept_line(int count, int key) { + if (strchr(rl_line_buffer, ';')) { + + rl_done = 1; + return 0; + } + rl_insert_text("\n"); + return 0; +} + +char* _sorm_autocompletion_generator(const char* text, int state) { + const char* completions[] = { + "exit", + sorm_last_query, + sorm_last_query_expanded, + "python", + "history", + "memory", + "truncate", + NULL + }; + int list_index; + + if (!state) { + list_index = 0; + } + + while (completions[list_index] != NULL) { + if (strncmp(completions[list_index], text, strlen(text)) == 0) { + return strdup(completions[list_index++]); + } + list_index++; + } + + return NULL; +} + + char** _sorm_autocomplete(const char* text, int start, int end) { + rl_attempted_completion_over = 1; + return rl_completion_matches(text, _sorm_autocompletion_generator); +} + + + + int +_hs_read_file (const char *filename, char *buffer, size_t size) +{ + int fd; + ssize_t bytes_read; + + fd = open (filename, O_RDONLY); + if (fd < 0) + return -1; + + bytes_read = read (fd, buffer, size); + + close (fd); + + if (bytes_read < 0 || (size_t)bytes_read != size) + return -1; + + return 0; +} + + +int +sorm_cli_history_dump (const char *filename) +{ + register int line_start, line_end; + char *input; + struct stat finfo; + size_t file_size; + + if (stat (filename, &finfo) < 0) + return -1; + + file_size = (size_t)finfo.st_size; + input = (char *)malloc (file_size + 1); + if (!input) + return -1; + + if (_hs_read_file (filename, input, file_size) < 0) { + free (input); + return -1; + } + input[file_size] = '\0'; + + for (line_start = line_end = 0; line_end < file_size; line_end++) { + if (input[line_end] == '\n') { + input[line_end] = '\0'; + + printf ("%s\n",input + line_start); + + line_start = line_end + 1; + } + } + + if (line_start < file_size) + printf ("%s\n",input + line_start); + + free (input); + + return where_history (); +} + + char sorm_history_filename[4096]; + +void sorm_cli_init(const char * history_filename){ + strcpy(sorm_history_filename, history_filename); + rl_bind_key('\n',NULL); + rl_bind_key('\r', NULL); + rl_add_defun("custom-accept-line", _sorm_readline_accept_line, '\n'); + rl_add_defun("custom-accept-line-cr", _sorm_readline_accept_line, '\r'); + rl_variable_bind("enable-bracketed-paste", "on"); + rl_attempted_completion_function = _sorm_autocomplete; + rl_variable_bind("show-all-if-ambiguous", "on"); + rl_variable_bind("menu-complete-display-prefix", "on"); + + using_history(); + read_history(sorm_history_filename); +} + +char sorm_cli_previous_command[sizeof(rl_line_buffer)]; +char * sorm_cli_readline(char * prompt){ + char * result = readline(prompt); + if(strcmp(rl_line_buffer,sorm_cli_previous_command)){ + add_history((const char *)result); + write_history(sorm_history_filename); + } + strcpy(sorm_cli_previous_command,rl_line_buffer); + + return result; +} + + +bool sormrepl_handle_command(char * command){ + if(!strncmp(command, "history",7)){ + sorm_cli_history_dump(history_filename); + return true; + } + return false; +} +void sormrepl(int sorm){ + sorm_t * db = sormg(sorm); + char sql[4097]; + + sorm_cli_init(history_filename); + char * query; + while((query = sorm_cli_readline("sql> "))){ + if(sormrepl_handle_command(query)) + continue; + sorm_ptr res = sormq(sorm,query); + if(res){ + if(sormqt(query) == SORM_SELECT){ + int length = sormlv(res); + sormfmtd(res); + free(res); + }else if(sormqt(query) == SORM_DELETE){ + printf("%d records affected.\n",res); + }else if(sormqt(query) == SORM_INSERT){ + printf("Last insert id: %d.\n",res); + } + } + + printf("Rows: %lld, Execute %s, Format: %s\n",sorm_row_count, format_time(_sorm_query_duration),format_time(_sorm_result_format_duration)); + printf("%s\n",rmalloc_stats()); + } +} +#endif \ No newline at end of file diff --git a/main.c b/main.c new file mode 100644 index 0000000..babdc2f --- /dev/null +++ b/main.c @@ -0,0 +1,39 @@ +#include "sorm.h" +#include "cli.h" + +int main() { + int db = sormc("db.sqlite3"); + //sormq(db,"DROP TABLE IF EXISTS pony;"); + printf("%d\n",db); + sormq(db, "CREATE TABLE IF NOT EXISTS pony (id INTEGER PRIMARY KEY AUTOINCREMENT,name,age);",NULL); + sorm_pk iid = sormq(db, "INSERT INTO pony (id,name,age) VALUES (NULL,%s,%d);", + "Teenii", + 19 + ); + iid = sormq(db, "INSERT INTO pony (id,name,age) VALUES (NULL,%s,%d);", + "Amber", + 20 + ); + iid = sormq(db, "INSERT INTO pony (id,name,age) VALUES (NULL,%s,%d);", + "Feuerherz", + 20 + ); + + iid = sormq(db, "INSERT INTO pony (id,name,age) VALUES (NULL,%s,%d);", + "Retoor", + 34 + ); + sorm_str csv = sormq(db, "SELECT * FROM pony WHERE id in (?i,?i,?i)",1,2,3); + sorm_str csv2 = sormq(db, "SELECT * FROM pony WHERE id = %d and age = %d ", 1,33); + sorm_str csv3 = sormq(db, "SELECT * FROM pony LIMIT 2"); + //free(csv3); + //free(csv2); + if(csv2) + printf("%s\n",csv2); + printf("%s\n",csv3); + free(csv3); + sormd(db); + printf("%s\n",rmalloc_stats()); + db = sormc("db.sqlite3"); + sormrepl(db); +} diff --git a/sorm b/sorm new file mode 100755 index 0000000..ba5071a Binary files /dev/null and b/sorm differ diff --git a/sorm.h b/sorm.h new file mode 100644 index 0000000..c920af9 --- /dev/null +++ b/sorm.h @@ -0,0 +1,576 @@ +#ifndef SORM_H +#define SORM_H +#include +#include +#include +#include +#include +#include +#include +#include +#include "str.h" + +sqlite3 *db; +sqlite3_stmt *stmt; + + +char * sorm_last_query = NULL; +char * sorm_last_query_expanded = NULL; + +nsecs_t _sorm_query_start = 0; +nsecs_t _sorm_query_end = 0; +nsecs_t _sorm_query_duration = 0; + +nsecs_t _sorm_result_format_start = 0; +nsecs_t _sorm_result_format_end = 0; +nsecs_t _sorm_result_format_duration = 0; + +int64_t sorm_row_count = 0; + +typedef struct sorm_t { + sqlite3 * conn; + int current_row; + int current_column; + char * csv; + nsecs_t time_query_start; + nsecs_t time_query_end; + nsecs_t time_query_duration; + nsecs_t time_result_format_start; + nsecs_t time_result_format_end; + nsecs_t time_result_format_duration; +} sorm_t; +typedef char * sorm_pk; +typedef char * sorm_int; +typedef char * sorm_ptr; +typedef unsigned char * sorm_str; +typedef double sorm_double; +typedef double sorm_float; +typedef bool sorm_bool; + +int sormc(char * path); +void sormd(int db); +char * sormpt(char * sql, int number); +unsigned int sormcq(char * sql, char * out); +unsigned int sormpc(char * sql); +sqlite3_stmt * sormb(sorm_t* db, char * sql, ...); +sorm_ptr sormq(int db, char * sql, ...); +char * sorm_csvc(int db, sqlite3_stmt * stmt); +char * sorm_csvd(int db, sqlite3_stmt * stmt); +char * sorm_csv(int db,sqlite3_stmt * stmt); + + + +typedef enum sorm_query_t { + SORM_UNKNOWN = 0, + SORM_SELECT = 1, + SORM_INSERT = 2, + SORM_UPDATE = 3, + SORM_DELETE = 4, + SORM_CREATE = 5 +} sorm_query_t; + +const int sorm_null = -1337; + + +sorm_t ** sorm_instances = NULL; +int sorm_instance_count = 0; + + + +int sormc(char * path){ + // sorm connect + printf("HIERR\n"); + sorm_instance_count++; + sorm_instance_count++; + sorm_instances = realloc(sorm_instances,sorm_instance_count * sizeof(sorm_t *) + sorm_instance_count * sizeof(sorm_t)); + + printf("HIERR\n"); + sorm_t * db = &sorm_instances[sorm_instance_count - 1]; + + printf("HIERR\n"); + db->conn = NULL; + + printf("HIERR\n"); + db->csv = NULL; + db->current_column = 0; + db->current_row = 0; + db->time_query_duration = 0; + db->time_query_end = 0; + db->time_query_start = 0; + db->time_result_format_duration = 0; + db->time_result_format_end = 0; + db->time_result_format_start = 0; + + if(sqlite3_open(path,&db->conn)) + { + fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db->conn)); + return 0; + } + printf("DONE!\n"); + return sorm_instance_count; +} +sorm_t * sormg(int ptr){ + return &sorm_instances[ptr -1]; +} + +char * sormgcsv(int ptr){ + /* sorm get csv*/ + sorm_t * db = sormg(ptr); + return db->csv; +} + +void sormd(int sorm){ + sorm_t * db = sormg(sorm); + if(sqlite3_close(db->conn)) + { + fprintf(stderr, "Error closing database: %s\n", sqlite3_errmsg(db->conn)); + } + if(sorm_last_query){ + free(sorm_last_query); + sorm_last_query = NULL; + } + if(sorm_last_query_expanded){ + free(sorm_last_query_expanded); + sorm_last_query_expanded = NULL; + } + +} + +char * sormpt(char * sql, int number){ + // param type + char * sqlp = sql; + char * result = NULL; + int index = 0; + while(*sqlp){ + if(*sqlp == '%' || *sqlp == '?'){ + sqlp++; + switch(*sqlp){ + case 'f': + result = "double"; + break; + case 's': + result = "text"; + break; + case 'd': + result = "int"; + break; + case 'b': + result = "blob"; + break; + default: + result = "?"; + break; + } + sqlp++; + index++; + + } + if(index == number){ + return result; + } + if(*sqlp) + sqlp++; + } + if(number > index){ + printf("RETURNED\n"); + return NULL; + } + return NULL; +} + + +unsigned int sormcq(char * sql, char * out){ + // clean query + // converts %s %i parameters to ? + + unsigned int count = 0; + while(*sql){ + if(*sql != '%' && *sql != '?') + *out = *sql; + else{ + count++; + sql++; + *out = '?'; + } + out++; + sql++; + } + *out = 0; + return count; +} + +unsigned int sormpc(char * sql){ + + int number = 0; + while(sormpt(sql,number) != NULL) + number++; + printf("FOUND: %d\n",number); + return number; +} + +char * sormcts(int column_type){ + if(column_type == SQLITE_INTEGER) + return "integer"; + else if(column_type == SQLITE_TEXT) + return "text"; + else if(column_type == SQLITE_FLOAT) + return "float"; + else if(column_type == SQLITE_NULL) + return "null"; + else if(column_type == SQLITE_BLOB) + return "blob"; + return "?"; +} +/* +Execute 3.35s, Format: 36.77s +Memory usage: 537 GB, 96.026 allocated, 96.024 freed, 2 in use. +*/ +char * sorm_csvc(int db, sqlite3_stmt * stmt){ + sormstr_t * str = sormstrn(512); + unsigned int column_count = sqlite3_column_count(stmt); + for(int i = 0; i < column_count; i++){ + const char * column_name = sqlite3_column_name(stmt,i); + sormstra(str,column_name); + sormstra(str,"("); + char column_type[1000] = ""; + sprintf(column_type,"%s",sormcts(sqlite3_column_type(stmt,i))); + sormstra(str,column_type); + sormstra(str,")"); + + // if(i != column_count - 1) + sormstra(str,";"); + } + return sormstrc(str); +} +char * sorm_csvd(int sorm, sqlite3_stmt * stmt) { + sorm_t * db = sormg(sorm); + int rc = SQLITE_ROW; + int column_count = sqlite3_column_count(stmt); + /* + sormstrn(1) + Execute 3.41s, Format: 36.77s + Memory usage: 5 MB, 96.061 (re)allocated, 96.024 unqiue freed, 2 in use. + sormstrn(512) + Execute 3.68s, Format: 36.83s + Memory usage: 537 GB, 96.026 allocated, 96.024 freed, 2 in use. + sormstrn(256) + xecute 3.42s, Format: 37.33s + Memory usage: 6 MB, 96.052 (re)allocated, 96.024 unqiue freed, 2 in use. + */ + sormstr_t * str = sormstrn(512); + while(rc == SQLITE_ROW){ + sorm_row_count++; + for(int field_index = 0; field_index < column_count; field_index++){ + if(sqlite3_column_type(stmt,field_index) == SQLITE_INTEGER){ + char temp[1000] = ""; + sprintf(temp, "%lld",sqlite3_column_int64(stmt,field_index)); + sormstra(str,temp); + }else if(sqlite3_column_type(stmt,field_index) == SQLITE_FLOAT){ + char temp[1000] = ""; + sprintf(temp, "%f",sqlite3_column_double(stmt,field_index)); + sormstra(str,temp); + } else if(sqlite3_column_type(stmt,field_index) == SQLITE3_TEXT){ + const char * temp = sqlite3_column_text(stmt,field_index); + sormstra(str,temp); + } else { + // exit(1); + } + // if(field_index != column_count - 1) + sormstra(str,";"); + } + sormstra(str,"\n"); + rc = sqlite3_step(stmt); + } + char * text = sormstrc(str); + if(*text) + if(text[strlen(text)-1] == '\n') + text[strlen(text)-1] = 0; + return strdup(text); +} + +char * sorm_csv(int sorm,sqlite3_stmt * stmt){ + sorm_t * db = sormg(sorm); + sorm_row_count = 0; + char * column_names = sorm_csvc(sorm,stmt); + char * data = sorm_csvd(sorm,stmt); + char * result = (char *)malloc(strlen(column_names) + strlen(data) + 2); + result[0] = 0; + strcat(result,column_names); + if(*column_names) + strcat(result,"\n"); + free(column_names); + strcat(result,data); + free(data); + return result; +} + +sqlite3_stmt * sormb(sorm_t* db, char * sql, ...){ + // Bind parameters to statement and return amount of parameters + int rc = 0; + sqlite3_stmt * stmt; + va_list args; + va_start(args,sql); + unsigned int number = 0; + char * clean_query = (char *)malloc(strlen(sql) + 1); + unsigned int parameter_count = sormcq(sql,clean_query); + free(clean_query); + + return stmt; +} + +char * sormm(sorm_t * db){ + /* sormmemory */ + return rmalloc_stats(); +} + +sorm_ptr sormq(int sorm, char * sql, ...){ + sorm_t * db = sormg(sorm); + if(db->csv){ + //free(db->csv); + //db->csv = NULL; + } + _sorm_query_start = nsecs(); + db->time_query_start = nsecs(); + va_list args; + va_start (args,sql); + sqlite3_stmt * stmt; + sorm_ptr result = NULL; + char * clean_query = malloc(strlen(sql) + 1); + unsigned int parameter_count = sormcq(sql,clean_query); + int rc = sqlite3_prepare_v2(db->conn, clean_query, -1, &stmt, 0); + if (rc != SQLITE_OK) { + fprintf(stderr, "%s\n", sqlite3_errmsg(db->conn)); + } + free(clean_query); + int number = 0; + for(int i = 0; i < parameter_count; i++){ + number++; + char * column_type = sormpt(sql,number); + int arg_index = number - 1; + if(!strcmp(column_type, "int") || !strcmp(column_type, "integer") || !strcmp(column_type, "number")) { + rc = sqlite3_bind_int(stmt, number, va_arg(args,int)); + }else if(!strcmp(column_type, "int64")){ + rc = sqlite3_bind_int64(stmt, number,va_arg(args,__uint64_t)); + }else if(!strcmp(column_type, "double") || !strcmp(column_type, "dec") || !strcmp(column_type, "decimal") || !strcmp(column_type, "float")){ + rc = sqlite3_bind_double(stmt, number,va_arg(args,double)); + }else if(!strcmp(column_type, "blob")){ + size_t size = (size_t)va_arg(args,size_t); + unsigned char * data = va_arg(args,unsigned char *); + rc = sqlite3_bind_blob(stmt, number, data, size, SQLITE_STATIC); + }else if(!strcmp(column_type,"text") || !strcmp(column_type,"str") || !strcmp(column_type,"string")) { + unsigned char * data = va_arg(args, unsigned char *); + rc = sqlite3_bind_text(stmt, number, data, -1, SQLITE_STATIC); + } + if (rc != SQLITE_OK) { + fprintf(stderr, "Failed to bind parameters: %s\n", sqlite3_errmsg(db->conn)); + result = NULL; + } + } + rc = sqlite3_step(stmt); + if (rc != SQLITE_DONE && rc != SQLITE_ROW) { + fprintf(stderr, "Execution failed: %s\n", sqlite3_errmsg(db->conn)); + }else if(rc == SQLITE_DONE){ + if(!sqlite3_strnicmp(sql,"SELECT",6)){ + result = 0; + }else { + result = (sorm_ptr)sqlite3_last_insert_rowid(db->conn); + } + } else if (rc == SQLITE_ROW) { + result = sorm_csv(sorm,stmt); + } + + else{ + fprintf(stderr, "Execution failed: %s\n", sqlite3_errmsg(db->conn)); + + } + if(sorm_last_query){ + free(sorm_last_query); + } + if(sorm_last_query){ + free(sorm_last_query_expanded); + } + sorm_last_query = strdup(sqlite3_sql(stmt)); + sorm_last_query_expanded = strdup(sqlite3_expanded_sql(stmt)); + sqlite3_finalize(stmt); + _sorm_query_end = nsecs(); + _sorm_query_duration = _sorm_query_end - _sorm_query_start; + db->time_query_end = nsecs(); + db->time_query_duration = db->time_query_end - db->time_query_start; + db->csv = result; + return result; +} + + + +char sormlc(char * sql){ + // returns last char + char last_char = 0; + while(*sql){ + if(*sql == ' ' || *sql == '\t' || *sql == '\n') + continue ; + + //printf("%c\n",*sql); + last_char = *sql; + sql++; + } + return last_char; +} + +int sormlv(char * csv){ + size_t longest = 0; + while(*csv){ + char * found = strstr(csv,";"); + if(found){ + if(found - csv > longest) + longest = found-csv; + csv = csv + (found - csv) + 1; + }else{ + break; + } + } + return longest; +} + +sorm_query_t sormqt(char * sql){ + while(*sql && isspace(*sql)) + sql++; + if(!sqlite3_strnicmp(sql,"select",6)) + return SORM_SELECT; + else if(!sqlite3_strnicmp(sql, "update",6)) + return SORM_UPDATE; + else if(!sqlite3_strnicmp(sql, "delete",6)) + return SORM_DELETE; + else if(!sqlite3_strnicmp(sql, "create",6)){ + return SORM_CREATE; + }else { + return SORM_UNKNOWN; + } +} + +char * sormrq(FILE * f){ + + static char buffer[4097]; + buffer[0] = 0; + char * bufferp = buffer; + char c; + bool in_string = false; + while((c = fgetc(f)) != EOF && strlen(buffer) != sizeof(buffer) -2){ + *bufferp = c; + if(c == '"') + { + in_string = !in_string; + } + if(!in_string && c == ';'){ + break; + } + bufferp++; + *bufferp = 0; + } + return strdup(buffer); + +} + +char * sormcsvn(char * csv){ + if(!csv || !*csv) + return NULL; + char * pos = strstr(csv,";"); + char * pos2 = strstr(csv,"\n"); + if(pos2){ + if(pos > pos2){ + pos = pos2; + } + //pos = pos > pos2 ? pos2 : pos; + } + if(!pos || !*pos) + return strdup(csv); + + int length = pos - csv; + + char * result = malloc(length + 2); + strncpy(result,csv,length); + result[length] = 0; + //csv += strlen(result); + return result; +} + +char * sormfmt(char * csv){ + _sorm_result_format_start = nsecs(); + size_t longest = sormlv(csv); + char * field; + /* + sormstrn(1) + Execute 3.77s, Format: 36.40s + Memory usage: 6 MB, 96.055 (re)allocated, 96.024 unqiue freed, 2 in use. + sormstrn(longest); + Execute 3.27s, Format: 36.61s + Memory usage: 6 MB, 96.053 (re)allocated, 96.024 unqiue freed, 2 in use. + sormstrn(longest * 2); + xecute 3.42s, Format: 37.33s + Memory usage: 6 MB, 96.052 (re)allocated, 96.024 unqiue freed, 2 in use. + sormstrn(512); + Execute 3.11s, Format: 36.45s + Memory usage: 6 MB, 96.048 (re)allocated, 96.024 unqiue freed, 2 in use. + */ + sormstr_t * str = sormstrn(longest + 2); + while(*csv && (field = sormcsvn(csv))){ + sormstra(str,field); + for(int i = 0; i < longest - strlen(field); i++) + sormstra(str," "); + + csv += strlen(field); + while( *csv == ';' || *csv == '\n'){ + if(*csv == '\n') + sormstra(str, "\n"); + csv++; + } + free(field); + } + _sorm_result_format_end = nsecs(); + _sorm_result_format_duration = _sorm_result_format_end - _sorm_result_format_start; + return sormstrc(str); +} + +void apply_colors(char * csv){ + char * end; + + bool even = false; + while(*csv){ + printf("%s\n",csv); + end = strstr(csv,"\n"); + char * line; + if(end) + { + line = (char *)malloc(end -csv + 1024); + strncpy(line,csv,end-csv); + }else{ + line = (char *)malloc(strlen(csv) + 1024); + strcpy(line, csv); + } + if(even){ + printf("%s","\033[37m"); + } + printf("%s\n",line); + free(line); + if(even){ + printf("%s","\033[0m"); + } + even = !even; + csv += end-csv; + + if(*csv && *(csv + 1)) + csv++; + } + +} + +void sormfmtd(char * csv){ + char * formatted = sormfmt(csv); + printf("%s\n",formatted); + free(formatted); +} + + + + + +#endif \ No newline at end of file diff --git a/sorm.py b/sorm.py new file mode 100644 index 0000000..9119455 --- /dev/null +++ b/sorm.py @@ -0,0 +1,162 @@ +import ctypes +import csv +from csv import DictReader +import io +import tempfile +import time + +class DictReader: + + def get_column_types(self): + types = [] + for column in self.columns: + name = column.split("(")[0] + type = column.split("(")[1] + type = type.split(")")[0] + if type == "integer": + types.append(int) + if type == "text": + types.append(str) + return types + + def get_column_names(self): + names = [] + for column in self.columns: + name = column.split("(")[0] + names.append(name) + return names + def __init__(self, data): + self.result = 0 + if type(data) == int: + self.data = '' + else: + self.data = data.decode(); + self.rows = [row.split(";")[:-1] for row in self.data.split("\n")] + + self.columns = self.rows[0] + self.rows.pop(0) + if type(data) == str: + self.result = len(self.rows) + else: + self.result = data + self.column_types = self.get_column_types() + for row in self.rows: + for index,field in enumerate(row): + row[index] = self.column_types[index](field) + self.column_names = self.get_column_names() + + + + def __iter__(self): + return self.rows.__iter__() + + + + +libc = ctypes.CDLL('libc.so.6'); + + +class Sorm: + + def __init__(self): + self.lib = ctypes.CDLL('./sorm.so') + self.sormq = self.lib.sormq + self.sormq.argtypes = [ctypes.c_int, ctypes.c_char_p]; + self.sormq.restype = ctypes.c_char_p + + self.sormc = self.lib.sormc + self.sormc.argtypes = [ctypes.c_char_p]; + self.sormc.restype = ctypes.c_int; + + self.sormd = self.lib.sormd + self.sormd.argtypes = [ctypes.c_int]; + self.sormd.restype = None + + self.sormm = self.lib.sormm + self.sormm.argtypes = [ctypes.c_int]; + self.sormm.restype = ctypes.c_char_p + +class SormDb(Sorm): + + def __init__(self,path): + super().__init__( + + ) + self.path = path + self.conn = None + + def c(self): + if not self.conn: + self.conn = self.sormc(self.path.encode()) + return self.conn + + def __enter__(self): + self.c() + return self + + def __exit__(self,*args, **kwargs): + self.d() + + def q(self, sql,*args) -> DictReader: + ctypes_list = [] + for arg in args: + if type(arg) == int: + ctypes_list.append(ctypes.c_int) + if type(arg) == str: + ctypes_list.append(ctypes.c_char_p) + self.sormq.argtypes = [ctypes.c_int, ctypes.c_char_p] + ctypes_list + if not sql.lower().startswith("select"): + self.sormq.restype = ctypes.c_int + else: + self.sormq.restype = ctypes.c_char_p + params = tuple([self.conn, sql.encode()] + list(arg.encode() if type(arg) == str else arg for arg in args)) + result = DictReader(self.sormq(*params)) + self.m = self.sormm(self.conn).decode() + return result + + def d(self): + if not self.conn: + return + self.sormd(self.conn) + self.conn = None + +# Load the shared library +lib = ctypes.CDLL('./sorm.so') + +free = libc.free +free.argtypes = [ctypes.c_void_p] +free.restype = None + + + + +rsomm = lib.sormm +rsomm.argtypes = [ctypes.c_int]; +rsomm.restype = ctypes.c_char_p; + +disconnect = lib.sormd +disconnect.argtypes = [ctypes.c_int]; + +start = time.time() +for x in range(1): + + with SormDb("db.sqlite3") as db: + + + + for x in range(1): + #db.q("BEGIN TRANSACTION") + #for x in range(100000): + # db.q("INSERT INTO pony (name,age) VALUES (?s,?d);","Python Pony",1337) + #db.q("COMMIT") + result = db.q("SELECT * FROM pony WHERE id > ?d AND name like ?s ORDER BY id",1337, "%hon Pon%"); + #for row in result: + # print(row) + print(result.column_names) + print(len(result.rows),"records") + print(db.m); +end = time.time() +duration = end - start +print("Duration: {}s".format(duration)) + + \ No newline at end of file diff --git a/sorm.so b/sorm.so new file mode 100755 index 0000000..369b11c Binary files /dev/null and b/sorm.so differ diff --git a/str.h b/str.h new file mode 100644 index 0000000..5781f98 --- /dev/null +++ b/str.h @@ -0,0 +1,50 @@ +#ifndef SORM_STR_H +#define SORM_STR_H +#include +#include +#include +#include + +typedef struct sormstr_t { + char * content; + size_t length; + size_t buffer_size; + size_t size; +} sormstr_t; + +sormstr_t * sormstrn(size_t buffer_size){ + sormstr_t * result = (sormstr_t *)malloc(sizeof(sormstr_t)); + result->length = 0; + result->size = buffer_size; + result->buffer_size = buffer_size; + result->content = (char *)malloc(buffer_size); + result->content[0] = 0; + return result; +} +void sormstra(sormstr_t * str, const char * to_append){ + size_t required_new_length = str->length + strlen(to_append); + str->length += strlen(to_append); + if(required_new_length > str->size){ + str->size += required_new_length + str->buffer_size; + str->content = realloc(str->content,str->size + 1); + }else{ + // printf("NO NDEED\n"); + } + strcat(str->content,to_append); + str->content[str->length] = 0; +} +void sormstrd(sormstr_t * str){ + if(str->content){ + free(str->content); + } + free(str); +} +char * sormstrc(sormstr_t * str){ + // sorm str convert + char * content = str->content; + str->content = NULL; + sormstrd(str); + return content; +} + +#endif \ No newline at end of file