From 18490c014a083235ee5615cfcaf8eccb3a136890 Mon Sep 17 00:00:00 2001 From: avsdev-cw Date: Wed, 23 Sep 2020 13:08:06 +0100 Subject: [PATCH] Created a WHERE query builder and implemented a TYPE to char * method for database values --- src/database.h | 2 + src/db_value.c | 188 +++++++++++ src/db_value.h | 9 + src/db_where-builder.c | 719 +++++++++++++++++++++++++++++++++++++++++ src/db_where-builder.h | 72 +++++ src/strext.c | 221 +++++++++++++ src/strext.h | 32 ++ 7 files changed, 1243 insertions(+) create mode 100644 src/db_value.c create mode 100644 src/db_value.h create mode 100644 src/db_where-builder.c create mode 100644 src/db_where-builder.h create mode 100644 src/strext.c create mode 100644 src/strext.h diff --git a/src/database.h b/src/database.h index 64f7056..20d707e 100644 --- a/src/database.h +++ b/src/database.h @@ -6,6 +6,8 @@ #include "db_transaction.h" #include "db_column.h" #include "db_query.h" +#include "db_value.h" +#include "db_where-builder.h" void sessionEnd(void); diff --git a/src/db_value.c b/src/db_value.c new file mode 100644 index 0000000..2f58384 --- /dev/null +++ b/src/db_value.c @@ -0,0 +1,188 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "strext.h" +#include "db_value.h" + +char *db_value(char **str, size_t *len, e_column_type type, uint32_t n_args, ...) +{ + va_list args; + char *ret; + + va_start(args, n_args); + ret = db_value_va(str, len, type, n_args, args); + va_end(args); + + return ret; +} + +char *db_value_va(char **str, size_t *len, e_column_type type, uint32_t n_args, va_list args) +{ + char buf[32], *tmp_str = 0, *esc_str = 0; + size_t tmp_len = 0, esc_len = 0; + MYSQL *sql; + + if (n_args == 0) { + return 0; + } + + memset(buf, 0, 32); + + switch(type) { + case TYPE_BOOL: + { + sprintf(buf, "%s", va_arg(args, int) == 0 ? "FALSE" : "TRUE"); + break; + } + + case TYPE_INT8: + { + vsprintf(buf, "%"PRIi8, args); + break; + } + case TYPE_UINT8: + { + vsprintf(buf, "%"PRIu8, args); + break; + } + + case TYPE_INT16: + { + vsprintf(buf, "%"PRIi16, args); + break; + } + case TYPE_UINT16: + { + vsprintf(buf, "%"PRIu16, args); + break; + } + + case TYPE_INT32: + { + vsprintf(buf, "%"PRIi32, args); + break; + } + case TYPE_TIMESTAMP: + case TYPE_UINT32: + { + vsprintf(buf, "%"PRIu32, args); + break; + } + + case TYPE_INT64: + { + vsprintf(buf, "%"PRIi64, args); + break; + } + case TYPE_UINT64: + { + vsprintf(buf, "%"PRIu64, args); + break; + } + + case TYPE_FLOAT: + { + vsprintf(buf, "%f", args); + break; + } + case TYPE_DOUBLE: + { + vsprintf(buf, "%lf", args); + break; + } + + case TYPE_STRING: + { + tmp_str = va_arg(args, char *); + if (n_args == 1) { + tmp_len = strlen(tmp_str); + } else { + tmp_len = va_arg(args, size_t); + } + __attribute__((fallthrough)); + } + case TYPE_BLOB: + { + if (tmp_str == 0) { + tmp_str = (char *)va_arg(args, void *); + tmp_len = va_arg(args, size_t); + } + + if (tmp_str == 0) { + esc_str = (char *)malloc(5); + if (esc_str == 0) { + fprintf(stderr, "[%d]malloc: (%d) %s\n", __LINE__, errno, strerror(errno)); + return 0; + } + memcpy(esc_str, "NULL", 4); + esc_str[4] = '\0'; + esc_len = 4; + break; + } + + sql = mysql_init(NULL); + if (sql == 0) { + fprintf(stderr, "[%d]mysql_init: (%d) %s\n", __LINE__, errno, strerror(errno)); + return 0; + } + + esc_str = (char *)malloc(tmp_len * 2 + 1 + 2); + if (esc_str == 0) { + fprintf(stderr, "[%d]malloc: (%d) %s\n", __LINE__, errno, strerror(errno)); + mysql_close(sql); + return 0; + } + + + esc_str[0] = '\''; + esc_len = mysql_real_escape_string(sql, esc_str + 1, tmp_str, tmp_len); + esc_str[esc_len + 1] = '\''; + esc_str[esc_len + 2] = '\0'; + esc_len += 2; + mysql_close(sql); + + tmp_str = esc_str; + esc_str = (char *)realloc(esc_str, esc_len + 1); + if (esc_str == 0) { + fprintf(stderr, "[%d]realloc: (%d) %s\n", __LINE__, errno, strerror(errno)); + return 0; + } + + break; + } + + case TYPE_RAW: + { + tmp_str = va_arg(args, char *); + tmp_len = va_arg(args, size_t); + + esc_str = (char *)malloc(tmp_len + 1); + if (esc_str == 0) { + fprintf(stderr, "[%d]malloc: (%d) %s\n", __LINE__, errno, strerror(errno)); + return 0; + } + memcpy(esc_str, tmp_str, tmp_len); + esc_str[tmp_len] = '\0'; + + break; + } + } + + if (esc_str == 0) { + if (strmemcpy(buf, strlen(buf), &esc_str, &esc_len) != 0) { + return 0; + } + } + + if (str != 0) { + *str = esc_str; + *len = esc_len; + } + + return esc_str; +} diff --git a/src/db_value.h b/src/db_value.h new file mode 100644 index 0000000..9e65720 --- /dev/null +++ b/src/db_value.h @@ -0,0 +1,9 @@ +#ifndef H__DB_VALUE__ +#define H__DB_VALUE__ + +#include "db_column.h" + +char *db_value(char **str, size_t *len, e_column_type type, uint32_t n_args, ...); +char *db_value_va(char **str, size_t *len, e_column_type type, uint32_t n_args, va_list args); + +#endif // H__DB_VALUE__ diff --git a/src/db_where-builder.c b/src/db_where-builder.c new file mode 100644 index 0000000..b550b78 --- /dev/null +++ b/src/db_where-builder.c @@ -0,0 +1,719 @@ +#include +#include +#include +#include + +#include "db_where-builder.h" +#include "db_connection.h" +#include "db_value.h" +#include "strext.h" + + +struct where_logic_t { + where_builder *up; + e_where_logic logic_type; + + where_builder **constructs; + size_t n_constructs; + size_t n_alloc; +}; +typedef struct where_logic_t where_logic; + +struct where_clause_t { + where_builder *up; + e_where_logic logic_type; + + // Table + char *table; + size_t table_len; + + // Column + char *col; + size_t col_len; + + // Op + e_where_op op; + + // Values + char **values; + size_t *values_len; + size_t n_values; + size_t n_alloc; +}; +typedef struct where_clause_t where_clause; + + +// private: +void freeLogic(where_logic **logic_ptr) +{ + where_logic *logic; + where_builder *construct; + + logic = *logic_ptr; + while (logic->n_constructs > 0) { + construct = logic->constructs[logic->n_constructs - 1]; + construct->up = 0; + destroyWhereBuilder(&construct); + logic->n_constructs--; + } + if (logic->constructs != 0) { + free(logic->constructs); + logic->constructs = 0; + } + free(*logic_ptr); + *logic_ptr = 0; +} +where_logic *createLogic(e_where_logic type, size_t initial_size) +{ + where_logic *logic = (where_logic *)malloc(sizeof(where_logic)); + if (logic == 0) { + fprintf(stderr, "[%d]malloc: (%d) %s\n", __LINE__, errno, strerror(errno)); + return 0; + } + memset(logic, 0, sizeof(where_logic)); + + logic->logic_type = type; + + if (initial_size == 0) { + return logic; + } + + logic->constructs = (where_builder **)malloc(sizeof(where_builder *) * initial_size); + if (logic->constructs == 0) { + fprintf(stderr, "[%d]malloc: (%d) %s\n", __LINE__, errno, strerror(errno)); + freeLogic(&logic); + return 0; + } + memset(logic->constructs, 0, sizeof(where_builder *) * initial_size); + logic->n_constructs = 0; + logic->n_alloc = initial_size; + + return logic; +} +where_builder *appendClause(where_builder *wb, where_builder *wb_clause) +{ + where_logic *logic = (where_logic *)wb; + where_builder **old_ptr; + size_t new_size; + + if (wb_clause == 0) { + return wb; + } + + if (!(wb->logic_type == AND || wb->logic_type == OR)) { + fprintf(stderr, "[%d]appendClause: Where-builder stack corrupted!\n", __LINE__); + return wb; + } + + if (logic->n_constructs == logic->n_alloc) { + old_ptr = (where_builder **)logic->constructs; + new_size = sizeof(where_builder *) * (logic->n_alloc + 1); + logic->constructs = (where_builder **)realloc(logic->constructs, new_size); + if (logic->constructs == 0) { + fprintf(stderr, "[%d]realloc: (%d) %s\n", __LINE__, errno, strerror(errno)); + logic->constructs = old_ptr; + return wb; + } + logic->n_alloc++; + } + + logic->constructs[logic->n_constructs] = wb_clause; + logic->n_constructs++; + wb_clause->up = (where_builder *)logic; + + return wb; +} + +int ensureValueSize(where_clause *clause, size_t new_size) +{ + size_t *old_lens; + char **old_values; + + if (clause->n_alloc >= new_size) { + return 0; + } + + old_values = clause->values; + clause->values = (char **)realloc(clause->values, sizeof(char *) * new_size); + if (clause->values == 0) { + fprintf(stderr, "[%d]realloc: (%d) %s\n", __LINE__, errno, strerror(errno)); + clause->values = old_values; + return 1; + } + + old_lens = clause->values_len; + clause->values_len = (size_t *)realloc(clause->values_len, sizeof(size_t) * new_size); + if (clause->values_len == 0) { + fprintf(stderr, "[%d]realloc: (%d) %s\n", __LINE__, errno, strerror(errno)); + clause->values_len = old_lens; + return 1; + } + + (clause->n_alloc)++; + + return 0; +} + +where_clause *createWhere(const char *tbl, const char *col, e_where_op op) +{ + where_clause *clause; + + clause = (where_clause *)malloc(sizeof(where_clause)); + if (clause == 0) { + fprintf(stderr, "[%d]malloc: (%d) %s\n", __LINE__, errno, strerror(errno)); + return 0; + } + memset(clause, 0, sizeof(where_clause)); + + clause->logic_type = CLAUSE; + clause->op = op; + + if (tbl != 0) { + if (strmemcpy(tbl, strlen(tbl), &clause->table, &clause->table_len) != 0) { + freeWhere((where_builder **)&clause); + return 0; + } + } + if (strmemcpy(col, strlen(col), &clause->col, &clause->col_len) != 0) { + freeWhere((where_builder **)&clause); + return 0; + } + + clause->values = (char **)malloc(sizeof(char *)); + clause->values_len = (size_t *)malloc(sizeof(size_t)); + if (clause->values == 0 || clause->values_len == 0) { + fprintf(stderr, "[%d]malloc: (%d) %s\n", __LINE__, errno, strerror(errno)); + freeWhere((where_builder **)&clause); + return 0; + } + clause->n_alloc = 1; + + return clause; +} + +where_builder *where_And_Or(where_builder *wb, where_builder *wb_clause, e_where_logic and_or) +{ + where_logic *logic; + + if (wb == 0) { + wb = (where_builder *)createLogic(and_or, 2); + if (wb == 0) { + return wb_clause; + } + } + + switch(wb->logic_type) { + case UNK: + { + if ((logic = createLogic(and_or, 2)) == 0) { + destroyWhereBuilder(&wb_clause); + return wb; + } + free(wb); + if (wb_clause != 0) { + logic->constructs[0] = wb_clause; + logic->n_constructs++; + wb_clause->up = (where_builder *)logic; + } + wb = (where_builder *)logic; + break; + } + + case CLAUSE: + { + if ((logic = createLogic(and_or, 2)) == 0) { + destroyWhereBuilder(&wb_clause); + return wb; + } + logic->constructs[0] = wb; + logic->n_constructs++; + wb->up = (where_builder *)logic; + if (wb_clause != 0) { + logic->constructs[1] = wb_clause; + logic->n_constructs++; + wb_clause->up = (where_builder *)logic; + } + wb = (where_builder *)logic; + break; + } + + case OR: + { + if (and_or == AND) { + if ((logic = createLogic(AND, 2)) == 0) { + destroyWhereBuilder(&wb_clause); + return wb; + } + if (whereOr(wb, (where_builder *)logic) == 0) { + freeLogic(&logic); + destroyWhereBuilder(&wb_clause); + return wb; + } + wb = appendClause((where_builder *)logic, wb_clause); + } else { + wb = appendClause(wb, wb_clause); + } + break; + } + + case AND: + { + if (and_or == AND) { + wb = appendClause(wb, wb_clause); + } else { + if ((logic = createLogic(OR, 2)) == 0) { + destroyWhereBuilder(&wb_clause); + return wb; + } + if (whereAnd(wb, (where_builder *)logic) == 0) { + freeLogic(&logic); + destroyWhereBuilder(&wb_clause); + return wb; + } + wb = appendClause((where_builder *)logic, wb_clause); + } + break; + } + } + + return wb; +} + +where_builder *where_In_notIn_va(where_builder *wb, const char *tbl, const char *col, e_where_op op, + e_column_type type, uint32_t n_args, va_list args) +{ + where_clause *clause; + where_builder *wb_clause; + + if (wb == 0) { + return where_va(tbl, col, op, type, n_args, args); + } + + switch (wb->logic_type) { + case UNK: + { + free(wb); + return where_va(tbl, col, op, type, n_args, args); + } + case CLAUSE: + { + clause = (where_clause *)wb; + if (clause->op != op + || (tbl != 0 && clause->table == 0) + || (tbl != 0 && clause->table != 0 && strcmp(clause->table, tbl) != 0) + || (col != 0 && clause->col == 0) + || (col != 0 && clause->col != 0 && strcmp(clause->col, col) != 0)) { + if (wb->up == 0) { + fprintf(stderr, "[%d]whereIn_notIn: Where-builder stack corrupted!\n", __LINE__); + return wb; + } + return where_In_notIn_va(wb->up, tbl, col, op, type, n_args, args); + } + + return setWhereValue_va((where_builder *)clause, type, n_args, args); + } + case AND: + case OR: + { + if ((wb_clause = where_va(tbl, col, op, type, n_args, args)) == 0) { + return wb; + } + if (wb->logic_type == AND) { + whereAnd(wb, wb_clause); + } else { + whereOr(wb, wb_clause); + } + return wb_clause; + } + } + + return wb; +} +where_builder *where_In_notIn(where_builder *wb, const char *tbl, const char *col, e_where_op op, + e_column_type type, uint32_t n_args, ...) +{ + where_builder *ret; + va_list args; + + va_start(args, n_args); + ret = where_In_notIn_va(wb, tbl, col, op, type, n_args, args); + va_end(args); + + return ret; +} + + +// public: +where_builder *createWhereBuilder(where_builder *initial_clause) +{ + if (initial_clause != 0) { + return initial_clause; + } + + where_builder *wb = (where_builder *)malloc(sizeof(where_builder)); + if (wb == 0) { + fprintf(stderr, "[%d]malloc: (%d) %s\n", __LINE__, errno, strerror(errno)); + return 0; + } + memset(wb, 0, sizeof(where_builder)); + + return wb; +} +void destroyWhereBuilder(where_builder **wb_ptr) +{ + where_builder *wb = finalizeWhere(*wb_ptr); + + if (*wb_ptr == 0) { + return; + } + + switch(wb->logic_type) { + case CLAUSE: + { + freeWhere(&wb); + break; + } + case OR: + case AND: + { + freeLogic((where_logic **)&wb); + break; + } + case UNK: + { + free(*wb_ptr); + break; + } + } + + *wb_ptr = 0; +} + + +where_builder *where(const char *tbl, const char *col, e_where_op op, e_column_type type, + uint32_t n_args, ...) +{ + va_list args; + where_builder *ret; + + va_start(args, n_args); + ret = where_va(tbl, col, op, type, n_args, args); + va_end(args); + + return ret; +} +where_builder *where_va(const char *tbl, const char *col, e_where_op op, e_column_type type, + uint32_t n_args, va_list args) +{ + where_builder *wb_clause = (where_builder *)createWhere(tbl, col, op); + + if (wb_clause == 0) { + return 0; + } + + return setWhereValue_va(wb_clause, type, n_args, args); +} +void freeWhere(where_builder **wb_clause_ptr) +{ + where_clause *clause = (where_clause *)(*wb_clause_ptr); + + if (wb_clause_ptr == 0 || (*wb_clause_ptr)->logic_type != CLAUSE) { + fprintf(stderr, "[%d]freeWhere: Where-builder stack corrupted!\n", __LINE__); + return; + } + + while (clause->n_values > 0) { + free(clause->values[clause->n_values - 1]); + clause->n_values--; + } + if (clause->values != 0) { + free(clause->values); + clause->values = 0; + } + if (clause->values_len != 0) { + free(clause->values_len); + clause->values_len = 0; + } + + if (clause->col != 0) { + free(clause->col); + clause->col = 0; + } + + if (clause->table) { + free(clause->table); + clause->table = 0; + } + + free(clause); + *wb_clause_ptr = 0; +} + + +where_builder *clearWhereValue(where_builder *wb_clause) +{ + where_clause *clause = (where_clause *)wb_clause; + + if (wb_clause->logic_type != CLAUSE) { + fprintf(stderr, "[%d]clearWhereValue: Where-builder stack corrupted!\n", __LINE__); + return wb_clause; + } + + while (clause->n_values > 0) { + clause->n_values--; + free(clause->values[clause->n_values]); + clause->values[clause->n_values] = 0; + clause->values_len[clause->n_values] = 0; + } + + return wb_clause; +} +where_builder *setWhereValue(where_builder *wb_clause, e_column_type type, uint32_t n_args, ...) +{ + va_list args; + where_builder *ret; + + va_start(args, n_args); + ret = setWhereValue_va(wb_clause, type, n_args, args); + va_end(args); + + return ret; +} +where_builder *setWhereValue_va(where_builder *wb_clause, + e_column_type type, uint32_t n_args, va_list args) +{ + where_clause *clause = (where_clause *)wb_clause; + char *value; + size_t value_len; + + if (wb_clause->logic_type != CLAUSE) { + fprintf(stderr, "[%d]setWhereValue: Where-builder stack corrupted!\n", __LINE__); + return wb_clause; + } + + switch (clause->op) { + case EQ: + case NEQ: + case GT: + case GTE: + case LT: + case LTE: + { + clearWhereValue(wb_clause); + + db_value_va(&value, &value_len, type, n_args, args); + + if (value != 0) { + clause->values[clause->n_values] = value; + clause->values_len[clause->n_values] = value_len; + } + clause->n_values++; + break; + } + + case IS_NULL: + case NOT_NULL: + { + break; + } + + case IN: + case NOT_IN: + { + if (ensureValueSize(clause, clause->n_values + n_args) != 0) { + freeWhere(&wb_clause); + return 0; + } + + for (uint32_t i = 0; i < n_args; i++) { + db_value_va(&value, &value_len, type, 1, args); + if (value != 0) { + clause->values[clause->n_values] = value; + clause->values_len[clause->n_values] = value_len; + } + clause->n_values++; + } + + break; + } + } + + return wb_clause; +} + + +where_builder *whereAnd(where_builder *wb, where_builder *wb_clause) +{ + return where_And_Or(wb, wb_clause, AND); +} +where_builder *whereOr(where_builder *wb, where_builder *wb_clause) +{ + return where_And_Or(wb, wb_clause, OR); +} + +where_builder *whereIn(where_builder *wb, const char *tbl, const char *col, + e_column_type type, uint32_t n_args, ...) +{ + where_builder *ret; + va_list args; + + va_start(args, n_args); + ret = whereIn_va(wb, tbl, col, type, n_args, args); + va_end(args); + + return ret; +} +where_builder *whereIn_va(where_builder *wb, const char *tbl, const char *col, + e_column_type type, uint32_t n_args, va_list args) +{ + return where_In_notIn_va(wb, tbl, col, IN, type, n_args, args); +} +where_builder *whereNotIn(where_builder *wb, const char *tbl, const char *col, + e_column_type type, uint32_t n_args, ...) +{ + where_builder *ret; + va_list args; + + va_start(args, n_args); + ret = whereIn_va(wb, tbl, col, type, n_args, args); + va_end(args); + + return ret; +} +where_builder *whereNotIn_va(where_builder *wb, const char *tbl, const char *col, + e_column_type type, uint32_t n_args, va_list args) +{ + + return where_In_notIn_va(wb, tbl, col, NOT_IN, type, n_args, args); +} + +where_builder *whereNext(where_builder *wb) +{ + if (wb->up == 0) { + fprintf(stderr, "[%d]whereEnd: Where-builder stack corrupted!\n", __LINE__); + return wb; + } + + return wb->up; +} +where_builder *finalizeWhere(where_builder *wb) +{ + while (wb->up != 0) { + wb = wb->up; + } + return wb; +} + + +int compileWhere(where_builder *wb, char **str, size_t *str_len) +{ + where_logic *logic; + where_clause *clause; + struct str_builder_t *sb; + char *tmp; + size_t tmp_len; + size_t idx; + + sb = strbld_create(); + if (sb == 0) { + return -1; + } + + switch(wb->logic_type) { + case UNK: + strbld_destroy(&sb); + return -1; + case CLAUSE: + { + clause = (where_clause *)wb; + + // Column + if (clause->table != 0) { + strbld_char(sb, '`'); + strbld_str(sb, clause->table, clause->table_len); + strbld_char(sb, '`'); + strbld_char(sb, '.'); + } + strbld_char(sb, '`'); + strbld_str(sb, clause->col, clause->col_len); + strbld_char(sb, '`'); + strbld_char(sb, ' '); + + // Op + switch(clause->op) { + case EQ: + strbld_char(sb, '='); + break; + case NEQ: + strbld_char(sb, '!'); + strbld_char(sb, '='); + break; + case GT: + strbld_char(sb, '>'); + break; + case GTE: + strbld_char(sb, '>'); + strbld_char(sb, '='); + break; + case LT: + strbld_char(sb, '<'); + break; + case LTE: + strbld_char(sb, '<'); + strbld_char(sb, '='); + break; + case IS_NULL: + strbld_str(sb, "IS NULL", 7); + break; + case NOT_NULL: + strbld_str(sb, "IS NOT NULL", 11); + break; + case IN: + strbld_str(sb, "IN(", 3); + break; + case NOT_IN: + strbld_str(sb, "NOT IN(", 7); + break; + } + + // Value + for (idx = 0; idx < clause->n_values; idx++) { + strbld_char(sb, ' '); + strbld_str(sb, clause->values[idx], clause->values_len[idx]); + if (idx < clause->n_values - 1 && (clause->op == IN || clause->op == NOT_IN)) { + strbld_char(sb, ','); + } + } + + if (clause->op == IN || clause->op == NOT_IN) { + strbld_char(sb, ' '); + strbld_char(sb, ')'); + } + } + break; + case OR: + case AND: + { + logic = (where_logic *)wb; + + if (logic->n_constructs == 0) { + break; + } + + strbld_char(sb, '('); + for (size_t i = 0; i < logic->n_constructs; i++) { + if (compileWhere(logic->constructs[i], &tmp, &tmp_len) == 0) { + strbld_str(sb, tmp, tmp_len); + free(tmp); + } + if (i < (logic->n_constructs - 1)) { + strbld_str(sb, (wb->logic_type == OR ? " OR " : " AND "), (wb->logic_type == OR ? 4 : 5)); + } + } + strbld_char(sb, ')'); + break; + } + } + + return strbld_finalize_or_destroy(&sb, str, str_len); +} diff --git a/src/db_where-builder.h b/src/db_where-builder.h new file mode 100644 index 0000000..db84327 --- /dev/null +++ b/src/db_where-builder.h @@ -0,0 +1,72 @@ +#ifndef DB_WHEREBUILDER_H +#define DB_WHEREBUILDER_H + +#include +#include "db_column.h" + +enum e_where_logic_t { + UNK, + CLAUSE, + AND, + OR +}; + +enum e_where_op_t { + EQ, + NEQ, + GT, + GTE, + LT, + LTE, + IS_NULL, + NOT_NULL, + IN, + NOT_IN +}; + +struct where_builder_t { + struct where_builder_t *up; + enum e_where_logic_t logic_type; +}; + +typedef enum e_where_logic_t e_where_logic; +typedef enum e_where_op_t e_where_op; +typedef struct where_builder_t where_builder; + + +where_builder *createWhereBuilder(where_builder *initial_clause); +void destroyWhereBuilder(where_builder **wb_ptr); + + +where_builder *where(const char *tbl, const char *col, e_where_op op, e_column_type type, + uint32_t n_args, ...); +where_builder *where_va(const char *tbl, const char *col, e_where_op op, e_column_type type, + uint32_t n_args, va_list args); +void freeWhere(where_builder **wb_clause_ptr); + + +where_builder *clearWhereValue(where_builder *wb_clause); +where_builder *setWhereValue(where_builder *wb_clause, e_column_type type, uint32_t n_args, ...); +where_builder *setWhereValue_va(where_builder *wb_clause, + e_column_type type, uint32_t n_args, va_list args); + + +where_builder *whereAnd(where_builder *wb, where_builder *wb_clause); +where_builder *whereOr(where_builder *wb, where_builder *wb_clause); + +where_builder *whereIn(where_builder *wb, const char *tbl, const char *col, + e_column_type type, uint32_t n_args, ...); +where_builder *whereIn_va(where_builder *wb, const char *tbl, const char *col, + e_column_type type, uint32_t n_args, va_list args); +where_builder *whereNotIn(where_builder *wb, const char *tbl, const char *col, + e_column_type type, uint32_t n_args, ...); +where_builder *whereNotIn_va(where_builder *wb, const char *tbl, const char *col, + e_column_type type, uint32_t n_args, va_list args); + +where_builder *whereNext(where_builder *wb); +where_builder *finalizeWhere(where_builder *wb); + + +int compileWhere(where_builder *wb, char **str, size_t *str_len); + +#endif // DB_WHEREBUILDER_H diff --git a/src/strext.c b/src/strext.c new file mode 100644 index 0000000..53a55b4 --- /dev/null +++ b/src/strext.c @@ -0,0 +1,221 @@ +#include +#include +#include +#include + +#include "strext.h" + +size_t strlen_norm(const char *str, size_t str_len) +{ + if (str == 0) { + return 0; + } + if (str_len == 0) { + return strlen(str); + } + while (str[str_len - 1] == '\0') { + str_len--; + } + return str_len; +} + +int strmemcpy(const char *str, size_t str_len, char **new_str, size_t *new_len) +{ + if (str == 0) { + return -1; + } + + str_len = strlen_norm(str, str_len); + + *new_str = (char *)malloc(str_len + 1); + if (*new_str == 0) { + fprintf(stderr, "[%d]malloc: (%d) %s\n", __LINE__, errno, strerror(errno)); + return -errno; + } + + memcpy(*new_str, str, str_len); + (*new_str)[str_len] = '\0'; + (*new_len) = str_len; + + return 0; +} + +size_t strapp(char **out, const char *a, size_t a_len) +{ + size_t s_len; + if (a[a_len - 1] == '\0') { + a_len -= 1; + } + + if (*out == 0) { + s_len = 0; + *out = (char *)malloc(a_len + 1); + if (*out == 0) { + fprintf(stderr, "[%d]malloc: (%d) %s\n", __LINE__, errno, strerror(errno)); + return 0; + } + } else { + s_len = strlen(*out); + *out = (char *)realloc(*out, s_len + a_len + 1); + if (*out == 0) { + fprintf(stderr, "[%d]realloc: (%d) %s\n", __LINE__, errno, strerror(errno)); + return 0; + } + } + strncpy(*out + s_len, a, a_len); + *(*out + s_len + a_len) = '\0'; + + return s_len + a_len; +} + +size_t strcmb(char **out, const char *a, size_t a_len, const char *b, size_t b_len) +{ + size_t s_len, t_len; + if (a_len == 0) { + a_len = strlen(a); + } else if (a[a_len - 1] == '\0') { + a_len -= 1; + } + if (b_len == 0) { + b_len = strlen(b); + } else if (b[b_len - 1] == '\0') { + b_len -= 1; + } + t_len = a_len + b_len; + + if (*out == 0) { + s_len = 0; + *out = (char *)malloc(t_len + 1); + if (*out == 0) { + fprintf(stderr, "[%d]malloc: (%d) %s\n", __LINE__, errno, strerror(errno)); + return 0; + } + } else { + s_len = strlen(*out); + *out = (char *)realloc(*out, s_len + t_len + 1); + if (*out == 0) { + fprintf(stderr, "[%d]realloc: (%d) %s\n", __LINE__, errno, strerror(errno)); + return 0; + } + } + strncpy(*out + s_len, a, a_len); + strncpy(*out + s_len + a_len, b, b_len); + *(*out + s_len + t_len) = '\0'; + + return s_len + t_len; +} + + +str_builder *strbld_create() +{ + str_builder *sb = malloc(sizeof(str_builder)); + if (sb == 0) { + fprintf(stderr, "[%d]malloc: (%d) %s\n", __LINE__, errno, strerror(errno)); + return 0; + } + + sb->str = (char *)malloc(32); + if (sb == 0) { + fprintf(stderr, "[%d]malloc: (%d) %s\n", __LINE__, errno, strerror(errno)); + free(sb); + return 0; + } + memset(sb->str, '\0', 32); + + sb->alloc = 32; + sb->len = 0; + sb->fails = 0; + + return sb; +} +void strbld_destroy(str_builder **sbp) +{ + str_builder *sb = *sbp; + if (sb->alloc > 0 && sb->str != 0) { + free(sb->str); + sb->alloc = 0; + sb->str = 0; + } + free(sb); + *sbp = 0; +} +void strbld_finalize(str_builder **sbp, char **str, size_t *len) +{ + str_builder *sb = *sbp; + *str = sb->str; + *len = sb->len; + sb->alloc = 0; + strbld_destroy(sbp); +} +int strbld_finalize_or_destroy(str_builder **sbp, char **str, size_t *len) +{ + if ((*sbp)->fails > 0) { + strbld_destroy(sbp); + return -1; + } else { + strbld_finalize(sbp, str, len); + return 0; + } +} + +int strbld_ensure_len(str_builder *sb, size_t len, int absolute) +{ + char *new_str; + if (sb->alloc > len) { + return 1; + } + + if (absolute) { + sb->alloc = len; + } else { + while (sb->alloc <= len) { + sb->alloc <<= 1; + } + } + + new_str = (char *)realloc(sb->str, sb->alloc); + if (new_str == 0) { + fprintf(stderr, "[%d]realloc: (%d) %s\n", __LINE__, errno, strerror(errno)); + return 0; + } + sb->str = new_str; + memset(sb->str + sb->len, '\0', sb->alloc - sb->len); + + return 1; +} + +int strbld_str(str_builder *sb, const char *str, size_t len) +{ + if (sb->fails > 0) { + return 0; + } + + if (len == 0) { + len = strlen(str); + } + if (str[len - 1] == '\0') { + len--; + } + + if (!strbld_ensure_len(sb, sb->len + len, 0)) { + sb->fails++; + return 0; + } + memmove(sb->str + sb->len, str, len); + sb->len += len; + + return 1; +} +int strbld_char(str_builder *sb, const char c) +{ + if (sb->fails > 0) { + return 0; + } + if (!strbld_ensure_len(sb, sb->len + 1, 0)) { + sb->fails++; + return 0; + } + *(sb->str + sb->len) = c; + sb->len++; + return 1; +} diff --git a/src/strext.h b/src/strext.h new file mode 100644 index 0000000..57493c9 --- /dev/null +++ b/src/strext.h @@ -0,0 +1,32 @@ +#ifndef H__STRCMB__ +#define H__STRCMB__ + +#include + +#define STR_LEN(x) (sizeof(x)/sizeof(x[0])-1) + +struct str_builder_t { + char *str; + size_t alloc; + size_t len; + size_t fails; +}; +typedef struct str_builder_t str_builder; + +size_t strlen_norm(const char *str, size_t str_len); +int strmemcpy(const char *str, size_t str_len, char **new_str, size_t *new_len); + +size_t strapp(char **out, const char *a, size_t a_len); +size_t strcmb(char **out, const char *a, size_t a_len, const char *b, size_t b_len); + +str_builder *strbld_create(void); +void strbld_destroy(str_builder **sb); +void strbld_finalize(str_builder **sb, char **str, size_t *len); +int strbld_finalize_or_destroy(str_builder **sb, char **str, size_t *len); + +int strbld_ensure_len(str_builder *sb, size_t len, int absolute); + +int strbld_str(str_builder *sb, const char *str, size_t len); +int strbld_char(str_builder *sb, const char c); + +#endif // H__STRCMB__