├── autogen.sh ├── Makefile.am ├── src ├── include │ ├── project.h │ ├── fi_btree.h │ ├── fi.h │ └── fi_map.h ├── Makefile.am ├── btree_test.c ├── simple_btree_test.c ├── simple_string_btree.c ├── demo_map_test.c └── main.c ├── tests ├── Makefile.am └── test_fi_btree.c ├── .gitignore ├── configure.ac ├── examples ├── Makefile ├── README.md ├── rdb │ ├── SUMMARY.md │ ├── TRANSACTION_README.md │ ├── simple_test.c │ ├── thread_safety_test.c │ ├── test_persistence.c │ ├── sql_parser.h │ ├── Makefile │ ├── thread_safe_demo.c │ ├── cached_rdb.h │ ├── cache_system.h │ ├── rdb_demo.c │ ├── transaction_demo.c │ ├── persistence.h │ ├── cached_rdb_demo.c │ └── multi_table_functions.c ├── map_simple.c ├── array_basic.c ├── btree_basic.c ├── map_basic.c └── student_management.c ├── .ycm_extra_conf.py └── README.md /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | autoreconf --install 3 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = src tests 2 | 3 | ACLOCAL_AMFLAGS = -I m4 4 | 5 | EXTRA_DIST = README.md 6 | -------------------------------------------------------------------------------- /src/include/project.h: -------------------------------------------------------------------------------- 1 | #ifndef PROJECT_H 2 | #define PROJECT_H 3 | 4 | #include 5 | 6 | /* Project version */ 7 | #define PROJECT_VERSION "1.0.0" 8 | 9 | /* Function declarations */ 10 | void print_version(void); 11 | 12 | #endif /* PROJECT_H */ 13 | -------------------------------------------------------------------------------- /tests/Makefile.am: -------------------------------------------------------------------------------- 1 | if ENABLE_TESTS 2 | 3 | # Check framework based tests 4 | check_PROGRAMS = test_fi_array test_fi_btree test_fi_map 5 | 6 | test_fi_map_SOURCES = test_fi_map.c 7 | test_fi_map_CFLAGS = -I$(top_srcdir)/src $(CHECK_CFLAGS) -Wall -Wextra -g 8 | test_fi_map_LDADD = $(top_builddir)/src/libfi.la $(CHECK_LIBS) 9 | # Test for fi_array 10 | test_fi_array_SOURCES = test_fi_array.c 11 | test_fi_array_CFLAGS = -I$(top_srcdir)/src $(CHECK_CFLAGS) -Wall -Wextra -g 12 | test_fi_array_LDADD = $(top_builddir)/src/libfi.la $(CHECK_LIBS) 13 | 14 | # Test for fi_btree 15 | test_fi_btree_SOURCES = test_fi_btree.c 16 | test_fi_btree_CFLAGS = -I$(top_srcdir)/src $(CHECK_CFLAGS) -Wall -Wextra -g 17 | test_fi_btree_LDADD = $(top_builddir)/src/libfi.la $(CHECK_LIBS) 18 | 19 | TESTS = test_fi_array test_fi_btree test_fi_map 20 | 21 | endif 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Automake/Autoconf 2 | Makefile 3 | Makefile.in 4 | aclocal.m4 5 | autom4te.cache/ 6 | compile 7 | config.h 8 | config.h.in 9 | config.h.in~ 10 | config.log 11 | config.status 12 | config.sub 13 | config.guess 14 | configure 15 | configure~ 16 | depcomp 17 | install-sh 18 | missing 19 | stamp-h1 20 | test-driver 21 | .deps/ 22 | .dirstamp 23 | libtool 24 | ltmain.sh 25 | m4/libtool.m4 26 | m4/ltoptions.m4 27 | m4/ltsugar.m4 28 | m4/ltversion.m4 29 | m4/lt~obsolete.m4 30 | ar-lib 31 | 32 | # Build artifacts 33 | build/ 34 | *.o 35 | *.a 36 | *.so 37 | *.dylib 38 | *.la 39 | *.lo 40 | .libs/ 41 | src/fi 42 | 43 | # Test files and executables 44 | tests/test_fi_map 45 | tests/test_fi_array 46 | tests/test_fi_btree 47 | tests/*.o 48 | tests/*.log 49 | tests/*.trs 50 | tests/*.test 51 | tests/*.exe 52 | *_test 53 | # Debug 54 | *.dSYM/ 55 | core 56 | vgcore.* 57 | debug_* 58 | 59 | # Examples 60 | array_basic 61 | btree_basic 62 | map_basic 63 | map_simple 64 | student_management 65 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ([2.69]) 2 | AC_INIT([fi], [1.0.0], [support@example.com]) 3 | AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects]) 4 | AC_CONFIG_SRCDIR([src/main.c]) 5 | AC_CONFIG_HEADERS([config.h]) 6 | 7 | # Checks for programs 8 | AC_PROG_CC 9 | AM_PROG_AR 10 | LT_INIT 11 | 12 | # Checks for header files 13 | AC_CHECK_HEADERS([stdlib.h string.h]) 14 | 15 | # Checks for typedefs, structures, and compiler characteristics 16 | AC_TYPE_SIZE_T 17 | 18 | # Checks for library functions 19 | AC_FUNC_MALLOC 20 | 21 | # Check for libcheck 22 | PKG_CHECK_MODULES([CHECK], [check >= 0.9.4], [ 23 | AC_DEFINE([HAVE_CHECK], [1], [Define to 1 if you have libcheck]) 24 | enable_tests=yes 25 | ], [ 26 | enable_tests=no 27 | AC_MSG_WARN([libcheck not found, tests will be disabled]) 28 | ]) 29 | 30 | AM_CONDITIONAL([ENABLE_TESTS], [test "x$enable_tests" = "xyes"]) 31 | 32 | AC_CONFIG_FILES([ 33 | Makefile 34 | src/Makefile 35 | tests/Makefile 36 | ]) 37 | 38 | AC_OUTPUT 39 | -------------------------------------------------------------------------------- /tests/test_fi_btree.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Test: Example test case for fi_btree 6 | START_TEST(test_example) { 7 | // TODO: Add your test implementation 8 | ck_assert_int_eq(1, 1); 9 | } 10 | END_TEST 11 | 12 | // Create test suite 13 | Suite *fi_btree_suite(void) { 14 | Suite *s; 15 | TCase *tc_core; 16 | 17 | s = suite_create("fi_btree"); 18 | 19 | // Core test case 20 | tc_core = tcase_create("Core"); 21 | tcase_add_test(tc_core, test_example); 22 | suite_add_tcase(s, tc_core); 23 | 24 | return s; 25 | } 26 | 27 | // Main function 28 | int main(void) { 29 | int number_failed; 30 | Suite *s; 31 | SRunner *sr; 32 | 33 | s = fi_btree_suite(); 34 | sr = srunner_create(s); 35 | 36 | // Run tests 37 | srunner_run_all(sr, CK_NORMAL); 38 | number_failed = srunner_ntests_failed(sr); 39 | srunner_free(sr); 40 | 41 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 42 | } 43 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | # Library to build 2 | lib_LTLIBRARIES = libfi.la 3 | libfi_la_SOURCES = fi_array.c fi_btree.c fi_map.c 4 | libfi_la_CFLAGS = -Wall -Wextra -std=c11 -g -I$(srcdir)/include 5 | libfi_la_LDFLAGS = -version-info 1:0:0 6 | 7 | # Programs to build 8 | bin_PROGRAMS = fi btree_test string_btree_demo demo_map_test 9 | 10 | # Source files 11 | fi_SOURCES = main.c 12 | fi_LDADD = libfi.la 13 | btree_test_SOURCES = btree_test.c 14 | btree_test_LDADD = libfi.la 15 | string_btree_demo_SOURCES = simple_string_btree.c 16 | string_btree_demo_LDADD = libfi.la 17 | demo_map_test_SOURCES = demo_map_test.c 18 | demo_map_test_LDADD = libfi.la 19 | 20 | # Compiler flags 21 | fi_CFLAGS = -Wall -Wextra -std=c11 -g -I$(srcdir)/include 22 | btree_test_CFLAGS = -Wall -Wextra -std=c11 -g -I$(srcdir)/include 23 | string_btree_demo_CFLAGS = -Wall -Wextra -std=c11 -g -I$(srcdir)/include 24 | demo_map_test_CFLAGS = -Wall -Wextra -std=c11 -g -I$(srcdir)/include 25 | 26 | # Custom build rule to put executable in build directory 27 | all-local: 28 | @mkdir -p build 29 | @if [ -f fi$(EXEEXT) ]; then mv fi$(EXEEXT) build/; fi 30 | @if [ -f btree_test$(EXEEXT) ]; then mv btree_test$(EXEEXT) build/; fi 31 | @if [ -f string_btree_demo$(EXEEXT) ]; then mv string_btree_demo$(EXEEXT) build/; fi 32 | @if [ -f demo_map_test$(EXEEXT) ]; then mv demo_map_test$(EXEEXT) build/; fi 33 | 34 | clean-local: 35 | @rm -rf build 36 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for FI library examples 2 | # 编译所有示例程序 3 | 4 | # 编译器设置 5 | CC = gcc 6 | CFLAGS = -Wall -Wextra -std=c99 -g -O2 7 | INCLUDES = -I../src/include 8 | LIBS = -L../src/.libs -lfi 9 | 10 | # 示例程序列表 11 | EXAMPLES = array_basic map_simple btree_basic student_management 12 | 13 | # 默认目标:编译所有示例 14 | all: $(EXAMPLES) 15 | 16 | # 数组基本操作示例 17 | array_basic: array_basic.c 18 | $(CC) $(CFLAGS) $(INCLUDES) -o $@ $< $(LIBS) 19 | 20 | # 映射基本操作示例 21 | map_simple: map_simple.c 22 | $(CC) $(CFLAGS) $(INCLUDES) -o $@ $< $(LIBS) 23 | 24 | # 二叉搜索树基本操作示例 25 | btree_basic: btree_basic.c 26 | $(CC) $(CFLAGS) $(INCLUDES) -o $@ $< $(LIBS) 27 | 28 | # 学生管理系统示例 29 | student_management: student_management.c 30 | $(CC) $(CFLAGS) $(INCLUDES) -o $@ $< $(LIBS) 31 | 32 | # 运行所有示例 33 | run-all: $(EXAMPLES) 34 | @echo "=== 运行数组基本操作示例 ===" 35 | LD_LIBRARY_PATH=../src/.libs ./array_basic 36 | @echo "" 37 | @echo "=== 运行映射基本操作示例 ===" 38 | LD_LIBRARY_PATH=../src/.libs ./map_simple 39 | @echo "" 40 | @echo "=== 运行二叉搜索树基本操作示例 ===" 41 | LD_LIBRARY_PATH=../src/.libs ./btree_basic 42 | @echo "" 43 | @echo "=== 运行学生管理系统示例 ===" 44 | LD_LIBRARY_PATH=../src/.libs ./student_management 45 | 46 | # 清理编译生成的文件 47 | clean: 48 | rm -f $(EXAMPLES) *.o 49 | 50 | # 安装示例(可选) 51 | install: all 52 | @echo "示例程序已编译完成,可以运行 ./run-all 来查看所有示例" 53 | 54 | # 帮助信息 55 | help: 56 | @echo "可用的目标:" 57 | @echo " all - 编译所有示例程序" 58 | @echo " array_basic - 编译数组基本操作示例" 59 | @echo " map_simple - 编译映射基本操作示例" 60 | @echo " btree_basic - 编译二叉搜索树基本操作示例" 61 | @echo " student_management - 编译学生管理系统示例" 62 | @echo " run-all - 编译并运行所有示例" 63 | @echo " clean - 清理编译生成的文件" 64 | @echo " help - 显示此帮助信息" 65 | 66 | .PHONY: all run-all clean install help 67 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # FI 数据结构库使用示例 2 | 3 | 这个目录包含了 FI 数据结构库的各种使用示例,展示了如何使用数组(fi_array)、哈希映射(fi_map)和二叉搜索树(fi_btree)等数据结构。 4 | 5 | ## 示例程序 6 | 7 | ### 1. array_basic.c - 数组基本操作示例 8 | 展示了 `fi_array` 的基本使用方法: 9 | - 创建和销毁数组 10 | - 添加、删除、修改元素 11 | - 搜索和过滤操作 12 | - 排序和反转 13 | - 数学运算(求和、求积) 14 | - 数组切片和合并 15 | 16 | ### 2. map_simple.c - 哈希映射基本操作示例 17 | 展示了 `fi_map` 的基本使用方法: 18 | - 创建整数到整数的映射 19 | - 插入、查找、删除键值对 20 | - 遍历映射 21 | - 负载因子和统计信息 22 | - 获取键和值的集合 23 | 24 | ### 3. btree_basic.c - 二叉搜索树基本操作示例 25 | 展示了 `fi_btree` 的基本使用方法: 26 | - 创建二叉搜索树 27 | - 插入和删除节点 28 | - 各种遍历方式(中序、前序、后序、层序) 29 | - 查找最小值和最大值 30 | - 查找后继和前驱节点 31 | - 树的性质检查 32 | 33 | ### 4. student_management.c - 学生管理系统示例 34 | 一个完整的实际应用示例,展示了如何在实际项目中使用 FI 库: 35 | - 使用 `fi_map` 存储学生信息(ID -> Student) 36 | - 使用 `fi_array` 处理学生列表 37 | - 实现学生信息的增删改查 38 | - 按条件筛选学生(按GPA、专业等) 39 | - 统计和分析功能 40 | 41 | ## 编译和运行 42 | 43 | ### 编译所有示例 44 | ```bash 45 | make all 46 | ``` 47 | 48 | ### 运行所有示例 49 | ```bash 50 | make run-all 51 | ``` 52 | 53 | ### 编译单个示例 54 | ```bash 55 | make array_basic 56 | make map_simple 57 | make btree_basic 58 | make student_management 59 | ``` 60 | 61 | ### 清理编译文件 62 | ```bash 63 | make clean 64 | ``` 65 | 66 | ### 查看帮助 67 | ```bash 68 | make help 69 | ``` 70 | 71 | ## 运行单个示例 72 | 73 | 编译完成后,可以直接运行各个示例程序: 74 | 75 | ```bash 76 | ./array_basic 77 | ./map_simple 78 | ./btree_basic 79 | ./student_management 80 | ``` 81 | 82 | ## 前提条件 83 | 84 | 在编译示例之前,请确保: 85 | 86 | 1. FI 库已经编译完成(在 `../src` 目录下) 87 | 2. 有 `libfi.a` 或 `libfi.so` 文件 88 | 3. 安装了 GCC 编译器 89 | 90 | 如果库还没有编译,请先回到项目根目录运行: 91 | 92 | ```bash 93 | ./configure 94 | make 95 | ``` 96 | 97 | ## 学习建议 98 | 99 | 1. **从基础开始**:建议先运行 `array_basic` 和 `map_simple` 示例,了解基本的数据结构操作 100 | 2. **理解树结构**:运行 `btree_basic` 示例,理解二叉搜索树的各种操作和遍历方式 101 | 3. **实际应用**:运行 `student_management` 示例,看看如何在实际项目中使用这些数据结构 102 | 103 | ## 扩展示例 104 | 105 | 你可以基于这些示例创建自己的程序: 106 | 107 | - 修改 `student_management.c` 添加更多功能 108 | - 创建新的示例展示其他 API 的使用 109 | - 组合使用多种数据结构解决复杂问题 110 | 111 | ## 注意事项 112 | 113 | - 所有示例都包含了完整的错误处理 114 | - 记得在使用完数据结构后调用相应的销毁函数 115 | - 注意内存管理,避免内存泄漏 116 | - 使用回调函数时要注意参数类型和返回值 117 | 118 | ## 问题反馈 119 | 120 | 如果在使用示例时遇到问题,请检查: 121 | 122 | 1. 编译环境是否正确 123 | 2. FI 库是否正确编译 124 | 3. 头文件路径是否正确 125 | 4. 链接库是否可用 126 | -------------------------------------------------------------------------------- /src/btree_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "fi.h" 5 | #include "fi_btree.h" 6 | 7 | int compare_ints(const void *a, const void *b) { 8 | const int *ia = (const int*)a; 9 | const int *ib = (const int*)b; 10 | return (*ia > *ib) - (*ia < *ib); 11 | } 12 | 13 | void print_int_visit(void *data, size_t depth, void *user_data) { 14 | (void)depth; 15 | (void)user_data; 16 | const int *value = (const int*)data; 17 | printf("%d ", *value); 18 | } 19 | 20 | int main() { 21 | printf("=== Simple BTree Test ===\n"); 22 | 23 | fi_btree *tree = fi_btree_create(sizeof(int), compare_ints); 24 | if (!tree) { 25 | printf("Failed to create BTree\n"); 26 | return 1; 27 | } 28 | 29 | printf("BTree created successfully\n"); 30 | 31 | // Insert a few integers 32 | int values[] = {5, 3, 7, 1, 4, 6, 8}; 33 | int count = sizeof(values) / sizeof(values[0]); 34 | 35 | printf("Inserting values: "); 36 | for (int i = 0; i < count; i++) { 37 | printf("%d ", values[i]); 38 | if (fi_btree_insert(tree, &values[i]) != 0) { 39 | printf("Failed to insert %d\n", values[i]); 40 | return 1; 41 | } 42 | } 43 | printf("\n"); 44 | 45 | printf("Tree size: %zu, height: %zu\n", fi_btree_size(tree), fi_btree_height(tree)); 46 | 47 | // Test search 48 | int search_val = 5; 49 | fi_btree_node *found = fi_btree_search(tree, &search_val); 50 | printf("Search for %d: %s\n", search_val, found ? "Found" : "Not found"); 51 | 52 | // Test inorder traversal 53 | printf("Inorder traversal: "); 54 | fi_btree_inorder(tree, print_int_visit, NULL); 55 | printf("\n"); 56 | 57 | // Test min/max 58 | fi_btree_node *min_node = fi_btree_find_min(tree->root); 59 | fi_btree_node *max_node = fi_btree_find_max(tree->root); 60 | 61 | if (min_node) printf("Min: %d\n", *(int*)min_node->data); 62 | if (max_node) printf("Max: %d\n", *(int*)max_node->data); 63 | 64 | // Test deletion 65 | printf("Deleting 5...\n"); 66 | fi_btree_delete(tree, &search_val); 67 | printf("After deletion - size: %zu\n", fi_btree_size(tree)); 68 | 69 | printf("Inorder after deletion: "); 70 | fi_btree_inorder(tree, print_int_visit, NULL); 71 | printf("\n"); 72 | 73 | fi_btree_destroy(tree); 74 | printf("BTree destroyed successfully\n"); 75 | 76 | printf("=== Simple BTree Test Complete ===\n"); 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /src/simple_btree_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "fi.h" 5 | #include "fi_btree.h" 6 | 7 | int compare_ints(const void *a, const void *b) { 8 | const int *ia = (const int*)a; 9 | const int *ib = (const int*)b; 10 | return (*ia > *ib) - (*ia < *ib); 11 | } 12 | 13 | void print_int_visit(void *data, size_t depth, void *user_data) { 14 | (void)depth; 15 | (void)user_data; 16 | const int *value = (const int*)data; 17 | printf("%d ", *value); 18 | } 19 | 20 | int main() { 21 | printf("=== Simple BTree Test ===\n"); 22 | 23 | fi_btree *tree = fi_btree_create(sizeof(int), compare_ints); 24 | if (!tree) { 25 | printf("Failed to create BTree\n"); 26 | return 1; 27 | } 28 | 29 | printf("BTree created successfully\n"); 30 | 31 | // Insert a few integers 32 | int values[] = {5, 3, 7, 1, 4, 6, 8}; 33 | int count = sizeof(values) / sizeof(values[0]); 34 | 35 | printf("Inserting values: "); 36 | for (int i = 0; i < count; i++) { 37 | printf("%d ", values[i]); 38 | if (fi_btree_insert(tree, &values[i]) != 0) { 39 | printf("Failed to insert %d\n", values[i]); 40 | return 1; 41 | } 42 | } 43 | printf("\n"); 44 | 45 | printf("Tree size: %zu, height: %zu\n", fi_btree_size(tree), fi_btree_height(tree)); 46 | 47 | // Test search 48 | int search_val = 5; 49 | fi_btree_node *found = fi_btree_search(tree, &search_val); 50 | printf("Search for %d: %s\n", search_val, found ? "Found" : "Not found"); 51 | 52 | // Test inorder traversal 53 | printf("Inorder traversal: "); 54 | fi_btree_inorder(tree, print_int_visit, NULL); 55 | printf("\n"); 56 | 57 | // Test min/max 58 | fi_btree_node *min_node = fi_btree_find_min(tree->root); 59 | fi_btree_node *max_node = fi_btree_find_max(tree->root); 60 | 61 | if (min_node) printf("Min: %d\n", *(int*)min_node->data); 62 | if (max_node) printf("Max: %d\n", *(int*)max_node->data); 63 | 64 | // Test deletion 65 | printf("Deleting 5...\n"); 66 | fi_btree_delete(tree, &search_val); 67 | printf("After deletion - size: %zu\n", fi_btree_size(tree)); 68 | 69 | printf("Inorder after deletion: "); 70 | fi_btree_inorder(tree, print_int_visit, NULL); 71 | printf("\n"); 72 | 73 | fi_btree_destroy(tree); 74 | printf("BTree destroyed successfully\n"); 75 | 76 | printf("=== Simple BTree Test Complete ===\n"); 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /src/include/fi_btree.h: -------------------------------------------------------------------------------- 1 | #ifndef __FI_BTREE_H__ 2 | #define __FI_BTREE_H__ 3 | 4 | #include "fi.h" 5 | 6 | /* BTree node structure */ 7 | typedef struct fi_btree_node { 8 | void *data; /* Pointer to the data */ 9 | struct fi_btree_node *left; /* Left child */ 10 | struct fi_btree_node *right; /* Right child */ 11 | struct fi_btree_node *parent; /* Parent node */ 12 | } fi_btree_node; 13 | 14 | /* BTree structure */ 15 | typedef struct fi_btree { 16 | fi_btree_node *root; /* Root node */ 17 | size_t element_size; /* Size of each element in bytes */ 18 | size_t count; /* Number of nodes */ 19 | int (*compare_func)(const void *a, const void *b); /* Comparison function */ 20 | } fi_btree; 21 | 22 | /* BTree operations */ 23 | fi_btree* fi_btree_create(size_t element_size, int (*compare_func)(const void *a, const void *b)); 24 | void fi_btree_destroy(fi_btree *tree); 25 | void fi_btree_clear(fi_btree *tree); 26 | 27 | /* Node operations */ 28 | fi_btree_node* fi_btree_create_node(const void *data, size_t element_size); 29 | void fi_btree_destroy_node(fi_btree_node *node); 30 | 31 | /* Insertion and deletion */ 32 | int fi_btree_insert(fi_btree *tree, const void *data); 33 | int fi_btree_delete(fi_btree *tree, const void *data); 34 | fi_btree_node* fi_btree_delete_node(fi_btree *tree, fi_btree_node *node); 35 | 36 | /* Search operations */ 37 | fi_btree_node* fi_btree_search(fi_btree *tree, const void *data); 38 | fi_btree_node* fi_btree_find_min(fi_btree_node *node); 39 | fi_btree_node* fi_btree_find_max(fi_btree_node *node); 40 | fi_btree_node* fi_btree_successor(fi_btree_node *node); 41 | fi_btree_node* fi_btree_predecessor(fi_btree_node *node); 42 | 43 | /* Tree properties */ 44 | size_t fi_btree_size(fi_btree *tree); 45 | size_t fi_btree_height(fi_btree *tree); 46 | size_t fi_btree_node_height(fi_btree_node *node); 47 | bool fi_btree_empty(fi_btree *tree); 48 | bool fi_btree_contains(fi_btree *tree, const void *data); 49 | 50 | /* Traversal operations */ 51 | typedef void (*fi_btree_visit_func)(void *data, size_t depth, void *user_data); 52 | 53 | void fi_btree_inorder(fi_btree *tree, fi_btree_visit_func visit, void *user_data); 54 | void fi_btree_preorder(fi_btree *tree, fi_btree_visit_func visit, void *user_data); 55 | void fi_btree_postorder(fi_btree *tree, fi_btree_visit_func visit, void *user_data); 56 | void fi_btree_level_order(fi_btree *tree, fi_btree_visit_func visit, void *user_data); 57 | 58 | /* Array conversion */ 59 | fi_array* fi_btree_to_array(fi_btree *tree); 60 | fi_array* fi_btree_to_array_inorder(fi_btree *tree); 61 | fi_array* fi_btree_to_array_preorder(fi_btree *tree); 62 | fi_array* fi_btree_to_array_postorder(fi_btree *tree); 63 | 64 | /* Tree construction from array */ 65 | fi_btree* fi_btree_from_array(fi_array *arr, int (*compare_func)(const void *a, const void *b)); 66 | fi_btree* fi_btree_from_sorted_array(fi_array *arr, int (*compare_func)(const void *a, const void *b)); 67 | 68 | /* Utility functions */ 69 | bool fi_btree_is_bst(fi_btree *tree); 70 | bool fi_btree_is_balanced(fi_btree *tree); 71 | fi_btree_node* fi_btree_lca(fi_btree *tree, const void *data1, const void *data2); 72 | fi_array* fi_btree_path_to_root(fi_btree *tree, const void *data); 73 | 74 | /* Tree manipulation */ 75 | void fi_btree_rotate_left(fi_btree *tree, fi_btree_node *node); 76 | void fi_btree_rotate_right(fi_btree *tree, fi_btree_node *node); 77 | fi_btree* fi_btree_mirror(fi_btree *tree); 78 | 79 | /* Visualization */ 80 | void fi_btree_print(fi_btree *tree, void (*print_func)(const void *data)); 81 | void fi_btree_print_tree(fi_btree *tree, void (*print_func)(const void *data)); 82 | 83 | #endif //__FI_BTREE_H__ 84 | -------------------------------------------------------------------------------- /examples/rdb/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # FI 关系型数据库项目总结 2 | 3 | ## 项目概述 4 | 5 | 本项目成功实现了一个基于 FI 数据结构库的简易关系型数据库系统,展示了如何使用 `fi_array`、`fi_map` 和 `fi_btree` 来构建数据库引擎。 6 | 7 | ## 完成的功能 8 | 9 | ### ✅ 已完成的核心功能 10 | 11 | 1. **数据库管理** 12 | - 创建、打开、关闭数据库 13 | - 数据库信息显示 14 | 15 | 2. **表管理** 16 | - 创建表结构 17 | - 定义列属性(类型、约束等) 18 | - 表信息显示 19 | 20 | 3. **数据类型支持** 21 | - INT(整数) 22 | - FLOAT(浮点数) 23 | - VARCHAR(可变长度字符串) 24 | - TEXT(长文本) 25 | - BOOLEAN(布尔值) 26 | 27 | 4. **数据操作** 28 | - 插入行数据 29 | - 数据验证和类型检查 30 | 31 | 5. **索引支持**(完整版本) 32 | - 基于 BTree 的索引实现 33 | - 主键和唯一索引 34 | 35 | 6. **SQL 解析器**(完整版本) 36 | - CREATE TABLE 语句解析 37 | - 基本 SQL 语法支持 38 | 39 | ## 技术实现 40 | 41 | ### 数据结构使用 42 | - **fi_array**: 用于存储表列表、行数据、列定义 43 | - **fi_map**: 用于存储表元数据(使用整数键避免字符串处理问题) 44 | - **fi_btree**: 用于实现索引结构 45 | 46 | ### 架构设计 47 | ``` 48 | Database 49 | ├── tables (fi_array: rdb_table_t*) 50 | │ └── Table 51 | │ ├── columns (fi_array: rdb_column_t*) 52 | │ ├── rows (fi_array: rdb_row_t*) 53 | │ └── indexes (fi_map: name -> fi_btree) 54 | ``` 55 | 56 | ## 遇到的问题和解决方案 57 | 58 | ### 问题1: FI 库字符串处理问题 59 | **问题**: FI 库的 `fi_map_hash_string` 函数在处理字符串键时出现段错误。 60 | 61 | **解决方案**: 62 | 1. 使用整数键替代字符串键 63 | 2. 通过线性搜索实现表名查找 64 | 3. 提供多个版本的实现以适应不同需求 65 | 66 | ### 问题2: 内存管理复杂性 67 | **问题**: 复杂的数据结构导致内存管理困难。 68 | 69 | **解决方案**: 70 | 1. 提供简化版本和最小版本 71 | 2. 使用静态链接避免动态库问题 72 | 3. 逐步构建功能,确保每个版本都稳定 73 | 74 | ## 项目文件结构 75 | 76 | ``` 77 | examples/rdb/ 78 | ├── README.md # 项目文档 79 | ├── SUMMARY.md # 项目总结 80 | ├── rdb.h # 完整版本头文件 81 | ├── rdb.c # 完整版本实现 82 | ├── rdb_simple.h # 简化版本头文件 83 | ├── rdb_simple.c # 简化版本实现 84 | ├── rdb_minimal.h # 最小版本头文件 85 | ├── rdb_minimal.c # 最小版本实现 86 | ├── sql_parser.h # SQL 解析器头文件 87 | ├── sql_parser.c # SQL 解析器实现 88 | ├── rdb_demo.c # 完整版本演示 89 | ├── simple_demo.c # 简化版本演示 90 | ├── minimal_demo.c # 最小版本演示 91 | ├── minimal_test.c # 基础测试程序 92 | ├── Makefile # 完整版本构建文件 93 | ├── Makefile.simple # 简化版本构建文件 94 | └── build/ # 编译输出目录 95 | ``` 96 | 97 | ## 演示结果 98 | 99 | ### 最小版本演示 100 | ``` 101 | === Minimal RDB Demo === 102 | Database created successfully 103 | Table 'users' created successfully 104 | Users table created 105 | Table 'products' created successfully 106 | Products table created 107 | 108 | === Database: test_db === 109 | Status: OPEN 110 | Tables: 2 111 | 112 | Table List: 113 | - users (0 rows) 114 | - products (0 rows) 115 | Database destroyed successfully 116 | === Demo completed successfully! === 117 | ``` 118 | 119 | ### 基础 FI 库测试 120 | ``` 121 | === Minimal Test === 122 | Testing FI array... 123 | Array created successfully 124 | Value pushed successfully 125 | Retrieved value: 42 126 | Array destroyed successfully 127 | Testing FI map with integer keys... 128 | Map created successfully 129 | Key-value pair inserted successfully 130 | Retrieved value: 100 131 | Map destroyed successfully 132 | === Test completed successfully! === 133 | ``` 134 | 135 | ## 学习价值 136 | 137 | 这个项目展示了: 138 | 139 | 1. **数据结构库的实际应用**: 如何使用现有的数据结构库构建更复杂的系统 140 | 2. **数据库引擎设计**: 关系型数据库的基本架构和组件 141 | 3. **内存管理**: 复杂数据结构的内存分配和释放 142 | 4. **问题解决**: 遇到技术问题时的调试和解决方案 143 | 5. **代码组织**: 模块化设计和版本管理 144 | 145 | ## 未来扩展方向 146 | 147 | 1. **持久化存储**: 添加数据文件存储功能 148 | 2. **查询优化**: 实现更智能的查询计划 149 | 3. **事务支持**: 添加 ACID 事务功能 150 | 4. **并发控制**: 支持多线程访问 151 | 5. **SQL 功能扩展**: 支持更多 SQL 语句类型 152 | 153 | ## 结论 154 | 155 | 本项目成功实现了关系型数据库的核心功能,展示了 FI 数据结构库的强大能力。虽然遇到了一些技术挑战,但通过提供多个版本的实现,确保了项目的可用性和教育价值。这个项目为学习和理解数据库系统提供了很好的实践机会。 156 | -------------------------------------------------------------------------------- /examples/rdb/TRANSACTION_README.md: -------------------------------------------------------------------------------- 1 | # FI 关系型数据库事务功能 2 | 3 | 本文档描述了FI关系型数据库系统中实现的事务功能。 4 | 5 | ## 概述 6 | 7 | 事务功能为FI关系型数据库提供了ACID属性的支持,包括: 8 | - **原子性 (Atomicity)**: 事务中的所有操作要么全部成功,要么全部回滚 9 | - **一致性 (Consistency)**: 事务执行前后数据库保持一致状态 10 | - **隔离性 (Isolation)**: 支持多种隔离级别 11 | - **持久性 (Durability)**: 提交的事务永久保存 12 | 13 | ## 主要功能 14 | 15 | ### 1. 事务管理 16 | - **BEGIN TRANSACTION**: 开始新事务 17 | - **COMMIT**: 提交事务 18 | - **ROLLBACK**: 回滚事务 19 | - **AUTOCOMMIT**: 自动提交模式控制 20 | 21 | ### 2. 隔离级别 22 | 支持四种标准隔离级别: 23 | - `READ_UNCOMMITTED`: 读未提交 24 | - `READ_COMMITTED`: 读已提交(默认) 25 | - `REPEATABLE_READ`: 可重复读 26 | - `SERIALIZABLE`: 串行化 27 | 28 | ### 3. 事务日志 29 | - 自动记录所有数据库操作 30 | - 支持事务回滚时的操作撤销 31 | - 维护事务历史记录 32 | 33 | ## 使用方法 34 | 35 | ### C API 使用 36 | 37 | ```c 38 | #include "rdb.h" 39 | 40 | // 创建数据库 41 | rdb_database_t *db = rdb_create_database("my_db"); 42 | rdb_open_database(db); 43 | 44 | // 开始事务 45 | rdb_begin_transaction(db, RDB_ISOLATION_READ_COMMITTED); 46 | 47 | // 执行数据库操作 48 | rdb_insert_row(db, "users", values); 49 | rdb_update_rows(db, "users", set_columns, set_values, where_conditions); 50 | 51 | // 提交或回滚事务 52 | rdb_commit_transaction(db); // 提交 53 | // 或 54 | rdb_rollback_transaction(db); // 回滚 55 | 56 | // 检查事务状态 57 | if (rdb_is_in_transaction(db)) { 58 | printf("当前在事务中\n"); 59 | } 60 | 61 | // 设置自动提交 62 | rdb_set_autocommit(db, false); // 禁用自动提交 63 | rdb_set_autocommit(db, true); // 启用自动提交 64 | 65 | // 设置默认隔离级别 66 | rdb_set_isolation_level(db, RDB_ISOLATION_SERIALIZABLE); 67 | ``` 68 | 69 | ### SQL 命令使用 70 | 71 | ```sql 72 | -- 开始事务 73 | BEGIN TRANSACTION; 74 | 75 | -- 执行SQL操作 76 | INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com'); 77 | UPDATE users SET email = 'newemail@example.com' WHERE name = 'Bob'; 78 | 79 | -- 提交事务 80 | COMMIT; 81 | 82 | -- 或者回滚事务 83 | ROLLBACK; 84 | ``` 85 | 86 | ## 演示程序 87 | 88 | 运行事务演示程序来查看功能示例: 89 | 90 | ```bash 91 | cd /home/xsd/Work/c-family/fi/examples/rdb 92 | make transaction_demo 93 | LD_LIBRARY_PATH=../../src/.libs ./transaction_demo 94 | ``` 95 | 96 | 演示程序包含以下功能测试: 97 | 1. **基本事务演示**: 展示BEGIN、COMMIT的基本使用 98 | 2. **事务回滚演示**: 展示ROLLBACK功能 99 | 3. **隔离级别演示**: 展示不同隔离级别的使用 100 | 4. **SQL事务命令演示**: 展示通过SQL命令使用事务 101 | 102 | ## 技术实现 103 | 104 | ### 核心组件 105 | 106 | 1. **事务管理器 (Transaction Manager)** 107 | - 管理当前活跃事务 108 | - 维护事务历史记录 109 | - 控制自动提交模式 110 | 111 | 2. **事务日志 (Transaction Log)** 112 | - 记录所有数据库操作 113 | - 存储操作前后的数据状态 114 | - 支持操作回滚 115 | 116 | 3. **SQL解析器扩展** 117 | - 支持BEGIN、COMMIT、ROLLBACK关键词 118 | - 解析事务相关SQL语句 119 | 120 | ### 数据结构 121 | 122 | ```c 123 | // 事务结构 124 | typedef struct { 125 | size_t transaction_id; // 事务ID 126 | rdb_transaction_state_t state; // 事务状态 127 | rdb_isolation_level_t isolation; // 隔离级别 128 | fi_array *log_entries; // 日志条目 129 | time_t start_time; // 开始时间 130 | time_t end_time; // 结束时间 131 | bool is_autocommit; // 是否自动提交 132 | } rdb_transaction_t; 133 | 134 | // 事务管理器 135 | struct rdb_transaction_manager { 136 | rdb_transaction_t *current_transaction; // 当前事务 137 | fi_array *transaction_history; // 事务历史 138 | size_t next_transaction_id; // 下一个事务ID 139 | rdb_isolation_level_t default_isolation; // 默认隔离级别 140 | bool autocommit_enabled; // 自动提交状态 141 | }; 142 | ``` 143 | 144 | ## 注意事项 145 | 146 | 1. **内存管理**: 事务日志会消耗额外内存,大型事务可能需要更多内存 147 | 2. **性能影响**: 事务功能会增加操作开销,特别是在回滚时 148 | 3. **并发限制**: 当前实现不支持并发事务,同一时间只能有一个活跃事务 149 | 4. **持久化**: 当前实现是内存数据库,重启后事务历史会丢失 150 | 151 | ## 扩展功能 152 | 153 | 未来可以考虑添加的功能: 154 | - 并发事务支持 155 | - 死锁检测和处理 156 | - 更复杂的隔离级别实现 157 | - 事务持久化到磁盘 158 | - 嵌套事务支持 159 | - 保存点 (Savepoint) 功能 160 | 161 | ## 编译说明 162 | 163 | 确保已构建FI库: 164 | ```bash 165 | cd /home/xsd/Work/c-family/fi 166 | make 167 | ``` 168 | 169 | 编译事务演示程序: 170 | ```bash 171 | cd examples/rdb 172 | gcc -o transaction_demo transaction_demo.c rdb.c sql_parser.c \ 173 | -I../../src/include -L../../src/.libs -lfi -lm 174 | ``` 175 | -------------------------------------------------------------------------------- /examples/map_simple.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file map_simple.c 3 | * @brief 简化的哈希映射操作示例 4 | * 5 | * 这个示例展示了如何使用fi_map进行基本的哈希映射操作, 6 | * 使用整数键来避免字符串处理的复杂性。 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "../src/include/fi_map.h" 13 | 14 | // 打印映射内容 15 | void print_map(fi_map *map, const char *title) { 16 | printf("%s:\n", title); 17 | 18 | fi_map_iterator iter = fi_map_iterator_create(map); 19 | while (fi_map_iterator_next(&iter)) { 20 | int *key = (int*)fi_map_iterator_key(&iter); 21 | int *value = (int*)fi_map_iterator_value(&iter); 22 | printf(" %d -> %d\n", *key, *value); 23 | } 24 | fi_map_iterator_destroy(&iter); 25 | printf("\n"); 26 | } 27 | 28 | // 回调函数:打印键值对 29 | void print_key_value(const void *key, const void *value, void *user_data) { 30 | (void)user_data; // 避免未使用参数警告 31 | int *key_int = (int*)key; 32 | int *value_int = (int*)value; 33 | printf(" %d: %d\n", *key_int, *value_int); 34 | } 35 | 36 | int main() { 37 | printf("=== FI Map 简化示例 ===\n\n"); 38 | 39 | // 1. 创建整数到整数的映射 40 | printf("1. 创建整数到整数的映射\n"); 41 | fi_map *score_map = fi_map_create(10, sizeof(int), sizeof(int), 42 | fi_map_hash_int32, fi_map_compare_int32); 43 | if (!score_map) { 44 | fprintf(stderr, "无法创建映射\n"); 45 | return 1; 46 | } 47 | 48 | // 2. 插入键值对 49 | printf("2. 插入键值对\n"); 50 | int keys[] = {1001, 1002, 1003, 1004, 1005}; 51 | int scores[] = {85, 92, 78, 96, 88}; 52 | 53 | for (int i = 0; i < 5; i++) { 54 | if (fi_map_put(score_map, &keys[i], &scores[i]) != 0) { 55 | fprintf(stderr, "插入失败: %d\n", keys[i]); 56 | } 57 | } 58 | print_map(score_map, "插入后"); 59 | 60 | // 3. 查找值 61 | printf("3. 查找值\n"); 62 | int search_key = 1002; 63 | int found_score; 64 | if (fi_map_get(score_map, &search_key, &found_score) == 0) { 65 | printf("找到键 %d 的值: %d\n", search_key, found_score); 66 | } else { 67 | printf("未找到键 %d\n", search_key); 68 | } 69 | 70 | // 4. 检查键是否存在 71 | printf("\n4. 检查键是否存在\n"); 72 | int check_key = 1003; 73 | if (fi_map_contains(score_map, &check_key)) { 74 | printf("映射包含键: %d\n", check_key); 75 | } else { 76 | printf("映射不包含键: %d\n", check_key); 77 | } 78 | 79 | // 5. 更新值 80 | printf("\n5. 更新值\n"); 81 | int new_score = 95; 82 | fi_map_put(score_map, &check_key, &new_score); 83 | printf("更新键 %d 的值后:\n", check_key); 84 | print_map(score_map, ""); 85 | 86 | // 6. 映射大小和负载因子 87 | printf("6. 映射统计信息\n"); 88 | printf("映射大小: %zu\n", fi_map_size(score_map)); 89 | printf("负载因子: %.2f\n", fi_map_load_factor(score_map)); 90 | 91 | // 7. 遍历映射 92 | printf("\n7. 遍历映射\n"); 93 | printf("使用 for_each 遍历:\n"); 94 | fi_map_for_each(score_map, print_key_value, NULL); 95 | 96 | // 8. 获取所有键和值 97 | printf("8. 获取所有键和值\n"); 98 | fi_array *map_keys = fi_map_keys(score_map); 99 | fi_array *map_values = fi_map_values(score_map); 100 | 101 | printf("所有键: "); 102 | for (size_t i = 0; i < fi_array_count(map_keys); i++) { 103 | int *key = (int*)fi_array_get(map_keys, i); 104 | printf("%d ", *key); 105 | } 106 | printf("\n"); 107 | 108 | printf("所有值: "); 109 | for (size_t i = 0; i < fi_array_count(map_values); i++) { 110 | int *value = (int*)fi_array_get(map_values, i); 111 | printf("%d ", *value); 112 | } 113 | printf("\n\n"); 114 | 115 | // 9. 删除键值对 116 | printf("9. 删除键值对\n"); 117 | int delete_key = 1004; 118 | if (fi_map_remove(score_map, &delete_key) == 0) { 119 | printf("成功删除键: %d\n", delete_key); 120 | } 121 | print_map(score_map, "删除后"); 122 | 123 | // 10. 清理资源 124 | printf("10. 清理资源\n"); 125 | fi_map_destroy(score_map); 126 | fi_array_destroy(map_keys); 127 | fi_array_destroy(map_values); 128 | 129 | printf("=== 示例完成 ===\n"); 130 | return 0; 131 | } 132 | -------------------------------------------------------------------------------- /.ycm_extra_conf.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2014 Google Inc. 2 | # 3 | # This file is part of ycmd. 4 | # 5 | # ycmd is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # ycmd is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with ycmd. If not, see . 17 | 18 | import os 19 | import ycm_core 20 | 21 | # These are the compilation flags that will be used in case there's no 22 | # compilation database set (by default, one is not set). 23 | # CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR. 24 | flags = [ 25 | '-Wall', 26 | '-Wextra', 27 | '-Werror', 28 | '-fexceptions', 29 | '-DNDEBUG', 30 | # THIS IS IMPORTANT! Without a "-std=" flag, clang won't know which 31 | # language to use when compiling headers. So it will guess. Badly. So C++ 32 | # headers will be compiled as C headers. You don't want that so ALWAYS specify 33 | # a "-std=". 34 | # For a C project, you would set this to something like 'c99' instead of 35 | # 'c++11'. 36 | '-std=c99', 37 | # ...and the same thing goes for the magic -x option which specifies the 38 | # language that the files to be compiled are written in. This is mostly 39 | # relevant for c++ headers. 40 | # For a C project, you would set this to 'c' instead of 'c++'. 41 | '-x', 42 | 'c', 43 | # Add the local src/include directory 44 | '-I', 45 | 'src/include', 46 | '-isystem', 47 | '/usr/include', 48 | '-isystem', 49 | '/usr/local/include', 50 | '-isystem', 51 | '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1', 52 | '-isystem', 53 | '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include', 54 | ] 55 | 56 | 57 | # Set this to the absolute path to the folder (NOT the file!) containing the 58 | # compile_commands.json file to use that instead of 'flags'. See here for 59 | # more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html 60 | # 61 | # Most projects will NOT need to set this to anything; you can just change the 62 | # 'flags' list of compilation flags. 63 | compilation_database_folder = '' 64 | 65 | if os.path.exists( compilation_database_folder ): 66 | database = ycm_core.CompilationDatabase( compilation_database_folder ) 67 | else: 68 | database = None 69 | 70 | SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ] 71 | 72 | def DirectoryOfThisScript(): 73 | return os.path.dirname( os.path.abspath( __file__ ) ) 74 | 75 | 76 | def IsHeaderFile( filename ): 77 | extension = os.path.splitext( filename )[ 1 ] 78 | return extension in [ '.h', '.hxx', '.hpp', '.hh' ] 79 | 80 | 81 | def GetCompilationInfoForFile( filename ): 82 | # The compilation_commands.json file generated by CMake does not have entries 83 | # for header files. So we do our best by asking the db for flags for a 84 | # corresponding source file, if any. If one exists, the flags for that file 85 | # should be good enough. 86 | if IsHeaderFile( filename ): 87 | basename = os.path.splitext( filename )[ 0 ] 88 | for extension in SOURCE_EXTENSIONS: 89 | replacement_file = basename + extension 90 | if os.path.exists( replacement_file ): 91 | compilation_info = database.GetCompilationInfoForFile( 92 | replacement_file ) 93 | if compilation_info.compiler_flags_: 94 | return compilation_info 95 | return None 96 | return database.GetCompilationInfoForFile( filename ) 97 | 98 | 99 | # This is the entry point; this function is called by ycmd to produce flags for 100 | # a file. 101 | def Settings( **kwargs ): 102 | if not database: 103 | return { 104 | 'flags': flags, 105 | 'include_paths_relative_to_dir': DirectoryOfThisScript() 106 | } 107 | filename = kwargs[ 'filename' ] 108 | compilation_info = GetCompilationInfoForFile( filename ) 109 | if not compilation_info: 110 | return None 111 | 112 | # Bear in mind that compilation_info.compiler_flags_ does NOT return a 113 | # python list, but a "list-like" StringVec object. 114 | return { 115 | 'flags': list( compilation_info.compiler_flags_ ), 116 | 'include_paths_relative_to_dir': compilation_info.compiler_working_dir_ 117 | } 118 | -------------------------------------------------------------------------------- /src/simple_string_btree.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "fi.h" 5 | #include "fi_btree.h" 6 | 7 | /* String comparison function */ 8 | int compare_strings(const void *a, const void *b) { 9 | const char *str_a = *(const char**)a; 10 | const char *str_b = *(const char**)b; 11 | return strcmp(str_a, str_b); 12 | } 13 | 14 | /* Print string visit function */ 15 | void print_string_visit(void *data, size_t depth, void *user_data) { 16 | (void)depth; /* Suppress unused parameter warning */ 17 | (void)user_data; /* Suppress unused parameter warning */ 18 | 19 | char **str_ptr = (char**)data; 20 | if (str_ptr && *str_ptr) { 21 | printf("\"%s\" ", *str_ptr); 22 | } 23 | } 24 | 25 | int main() { 26 | printf("=== String BTree Demo ===\n"); 27 | 28 | // Create BTree for strings (storing char* pointers) 29 | fi_btree *tree = fi_btree_create(sizeof(char*), compare_strings); 30 | if (!tree) { 31 | printf("Failed to create BTree\n"); 32 | return 1; 33 | } 34 | 35 | printf("BTree created successfully\n"); 36 | 37 | // Insert strings 38 | const char *words[] = {"apple", "banana", "cherry", "date", "elderberry"}; 39 | int count = sizeof(words) / sizeof(words[0]); 40 | 41 | printf("Inserting words: "); 42 | for (int i = 0; i < count; i++) { 43 | printf("%s ", words[i]); 44 | 45 | // Create a copy of the string 46 | char *word_copy = malloc(strlen(words[i]) + 1); 47 | if (!word_copy) { 48 | printf("Memory allocation failed for: %s\n", words[i]); 49 | continue; 50 | } 51 | strcpy(word_copy, words[i]); 52 | 53 | // Insert the pointer to the string 54 | if (fi_btree_insert(tree, &word_copy) != 0) { 55 | printf("Failed to insert: %s\n", words[i]); 56 | free(word_copy); 57 | } 58 | } 59 | printf("\n"); 60 | 61 | printf("Tree size: %zu, height: %zu\n", fi_btree_size(tree), fi_btree_height(tree)); 62 | 63 | // Print tree contents (inorder - alphabetical order) 64 | printf("Tree contents (inorder): "); 65 | fi_btree_inorder(tree, print_string_visit, NULL); 66 | printf("\n"); 67 | 68 | // Search for a word 69 | const char *search_word = "cherry"; 70 | fi_btree_node *found = fi_btree_search(tree, &search_word); 71 | printf("Search for '%s': %s\n", search_word, found ? "Found" : "Not found"); 72 | 73 | if (found) { 74 | char **found_str = (char**)found->data; 75 | printf("Found word: \"%s\"\n", *found_str); 76 | } 77 | 78 | // Find min and max (alphabetically first and last) 79 | fi_btree_node *min_node = fi_btree_find_min(tree->root); 80 | fi_btree_node *max_node = fi_btree_find_max(tree->root); 81 | 82 | if (min_node) { 83 | char **min_str = (char**)min_node->data; 84 | printf("Alphabetically first: \"%s\"\n", *min_str); 85 | } 86 | if (max_node) { 87 | char **max_str = (char**)max_node->data; 88 | printf("Alphabetically last: \"%s\"\n", *max_str); 89 | } 90 | 91 | // Test different traversals 92 | printf("\nTraversals:\n"); 93 | printf("Preorder: "); 94 | fi_btree_preorder(tree, print_string_visit, NULL); 95 | printf("\n"); 96 | 97 | printf("Inorder: "); 98 | fi_btree_inorder(tree, print_string_visit, NULL); 99 | printf("\n"); 100 | 101 | printf("Postorder: "); 102 | fi_btree_postorder(tree, print_string_visit, NULL); 103 | printf("\n"); 104 | 105 | // Convert to array 106 | fi_array *arr = fi_btree_to_array(tree); 107 | if (arr) { 108 | printf("\nTree as array: ["); 109 | for (size_t i = 0; i < fi_array_count(arr); i++) { 110 | char **str_ptr = (char**)fi_array_get(arr, i); 111 | if (str_ptr && *str_ptr) { 112 | printf("\"%s\"", *str_ptr); 113 | if (i < fi_array_count(arr) - 1) { 114 | printf(", "); 115 | } 116 | } 117 | } 118 | printf("]\n"); 119 | fi_array_destroy(arr); 120 | } 121 | 122 | // Delete a word 123 | const char *delete_word = "banana"; 124 | printf("\nDeleting '%s'...\n", delete_word); 125 | fi_btree_delete(tree, &delete_word); 126 | printf("After deletion - size: %zu\n", fi_btree_size(tree)); 127 | 128 | printf("Tree after deletion: "); 129 | fi_btree_inorder(tree, print_string_visit, NULL); 130 | printf("\n"); 131 | 132 | fi_btree_destroy(tree); 133 | printf("BTree destroyed successfully\n"); 134 | 135 | printf("\n=== String BTree Demo Complete ===\n"); 136 | return 0; 137 | } 138 | -------------------------------------------------------------------------------- /examples/array_basic.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file array_basic.c 3 | * @brief 基本数组操作示例 4 | * 5 | * 这个示例展示了如何使用fi_array进行基本的数组操作, 6 | * 包括创建、添加、删除、搜索等操作。 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "../src/include/fi.h" 13 | 14 | // 整数比较函数 15 | int compare_int(const void *a, const void *b) { 16 | int ia = *(int*)a; 17 | int ib = *(int*)b; 18 | return ia - ib; 19 | } 20 | 21 | // 打印整数数组 22 | void print_int_array(fi_array *arr, const char *title) { 23 | printf("%s: ", title); 24 | for (size_t i = 0; i < fi_array_count(arr); i++) { 25 | int *value = (int*)fi_array_get(arr, i); 26 | printf("%d ", *value); 27 | } 28 | printf("\n"); 29 | } 30 | 31 | // 回调函数:查找大于10的元素 32 | bool find_greater_than_10(void *element, size_t index, void *user_data) { 33 | (void)index; // 避免未使用参数警告 34 | int value = *(int*)element; 35 | int threshold = *(int*)user_data; 36 | return value > threshold; 37 | } 38 | 39 | // 回调函数:计算平方 40 | bool square_element(void *element, size_t index, void *user_data) { 41 | (void)index; // 避免未使用参数警告 42 | (void)user_data; // 避免未使用参数警告 43 | int *value = (int*)element; 44 | *value = (*value) * (*value); 45 | return true; 46 | } 47 | 48 | int main() { 49 | printf("=== FI Array 基本操作示例 ===\n\n"); 50 | 51 | // 1. 创建数组 52 | printf("1. 创建整数数组\n"); 53 | fi_array *numbers = fi_array_create(5, sizeof(int)); 54 | if (!numbers) { 55 | fprintf(stderr, "无法创建数组\n"); 56 | return 1; 57 | } 58 | 59 | // 2. 添加元素 60 | printf("2. 添加元素\n"); 61 | int values[] = {5, 15, 3, 20, 8, 12, 1}; 62 | for (int i = 0; i < 7; i++) { 63 | fi_array_push(numbers, &values[i]); 64 | } 65 | print_int_array(numbers, "添加元素后"); 66 | 67 | // 3. 数组大小和容量 68 | printf("3. 数组大小: %zu, 容量: %zu\n\n", 69 | fi_array_count(numbers), numbers->capacity); 70 | 71 | // 4. 访问和修改元素 72 | printf("4. 访问和修改元素\n"); 73 | int *first = (int*)fi_array_get(numbers, 0); 74 | printf("第一个元素: %d\n", *first); 75 | 76 | int new_value = 100; 77 | fi_array_set(numbers, 0, &new_value); 78 | printf("修改第一个元素为100后: "); 79 | print_int_array(numbers, ""); 80 | 81 | // 5. 栈操作 82 | printf("\n5. 栈操作\n"); 83 | int pop_value; 84 | if (fi_array_pop(numbers, &pop_value) == 0) { 85 | printf("弹出元素: %d\n", pop_value); 86 | } 87 | 88 | int push_value = 99; 89 | fi_array_push(numbers, &push_value); 90 | print_int_array(numbers, "弹出并推入后"); 91 | 92 | // 6. 搜索操作 93 | printf("\n6. 搜索操作\n"); 94 | int search_value = 20; 95 | ssize_t index = fi_array_search(numbers, &search_value); 96 | if (index >= 0) { 97 | printf("找到值 %d 在索引 %zd\n", search_value, index); 98 | } else { 99 | printf("未找到值 %d\n", search_value); 100 | } 101 | 102 | // 7. 使用回调函数查找 103 | int threshold = 10; 104 | void *found = fi_array_find(numbers, find_greater_than_10, &threshold); 105 | if (found) { 106 | printf("第一个大于10的元素: %d\n", *(int*)found); 107 | } 108 | 109 | // 8. 数组过滤 110 | printf("\n7. 数组过滤\n"); 111 | fi_array *filtered = fi_array_filter(numbers, find_greater_than_10, &threshold); 112 | print_int_array(filtered, "大于10的元素"); 113 | 114 | // 9. 数组排序 115 | printf("\n8. 数组排序\n"); 116 | fi_array_sort(numbers, compare_int); 117 | print_int_array(numbers, "排序后"); 118 | 119 | // 10. 数组反转 120 | printf("\n9. 数组反转\n"); 121 | fi_array_reverse(numbers); 122 | print_int_array(numbers, "反转后"); 123 | 124 | // 11. 数组切片 125 | printf("\n10. 数组切片\n"); 126 | fi_array *slice = fi_array_slice(numbers, 1, 3); 127 | print_int_array(slice, "从索引1开始取3个元素"); 128 | 129 | // 12. 数组合并 130 | printf("\n11. 数组合并\n"); 131 | fi_array *another = fi_array_create(3, sizeof(int)); 132 | int more_values[] = {50, 60, 70}; 133 | for (int i = 0; i < 3; i++) { 134 | fi_array_push(another, &more_values[i]); 135 | } 136 | fi_array_merge(numbers, another); 137 | print_int_array(numbers, "合并后"); 138 | 139 | // 13. 数学运算 140 | printf("\n12. 数学运算\n"); 141 | double sum = fi_array_sum(numbers); 142 | double product = fi_array_product(numbers); 143 | printf("数组元素和: %.2f\n", sum); 144 | printf("数组元素积: %.2f\n", product); 145 | 146 | // 14. 数组去重 147 | printf("\n13. 数组去重\n"); 148 | fi_array *unique = fi_array_unique(numbers); 149 | print_int_array(unique, "去重后"); 150 | 151 | // 15. 清理资源 152 | printf("\n14. 清理资源\n"); 153 | fi_array_destroy(numbers); 154 | fi_array_destroy(filtered); 155 | fi_array_destroy(slice); 156 | fi_array_destroy(another); 157 | fi_array_destroy(unique); 158 | 159 | printf("\n=== 示例完成 ===\n"); 160 | return 0; 161 | } 162 | -------------------------------------------------------------------------------- /src/demo_map_test.c: -------------------------------------------------------------------------------- 1 | #include "include/fi_map.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static void print_string_int_pair(const void *key, const void *value, void *user_data) { 8 | (void)user_data; /* Suppress unused parameter warning */ 9 | printf(" %s: %d\n", *(const char**)key, *(const int*)value); 10 | } 11 | 12 | static bool filter_even_values(const void *key, const void *value, void *user_data) { 13 | (void)key; /* Suppress unused parameter warning */ 14 | (void)user_data; /* Suppress unused parameter warning */ 15 | return (*(const int*)value) % 2 == 0; 16 | } 17 | 18 | int main() { 19 | printf("fi_map Demonstration\n"); 20 | printf("===================\n\n"); 21 | 22 | /* Create a map with string keys and int values */ 23 | fi_map *map = fi_map_create(16, sizeof(char*), sizeof(int32_t), 24 | fi_map_hash_string, fi_map_compare_string); 25 | assert(map != NULL); 26 | 27 | printf("1. Basic Operations:\n"); 28 | 29 | /* Add some entries */ 30 | const char *key1 = "apple"; 31 | const char *key2 = "banana"; 32 | const char *key3 = "cherry"; 33 | int value1 = 10, value2 = 20, value3 = 30; 34 | 35 | fi_map_put(map, &key1, &value1); 36 | fi_map_put(map, &key2, &value2); 37 | fi_map_put(map, &key3, &value3); 38 | 39 | printf(" Added 3 entries. Map size: %zu\n", fi_map_size(map)); 40 | 41 | /* Retrieve values */ 42 | int retrieved_value; 43 | fi_map_get(map, &key1, &retrieved_value); 44 | printf(" Value for 'apple': %d\n", retrieved_value); 45 | 46 | /* Check if key exists */ 47 | const char *missing_key = "orange"; 48 | printf(" 'apple' exists: %s\n", fi_map_contains(map, &key1) ? "true" : "false"); 49 | printf(" 'orange' exists: %s\n", fi_map_contains(map, &missing_key) ? "true" : "false"); 50 | 51 | printf("\n2. Iteration:\n"); 52 | printf(" All entries:\n"); 53 | fi_map_for_each(map, print_string_int_pair, NULL); 54 | 55 | printf("\n3. Advanced Operations:\n"); 56 | 57 | /* Test put_if_absent */ 58 | const char *new_key = "date"; 59 | int new_value = 40; 60 | int result = fi_map_put_if_absent(map, &new_key, &new_value); 61 | printf(" put_if_absent('date', 40): %s\n", result == 0 ? "added" : "already exists"); 62 | 63 | result = fi_map_put_if_absent(map, &key1, &new_value); 64 | printf(" put_if_absent('apple', 40): %s\n", result == 0 ? "added" : "already exists"); 65 | 66 | /* Test replace */ 67 | int replacement_value = 100; 68 | result = fi_map_replace(map, &key2, &replacement_value); 69 | printf(" replace('banana', 100): %s\n", result == 0 ? "replaced" : "not found"); 70 | 71 | printf("\n4. Functional Operations:\n"); 72 | 73 | /* Test filter for even values */ 74 | fi_map *even_map = fi_map_filter(map, filter_even_values, NULL); 75 | printf(" Even values only:\n"); 76 | fi_map_for_each(even_map, print_string_int_pair, NULL); 77 | 78 | /* Test any/all */ 79 | printf(" Has even values: %s\n", fi_map_any(map, filter_even_values, NULL) ? "true" : "false"); 80 | printf(" All values even: %s\n", fi_map_all(map, filter_even_values, NULL) ? "true" : "false"); 81 | 82 | printf("\n5. Hash Function Performance:\n"); 83 | 84 | /* Test hash consistency */ 85 | const char *test_string = "performance_test"; 86 | uint32_t hash1 = fi_map_hash_string(&test_string, strlen(test_string)); 87 | uint32_t hash2 = fi_map_hash_string(&test_string, strlen(test_string)); 88 | printf(" Hash consistency: %s\n", hash1 == hash2 ? "consistent" : "inconsistent"); 89 | 90 | /* Test int32 hash */ 91 | int32_t test_int = 12345; 92 | hash1 = fi_map_hash_int32(&test_int, sizeof(int32_t)); 93 | hash2 = fi_map_hash_int32(&test_int, sizeof(int32_t)); 94 | printf(" Int32 hash consistency: %s\n", hash1 == hash2 ? "consistent" : "inconsistent"); 95 | 96 | printf("\n6. Final State:\n"); 97 | printf(" Map size: %zu\n", fi_map_size(map)); 98 | printf(" Load factor: %.2f%%\n", fi_map_load_factor(map)); 99 | printf(" All entries:\n"); 100 | fi_map_for_each(map, print_string_int_pair, NULL); 101 | 102 | /* Cleanup */ 103 | fi_map_destroy(map); 104 | fi_map_destroy(even_map); 105 | 106 | printf("\n🎉 fi_map demonstration completed successfully!\n"); 107 | printf("\nKey Features Demonstrated:\n"); 108 | printf(" ✓ xxHash-based fast hashing\n"); 109 | printf(" ✓ Robin Hood open addressing\n"); 110 | printf(" ✓ CRUD operations (Create, Read, Update, Delete)\n"); 111 | printf(" ✓ Iterator support\n"); 112 | printf(" ✓ Functional operations (filter, any, all)\n"); 113 | printf(" ✓ Advanced operations (put_if_absent, replace)\n"); 114 | printf(" ✓ Hash function consistency\n"); 115 | printf(" ✓ Memory management\n"); 116 | 117 | return 0; 118 | } 119 | -------------------------------------------------------------------------------- /src/include/fi.h: -------------------------------------------------------------------------------- 1 | #ifndef __FI_H__ 2 | #define __FI_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include /* for ssize_t */ 10 | #include /* for uint32_t */ 11 | 12 | /* Array data structure */ 13 | typedef struct fi_array { 14 | void **data; /* Array of void pointers to hold any data type */ 15 | size_t size; /* Current number of elements */ 16 | size_t capacity; /* Maximum capacity before reallocation */ 17 | size_t element_size; /* Size of each element in bytes */ 18 | } fi_array; 19 | 20 | /* Callback function types */ 21 | typedef bool (*fi_array_callback_func)(void *element, size_t index, void *user_data); 22 | typedef int (*fi_array_compare_func)(const void *a, const void *b); 23 | typedef void (*fi_array_walk_func)(void *element, size_t index, void *user_data); 24 | 25 | /* Basic array operations */ 26 | fi_array* fi_array_create(size_t initial_capacity, size_t element_size); 27 | void fi_array_destroy(fi_array *arr); 28 | void fi_array_free(fi_array *arr); 29 | fi_array* fi_array_copy(const fi_array *arr); 30 | fi_array* fi_array_slice(const fi_array *arr, size_t offset, size_t length); 31 | 32 | /* Element access */ 33 | void* fi_array_get(const fi_array *arr, size_t index); 34 | void fi_array_set(fi_array *arr, size_t index, const void *value); 35 | bool fi_array_key_exists(const fi_array *arr, size_t index); 36 | size_t fi_array_count(const fi_array *arr); 37 | bool fi_array_empty(const fi_array *arr); 38 | 39 | /* Stack operations */ 40 | int fi_array_push(fi_array *arr, const void *value); 41 | int fi_array_pop(fi_array *arr, void *value); 42 | int fi_array_unshift(fi_array *arr, const void *value); 43 | int fi_array_shift(fi_array *arr, void *value); 44 | 45 | /* Array manipulation */ 46 | int fi_array_merge(fi_array *dest, const fi_array *src); 47 | int fi_array_splice(fi_array *arr, size_t offset, size_t length, const void *replacement); 48 | int fi_array_pad(fi_array *arr, size_t size, const void *value); 49 | int fi_array_fill(fi_array *arr, size_t start, size_t num, const void *value); 50 | 51 | /* Search operations */ 52 | ssize_t fi_array_search(const fi_array *arr, const void *value); 53 | bool fi_array_in_array(const fi_array *arr, const void *value); 54 | void* fi_array_find(const fi_array *arr, fi_array_callback_func callback, void *user_data); 55 | size_t fi_array_find_key(const fi_array *arr, fi_array_callback_func callback, void *user_data); 56 | 57 | /* Callback operations */ 58 | bool fi_array_all(const fi_array *arr, fi_array_callback_func callback, void *user_data); 59 | bool fi_array_any(const fi_array *arr, fi_array_callback_func callback, void *user_data); 60 | fi_array* fi_array_filter(const fi_array *arr, fi_array_callback_func callback, void *user_data); 61 | fi_array* fi_array_map(const fi_array *arr, fi_array_callback_func callback, void *user_data); 62 | void* fi_array_reduce(const fi_array *arr, fi_array_callback_func callback, void *initial, void *result); 63 | void fi_array_walk(fi_array *arr, fi_array_walk_func callback, void *user_data); 64 | 65 | /* Comparison operations */ 66 | fi_array* fi_array_diff(const fi_array *arr1, const fi_array *arr2); 67 | fi_array* fi_array_intersect(const fi_array *arr1, const fi_array *arr2); 68 | fi_array* fi_array_unique(const fi_array *arr); 69 | 70 | /* Sorting operations */ 71 | void fi_array_sort(fi_array *arr, fi_array_compare_func compare); 72 | void fi_array_reverse(fi_array *arr); 73 | void fi_array_shuffle(fi_array *arr); 74 | 75 | /* Utility functions */ 76 | fi_array* fi_array_keys(const fi_array *arr); 77 | fi_array* fi_array_values(const fi_array *arr); 78 | fi_array* fi_array_flip(const fi_array *arr); 79 | fi_array* fi_array_chunk(const fi_array *arr, size_t size); 80 | fi_array* fi_array_combine(const fi_array *keys, const fi_array *values); 81 | fi_array* fi_array_rand(const fi_array *arr, size_t num); 82 | 83 | /* Mathematical operations */ 84 | double fi_array_sum(const fi_array *arr); 85 | double fi_array_product(const fi_array *arr); 86 | 87 | /* Iterator operations */ 88 | void* fi_array_current(const fi_array *arr); 89 | size_t fi_array_key(const fi_array *arr); 90 | void* fi_array_next(fi_array *arr); 91 | void* fi_array_prev(fi_array *arr); 92 | void* fi_array_reset(fi_array *arr); 93 | void* fi_array_end(fi_array *arr); 94 | 95 | /* Special functions */ 96 | fi_array* fi_array_range(long start, long end, long step); 97 | fi_array* fi_array_compact(const fi_array *arr); 98 | void fi_array_extract(const fi_array *arr, const char *prefix); 99 | 100 | /* Aliases for compatibility */ 101 | #define fi_array_key_exists fi_array_key_exists 102 | #define fi_count fi_array_count 103 | #define fi_current fi_array_current 104 | #define fi_key fi_array_key 105 | #define fi_next fi_array_next 106 | #define fi_prev fi_array_prev 107 | #define fi_reset fi_array_reset 108 | #define fi_end fi_array_end 109 | #define fi_in_array fi_array_in_array 110 | #define fi_sizeof fi_array_count 111 | #define fi_pos fi_array_current 112 | 113 | #endif //__FI_H__ 114 | -------------------------------------------------------------------------------- /examples/rdb/simple_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "rdb.h" 5 | #include "persistence.h" 6 | 7 | int main() { 8 | printf("=== Simple RDB Test with Persistence ===\n"); 9 | 10 | /* Create database */ 11 | rdb_database_t *db = rdb_create_database("test_db"); 12 | if (!db) { 13 | printf("Failed to create database\n"); 14 | return 1; 15 | } 16 | 17 | printf("Database created successfully\n"); 18 | 19 | /* Create persistence manager */ 20 | rdb_persistence_manager_t *pm = rdb_persistence_create("./test_data", RDB_PERSISTENCE_FULL); 21 | if (!pm) { 22 | printf("Failed to create persistence manager\n"); 23 | rdb_destroy_database(db); 24 | return 1; 25 | } 26 | 27 | /* Initialize persistence */ 28 | if (rdb_persistence_init(pm) != 0) { 29 | printf("Failed to initialize persistence\n"); 30 | rdb_persistence_destroy(pm); 31 | rdb_destroy_database(db); 32 | return 1; 33 | } 34 | 35 | printf("Persistence manager initialized\n"); 36 | 37 | /* Test table existence check before creation */ 38 | const char *table_name = "users"; 39 | printf("Checking if table '%s' exists before creation...\n", table_name); 40 | 41 | bool exists = rdb_table_exists(db, table_name); 42 | printf("Table exists: %s\n", exists ? "true" : "false"); 43 | 44 | /* Create table columns */ 45 | fi_array *columns = fi_array_create(2, sizeof(rdb_column_t*)); 46 | if (!columns) { 47 | printf("Failed to create columns array\n"); 48 | goto cleanup; 49 | } 50 | 51 | /* Create ID column */ 52 | rdb_column_t *id_col = malloc(sizeof(rdb_column_t)); 53 | strcpy(id_col->name, "id"); 54 | id_col->type = RDB_TYPE_INT; 55 | id_col->max_length = 0; 56 | id_col->nullable = false; 57 | id_col->primary_key = true; 58 | id_col->unique = true; 59 | strcpy(id_col->default_value, ""); 60 | strcpy(id_col->foreign_table, ""); 61 | strcpy(id_col->foreign_column, ""); 62 | id_col->is_foreign_key = false; 63 | fi_array_push(columns, &id_col); 64 | 65 | /* Create name column */ 66 | rdb_column_t *name_col = malloc(sizeof(rdb_column_t)); 67 | strcpy(name_col->name, "name"); 68 | name_col->type = RDB_TYPE_VARCHAR; 69 | name_col->max_length = 100; 70 | name_col->nullable = false; 71 | name_col->primary_key = false; 72 | name_col->unique = false; 73 | strcpy(name_col->default_value, ""); 74 | strcpy(name_col->foreign_table, ""); 75 | strcpy(name_col->foreign_column, ""); 76 | name_col->is_foreign_key = false; 77 | fi_array_push(columns, &name_col); 78 | 79 | /* Create table */ 80 | if (rdb_create_table(db, table_name, columns) != 0) { 81 | printf("Failed to create table\n"); 82 | goto cleanup; 83 | } 84 | 85 | printf("Table '%s' created successfully\n", table_name); 86 | 87 | /* Test table existence check after creation */ 88 | printf("Checking if table '%s' exists after creation...\n", table_name); 89 | exists = rdb_table_exists(db, table_name); 90 | printf("Table exists: %s\n", exists ? "true" : "false"); 91 | 92 | /* Save database to disk */ 93 | if (rdb_persistence_save_database(pm, db) != 0) { 94 | printf("Failed to save database to disk\n"); 95 | goto cleanup; 96 | } 97 | 98 | printf("Database saved to disk successfully\n"); 99 | 100 | /* Clean up current database */ 101 | rdb_destroy_database(db); 102 | 103 | /* Create new database and load from disk */ 104 | db = rdb_create_database("test_db"); 105 | if (!db) { 106 | printf("Failed to create new database for loading\n"); 107 | goto cleanup; 108 | } 109 | 110 | /* Load database from disk */ 111 | if (rdb_persistence_load_database(pm, db) != 0) { 112 | printf("Failed to load database from disk\n"); 113 | goto cleanup; 114 | } 115 | 116 | printf("Database loaded from disk successfully\n"); 117 | 118 | /* Verify table still exists after loading */ 119 | printf("Checking if table '%s' exists after loading from disk...\n", table_name); 120 | exists = rdb_table_exists(db, table_name); 121 | printf("Table exists: %s\n", exists ? "true" : "false"); 122 | 123 | if (exists) { 124 | rdb_table_t *table = rdb_get_table(db, table_name); 125 | if (table) { 126 | printf("Table '%s' found with %zu columns and %zu rows\n", 127 | table_name, 128 | fi_array_count(table->columns), 129 | table->rows ? fi_array_count(table->rows) : 0); 130 | } 131 | } 132 | 133 | printf("Test completed successfully!\n"); 134 | 135 | cleanup: 136 | /* Clean up */ 137 | if (pm) { 138 | rdb_persistence_destroy(pm); 139 | } 140 | if (db) { 141 | rdb_destroy_database(db); 142 | } 143 | 144 | return 0; 145 | } -------------------------------------------------------------------------------- /examples/rdb/thread_safety_test.c: -------------------------------------------------------------------------------- 1 | #include "rdb.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define NUM_THREADS 3 8 | #define OPERATIONS_PER_THREAD 5 9 | 10 | /* Global database instance */ 11 | rdb_database_t *test_db = NULL; 12 | 13 | /* Test thread function for concurrent operations */ 14 | void* test_thread(void* arg) { 15 | int thread_id = *(int*)arg; 16 | 17 | printf("Test Thread %d: Starting operations\n", thread_id); 18 | 19 | for (int i = 0; i < OPERATIONS_PER_THREAD; i++) { 20 | /* Test INSERT */ 21 | fi_array *values = fi_array_create(2, sizeof(rdb_value_t*)); 22 | if (values) { 23 | rdb_value_t *id_val = rdb_create_int_value(thread_id * 100 + i); 24 | rdb_value_t *name_val = rdb_create_string_value("Test User"); 25 | 26 | fi_array_push(values, &id_val); 27 | fi_array_push(values, &name_val); 28 | 29 | int result = rdb_insert_row_thread_safe(test_db, "test_table", values); 30 | assert(result == 0); /* Should succeed */ 31 | 32 | rdb_value_free(id_val); 33 | rdb_value_free(name_val); 34 | fi_array_destroy(values); 35 | } 36 | 37 | /* Test SELECT */ 38 | fi_array *columns = fi_array_create(0, sizeof(char*)); 39 | fi_array *conditions = fi_array_create(0, sizeof(char*)); 40 | 41 | if (columns && conditions) { 42 | fi_array *result = rdb_select_rows_thread_safe(test_db, "test_table", columns, conditions); 43 | assert(result != NULL); /* Should succeed */ 44 | 45 | /* Cleanup result */ 46 | if (result) { 47 | for (size_t j = 0; j < fi_array_count(result); j++) { 48 | rdb_row_t *row = *(rdb_row_t**)fi_array_get(result, j); 49 | if (row) { 50 | rdb_row_free(row); 51 | } 52 | } 53 | fi_array_destroy(result); 54 | } 55 | 56 | fi_array_destroy(columns); 57 | fi_array_destroy(conditions); 58 | } 59 | 60 | /* Small delay */ 61 | sleep(0); 62 | } 63 | 64 | printf("Test Thread %d: Completed operations\n", thread_id); 65 | return NULL; 66 | } 67 | 68 | /* Helper function to create a column */ 69 | rdb_column_t* create_test_column(const char *name, rdb_data_type_t type, bool nullable, bool unique, bool primary_key) { 70 | rdb_column_t *col = malloc(sizeof(rdb_column_t)); 71 | if (!col) return NULL; 72 | 73 | strncpy(col->name, name, sizeof(col->name) - 1); 74 | col->name[sizeof(col->name) - 1] = '\0'; 75 | col->type = type; 76 | col->max_length = 0; 77 | col->nullable = nullable; 78 | col->primary_key = primary_key; 79 | col->unique = unique; 80 | col->default_value[0] = '\0'; 81 | col->foreign_table[0] = '\0'; 82 | col->foreign_column[0] = '\0'; 83 | col->is_foreign_key = false; 84 | 85 | return col; 86 | } 87 | 88 | int main() { 89 | printf("=== FI RDB Thread Safety Test ===\n\n"); 90 | 91 | /* Create database */ 92 | test_db = rdb_create_database("thread_safety_test"); 93 | assert(test_db != NULL); 94 | 95 | /* Open database */ 96 | int result = rdb_open_database(test_db); 97 | assert(result == 0); 98 | 99 | /* Create table */ 100 | fi_array *columns = fi_array_create(2, sizeof(rdb_column_t*)); 101 | assert(columns != NULL); 102 | 103 | rdb_column_t *id_col = create_test_column("id", RDB_TYPE_INT, false, true, true); 104 | rdb_column_t *name_col = create_test_column("name", RDB_TYPE_VARCHAR, false, false, false); 105 | 106 | assert(id_col != NULL); 107 | assert(name_col != NULL); 108 | 109 | fi_array_push(columns, &id_col); 110 | fi_array_push(columns, &name_col); 111 | 112 | /* Create table using thread-safe function */ 113 | result = rdb_create_table_thread_safe(test_db, "test_table", columns); 114 | assert(result == 0); 115 | 116 | printf("Database and table created successfully\n"); 117 | printf("Starting thread safety test with %d threads...\n\n", NUM_THREADS); 118 | 119 | /* Create threads */ 120 | pthread_t threads[NUM_THREADS]; 121 | int thread_ids[NUM_THREADS]; 122 | 123 | /* Create and start threads */ 124 | for (int i = 0; i < NUM_THREADS; i++) { 125 | thread_ids[i] = i; 126 | int create_result = pthread_create(&threads[i], NULL, test_thread, &thread_ids[i]); 127 | assert(create_result == 0); 128 | } 129 | 130 | /* Wait for all threads to complete */ 131 | for (int i = 0; i < NUM_THREADS; i++) { 132 | int join_result = pthread_join(threads[i], NULL); 133 | assert(join_result == 0); 134 | } 135 | 136 | printf("\nAll test threads completed successfully!\n"); 137 | 138 | /* Verify final state */ 139 | printf("\n=== Final Database State ===\n"); 140 | rdb_print_database_info(test_db); 141 | rdb_print_table_data(test_db, "test_table", 10); 142 | 143 | /* Cleanup */ 144 | rdb_column_free(id_col); 145 | rdb_column_free(name_col); 146 | fi_array_destroy(columns); 147 | rdb_destroy_database(test_db); 148 | 149 | printf("\nThread safety test PASSED!\n"); 150 | return 0; 151 | } 152 | -------------------------------------------------------------------------------- /examples/btree_basic.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file btree_basic.c 3 | * @brief 基本二叉搜索树操作示例 4 | * 5 | * 这个示例展示了如何使用fi_btree进行基本的二叉搜索树操作, 6 | * 包括创建、插入、删除、搜索、遍历等操作。 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "../src/include/fi_btree.h" 13 | 14 | // 整数比较函数 15 | int compare_int(const void *a, const void *b) { 16 | int ia = *(int*)a; 17 | int ib = *(int*)b; 18 | return ia - ib; 19 | } 20 | 21 | // 打印整数(用于遍历) 22 | void print_int(void *data, size_t depth, void *user_data) { 23 | (void)depth; // 避免未使用参数警告 24 | (void)user_data; // 避免未使用参数警告 25 | printf("%d ", *(int*)data); 26 | } 27 | 28 | // 打印整数(用于打印树结构) 29 | void print_int_simple(const void *data) { 30 | printf("%d", *(int*)data); 31 | } 32 | 33 | // 遍历访问函数 34 | void visit_node(void *data, size_t depth, void *user_data) { 35 | (void)user_data; // 避免未使用参数警告 36 | int value = *(int*)data; 37 | printf("%*s%d (深度: %zu)\n", (int)depth * 2, "", value, depth); 38 | } 39 | 40 | // 计算节点数的访问函数 41 | void count_nodes(void *data, size_t depth, void *user_data) { 42 | (void)data; // 避免未使用参数警告 43 | (void)depth; // 避免未使用参数警告 44 | (*(int*)user_data)++; 45 | } 46 | 47 | // 查找偶数的访问函数 48 | void find_even_nodes(void *data, size_t depth, void *user_data) { 49 | (void)user_data; // 避免未使用参数警告 50 | int value = *(int*)data; 51 | if (value % 2 == 0) { 52 | printf("找到偶数节点: %d (深度: %zu)\n", value, depth); 53 | } 54 | } 55 | 56 | int main() { 57 | printf("=== FI BTree 基本操作示例 ===\n\n"); 58 | 59 | // 1. 创建二叉搜索树 60 | printf("1. 创建二叉搜索树\n"); 61 | fi_btree *tree = fi_btree_create(sizeof(int), compare_int); 62 | if (!tree) { 63 | fprintf(stderr, "无法创建二叉搜索树\n"); 64 | return 1; 65 | } 66 | 67 | // 2. 插入元素 68 | printf("2. 插入元素\n"); 69 | int values[] = {50, 30, 70, 20, 40, 60, 80, 10, 25, 35, 45}; 70 | int num_values = sizeof(values) / sizeof(values[0]); 71 | 72 | for (int i = 0; i < num_values; i++) { 73 | if (fi_btree_insert(tree, &values[i]) != 0) { 74 | fprintf(stderr, "插入失败: %d\n", values[i]); 75 | } 76 | } 77 | 78 | printf("插入的元素: "); 79 | for (int i = 0; i < num_values; i++) { 80 | printf("%d ", values[i]); 81 | } 82 | printf("\n"); 83 | 84 | // 3. 树的基本信息 85 | printf("\n3. 树的基本信息\n"); 86 | printf("树的大小: %zu\n", fi_btree_size(tree)); 87 | printf("树的高度: %zu\n", fi_btree_height(tree)); 88 | printf("树是否为空: %s\n", fi_btree_empty(tree) ? "是" : "否"); 89 | 90 | // 4. 搜索操作 91 | printf("\n4. 搜索操作\n"); 92 | int search_value = 40; 93 | fi_btree_node *found_node = fi_btree_search(tree, &search_value); 94 | if (found_node) { 95 | printf("找到值 %d\n", search_value); 96 | } else { 97 | printf("未找到值 %d\n", search_value); 98 | } 99 | 100 | // 5. 查找最小值和最大值 101 | printf("\n5. 查找最小值和最大值\n"); 102 | fi_btree_node *min_node = fi_btree_find_min(tree->root); 103 | fi_btree_node *max_node = fi_btree_find_max(tree->root); 104 | 105 | if (min_node) { 106 | printf("最小值: %d\n", *(int*)min_node->data); 107 | } 108 | if (max_node) { 109 | printf("最大值: %d\n", *(int*)max_node->data); 110 | } 111 | 112 | // 6. 树的遍历 113 | printf("\n6. 树的遍历\n"); 114 | 115 | printf("中序遍历 (左-根-右):\n"); 116 | fi_btree_inorder(tree, visit_node, NULL); 117 | printf("\n"); 118 | 119 | printf("前序遍历 (根-左-右):\n"); 120 | fi_btree_preorder(tree, visit_node, NULL); 121 | printf("\n"); 122 | 123 | printf("后序遍历 (左-右-根):\n"); 124 | fi_btree_postorder(tree, visit_node, NULL); 125 | printf("\n"); 126 | 127 | printf("层序遍历:\n"); 128 | fi_btree_level_order(tree, visit_node, NULL); 129 | printf("\n"); 130 | 131 | // 7. 转换为数组 132 | printf("7. 转换为数组\n"); 133 | fi_array *inorder_array = fi_btree_to_array_inorder(tree); 134 | printf("中序遍历数组: "); 135 | for (size_t i = 0; i < fi_array_count(inorder_array); i++) { 136 | int *value = (int*)fi_array_get(inorder_array, i); 137 | printf("%d ", *value); 138 | } 139 | printf("\n"); 140 | 141 | // 8. 删除操作 142 | printf("\n8. 删除操作\n"); 143 | int delete_value = 30; 144 | printf("删除值 %d 前,树大小: %zu\n", delete_value, fi_btree_size(tree)); 145 | 146 | if (fi_btree_delete(tree, &delete_value) == 0) { 147 | printf("成功删除值 %d\n", delete_value); 148 | printf("删除后树大小: %zu\n", fi_btree_size(tree)); 149 | 150 | printf("删除后的中序遍历: "); 151 | fi_btree_inorder(tree, print_int, NULL); 152 | printf("\n"); 153 | } else { 154 | printf("删除值 %d 失败\n", delete_value); 155 | } 156 | 157 | // 9. 后继和前驱 158 | printf("\n9. 后继和前驱\n"); 159 | fi_btree_node *node_40 = fi_btree_search(tree, &(int){40}); 160 | if (node_40) { 161 | fi_btree_node *successor = fi_btree_successor(node_40); 162 | fi_btree_node *predecessor = fi_btree_predecessor(node_40); 163 | 164 | if (successor) { 165 | printf("40 的后继: %d\n", *(int*)successor->data); 166 | } 167 | if (predecessor) { 168 | printf("40 的前驱: %d\n", *(int*)predecessor->data); 169 | } 170 | } 171 | 172 | // 10. 树的性质检查 173 | printf("\n10. 树的性质检查\n"); 174 | printf("是否为有效的二叉搜索树: %s\n", 175 | fi_btree_is_bst(tree) ? "是" : "否"); 176 | 177 | // 11. 清理资源 178 | printf("\n11. 清理资源\n"); 179 | fi_btree_destroy(tree); 180 | fi_array_destroy(inorder_array); 181 | 182 | printf("=== 示例完成 ===\n"); 183 | return 0; 184 | } 185 | -------------------------------------------------------------------------------- /src/include/fi_map.h: -------------------------------------------------------------------------------- 1 | #ifndef __FI_MAP_H__ 2 | #define __FI_MAP_H__ 3 | 4 | #include "fi.h" 5 | 6 | /* Hash map entry structure */ 7 | typedef struct fi_map_entry { 8 | void *key; /* Key data */ 9 | void *value; /* Value data */ 10 | uint32_t hash; /* Cached hash value */ 11 | uint32_t distance; /* Distance from ideal position (Robin Hood hashing) */ 12 | bool is_deleted; /* Tombstone flag for deleted entries */ 13 | } fi_map_entry; 14 | 15 | /* Hash map structure */ 16 | typedef struct fi_map { 17 | fi_map_entry *buckets; /* Array of hash buckets */ 18 | size_t bucket_count; /* Number of buckets (always power of 2) */ 19 | size_t size; /* Current number of elements */ 20 | size_t key_size; /* Size of key in bytes */ 21 | size_t value_size; /* Size of value in bytes */ 22 | uint32_t (*hash_func)(const void *key, size_t key_size); /* Hash function */ 23 | int (*key_compare)(const void *key1, const void *key2); /* Key comparison function */ 24 | void (*key_free)(void *key); /* Key destructor function */ 25 | void (*value_free)(void *value); /* Value destructor function */ 26 | size_t load_factor_threshold; /* Load factor threshold for resizing */ 27 | } fi_map; 28 | 29 | /* Hash map creation and destruction */ 30 | fi_map* fi_map_create(size_t initial_capacity, 31 | size_t key_size, 32 | size_t value_size, 33 | uint32_t (*hash_func)(const void *key, size_t key_size), 34 | int (*key_compare)(const void *key1, const void *key2)); 35 | fi_map* fi_map_create_with_destructors(size_t initial_capacity, 36 | size_t key_size, 37 | size_t value_size, 38 | uint32_t (*hash_func)(const void *key, size_t key_size), 39 | int (*key_compare)(const void *key1, const void *key2), 40 | void (*key_free)(void *key), 41 | void (*value_free)(void *value)); 42 | void fi_map_destroy(fi_map *map); 43 | void fi_map_clear(fi_map *map); 44 | 45 | /* Basic operations */ 46 | int fi_map_put(fi_map *map, const void *key, const void *value); 47 | int fi_map_get(const fi_map *map, const void *key, void *value); 48 | int fi_map_remove(fi_map *map, const void *key); 49 | bool fi_map_contains(const fi_map *map, const void *key); 50 | bool fi_map_empty(const fi_map *map); 51 | size_t fi_map_size(const fi_map *map); 52 | 53 | /* Advanced operations */ 54 | int fi_map_put_if_absent(fi_map *map, const void *key, const void *value); 55 | int fi_map_replace(fi_map *map, const void *key, const void *value); 56 | int fi_map_get_or_default(const fi_map *map, const void *key, void *value, const void *default_value); 57 | int fi_map_merge(fi_map *dest, const fi_map *src); 58 | 59 | /* Iteration */ 60 | typedef struct fi_map_iterator { 61 | fi_map *map; 62 | size_t current_bucket; 63 | fi_map_entry *current_entry; 64 | bool is_valid; 65 | } fi_map_iterator; 66 | 67 | fi_map_iterator fi_map_iterator_create(fi_map *map); 68 | bool fi_map_iterator_next(fi_map_iterator *iter); 69 | bool fi_map_iterator_has_next(const fi_map_iterator *iter); 70 | void* fi_map_iterator_key(const fi_map_iterator *iter); 71 | void* fi_map_iterator_value(const fi_map_iterator *iter); 72 | void fi_map_iterator_destroy(fi_map_iterator *iter); 73 | 74 | /* Callback operations */ 75 | typedef bool (*fi_map_callback_func)(const void *key, const void *value, void *user_data); 76 | typedef void (*fi_map_visit_func)(const void *key, const void *value, void *user_data); 77 | 78 | void fi_map_for_each(fi_map *map, fi_map_visit_func visit, void *user_data); 79 | fi_map* fi_map_filter(fi_map *map, fi_map_callback_func callback, void *user_data); 80 | bool fi_map_any(fi_map *map, fi_map_callback_func callback, void *user_data); 81 | bool fi_map_all(fi_map *map, fi_map_callback_func callback, void *user_data); 82 | 83 | /* Utility functions */ 84 | fi_array* fi_map_keys(const fi_map *map); 85 | fi_array* fi_map_values(const fi_map *map); 86 | fi_array* fi_map_entries(const fi_map *map); 87 | double fi_map_load_factor(const fi_map *map); 88 | void fi_map_resize(fi_map *map, size_t new_capacity); 89 | 90 | /* Built-in hash functions */ 91 | uint32_t fi_map_hash_string(const void *key, size_t key_size); 92 | uint32_t fi_map_hash_int32(const void *key, size_t key_size); 93 | uint32_t fi_map_hash_int64(const void *key, size_t key_size); 94 | uint32_t fi_map_hash_ptr(const void *key, size_t key_size); 95 | uint32_t fi_map_hash_bytes(const void *key, size_t key_size); 96 | 97 | /* Built-in comparison functions */ 98 | int fi_map_compare_string(const void *key1, const void *key2); 99 | int fi_map_compare_int32(const void *key1, const void *key2); 100 | int fi_map_compare_int64(const void *key1, const void *key2); 101 | int fi_map_compare_ptr(const void *key1, const void *key2); 102 | int fi_map_compare_bytes(const void *key1, const void *key2); 103 | 104 | /* Specialized map creation functions */ 105 | fi_map* fi_map_create_string_string(size_t initial_capacity); 106 | fi_map* fi_map_create_string_ptr(size_t initial_capacity); 107 | fi_map* fi_map_create_int32_ptr(size_t initial_capacity); 108 | fi_map* fi_map_create_int64_ptr(size_t initial_capacity); 109 | fi_map* fi_map_create_ptr_ptr(size_t initial_capacity); 110 | 111 | /* Statistics and debugging */ 112 | void fi_map_print_stats(const fi_map *map); 113 | size_t fi_map_max_probe_distance(const fi_map *map); 114 | double fi_map_average_probe_distance(const fi_map *map); 115 | 116 | #endif //__FI_MAP_H__ 117 | -------------------------------------------------------------------------------- /examples/map_basic.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file map_basic.c 3 | * @brief 基本哈希映射操作示例 4 | * 5 | * 这个示例展示了如何使用fi_map进行基本的哈希映射操作, 6 | * 包括创建、插入、查找、删除、遍历等操作。 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "../src/include/fi_map.h" 13 | 14 | // 字符串比较函数 15 | int compare_string(const void *key1, const void *key2) { 16 | return strcmp((char*)key1, (char*)key2); 17 | } 18 | 19 | // 打印映射内容 20 | void print_map(fi_map *map, const char *title) { 21 | printf("%s:\n", title); 22 | 23 | fi_map_iterator iter = fi_map_iterator_create(map); 24 | while (fi_map_iterator_next(&iter)) { 25 | char *key = (char*)fi_map_iterator_key(&iter); 26 | int *value = (int*)fi_map_iterator_value(&iter); 27 | printf(" %s -> %d\n", key, *value); 28 | } 29 | fi_map_iterator_destroy(&iter); 30 | printf("\n"); 31 | } 32 | 33 | // 回调函数:打印键值对 34 | void print_key_value(const void *key, const void *value, void *user_data) { 35 | (void)user_data; // 避免未使用参数警告 36 | char *key_str = (char*)key; 37 | int *value_int = (int*)value; 38 | printf(" %s: %d\n", key_str, *value_int); 39 | } 40 | 41 | // 回调函数:查找值为偶数的元素 42 | bool find_even_values(const void *key, const void *value, void *user_data) { 43 | (void)key; // 避免未使用参数警告 44 | (void)user_data; // 避免未使用参数警告 45 | int *value_int = (int*)value; 46 | return (*value_int) % 2 == 0; 47 | } 48 | 49 | int main() { 50 | printf("=== FI Map 基本操作示例 ===\n\n"); 51 | 52 | // 1. 创建字符串到整数的映射 53 | printf("1. 创建字符串到整数的映射\n"); 54 | fi_map *age_map = fi_map_create(10, sizeof(char*), sizeof(int), 55 | fi_map_hash_string, fi_map_compare_string); 56 | if (!age_map) { 57 | fprintf(stderr, "无法创建映射\n"); 58 | return 1; 59 | } 60 | 61 | // 2. 插入键值对 62 | printf("2. 插入键值对\n"); 63 | char *names[] = {"Alice", "Bob", "Charlie", "David", "Eve"}; 64 | int ages[] = {25, 30, 35, 28, 32}; 65 | 66 | for (int i = 0; i < 5; i++) { 67 | if (fi_map_put(age_map, names[i], &ages[i]) != 0) { 68 | fprintf(stderr, "插入失败: %s\n", names[i]); 69 | } 70 | } 71 | print_map(age_map, "插入后"); 72 | 73 | // 3. 查找值 74 | printf("3. 查找值\n"); 75 | char *search_name = "Bob"; 76 | int found_age; 77 | if (fi_map_get(age_map, search_name, &found_age) == 0) { 78 | printf("找到 %s 的年龄: %d\n", search_name, found_age); 79 | } else { 80 | printf("未找到 %s\n", search_name); 81 | } 82 | 83 | // 4. 检查键是否存在 84 | printf("\n4. 检查键是否存在\n"); 85 | char *check_name = "Charlie"; 86 | if (fi_map_contains(age_map, check_name)) { 87 | printf("映射包含键: %s\n", check_name); 88 | } else { 89 | printf("映射不包含键: %s\n", check_name); 90 | } 91 | 92 | // 5. 更新值 93 | printf("\n5. 更新值\n"); 94 | int new_age = 31; 95 | fi_map_put(age_map, "Bob", &new_age); 96 | printf("更新 Bob 的年龄后:\n"); 97 | print_map(age_map, ""); 98 | 99 | // 6. 使用 put_if_absent 100 | printf("6. 使用 put_if_absent\n"); 101 | int default_age = 20; 102 | char *new_name = "Frank"; 103 | if (fi_map_put_if_absent(age_map, new_name, &default_age) == 0) { 104 | printf("成功添加新键: %s\n", new_name); 105 | } else { 106 | printf("键 %s 已存在\n", new_name); 107 | } 108 | print_map(age_map, "使用 put_if_absent 后"); 109 | 110 | // 7. 获取或设置默认值 111 | printf("7. 获取或设置默认值\n"); 112 | char *missing_name = "Grace"; 113 | int default_value = 18; 114 | int result_age; 115 | fi_map_get_or_default(age_map, missing_name, &result_age, &default_value); 116 | printf("获取 %s 的年龄(不存在时使用默认值): %d\n", missing_name, result_age); 117 | 118 | // 8. 删除键值对 119 | printf("\n8. 删除键值对\n"); 120 | char *delete_name = "David"; 121 | if (fi_map_remove(age_map, delete_name) == 0) { 122 | printf("成功删除键: %s\n", delete_name); 123 | } 124 | print_map(age_map, "删除后"); 125 | 126 | // 9. 映射大小和负载因子 127 | printf("9. 映射统计信息\n"); 128 | printf("映射大小: %zu\n", fi_map_size(age_map)); 129 | printf("负载因子: %.2f\n", fi_map_load_factor(age_map)); 130 | printf("最大探测距离: %zu\n", fi_map_max_probe_distance(age_map)); 131 | printf("平均探测距离: %.2f\n", fi_map_average_probe_distance(age_map)); 132 | 133 | // 10. 遍历映射 134 | printf("\n10. 遍历映射\n"); 135 | printf("使用 for_each 遍历:\n"); 136 | fi_map_for_each(age_map, print_key_value, NULL); 137 | 138 | // 11. 过滤映射 139 | printf("11. 过滤映射(只保留年龄为偶数的)\n"); 140 | fi_map *filtered_map = fi_map_filter(age_map, find_even_values, NULL); 141 | print_map(filtered_map, "过滤后(偶数年龄)"); 142 | 143 | // 12. 获取所有键和值 144 | printf("12. 获取所有键和值\n"); 145 | fi_array *keys = fi_map_keys(age_map); 146 | fi_array *values = fi_map_values(age_map); 147 | 148 | printf("所有键: "); 149 | for (size_t i = 0; i < fi_array_count(keys); i++) { 150 | char *key = *(char**)fi_array_get(keys, i); 151 | printf("%s ", key); 152 | } 153 | printf("\n"); 154 | 155 | printf("所有值: "); 156 | for (size_t i = 0; i < fi_array_count(values); i++) { 157 | int *value = *(int**)fi_array_get(values, i); 158 | printf("%d ", *value); 159 | } 160 | printf("\n\n"); 161 | 162 | // 13. 创建第二个映射并合并 163 | printf("13. 创建第二个映射并合并\n"); 164 | fi_map *another_map = fi_map_create(5, sizeof(char*), sizeof(int), 165 | fi_map_hash_string, fi_map_compare_string); 166 | char *more_names[] = {"Henry", "Ivy"}; 167 | int more_ages[] = {27, 29}; 168 | 169 | for (int i = 0; i < 2; i++) { 170 | fi_map_put(another_map, more_names[i], &more_ages[i]); 171 | } 172 | print_map(another_map, "第二个映射"); 173 | 174 | fi_map_merge(age_map, another_map); 175 | print_map(age_map, "合并后"); 176 | 177 | // 14. 检查映射是否为空 178 | printf("14. 检查映射状态\n"); 179 | printf("映射是否为空: %s\n", fi_map_empty(age_map) ? "是" : "否"); 180 | 181 | // 15. 清理资源 182 | printf("\n15. 清理资源\n"); 183 | fi_map_destroy(age_map); 184 | fi_map_destroy(filtered_map); 185 | fi_map_destroy(another_map); 186 | fi_array_destroy(keys); 187 | fi_array_destroy(values); 188 | 189 | printf("=== 示例完成 ===\n"); 190 | return 0; 191 | } 192 | -------------------------------------------------------------------------------- /examples/rdb/test_persistence.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "rdb.h" 6 | #include "persistence.h" 7 | 8 | int main() { 9 | printf("=== Testing RDB Persistence ===\n"); 10 | 11 | /* Create database */ 12 | rdb_database_t *db = rdb_create_database("test_db"); 13 | if (!db) { 14 | printf("Failed to create database\n"); 15 | return 1; 16 | } 17 | 18 | /* Create persistence manager */ 19 | rdb_persistence_manager_t *pm = rdb_persistence_create("./test_data", RDB_PERSISTENCE_FULL); 20 | if (!pm) { 21 | printf("Failed to create persistence manager\n"); 22 | rdb_destroy_database(db); 23 | return 1; 24 | } 25 | 26 | /* Initialize persistence */ 27 | if (rdb_persistence_init(pm) != 0) { 28 | printf("Failed to initialize persistence\n"); 29 | rdb_persistence_destroy(pm); 30 | rdb_destroy_database(db); 31 | return 1; 32 | } 33 | 34 | /* Create a test table */ 35 | fi_array *columns = fi_array_create(3, sizeof(rdb_column_t*)); 36 | if (!columns) { 37 | printf("Failed to create columns array\n"); 38 | goto cleanup; 39 | } 40 | 41 | /* Create columns */ 42 | rdb_column_t *id_col = malloc(sizeof(rdb_column_t)); 43 | strcpy(id_col->name, "id"); 44 | id_col->type = RDB_TYPE_INT; 45 | id_col->max_length = 0; 46 | id_col->nullable = false; 47 | id_col->primary_key = true; 48 | id_col->unique = true; 49 | strcpy(id_col->default_value, ""); 50 | strcpy(id_col->foreign_table, ""); 51 | strcpy(id_col->foreign_column, ""); 52 | id_col->is_foreign_key = false; 53 | fi_array_push(columns, &id_col); 54 | 55 | rdb_column_t *name_col = malloc(sizeof(rdb_column_t)); 56 | strcpy(name_col->name, "name"); 57 | name_col->type = RDB_TYPE_VARCHAR; 58 | name_col->max_length = 100; 59 | name_col->nullable = false; 60 | name_col->primary_key = false; 61 | name_col->unique = false; 62 | strcpy(name_col->default_value, ""); 63 | strcpy(name_col->foreign_table, ""); 64 | strcpy(name_col->foreign_column, ""); 65 | name_col->is_foreign_key = false; 66 | fi_array_push(columns, &name_col); 67 | 68 | rdb_column_t *age_col = malloc(sizeof(rdb_column_t)); 69 | strcpy(age_col->name, "age"); 70 | age_col->type = RDB_TYPE_INT; 71 | age_col->max_length = 0; 72 | age_col->nullable = true; 73 | age_col->primary_key = false; 74 | age_col->unique = false; 75 | strcpy(age_col->default_value, "0"); 76 | strcpy(age_col->foreign_table, ""); 77 | strcpy(age_col->foreign_column, ""); 78 | age_col->is_foreign_key = false; 79 | fi_array_push(columns, &age_col); 80 | 81 | /* Create table */ 82 | const char *table_name = "users"; 83 | if (rdb_create_table(db, table_name, columns) != 0) { 84 | printf("Failed to create table\n"); 85 | goto cleanup; 86 | } 87 | 88 | /* Insert some test data */ 89 | fi_array *values1 = fi_array_create(3, sizeof(rdb_value_t*)); 90 | rdb_value_t *id_val1 = rdb_create_int_value(1); 91 | rdb_value_t *name_val1 = rdb_create_string_value("Alice"); 92 | rdb_value_t *age_val1 = rdb_create_int_value(25); 93 | fi_array_push(values1, &id_val1); 94 | fi_array_push(values1, &name_val1); 95 | fi_array_push(values1, &age_val1); 96 | 97 | if (rdb_insert_row(db, "users", values1) != 0) { 98 | printf("Failed to insert row 1\n"); 99 | goto cleanup; 100 | } 101 | 102 | fi_array *values2 = fi_array_create(3, sizeof(rdb_value_t*)); 103 | rdb_value_t *id_val2 = rdb_create_int_value(2); 104 | rdb_value_t *name_val2 = rdb_create_string_value("Bob"); 105 | rdb_value_t *age_val2 = rdb_create_int_value(30); 106 | fi_array_push(values2, &id_val2); 107 | fi_array_push(values2, &name_val2); 108 | fi_array_push(values2, &age_val2); 109 | 110 | if (rdb_insert_row(db, "users", values2) != 0) { 111 | printf("Failed to insert row 2\n"); 112 | goto cleanup; 113 | } 114 | 115 | printf("Created table with 2 rows\n"); 116 | 117 | /* Save database to disk */ 118 | if (rdb_persistence_save_database(pm, db) != 0) { 119 | printf("Failed to save database\n"); 120 | goto cleanup; 121 | } 122 | 123 | printf("Database saved to disk\n"); 124 | 125 | /* Print persistence statistics */ 126 | rdb_persistence_print_stats(pm); 127 | 128 | /* Clean up current database */ 129 | rdb_destroy_database(db); 130 | 131 | /* Create new database and load from disk */ 132 | db = rdb_create_database("test_db"); 133 | if (!db) { 134 | printf("Failed to create new database\n"); 135 | goto cleanup; 136 | } 137 | 138 | /* Load database from disk */ 139 | if (rdb_persistence_load_database(pm, db) != 0) { 140 | printf("Failed to load database\n"); 141 | printf("Checking if data directory exists...\n"); 142 | system("ls -la ./test_data/"); 143 | goto cleanup; 144 | } 145 | 146 | printf("Database loaded from disk\n"); 147 | 148 | /* Verify data was loaded correctly */ 149 | rdb_table_t *table = rdb_get_table(db, "users"); 150 | if (table) { 151 | printf("Table 'users' found with %zu rows\n", fi_array_count(table->rows)); 152 | 153 | /* Print loaded data */ 154 | for (size_t i = 0; i < fi_array_count(table->rows); i++) { 155 | rdb_row_t **row_ptr = (rdb_row_t**)fi_array_get(table->rows, i); 156 | if (row_ptr && *row_ptr) { 157 | rdb_row_t *row = *row_ptr; 158 | printf("Row %zu: ID=%ld, Name=%s, Age=%ld\n", 159 | i, 160 | rdb_get_int_value(*(rdb_value_t**)fi_array_get(row->values, 0)), 161 | rdb_get_string_value(*(rdb_value_t**)fi_array_get(row->values, 1)), 162 | rdb_get_int_value(*(rdb_value_t**)fi_array_get(row->values, 2))); 163 | } 164 | } 165 | } else { 166 | printf("Table 'users' not found after loading\n"); 167 | } 168 | 169 | printf("Persistence test completed successfully!\n"); 170 | 171 | cleanup: 172 | /* Clean up */ 173 | if (pm) { 174 | rdb_persistence_destroy(pm); 175 | } 176 | if (db) { 177 | rdb_destroy_database(db); 178 | } 179 | 180 | /* Clean up test data directory */ 181 | /* system("rm -rf ./test_data"); */ 182 | 183 | return 0; 184 | } 185 | -------------------------------------------------------------------------------- /examples/rdb/sql_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef __SQL_PARSER_H__ 2 | #define __SQL_PARSER_H__ 3 | 4 | #include "rdb.h" 5 | 6 | /* SQL token types */ 7 | typedef enum { 8 | SQL_TOKEN_KEYWORD, 9 | SQL_TOKEN_IDENTIFIER, 10 | SQL_TOKEN_STRING, 11 | SQL_TOKEN_NUMBER, 12 | SQL_TOKEN_OPERATOR, 13 | SQL_TOKEN_PUNCTUATION, 14 | SQL_TOKEN_EOF, 15 | SQL_TOKEN_UNKNOWN 16 | } sql_token_type_t; 17 | 18 | /* SQL token structure */ 19 | typedef struct { 20 | sql_token_type_t type; 21 | char *value; 22 | size_t length; 23 | size_t position; 24 | } sql_token_t; 25 | 26 | /* SQL parser state */ 27 | typedef struct { 28 | char *sql; 29 | size_t pos; 30 | size_t length; 31 | sql_token_t current_token; 32 | bool has_error; 33 | char error_message[256]; 34 | } sql_parser_t; 35 | 36 | /* SQL keywords */ 37 | typedef enum { 38 | SQL_KW_SELECT = 1, 39 | SQL_KW_FROM, 40 | SQL_KW_WHERE, 41 | SQL_KW_INSERT, 42 | SQL_KW_INTO, 43 | SQL_KW_VALUES, 44 | SQL_KW_UPDATE, 45 | SQL_KW_SET, 46 | SQL_KW_DELETE, 47 | SQL_KW_CREATE, 48 | SQL_KW_TABLE, 49 | SQL_KW_INDEX, 50 | SQL_KW_DROP, 51 | SQL_KW_INT, 52 | SQL_KW_FLOAT, 53 | SQL_KW_VARCHAR, 54 | SQL_KW_TEXT, 55 | SQL_KW_BOOLEAN, 56 | SQL_KW_PRIMARY, 57 | SQL_KW_KEY, 58 | SQL_KW_UNIQUE, 59 | SQL_KW_NOT, 60 | SQL_KW_NULL, 61 | SQL_KW_DEFAULT, 62 | SQL_KW_AND, 63 | SQL_KW_OR, 64 | SQL_KW_ORDER, 65 | SQL_KW_BY, 66 | SQL_KW_LIMIT, 67 | SQL_KW_OFFSET, 68 | SQL_KW_JOIN, 69 | SQL_KW_INNER, 70 | SQL_KW_LEFT, 71 | SQL_KW_RIGHT, 72 | SQL_KW_FULL, 73 | SQL_KW_ON, 74 | SQL_KW_FOREIGN, 75 | SQL_KW_REFERENCES, 76 | SQL_KW_CASCADE, 77 | SQL_KW_CONSTRAINT, 78 | SQL_KW_BEGIN, 79 | SQL_KW_COMMIT, 80 | SQL_KW_ROLLBACK, 81 | SQL_KW_TRANSACTION, 82 | SQL_KW_AUTOCOMMIT, 83 | SQL_KW_ISOLATION, 84 | SQL_KW_LEVEL, 85 | SQL_KW_READ, 86 | SQL_KW_UNCOMMITTED, 87 | SQL_KW_COMMITTED, 88 | SQL_KW_REPEATABLE, 89 | SQL_KW_SERIALIZABLE, 90 | SQL_KW_TRUE, 91 | SQL_KW_FALSE, 92 | SQL_KW_LIKE, 93 | SQL_KW_IS, 94 | SQL_KW_IN 95 | } sql_keyword_t; 96 | 97 | /* SQL operators */ 98 | typedef enum { 99 | SQL_OP_EQUAL = 1, 100 | SQL_OP_NOT_EQUAL, 101 | SQL_OP_LESS_THAN, 102 | SQL_OP_GREATER_THAN, 103 | SQL_OP_LESS_EQUAL, 104 | SQL_OP_GREATER_EQUAL, 105 | SQL_OP_LIKE, 106 | SQL_OP_IS, 107 | SQL_OP_IN 108 | } sql_operator_t; 109 | 110 | /* WHERE condition */ 111 | typedef struct { 112 | char column_name[64]; 113 | sql_operator_t operator; 114 | rdb_value_t *value; 115 | char logical_connector[8]; /* AND, OR */ 116 | } sql_where_condition_t; 117 | 118 | /* Parser functions */ 119 | sql_parser_t* sql_parser_create(const char *sql); 120 | void sql_parser_destroy(sql_parser_t *parser); 121 | 122 | /* Tokenization */ 123 | int sql_parser_next_token(sql_parser_t *parser); 124 | sql_token_t* sql_parser_get_current_token(sql_parser_t *parser); 125 | bool sql_parser_has_error(sql_parser_t *parser); 126 | const char* sql_parser_get_error(sql_parser_t *parser); 127 | 128 | /* Statement parsing */ 129 | rdb_statement_t* sql_parse_statement(sql_parser_t *parser); 130 | rdb_statement_t* sql_parse_create_table(sql_parser_t *parser, rdb_statement_t *stmt); 131 | rdb_statement_t* sql_parse_drop_table(sql_parser_t *parser, rdb_statement_t *stmt); 132 | rdb_statement_t* sql_parse_insert(sql_parser_t *parser); 133 | rdb_statement_t* sql_parse_select(sql_parser_t *parser); 134 | rdb_statement_t* sql_parse_update(sql_parser_t *parser); 135 | rdb_statement_t* sql_parse_delete(sql_parser_t *parser); 136 | rdb_statement_t* sql_parse_create_index(sql_parser_t *parser); 137 | rdb_statement_t* sql_parse_drop_index(sql_parser_t *parser, rdb_statement_t *stmt); 138 | rdb_statement_t* sql_parse_begin_transaction(sql_parser_t *parser); 139 | rdb_statement_t* sql_parse_commit_transaction(sql_parser_t *parser); 140 | rdb_statement_t* sql_parse_rollback_transaction(sql_parser_t *parser); 141 | 142 | /* Helper functions */ 143 | sql_keyword_t sql_get_keyword(const char *keyword); 144 | bool sql_is_keyword(const char *word); 145 | bool sql_is_operator(const char *op); 146 | sql_operator_t sql_get_operator(const char *op); 147 | bool sql_is_punctuation(char c); 148 | bool sql_is_whitespace(char c); 149 | bool sql_is_identifier_char(char c); 150 | 151 | /* Column parsing */ 152 | int sql_parse_column_definition(sql_parser_t *parser, rdb_column_t *column); 153 | int sql_parse_column_list(sql_parser_t *parser, fi_array *columns); 154 | int sql_parse_value_list(sql_parser_t *parser, fi_array *values); 155 | int sql_parse_where_clause(sql_parser_t *parser, fi_array *conditions); 156 | int sql_parse_set_clause(sql_parser_t *parser, fi_array *columns, fi_array *values); 157 | 158 | /* JOIN parsing */ 159 | int sql_parse_join_clause(sql_parser_t *parser, fi_array *join_conditions); 160 | int sql_parse_join_type(sql_parser_t *parser, rdb_join_type_t *join_type); 161 | int sql_parse_join_condition(sql_parser_t *parser, rdb_join_condition_t *condition); 162 | int sql_parse_from_clause(sql_parser_t *parser, fi_array *from_tables); 163 | 164 | /* Foreign key parsing */ 165 | int sql_parse_foreign_key_definition(sql_parser_t *parser, rdb_foreign_key_t *foreign_key); 166 | int sql_parse_constraint_definition(sql_parser_t *parser, rdb_foreign_key_t *foreign_key); 167 | 168 | /* Transaction parsing */ 169 | int sql_parse_isolation_level(sql_parser_t *parser, rdb_isolation_level_t *level); 170 | 171 | /* Value parsing */ 172 | rdb_value_t* sql_parse_value(sql_parser_t *parser); 173 | rdb_value_t* sql_parse_string_value(const char *str); 174 | rdb_value_t* sql_parse_number_value(const char *str); 175 | rdb_value_t* sql_parse_boolean_value(const char *str); 176 | 177 | /* Statement execution */ 178 | int sql_execute_statement(rdb_database_t *db, const rdb_statement_t *stmt); 179 | 180 | /* Utility functions */ 181 | void sql_parser_skip_whitespace(sql_parser_t *parser); 182 | char sql_parser_peek_char(sql_parser_t *parser); 183 | char sql_parser_next_char(sql_parser_t *parser); 184 | bool sql_parser_match_keyword(sql_parser_t *parser, const char *keyword); 185 | bool sql_parser_match_punctuation(sql_parser_t *parser, char punct); 186 | bool sql_parser_expect_punctuation(sql_parser_t *parser, char punct); 187 | bool sql_parser_expect_keyword(sql_parser_t *parser, const char *keyword); 188 | 189 | /* Error handling */ 190 | void sql_parser_set_error(sql_parser_t *parser, const char *format, ...); 191 | 192 | /* Statement cleanup */ 193 | void sql_statement_free(rdb_statement_t *stmt); 194 | void sql_where_condition_free(void *condition); 195 | 196 | #endif //__SQL_PARSER_H__ 197 | -------------------------------------------------------------------------------- /examples/rdb/Makefile: -------------------------------------------------------------------------------- 1 | # FI Relational Database Makefile 2 | 3 | # Compiler and flags 4 | CC = gcc 5 | # CFLAGS = -Wall -Wextra -std=c99 -g -O2 -pthread 6 | CFLAGS = -Wall -Wextra -std=c99 -g -pthread 7 | INCLUDES = -I../../src/include -I. 8 | LDFLAGS = -L../../src/.libs -static -lfi -lm -pthread 9 | 10 | # Directories 11 | SRC_DIR = . 12 | BUILD_DIR = build 13 | LIB_DIR = ../../src 14 | 15 | # Source files 16 | RDB_SOURCES = rdb.c sql_parser.c cache_system.c persistence.c cached_rdb.c 17 | DEMO_SOURCES = rdb_demo.c multi_table_demo.c thread_safe_demo.c thread_safety_test.c interactive_sql.c cached_rdb_demo.c test_persistence.c simple_test.c 18 | ALL_SOURCES = $(RDB_SOURCES) $(DEMO_SOURCES) 19 | 20 | # Object files 21 | RDB_OBJECTS = $(RDB_SOURCES:%.c=$(BUILD_DIR)/%.o) 22 | DEMO_OBJECTS = $(DEMO_SOURCES:%.c=$(BUILD_DIR)/%.o) 23 | 24 | # Executables 25 | RDB_DEMO = $(BUILD_DIR)/rdb_demo 26 | MULTI_TABLE_DEMO = $(BUILD_DIR)/multi_table_demo 27 | THREAD_SAFE_DEMO = $(BUILD_DIR)/thread_safe_demo 28 | THREAD_SAFETY_TEST = $(BUILD_DIR)/thread_safety_test 29 | INTERACTIVE_SQL = $(BUILD_DIR)/interactive_sql 30 | CACHED_RDB_DEMO = $(BUILD_DIR)/cached_rdb_demo 31 | TEST_PERSISTENCE = $(BUILD_DIR)/test_persistence 32 | SIMPLE_TEST = $(BUILD_DIR)/simple_test 33 | RDB_LIB = $(BUILD_DIR)/librdb.a 34 | 35 | # Default target 36 | all: $(RDB_DEMO) $(MULTI_TABLE_DEMO) $(THREAD_SAFE_DEMO) $(THREAD_SAFETY_TEST) $(INTERACTIVE_SQL) $(CACHED_RDB_DEMO) $(TEST_PERSISTENCE) $(SIMPLE_TEST) 37 | 38 | # Create build directory 39 | $(BUILD_DIR): 40 | mkdir -p $(BUILD_DIR) 41 | 42 | # Build the demo executable 43 | $(RDB_DEMO): $(BUILD_DIR) $(RDB_OBJECTS) $(BUILD_DIR)/rdb_demo.o 44 | $(CC) $(BUILD_DIR)/rdb_demo.o $(RDB_OBJECTS) $(LDFLAGS) -o $@ 45 | 46 | # Build the multi-table demo executable 47 | $(MULTI_TABLE_DEMO): $(BUILD_DIR) $(RDB_OBJECTS) $(BUILD_DIR)/multi_table_demo.o 48 | $(CC) $(BUILD_DIR)/multi_table_demo.o $(RDB_OBJECTS) $(LDFLAGS) -o $@ 49 | 50 | # Build the thread-safe demo executable 51 | $(THREAD_SAFE_DEMO): $(BUILD_DIR) $(RDB_OBJECTS) $(BUILD_DIR)/thread_safe_demo.o 52 | $(CC) $(BUILD_DIR)/thread_safe_demo.o $(RDB_OBJECTS) $(LDFLAGS) -o $@ 53 | 54 | # Build the thread safety test executable 55 | $(THREAD_SAFETY_TEST): $(BUILD_DIR) $(RDB_OBJECTS) $(BUILD_DIR)/thread_safety_test.o 56 | $(CC) $(BUILD_DIR)/thread_safety_test.o $(RDB_OBJECTS) $(LDFLAGS) -o $@ 57 | 58 | # Build the interactive SQL executable 59 | $(INTERACTIVE_SQL): $(BUILD_DIR) $(RDB_OBJECTS) $(BUILD_DIR)/interactive_sql.o 60 | $(CC) $(BUILD_DIR)/interactive_sql.o $(RDB_OBJECTS) $(LDFLAGS) -o $@ 61 | 62 | # Build the cached RDB demo executable 63 | $(CACHED_RDB_DEMO): $(BUILD_DIR) $(RDB_OBJECTS) $(BUILD_DIR)/cached_rdb_demo.o 64 | $(CC) $(BUILD_DIR)/cached_rdb_demo.o $(RDB_OBJECTS) $(LDFLAGS) -o $@ 65 | 66 | # Build the persistence test executable 67 | $(TEST_PERSISTENCE): $(BUILD_DIR) $(RDB_OBJECTS) $(BUILD_DIR)/test_persistence.o 68 | $(CC) $(BUILD_DIR)/test_persistence.o $(RDB_OBJECTS) $(LDFLAGS) -o $@ 69 | 70 | # Build the simple test executable 71 | $(SIMPLE_TEST): $(BUILD_DIR) $(RDB_OBJECTS) $(BUILD_DIR)/simple_test.o 72 | $(CC) $(BUILD_DIR)/simple_test.o $(RDB_OBJECTS) $(LDFLAGS) -o $@ 73 | 74 | # Build static library 75 | $(RDB_LIB): $(BUILD_DIR) $(RDB_OBJECTS) 76 | ar rcs $@ $(RDB_OBJECTS) 77 | 78 | # Compile object files 79 | $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c 80 | $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ 81 | 82 | # Run the demo 83 | run: $(RDB_DEMO) 84 | ./$(RDB_DEMO) 85 | 86 | # Run the multi-table demo 87 | run-multi: $(MULTI_TABLE_DEMO) 88 | ./$(MULTI_TABLE_DEMO) 89 | 90 | # Run the thread-safe demo 91 | run-thread-safe: $(THREAD_SAFE_DEMO) 92 | ./$(THREAD_SAFE_DEMO) 93 | 94 | # Run the thread safety test 95 | test-thread-safety: $(THREAD_SAFETY_TEST) 96 | ./$(THREAD_SAFETY_TEST) 97 | 98 | # Run the interactive SQL client 99 | run-interactive: $(INTERACTIVE_SQL) 100 | ./$(INTERACTIVE_SQL) 101 | 102 | # Run the cached RDB demo 103 | run-cached: $(CACHED_RDB_DEMO) 104 | ./$(CACHED_RDB_DEMO) 105 | 106 | # Run the persistence test 107 | test-persistence: $(TEST_PERSISTENCE) 108 | ./$(TEST_PERSISTENCE) 109 | 110 | # Run the simple test 111 | test-simple: $(SIMPLE_TEST) 112 | ./$(SIMPLE_TEST) 113 | 114 | # Run with valgrind for memory checking 115 | valgrind: $(RDB_DEMO) 116 | valgrind --leak-check=full --show-leak-kinds=all ./$(RDB_DEMO) 117 | 118 | # Clean build files 119 | clean: 120 | rm -rf $(BUILD_DIR) 121 | 122 | # Install (copy library and headers) 123 | install: $(RDB_LIB) 124 | @echo "Installing RDB library..." 125 | @mkdir -p /usr/local/include/fi 126 | @mkdir -p /usr/local/lib 127 | cp rdb.h sql_parser.h /usr/local/include/fi/ 128 | cp $(RDB_LIB) /usr/local/lib/ 129 | @echo "Installation complete!" 130 | 131 | # Uninstall 132 | uninstall: 133 | @echo "Uninstalling RDB library..." 134 | rm -f /usr/local/include/fi/rdb.h 135 | rm -f /usr/local/include/fi/sql_parser.h 136 | rm -f /usr/local/lib/librdb.a 137 | @echo "Uninstallation complete!" 138 | 139 | # Show help 140 | help: 141 | @echo "Available targets:" 142 | @echo " all - Build all RDB demos and tests (default)" 143 | @echo " run - Build and run the basic demo" 144 | @echo " run-multi - Build and run the multi-table demo" 145 | @echo " run-thread-safe - Build and run the thread-safe demo" 146 | @echo " test-thread-safety - Build and run the thread safety test" 147 | @echo " run-interactive - Build and run the interactive SQL client" 148 | @echo " valgrind - Run basic demo with valgrind memory checker" 149 | @echo " lib - Build static library (librdb.a)" 150 | @echo " clean - Remove build files" 151 | @echo " install - Install library and headers to system" 152 | @echo " uninstall - Remove installed files" 153 | @echo " help - Show this help message" 154 | 155 | # Dependencies 156 | $(BUILD_DIR)/rdb.o: rdb.h 157 | $(BUILD_DIR)/sql_parser.o: sql_parser.h rdb.h 158 | $(BUILD_DIR)/rdb_demo.o: rdb.h sql_parser.h 159 | $(BUILD_DIR)/multi_table_demo.o: rdb.h sql_parser.h 160 | $(BUILD_DIR)/thread_safe_demo.o: rdb.h sql_parser.h 161 | $(BUILD_DIR)/thread_safety_test.o: rdb.h sql_parser.h 162 | $(BUILD_DIR)/interactive_sql.o: rdb.h sql_parser.h 163 | 164 | # Phony targets 165 | .PHONY: all run run-multi run-thread-safe test-thread-safety run-interactive test-persistence test-simple valgrind clean install uninstall help 166 | 167 | # Ensure FI library is built 168 | check-fi-lib: 169 | @if [ ! -f "$(LIB_DIR)/.libs/libfi.a" ] && [ ! -f "$(LIB_DIR)/.libs/libfi.so" ]; then \ 170 | echo "Error: FI library not found. Please build it first:"; \ 171 | echo " cd ../../src && make"; \ 172 | exit 1; \ 173 | fi 174 | 175 | # Make all depend on FI library check 176 | all: check-fi-lib 177 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "project.h" 4 | #include "fi.h" 5 | 6 | void print_version(void) { 7 | printf("Version: %s\n", PROJECT_VERSION); 8 | } 9 | 10 | /* Test callback functions */ 11 | bool is_positive(void *element, size_t index, void *user_data) { 12 | (void)index; 13 | (void)user_data; 14 | if (!element) return false; 15 | int *value = (int*)element; 16 | return *value > 0; 17 | } 18 | 19 | bool is_even(void *element, size_t index, void *user_data) { 20 | (void)index; 21 | (void)user_data; 22 | if (!element) return false; 23 | int *value = (int*)element; 24 | return *value % 2 == 0; 25 | } 26 | 27 | int compare_ints(const void *a, const void *b) { 28 | const int *ia = (const int*)a; 29 | const int *ib = (const int*)b; 30 | return (*ia > *ib) - (*ia < *ib); 31 | } 32 | 33 | void print_array(fi_array *arr, const char *name) { 34 | printf("%s: [", name); 35 | for (size_t i = 0; i < fi_array_count(arr); i++) { 36 | int *value = (int*)fi_array_get(arr, i); 37 | if (value) { 38 | printf("%d", *value); 39 | if (i < fi_array_count(arr) - 1) { 40 | printf(", "); 41 | } 42 | } 43 | } 44 | printf("] (size: %zu)\n", fi_array_count(arr)); 45 | } 46 | 47 | void demo_basic_operations() { 48 | printf("\n=== Basic Array Operations Demo ===\n"); 49 | 50 | // Create array 51 | fi_array *arr = fi_array_create(5, sizeof(int)); 52 | if (!arr) { 53 | printf("Failed to create array\n"); 54 | return; 55 | } 56 | 57 | // Add elements 58 | int values[] = {10, 20, 30, 40, 50}; 59 | for (int i = 0; i < 5; i++) { 60 | fi_array_push(arr, &values[i]); 61 | } 62 | print_array(arr, "After push"); 63 | 64 | // Get element 65 | int *element = (int*)fi_array_get(arr, 2); 66 | if (element) { 67 | printf("Element at index 2: %d\n", *element); 68 | } 69 | 70 | // Set element 71 | int new_value = 99; 72 | fi_array_set(arr, 2, &new_value); 73 | print_array(arr, "After set index 2 to 99"); 74 | 75 | // Pop element 76 | int popped; 77 | if (fi_array_pop(arr, &popped) == 0) { 78 | printf("Popped element: %d\n", popped); 79 | } 80 | print_array(arr, "After pop"); 81 | 82 | // Unshift element 83 | int unshifted = 5; 84 | fi_array_unshift(arr, &unshifted); 85 | print_array(arr, "After unshift 5"); 86 | 87 | fi_array_destroy(arr); 88 | } 89 | 90 | void demo_callback_operations() { 91 | printf("\n=== Callback Operations Demo ===\n"); 92 | 93 | fi_array *arr = fi_array_create(10, sizeof(int)); 94 | if (!arr) { 95 | printf("Failed to create array\n"); 96 | return; 97 | } 98 | 99 | int values[] = {-5, 10, -3, 8, -1, 6, -2, 4, -7, 9}; 100 | for (int i = 0; i < 10; i++) { 101 | fi_array_push(arr, &values[i]); 102 | } 103 | print_array(arr, "Original array"); 104 | 105 | // Test all positive 106 | bool all_positive = fi_array_all(arr, is_positive, NULL); 107 | printf("All elements positive: %s\n", all_positive ? "true" : "false"); 108 | 109 | // Test any positive 110 | bool any_positive = fi_array_any(arr, is_positive, NULL); 111 | printf("Any elements positive: %s\n", any_positive ? "true" : "false"); 112 | 113 | // Filter positive elements 114 | fi_array *positive_only = fi_array_filter(arr, is_positive, NULL); 115 | if (positive_only) { 116 | print_array(positive_only, "Positive elements only"); 117 | fi_array_destroy(positive_only); 118 | } 119 | 120 | // Filter even elements 121 | fi_array *even_only = fi_array_filter(arr, is_even, NULL); 122 | if (even_only) { 123 | print_array(even_only, "Even elements only"); 124 | fi_array_destroy(even_only); 125 | } 126 | 127 | fi_array_destroy(arr); 128 | } 129 | 130 | void demo_sorting_operations() { 131 | printf("\n=== Sorting Operations Demo ===\n"); 132 | 133 | fi_array *arr = fi_array_create(10, sizeof(int)); 134 | if (!arr) { 135 | printf("Failed to create array\n"); 136 | return; 137 | } 138 | 139 | int values[] = {64, 34, 25, 12, 22, 11, 90, 88, 76, 50}; 140 | for (int i = 0; i < 10; i++) { 141 | fi_array_push(arr, &values[i]); 142 | } 143 | print_array(arr, "Original array"); 144 | 145 | // Sort array 146 | fi_array_sort(arr, compare_ints); 147 | print_array(arr, "After sorting"); 148 | 149 | // Reverse array 150 | fi_array_reverse(arr); 151 | print_array(arr, "After reverse"); 152 | 153 | // Shuffle array 154 | fi_array_shuffle(arr); 155 | print_array(arr, "After shuffle"); 156 | 157 | fi_array_destroy(arr); 158 | } 159 | 160 | void demo_utility_operations() { 161 | printf("\n=== Utility Operations Demo ===\n"); 162 | 163 | fi_array *arr1 = fi_array_create(5, sizeof(int)); 164 | fi_array *arr2 = fi_array_create(5, sizeof(int)); 165 | 166 | if (!arr1 || !arr2) { 167 | printf("Failed to create arrays\n"); 168 | return; 169 | } 170 | 171 | int values1[] = {1, 2, 3, 4, 5}; 172 | int values2[] = {3, 4, 5, 6, 7}; 173 | 174 | for (int i = 0; i < 5; i++) { 175 | fi_array_push(arr1, &values1[i]); 176 | fi_array_push(arr2, &values2[i]); 177 | } 178 | 179 | print_array(arr1, "Array 1"); 180 | print_array(arr2, "Array 2"); 181 | 182 | // Array difference 183 | fi_array *diff = fi_array_diff(arr1, arr2); 184 | if (diff) { 185 | print_array(diff, "Array 1 - Array 2 (diff)"); 186 | fi_array_destroy(diff); 187 | } 188 | 189 | // Array intersection 190 | fi_array *intersect = fi_array_intersect(arr1, arr2); 191 | if (intersect) { 192 | print_array(intersect, "Array 1 ∩ Array 2 (intersect)"); 193 | fi_array_destroy(intersect); 194 | } 195 | 196 | // Unique elements 197 | fi_array *unique = fi_array_unique(arr1); 198 | if (unique) { 199 | print_array(unique, "Unique elements of Array 1"); 200 | fi_array_destroy(unique); 201 | } 202 | 203 | // Search for element 204 | int search_value = 3; 205 | ssize_t found_index = fi_array_search(arr1, &search_value); 206 | if (found_index >= 0) { 207 | printf("Found value %d at index %zd\n", search_value, found_index); 208 | } 209 | 210 | // Check if element exists 211 | int check_value = 10; 212 | bool exists = fi_array_in_array(arr1, &check_value); 213 | printf("Value %d exists in array: %s\n", check_value, exists ? "true" : "false"); 214 | 215 | fi_array_destroy(arr1); 216 | fi_array_destroy(arr2); 217 | } 218 | 219 | void demo_range_operations() { 220 | printf("\n=== Range Operations Demo ===\n"); 221 | 222 | // Create range 1 to 10 223 | fi_array *range = fi_array_range(1, 11, 1); 224 | if (range) { 225 | print_array(range, "Range 1-10"); 226 | fi_array_destroy(range); 227 | } 228 | 229 | // Create range 10 to 1 230 | range = fi_array_range(10, 0, -1); 231 | if (range) { 232 | print_array(range, "Range 10-1"); 233 | fi_array_destroy(range); 234 | } 235 | 236 | // Create range 0 to 20 step 2 237 | range = fi_array_range(0, 21, 2); 238 | if (range) { 239 | print_array(range, "Range 0-20 step 2"); 240 | fi_array_destroy(range); 241 | } 242 | } 243 | 244 | int main(int argc, char *argv[]) { 245 | (void)argc; /* Suppress unused parameter warning */ 246 | (void)argv; /* Suppress unused parameter warning */ 247 | 248 | printf("=== FI Array Library Demo ===\n"); 249 | print_version(); 250 | 251 | demo_basic_operations(); 252 | demo_callback_operations(); 253 | demo_sorting_operations(); 254 | demo_utility_operations(); 255 | demo_range_operations(); 256 | 257 | printf("\n=== Demo Complete ===\n"); 258 | return 0; 259 | } 260 | -------------------------------------------------------------------------------- /examples/student_management.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file student_management.c 3 | * @brief 学生管理系统示例 4 | * 5 | * 这个示例展示了一个完整的学生管理系统,使用fi_map来存储学生信息, 6 | * 使用fi_array来处理学生列表,演示了实际应用中的数据结构使用。 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "../src/include/fi_map.h" 13 | #include "../src/include/fi.h" 14 | 15 | // 学生结构体 16 | typedef struct { 17 | int id; 18 | char name[50]; 19 | int age; 20 | float gpa; 21 | char major[30]; 22 | } Student; 23 | 24 | // 创建学生 25 | Student* create_student(int id, const char* name, int age, float gpa, const char* major) { 26 | Student* student = (Student*)malloc(sizeof(Student)); 27 | if (!student) return NULL; 28 | 29 | student->id = id; 30 | strncpy(student->name, name, sizeof(student->name) - 1); 31 | student->name[sizeof(student->name) - 1] = '\0'; 32 | student->age = age; 33 | student->gpa = gpa; 34 | strncpy(student->major, major, sizeof(student->major) - 1); 35 | student->major[sizeof(student->major) - 1] = '\0'; 36 | 37 | return student; 38 | } 39 | 40 | // 释放学生内存 41 | void free_student(void* student) { 42 | if (student) { 43 | free(student); 44 | } 45 | } 46 | 47 | // 打印学生信息 48 | void print_student(const Student* student) { 49 | printf("ID: %d, 姓名: %s, 年龄: %d, GPA: %.2f, 专业: %s\n", 50 | student->id, student->name, student->age, student->gpa, student->major); 51 | } 52 | 53 | // 打印学生信息(用于回调) 54 | void print_student_callback(const void* key, const void* value, void* user_data) { 55 | (void)user_data; // 避免未使用参数警告 56 | int id = *(int*)key; 57 | Student* student = *(Student**)value; 58 | printf("学生ID %d: %s, 年龄: %d, GPA: %.2f, 专业: %s\n", 59 | id, student->name, student->age, student->gpa, student->major); 60 | } 61 | 62 | // 查找GPA大于3.5的学生 63 | bool find_high_gpa_students(const void* key, const void* value, void* user_data) { 64 | (void)key; // 避免未使用参数警告 65 | (void)user_data; // 避免未使用参数警告 66 | Student* student = *(Student**)value; 67 | return student->gpa > 3.5f; 68 | } 69 | 70 | // 查找特定专业的学生 71 | bool find_major_students(const void* key, const void* value, void* user_data) { 72 | (void)key; // 避免未使用参数警告 73 | Student* student = *(Student**)value; 74 | char* target_major = (char*)user_data; 75 | return strcmp(student->major, target_major) == 0; 76 | } 77 | 78 | // 按GPA排序的比较函数 79 | int compare_students_by_gpa(const void* a, const void* b) { 80 | Student* student_a = *(Student**)a; 81 | Student* student_b = *(Student**)b; 82 | 83 | if (student_a->gpa > student_b->gpa) return -1; // 降序排列 84 | if (student_a->gpa < student_b->gpa) return 1; 85 | return 0; 86 | } 87 | 88 | int main() { 89 | printf("=== 学生管理系统示例 ===\n\n"); 90 | 91 | // 1. 创建学生映射表(ID -> Student*) 92 | printf("1. 创建学生映射表\n"); 93 | fi_map* students_map = fi_map_create_with_destructors( 94 | 10, // 初始容量 95 | sizeof(int), // 键大小(学生ID) 96 | sizeof(Student*), // 值大小(学生指针) 97 | fi_map_hash_int32, // 整数哈希函数 98 | fi_map_compare_int32, // 整数比较函数 99 | NULL, // 键释放函数(ID不需要释放) 100 | free_student // 值释放函数(释放学生结构体) 101 | ); 102 | 103 | if (!students_map) { 104 | fprintf(stderr, "无法创建学生映射表\n"); 105 | return 1; 106 | } 107 | 108 | // 2. 添加学生 109 | printf("2. 添加学生\n"); 110 | Student* students[] = { 111 | create_student(1001, "张三", 20, 3.8, "计算机科学"), 112 | create_student(1002, "李四", 19, 3.6, "数学"), 113 | create_student(1003, "王五", 21, 3.9, "计算机科学"), 114 | create_student(1004, "赵六", 20, 3.2, "物理学"), 115 | create_student(1005, "钱七", 22, 3.7, "数学"), 116 | create_student(1006, "孙八", 19, 3.5, "计算机科学"), 117 | create_student(1007, "周九", 21, 3.1, "化学"), 118 | create_student(1008, "吴十", 20, 3.8, "物理学") 119 | }; 120 | 121 | for (int i = 0; i < 8; i++) { 122 | if (fi_map_put(students_map, &students[i]->id, &students[i]) != 0) { 123 | fprintf(stderr, "添加学生失败: %s\n", students[i]->name); 124 | } 125 | } 126 | 127 | printf("成功添加 %zu 名学生\n", fi_map_size(students_map)); 128 | 129 | // 3. 显示所有学生 130 | printf("\n3. 显示所有学生\n"); 131 | fi_map_for_each(students_map, print_student_callback, NULL); 132 | 133 | // 4. 查找特定学生 134 | printf("\n4. 查找特定学生\n"); 135 | int search_id = 1003; 136 | Student** found_student = (Student**)malloc(sizeof(Student*)); 137 | if (fi_map_get(students_map, &search_id, found_student) == 0) { 138 | printf("找到学生ID %d: ", search_id); 139 | print_student(*found_student); 140 | } else { 141 | printf("未找到学生ID %d\n", search_id); 142 | } 143 | free(found_student); 144 | 145 | // 5. 更新学生信息 146 | printf("\n5. 更新学生信息\n"); 147 | int update_id = 1001; 148 | Student** student_to_update = (Student**)malloc(sizeof(Student*)); 149 | if (fi_map_get(students_map, &update_id, student_to_update) == 0) { 150 | (*student_to_update)->gpa = 3.9f; // 提高GPA 151 | printf("更新学生ID %d 的GPA为 %.2f\n", update_id, (*student_to_update)->gpa); 152 | } 153 | free(student_to_update); 154 | 155 | // 6. 查找高GPA学生 156 | printf("\n6. 查找GPA大于3.5的学生\n"); 157 | fi_map* high_gpa_students = fi_map_filter(students_map, find_high_gpa_students, NULL); 158 | printf("找到 %zu 名高GPA学生:\n", fi_map_size(high_gpa_students)); 159 | fi_map_for_each(high_gpa_students, print_student_callback, NULL); 160 | 161 | // 7. 按专业查找学生 162 | printf("\n7. 按专业查找学生\n"); 163 | char target_major[] = "计算机科学"; 164 | fi_map* cs_students = fi_map_filter(students_map, find_major_students, target_major); 165 | printf("计算机科学专业的学生 (%zu 名):\n", fi_map_size(cs_students)); 166 | fi_map_for_each(cs_students, print_student_callback, NULL); 167 | 168 | // 8. 获取所有学生并排序 169 | printf("\n8. 按GPA排序所有学生\n"); 170 | fi_array* all_students = fi_map_values(students_map); 171 | 172 | // 将指针数组转换为学生数组 173 | fi_array* student_array = fi_array_create(fi_array_count(all_students), sizeof(Student*)); 174 | for (size_t i = 0; i < fi_array_count(all_students); i++) { 175 | Student** student_ptr = (Student**)fi_array_get(all_students, i); 176 | fi_array_push(student_array, student_ptr); 177 | } 178 | 179 | // 按GPA排序 180 | fi_array_sort(student_array, compare_students_by_gpa); 181 | 182 | printf("按GPA排序的学生列表:\n"); 183 | for (size_t i = 0; i < fi_array_count(student_array); i++) { 184 | Student** student_ptr = (Student**)fi_array_get(student_array, i); 185 | printf("%zu. ", i + 1); 186 | print_student(*student_ptr); 187 | } 188 | 189 | // 9. 计算平均GPA 190 | printf("\n9. 计算平均GPA\n"); 191 | double total_gpa = 0.0; 192 | fi_map_iterator iter = fi_map_iterator_create(students_map); 193 | while (fi_map_iterator_next(&iter)) { 194 | Student* student = *(Student**)fi_map_iterator_value(&iter); 195 | total_gpa += student->gpa; 196 | } 197 | fi_map_iterator_destroy(&iter); 198 | 199 | double average_gpa = total_gpa / fi_map_size(students_map); 200 | printf("所有学生的平均GPA: %.2f\n", average_gpa); 201 | 202 | // 10. 统计各专业学生数量 203 | printf("\n10. 统计各专业学生数量\n"); 204 | fi_array* all_values = fi_map_values(students_map); 205 | fi_array* majors = fi_array_create(0, 30); // 存储专业名称 206 | 207 | // 收集所有专业 208 | for (size_t i = 0; i < fi_array_count(all_values); i++) { 209 | Student* student = *(Student**)fi_array_get(all_values, i); 210 | bool found = false; 211 | 212 | // 检查专业是否已存在 213 | for (size_t j = 0; j < fi_array_count(majors); j++) { 214 | char* major = (char*)fi_array_get(majors, j); 215 | if (strcmp(major, student->major) == 0) { 216 | found = true; 217 | break; 218 | } 219 | } 220 | 221 | if (!found) { 222 | fi_array_push(majors, student->major); 223 | } 224 | } 225 | 226 | // 统计每个专业的学生数量 227 | for (size_t i = 0; i < fi_array_count(majors); i++) { 228 | char* major = (char*)fi_array_get(majors, i); 229 | fi_map* major_students = fi_map_filter(students_map, find_major_students, major); 230 | printf("%s: %zu 名学生\n", major, fi_map_size(major_students)); 231 | fi_map_destroy(major_students); 232 | } 233 | 234 | // 11. 删除学生 235 | printf("\n11. 删除学生\n"); 236 | int delete_id = 1004; 237 | if (fi_map_remove(students_map, &delete_id) == 0) { 238 | printf("成功删除学生ID %d\n", delete_id); 239 | printf("剩余学生数量: %zu\n", fi_map_size(students_map)); 240 | } else { 241 | printf("删除学生ID %d 失败\n", delete_id); 242 | } 243 | 244 | // 12. 显示最终统计信息 245 | printf("\n12. 最终统计信息\n"); 246 | printf("学生总数: %zu\n", fi_map_size(students_map)); 247 | printf("映射负载因子: %.2f\n", fi_map_load_factor(students_map)); 248 | printf("最大探测距离: %zu\n", fi_map_max_probe_distance(students_map)); 249 | 250 | // 13. 清理资源 251 | printf("\n13. 清理资源\n"); 252 | fi_map_destroy(students_map); 253 | fi_map_destroy(high_gpa_students); 254 | fi_map_destroy(cs_students); 255 | fi_array_destroy(all_students); 256 | fi_array_destroy(student_array); 257 | fi_array_destroy(all_values); 258 | fi_array_destroy(majors); 259 | 260 | printf("=== 示例完成 ===\n"); 261 | return 0; 262 | } 263 | -------------------------------------------------------------------------------- /examples/rdb/thread_safe_demo.c: -------------------------------------------------------------------------------- 1 | #include "rdb.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define NUM_THREADS 5 8 | #define OPERATIONS_PER_THREAD 10 9 | 10 | /* Global database instance */ 11 | rdb_database_t *global_db = NULL; 12 | 13 | /* Thread function for concurrent insertions */ 14 | void* insert_thread(void* arg) { 15 | int thread_id = *(int*)arg; 16 | 17 | printf("Thread %d: Starting insertions\n", thread_id); 18 | 19 | for (int i = 0; i < OPERATIONS_PER_THREAD; i++) { 20 | /* Create values for insertion */ 21 | fi_array *values = fi_array_create(3, sizeof(rdb_value_t*)); 22 | if (!values) { 23 | printf("Thread %d: Failed to create values array\n", thread_id); 24 | continue; 25 | } 26 | 27 | /* Create row data */ 28 | rdb_value_t *id_val = rdb_create_int_value(thread_id * 1000 + i); 29 | rdb_value_t *name_val = rdb_create_string_value("Thread User"); 30 | rdb_value_t *age_val = rdb_create_int_value(20 + (i % 50)); 31 | 32 | fi_array_push(values, &id_val); 33 | fi_array_push(values, &name_val); 34 | fi_array_push(values, &age_val); 35 | 36 | /* Insert using thread-safe function */ 37 | int result = rdb_insert_row_thread_safe(global_db, "users", values); 38 | if (result == 0) { 39 | printf("Thread %d: Successfully inserted row %d\n", thread_id, i); 40 | } else { 41 | printf("Thread %d: Failed to insert row %d\n", thread_id, i); 42 | } 43 | 44 | /* Cleanup */ 45 | rdb_value_free(id_val); 46 | rdb_value_free(name_val); 47 | rdb_value_free(age_val); 48 | fi_array_destroy(values); 49 | 50 | /* Small delay to simulate real work */ 51 | sleep(0); /* Yield CPU */ 52 | } 53 | 54 | printf("Thread %d: Finished insertions\n", thread_id); 55 | return NULL; 56 | } 57 | 58 | /* Thread function for concurrent reads */ 59 | void* read_thread(void* arg) { 60 | int thread_id = *(int*)arg; 61 | 62 | printf("Thread %d: Starting reads\n", thread_id); 63 | 64 | for (int i = 0; i < OPERATIONS_PER_THREAD; i++) { 65 | /* Create empty conditions for SELECT * */ 66 | fi_array *columns = fi_array_create(0, sizeof(char*)); 67 | fi_array *conditions = fi_array_create(0, sizeof(char*)); 68 | 69 | if (!columns || !conditions) { 70 | printf("Thread %d: Failed to create query arrays\n", thread_id); 71 | continue; 72 | } 73 | 74 | /* Select using thread-safe function */ 75 | fi_array *result = rdb_select_rows_thread_safe(global_db, "users", columns, conditions); 76 | if (result) { 77 | size_t count = fi_array_count(result); 78 | printf("Thread %d: Read %zu rows (operation %d)\n", thread_id, count, i); 79 | 80 | /* Cleanup result */ 81 | for (size_t j = 0; j < count; j++) { 82 | rdb_row_t *row = *(rdb_row_t**)fi_array_get(result, j); 83 | if (row) { 84 | rdb_row_free(row); 85 | } 86 | } 87 | fi_array_destroy(result); 88 | } else { 89 | printf("Thread %d: Failed to read rows (operation %d)\n", thread_id, i); 90 | } 91 | 92 | /* Cleanup */ 93 | fi_array_destroy(columns); 94 | fi_array_destroy(conditions); 95 | 96 | /* Small delay to simulate real work */ 97 | sleep(0); /* Yield CPU */ 98 | } 99 | 100 | printf("Thread %d: Finished reads\n", thread_id); 101 | return NULL; 102 | } 103 | 104 | /* Thread function for concurrent updates */ 105 | void* update_thread(void* arg) { 106 | int thread_id = *(int*)arg; 107 | 108 | printf("Thread %d: Starting updates\n", thread_id); 109 | 110 | for (int i = 0; i < OPERATIONS_PER_THREAD; i++) { 111 | /* Create update data */ 112 | fi_array *set_columns = fi_array_create(1, sizeof(char*)); 113 | fi_array *set_values = fi_array_create(1, sizeof(rdb_value_t*)); 114 | fi_array *conditions = fi_array_create(0, sizeof(char*)); 115 | 116 | if (!set_columns || !set_values || !conditions) { 117 | printf("Thread %d: Failed to create update arrays\n", thread_id); 118 | continue; 119 | } 120 | 121 | /* Set age column */ 122 | const char *age_col = "age"; 123 | rdb_value_t *new_age = rdb_create_int_value(30 + (i % 20)); 124 | 125 | fi_array_push(set_columns, &age_col); 126 | fi_array_push(set_values, &new_age); 127 | 128 | /* Update using thread-safe function */ 129 | int result = rdb_update_rows_thread_safe(global_db, "users", set_columns, set_values, conditions); 130 | if (result >= 0) { 131 | printf("Thread %d: Updated %d rows (operation %d)\n", thread_id, result, i); 132 | } else { 133 | printf("Thread %d: Failed to update rows (operation %d)\n", thread_id, i); 134 | } 135 | 136 | /* Cleanup */ 137 | rdb_value_free(new_age); 138 | fi_array_destroy(set_columns); 139 | fi_array_destroy(set_values); 140 | fi_array_destroy(conditions); 141 | 142 | /* Small delay to simulate real work */ 143 | sleep(0); /* Yield CPU */ 144 | } 145 | 146 | printf("Thread %d: Finished updates\n", thread_id); 147 | return NULL; 148 | } 149 | 150 | /* Helper function to create a column */ 151 | rdb_column_t* create_column(const char *name, rdb_data_type_t type, bool nullable, bool unique, bool primary_key) { 152 | rdb_column_t *col = malloc(sizeof(rdb_column_t)); 153 | if (!col) return NULL; 154 | 155 | strncpy(col->name, name, sizeof(col->name) - 1); 156 | col->name[sizeof(col->name) - 1] = '\0'; 157 | col->type = type; 158 | col->max_length = 0; 159 | col->nullable = nullable; 160 | col->primary_key = primary_key; 161 | col->unique = unique; 162 | col->default_value[0] = '\0'; 163 | col->foreign_table[0] = '\0'; 164 | col->foreign_column[0] = '\0'; 165 | col->is_foreign_key = false; 166 | 167 | return col; 168 | } 169 | 170 | int main() { 171 | printf("=== FI RDB Thread Safety Demo ===\n\n"); 172 | 173 | /* Create database */ 174 | global_db = rdb_create_database("thread_safe_demo"); 175 | if (!global_db) { 176 | printf("Failed to create database\n"); 177 | return 1; 178 | } 179 | 180 | /* Open database */ 181 | if (rdb_open_database(global_db) != 0) { 182 | printf("Failed to open database\n"); 183 | rdb_destroy_database(global_db); 184 | return 1; 185 | } 186 | 187 | /* Create table */ 188 | fi_array *columns = fi_array_create(3, sizeof(rdb_column_t*)); 189 | if (!columns) { 190 | printf("Failed to create columns array\n"); 191 | rdb_destroy_database(global_db); 192 | return 1; 193 | } 194 | 195 | rdb_column_t *id_col = create_column("id", RDB_TYPE_INT, false, true, true); 196 | rdb_column_t *name_col = create_column("name", RDB_TYPE_VARCHAR, false, false, false); 197 | rdb_column_t *age_col = create_column("age", RDB_TYPE_INT, false, false, false); 198 | 199 | if (!id_col || !name_col || !age_col) { 200 | printf("Failed to create columns\n"); 201 | rdb_destroy_database(global_db); 202 | return 1; 203 | } 204 | 205 | fi_array_push(columns, &id_col); 206 | fi_array_push(columns, &name_col); 207 | fi_array_push(columns, &age_col); 208 | 209 | /* Create table using thread-safe function */ 210 | if (rdb_create_table_thread_safe(global_db, "users", columns) != 0) { 211 | printf("Failed to create table\n"); 212 | rdb_destroy_database(global_db); 213 | return 1; 214 | } 215 | 216 | printf("Database and table created successfully\n"); 217 | printf("Starting concurrent operations with %d threads...\n\n", NUM_THREADS); 218 | 219 | /* Create threads */ 220 | pthread_t threads[NUM_THREADS]; 221 | int thread_ids[NUM_THREADS]; 222 | 223 | /* Start time */ 224 | clock_t start_time = clock(); 225 | 226 | /* Create and start threads */ 227 | for (int i = 0; i < NUM_THREADS; i++) { 228 | thread_ids[i] = i; 229 | 230 | /* Alternate between different operations */ 231 | int result; 232 | switch (i % 3) { 233 | case 0: 234 | result = pthread_create(&threads[i], NULL, insert_thread, &thread_ids[i]); 235 | break; 236 | case 1: 237 | result = pthread_create(&threads[i], NULL, read_thread, &thread_ids[i]); 238 | break; 239 | case 2: 240 | result = pthread_create(&threads[i], NULL, update_thread, &thread_ids[i]); 241 | break; 242 | } 243 | 244 | if (result != 0) { 245 | printf("Failed to create thread %d\n", i); 246 | } 247 | } 248 | 249 | /* Wait for all threads to complete */ 250 | for (int i = 0; i < NUM_THREADS; i++) { 251 | pthread_join(threads[i], NULL); 252 | } 253 | 254 | /* End time */ 255 | clock_t end_time = clock(); 256 | double execution_time = ((double)(end_time - start_time)) / CLOCKS_PER_SEC; 257 | 258 | printf("\nAll threads completed!\n"); 259 | printf("Total execution time: %.3f seconds\n", execution_time); 260 | 261 | /* Print final database state */ 262 | printf("\n=== Final Database State ===\n"); 263 | rdb_print_database_info(global_db); 264 | rdb_print_table_data(global_db, "users", 20); 265 | 266 | /* Cleanup */ 267 | rdb_column_free(id_col); 268 | rdb_column_free(name_col); 269 | rdb_column_free(age_col); 270 | fi_array_destroy(columns); 271 | rdb_destroy_database(global_db); 272 | 273 | printf("\nThread safety demo completed successfully!\n"); 274 | return 0; 275 | } 276 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fi 2 | 3 | impl array struct and have below functions. 4 | 5 | ## functions 6 | 7 | ### array 8 | 9 | ``` 10 | array — Create an array 11 | fi_array_all — Checks if all array elements satisfy a callback function 12 | fi_array_any — Checks if at least one array element satisfies a callback function 13 | fi_array_change_key_case — Changes the case of all keys in an array 14 | fi_array_chunk — Split an array into chunks 15 | fi_array_column — Return the values from a single column in the input array 16 | fi_array_combine — Creates an array by using one array for keys and another for its values 17 | fi_array_count_values — Counts the occurrences of each distinct value in an array 18 | fi_array_diff — Computes the difference of arrays 19 | fi_array_diff_assoc — Computes the difference of arrays with additional index check 20 | fi_array_diff_key — Computes the difference of arrays using keys for comparison 21 | fi_array_diff_uassoc — Computes the difference of arrays with additional index check which is performed by a user supplied callback function 22 | fi_array_diff_ukey — Computes the difference of arrays using a callback function on the keys for comparison 23 | fi_array_fill — Fill an array with values 24 | fi_array_fill_keys — Fill an array with values, specifying keys 25 | fi_array_filter — Filters elements of an array using a callback function 26 | fi_array_find — Returns the first element satisfying a callback function 27 | fi_array_find_key — Returns the key of the first element satisfying a callback function 28 | fi_array_flip — Exchanges all keys with their associated values in an array 29 | fi_array_intersect — Computes the intersection of arrays 30 | fi_array_intersect_assoc — Computes the intersection of arrays with additional index check 31 | fi_array_intersect_key — Computes the intersection of arrays using keys for comparison 32 | fi_array_intersect_uassoc — Computes the intersection of arrays with additional index check, compares indexes by a callback function 33 | fi_array_intersect_ukey — Computes the intersection of arrays using a callback function on the keys for comparison 34 | fi_array_is_list — Checks whether a given array is a list 35 | fi_array_key_exists — Checks if the given key or index exists in the array 36 | fi_array_key_first — Gets the first key of an array 37 | fi_array_key_last — Gets the last key of an array 38 | fi_array_keys — Return all the keys or a subset of the keys of an array 39 | fi_array_map — Applies the callback to the elements of the given arrays 40 | fi_array_merge — Merge one or more arrays 41 | fi_array_merge_recursive — Merge one or more arrays recursively 42 | fi_array_multisort — Sort multiple or multi-dimensional arrays 43 | fi_array_pad — Pad array to the specified length with a value 44 | fi_array_pop — Pop the element off the end of array 45 | fi_array_product — Calculate the product of values in an array 46 | fi_array_push — Push one or more elements onto the end of array 47 | fi_array_rand — Pick one or more random keys out of an array 48 | fi_array_reduce — Iteratively reduce the array to a single value using a callback function 49 | fi_array_replace — Replaces elements from passed arrays into the first array 50 | fi_array_replace_recursive — Replaces elements from passed arrays into the first array recursively 51 | fi_array_reverse — Return an array with elements in reverse order 52 | fi_array_search — Searches the array for a given value and returns the first corresponding key if successful 53 | fi_array_shift — Shift an element off the beginning of array 54 | fi_array_slice — Extract a slice of the array 55 | fi_array_splice — Remove a portion of the array and replace it with something else 56 | fi_array_sum — Calculate the sum of values in an array 57 | fi_array_udiff — Computes the difference of arrays by using a callback function for data comparison 58 | fi_array_udiff_assoc — Computes the difference of arrays with additional index check, compares data by a callback function 59 | fi_array_udiff_uassoc — Computes the difference of arrays with additional index check, compares data and indexes by a callback function 60 | fi_array_uintersect — Computes the intersection of arrays, compares data by a callback function 61 | fi_array_uintersect_assoc — Computes the intersection of arrays with additional index check, compares data by a callback function 62 | fi_array_uintersect_uassoc — Computes the intersection of arrays with additional index check, compares data and indexes by separate callback functions 63 | fi_array_unique — Removes duplicate values from an array 64 | fi_array_unshift — Prepend one or more elements to the beginning of an array 65 | fi_array_values — Return all the values of an array 66 | fi_array_walk — Apply a user supplied function to every member of an array 67 | fi_array_walk_recursive — Apply a user function recursively to every member of an array 68 | fi_arsort — Sort an array in descending order and maintain index association 69 | fi_asort — Sort an array in ascending order and maintain index association 70 | fi_compact — Create array containing variables and their values 71 | fi_count — Counts all elements in an array or in a Countable object 72 | fi_current — Return the current element in an array 73 | fi_each — Return the current key and value pair from an array and advance the array cursor 74 | fi_end — Set the internal pointer of an array to its last element 75 | fi_extract — Import variables into the current symbol table from an array 76 | fi_in_array — Checks if a value exists in an array 77 | fi_key — Fetch a key from an array 78 | fi_key_exists — Alias of array_key_exists 79 | fi_krsort — Sort an array by key in descending order 80 | fi_ksort — Sort an array by key in ascending order 81 | fi_list — Assign variables as if they were an array 82 | fi_natcasesort — Sort an array using a case insensitive "natural order" algorithm 83 | fi_natsort — Sort an array using a "natural order" algorithm 84 | fi_next — Advance the internal pointer of an array 85 | fi_pos — Alias of current 86 | fi_prev — Rewind the internal array pointer 87 | fi_range — Create an array containing a range of elements 88 | fi_reset — Set the internal pointer of an array to its first element 89 | fi_rsort — Sort an array in descending order 90 | fi_shuffle — Shuffle an array 91 | fi_sizeof — Alias of count 92 | fi_sort — Sort an array in ascending order 93 | fi_uasort — Sort an array with a user-defined comparison function and maintain index association 94 | fi_uksort — Sort an array by keys using a user-defined comparison function 95 | fi_usort — Sort an array by values using a user-defined comparison function 96 | ``` 97 | 98 | ### btree 99 | 100 | ``` 101 | fi_btree_create — Create a new binary search tree 102 | fi_btree_destroy — Destroy the entire tree and free all memory 103 | fi_btree_clear — Clear all nodes from the tree 104 | fi_btree_create_node — Create a new tree node 105 | fi_btree_destroy_node — Destroy a tree node 106 | fi_btree_insert — Insert data into the tree 107 | fi_btree_delete — Delete data from the tree 108 | fi_btree_delete_node — Delete a specific node from the tree 109 | fi_btree_search — Search for data in the tree 110 | fi_btree_find_min — Find minimum node in subtree 111 | fi_btree_find_max — Find maximum node in subtree 112 | fi_btree_successor — Find successor of a node 113 | fi_btree_predecessor — Find predecessor of a node 114 | fi_btree_size — Get the number of nodes in the tree 115 | fi_btree_height — Get the height of the tree 116 | fi_btree_node_height — Get the height of a specific node 117 | fi_btree_empty — Check if tree is empty 118 | fi_btree_contains — Check if tree contains specific data 119 | fi_btree_inorder — Perform inorder traversal of the tree 120 | fi_btree_preorder — Perform preorder traversal of the tree 121 | fi_btree_postorder — Perform postorder traversal of the tree 122 | fi_btree_level_order — Perform level-order traversal of the tree 123 | fi_btree_to_array — Convert tree to array (inorder) 124 | fi_btree_to_array_inorder — Convert tree to array using inorder traversal 125 | fi_btree_to_array_preorder — Convert tree to array using preorder traversal 126 | fi_btree_to_array_postorder — Convert tree to array using postorder traversal 127 | fi_btree_from_array — Build tree from array 128 | fi_btree_from_sorted_array — Build balanced tree from sorted array 129 | fi_btree_is_bst — Check if tree is a valid binary search tree 130 | fi_btree_print — Print tree contents 131 | ``` 132 | 133 | ## Examples 134 | 135 | ### Array Usage 136 | 137 | ```c 138 | #include "fi.h" 139 | 140 | // Create an integer array 141 | fi_array *arr = fi_array_create(10, sizeof(int)); 142 | 143 | // Add elements 144 | int values[] = {10, 20, 30, 40, 50}; 145 | for (int i = 0; i < 5; i++) { 146 | fi_array_push(arr, &values[i]); 147 | } 148 | 149 | // Search for element 150 | int search_val = 30; 151 | ssize_t index = fi_array_search(arr, &search_val); 152 | printf("Found at index: %zd\n", index); 153 | 154 | // Filter elements 155 | fi_array *filtered = fi_array_filter(arr, is_positive, NULL); 156 | 157 | // Clean up 158 | fi_array_destroy(arr); 159 | ``` 160 | 161 | ### BTree Usage 162 | 163 | ```c 164 | #include "fi_btree.h" 165 | 166 | // Comparison function for integers 167 | int compare_ints(const void *a, const void *b) { 168 | const int *ia = (const int*)a; 169 | const int *ib = (const int*)b; 170 | return (*ia > *ib) - (*ia < *ib); 171 | } 172 | 173 | // Create a BTree 174 | fi_btree *tree = fi_btree_create(sizeof(int), compare_ints); 175 | 176 | // Insert elements 177 | int values[] = {50, 30, 70, 20, 40, 60, 80}; 178 | for (int i = 0; i < 7; i++) { 179 | fi_btree_insert(tree, &values[i]); 180 | } 181 | 182 | // Search for element 183 | int search_val = 40; 184 | fi_btree_node *found = fi_btree_search(tree, &search_val); 185 | printf("Found: %s\n", found ? "Yes" : "No"); 186 | 187 | // Traverse tree 188 | printf("Inorder: "); 189 | fi_btree_inorder(tree, print_visit, NULL); 190 | 191 | // Clean up 192 | fi_btree_destroy(tree); 193 | ``` 194 | 195 | ## Features 196 | 197 | - **Type-agnostic**: Works with any data type using void pointers 198 | - **Memory management**: Automatic allocation and cleanup 199 | - **BST properties**: Maintains binary search tree ordering 200 | - **Array integration**: Uses fi_array for level-order traversal 201 | - **Comprehensive API**: 90+ array functions and 25+ tree functions 202 | - **Performance**: O(log n) tree operations, O(1) array access -------------------------------------------------------------------------------- /examples/rdb/cached_rdb.h: -------------------------------------------------------------------------------- 1 | #ifndef __CACHED_RDB_H__ 2 | #define __CACHED_RDB_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | /* Use mutex for thread safety (rwlock compatibility) */ 13 | #define pthread_rwlock_t pthread_mutex_t 14 | #define pthread_rwlock_init(lock, attr) pthread_mutex_init(lock, attr) 15 | #define pthread_rwlock_destroy(lock) pthread_mutex_destroy(lock) 16 | #define pthread_rwlock_rdlock(lock) pthread_mutex_lock(lock) 17 | #define pthread_rwlock_wrlock(lock) pthread_mutex_lock(lock) 18 | #define pthread_rwlock_unlock(lock) pthread_mutex_unlock(lock) 19 | 20 | /* Include FI data structures */ 21 | #include "../../src/include/fi.h" 22 | #include "../../src/include/fi_map.h" 23 | #include "../../src/include/fi_btree.h" 24 | 25 | /* Include RDB structures */ 26 | #include "rdb.h" 27 | #include "cache_system.h" 28 | #include "persistence.h" 29 | 30 | /* Cached RDB configuration */ 31 | #define CACHED_RDB_DEFAULT_CACHE_LEVELS 2 32 | #define CACHED_RDB_DEFAULT_CACHE_SIZE 64 * 1024 * 1024 /* 64MB default cache */ 33 | #define CACHED_RDB_DEFAULT_PERSISTENCE_MODE RDB_PERSISTENCE_FULL 34 | 35 | /* Cache key types for different RDB operations */ 36 | typedef enum { 37 | CACHED_RDB_KEY_TABLE = 1, /* Table metadata cache key */ 38 | CACHED_RDB_KEY_ROW, /* Row data cache key */ 39 | CACHED_RDB_KEY_INDEX, /* Index cache key */ 40 | CACHED_RDB_KEY_QUERY /* Query result cache key */ 41 | } cached_rdb_key_type_t; 42 | 43 | /* Cache key structure */ 44 | typedef struct { 45 | cached_rdb_key_type_t type; /* Key type */ 46 | char table_name[64]; /* Table name */ 47 | uint64_t row_id; /* Row ID (for row keys) */ 48 | char index_name[64]; /* Index name (for index keys) */ 49 | uint32_t query_hash; /* Query hash (for query keys) */ 50 | uint32_t checksum; /* Key checksum */ 51 | } cached_rdb_key_t; 52 | 53 | /* Cached RDB statistics */ 54 | typedef struct { 55 | uint64_t cache_hits; /* Cache hits */ 56 | uint64_t cache_misses; /* Cache misses */ 57 | uint64_t disk_reads; /* Disk read operations */ 58 | uint64_t disk_writes; /* Disk write operations */ 59 | uint64_t cache_evictions; /* Cache evictions */ 60 | uint64_t persistence_operations; /* Persistence operations */ 61 | double cache_hit_ratio; /* Cache hit ratio */ 62 | double average_query_time; /* Average query time in microseconds */ 63 | time_t last_reset; /* Last statistics reset time */ 64 | } cached_rdb_stats_t; 65 | 66 | /* Cached RDB configuration */ 67 | typedef struct { 68 | size_t cache_levels; /* Number of cache levels */ 69 | rdb_cache_level_config_t *cache_configs; /* Cache level configurations */ 70 | rdb_persistence_mode_t persistence_mode; /* Persistence mode */ 71 | char *persistence_dir; /* Persistence directory */ 72 | bool enable_query_cache; /* Whether to enable query result caching */ 73 | size_t query_cache_size; /* Query cache size */ 74 | bool enable_auto_tuning; /* Whether to enable automatic cache tuning */ 75 | double target_hit_ratio; /* Target cache hit ratio */ 76 | uint64_t tune_interval; /* Auto-tuning interval in seconds */ 77 | } cached_rdb_config_t; 78 | 79 | /* Cached RDB instance */ 80 | typedef struct { 81 | rdb_database_t *db; /* Underlying RDB database */ 82 | rdb_cache_system_t *cache_system; /* Multi-level cache system */ 83 | rdb_persistence_manager_t *persistence_manager; /* Persistence manager */ 84 | cached_rdb_config_t config; /* Configuration */ 85 | cached_rdb_stats_t stats; /* Statistics */ 86 | 87 | /* Query cache for frequently executed queries */ 88 | fi_map *query_cache; /* Map of query_hash -> result */ 89 | 90 | /* Thread safety */ 91 | pthread_rwlock_t rwlock; /* Read-write lock for cached RDB operations */ 92 | pthread_mutex_t stats_mutex; /* Mutex for statistics updates */ 93 | pthread_mutex_t config_mutex; /* Mutex for configuration changes */ 94 | 95 | /* Auto-tuning */ 96 | bool auto_tuning_enabled; /* Whether auto-tuning is enabled */ 97 | time_t last_tune_time; /* Last auto-tuning time */ 98 | pthread_t tuning_thread; /* Auto-tuning thread */ 99 | bool tuning_thread_running; /* Whether tuning thread is running */ 100 | } cached_rdb_t; 101 | 102 | /* Cached RDB operations */ 103 | cached_rdb_t* cached_rdb_create(const char *name, const cached_rdb_config_t *config); 104 | void cached_rdb_destroy(cached_rdb_t *cached_rdb); 105 | int cached_rdb_init(cached_rdb_t *cached_rdb); 106 | int cached_rdb_shutdown(cached_rdb_t *cached_rdb); 107 | 108 | /* Database operations */ 109 | int cached_rdb_open(cached_rdb_t *cached_rdb); 110 | int cached_rdb_close(cached_rdb_t *cached_rdb); 111 | bool cached_rdb_is_open(cached_rdb_t *cached_rdb); 112 | 113 | /* Table operations */ 114 | int cached_rdb_create_table(cached_rdb_t *cached_rdb, const char *table_name, fi_array *columns); 115 | int cached_rdb_drop_table(cached_rdb_t *cached_rdb, const char *table_name); 116 | rdb_table_t* cached_rdb_get_table(cached_rdb_t *cached_rdb, const char *table_name); 117 | bool cached_rdb_table_exists(cached_rdb_t *cached_rdb, const char *table_name); 118 | 119 | /* Row operations */ 120 | int cached_rdb_insert_row(cached_rdb_t *cached_rdb, const char *table_name, fi_array *values); 121 | int cached_rdb_update_rows(cached_rdb_t *cached_rdb, const char *table_name, fi_array *set_columns, 122 | fi_array *set_values, fi_array *where_conditions); 123 | int cached_rdb_delete_rows(cached_rdb_t *cached_rdb, const char *table_name, fi_array *where_conditions); 124 | fi_array* cached_rdb_select_rows(cached_rdb_t *cached_rdb, const char *table_name, fi_array *columns, 125 | fi_array *where_conditions); 126 | 127 | /* Index operations */ 128 | int cached_rdb_create_index(cached_rdb_t *cached_rdb, const char *table_name, const char *index_name, 129 | const char *column_name); 130 | int cached_rdb_drop_index(cached_rdb_t *cached_rdb, const char *table_name, const char *index_name); 131 | 132 | /* Query operations with caching */ 133 | fi_array* cached_rdb_select_rows_cached(cached_rdb_t *cached_rdb, const char *table_name, 134 | fi_array *columns, fi_array *where_conditions); 135 | int cached_rdb_execute_query(cached_rdb_t *cached_rdb, const char *sql, fi_array **result); 136 | 137 | /* Transaction operations */ 138 | int cached_rdb_begin_transaction(cached_rdb_t *cached_rdb, rdb_isolation_level_t isolation); 139 | int cached_rdb_commit_transaction(cached_rdb_t *cached_rdb); 140 | int cached_rdb_rollback_transaction(cached_rdb_t *cached_rdb); 141 | bool cached_rdb_is_in_transaction(cached_rdb_t *cached_rdb); 142 | 143 | /* Cache operations */ 144 | int cached_rdb_cache_put(cached_rdb_t *cached_rdb, cached_rdb_key_type_t type, const char *table_name, 145 | uint64_t row_id, const char *index_name, const void *data, size_t data_size); 146 | int cached_rdb_cache_get(cached_rdb_t *cached_rdb, cached_rdb_key_type_t type, const char *table_name, 147 | uint64_t row_id, const char *index_name, void **data, size_t *data_size); 148 | int cached_rdb_cache_remove(cached_rdb_t *cached_rdb, cached_rdb_key_type_t type, const char *table_name, 149 | uint64_t row_id, const char *index_name); 150 | int cached_rdb_cache_clear(cached_rdb_t *cached_rdb); 151 | 152 | /* Persistence operations */ 153 | int cached_rdb_save(cached_rdb_t *cached_rdb); 154 | int cached_rdb_load(cached_rdb_t *cached_rdb); 155 | int cached_rdb_checkpoint(cached_rdb_t *cached_rdb); 156 | 157 | /* Configuration operations */ 158 | int cached_rdb_set_cache_levels(cached_rdb_t *cached_rdb, size_t levels); 159 | int cached_rdb_set_cache_size(cached_rdb_t *cached_rdb, size_t level, size_t size); 160 | int cached_rdb_set_cache_algorithm(cached_rdb_t *cached_rdb, size_t level, rdb_cache_algorithm_t algorithm); 161 | int cached_rdb_set_persistence_mode(cached_rdb_t *cached_rdb, rdb_persistence_mode_t mode); 162 | int cached_rdb_set_auto_tuning(cached_rdb_t *cached_rdb, bool enable, double target_ratio); 163 | 164 | /* Statistics and monitoring */ 165 | void cached_rdb_print_stats(cached_rdb_t *cached_rdb); 166 | void cached_rdb_print_cache_stats(cached_rdb_t *cached_rdb); 167 | void cached_rdb_print_persistence_stats(cached_rdb_t *cached_rdb); 168 | cached_rdb_stats_t* cached_rdb_get_stats(cached_rdb_t *cached_rdb); 169 | 170 | /* Utility functions */ 171 | uint32_t cached_rdb_hash_key(const cached_rdb_key_t *key); 172 | int cached_rdb_compare_keys(const cached_rdb_key_t *key1, const cached_rdb_key_t *key2); 173 | cached_rdb_key_t* cached_rdb_create_key(cached_rdb_key_type_t type, const char *table_name, 174 | uint64_t row_id, const char *index_name, uint32_t query_hash); 175 | void cached_rdb_free_key(cached_rdb_key_t *key); 176 | 177 | /* Thread-safe operations */ 178 | int cached_rdb_insert_row_thread_safe(cached_rdb_t *cached_rdb, const char *table_name, fi_array *values); 179 | int cached_rdb_update_rows_thread_safe(cached_rdb_t *cached_rdb, const char *table_name, fi_array *set_columns, 180 | fi_array *set_values, fi_array *where_conditions); 181 | int cached_rdb_delete_rows_thread_safe(cached_rdb_t *cached_rdb, const char *table_name, fi_array *where_conditions); 182 | fi_array* cached_rdb_select_rows_thread_safe(cached_rdb_t *cached_rdb, const char *table_name, fi_array *columns, 183 | fi_array *where_conditions); 184 | 185 | /* Auto-tuning */ 186 | int cached_rdb_start_auto_tuning(cached_rdb_t *cached_rdb); 187 | int cached_rdb_stop_auto_tuning(cached_rdb_t *cached_rdb); 188 | void* cached_rdb_auto_tuning_thread(void *arg); 189 | 190 | /* Default configuration */ 191 | cached_rdb_config_t* cached_rdb_get_default_config(void); 192 | void cached_rdb_free_config(cached_rdb_config_t *config); 193 | 194 | /* Performance optimization */ 195 | int cached_rdb_warmup_cache(cached_rdb_t *cached_rdb, const char *warmup_queries_file); 196 | int cached_rdb_preload_table(cached_rdb_t *cached_rdb, const char *table_name); 197 | int cached_rdb_optimize_cache(cached_rdb_t *cached_rdb); 198 | 199 | #endif //__CACHED_RDB_H__ 200 | -------------------------------------------------------------------------------- /examples/rdb/cache_system.h: -------------------------------------------------------------------------------- 1 | #ifndef __CACHE_SYSTEM_H__ 2 | #define __CACHE_SYSTEM_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | /* Use mutex for thread safety (rwlock compatibility) */ 16 | #define pthread_rwlock_t pthread_mutex_t 17 | #define pthread_rwlock_init(lock, attr) pthread_mutex_init(lock, attr) 18 | #define pthread_rwlock_destroy(lock) pthread_mutex_destroy(lock) 19 | #define pthread_rwlock_rdlock(lock) pthread_mutex_lock(lock) 20 | #define pthread_rwlock_wrlock(lock) pthread_mutex_lock(lock) 21 | #define pthread_rwlock_unlock(lock) pthread_mutex_unlock(lock) 22 | 23 | /* Include FI data structures */ 24 | #include "../../src/include/fi.h" 25 | #include "../../src/include/fi_map.h" 26 | #include "../../src/include/fi_btree.h" 27 | 28 | /* Cache configuration */ 29 | #define RDB_CACHE_MAX_LEVELS 8 30 | #define RDB_CACHE_DEFAULT_LEVELS 2 31 | #define RDB_CACHE_DEFAULT_SIZE 1024 * 1024 /* 1MB default cache size */ 32 | #define RDB_CACHE_MAX_SIZE 1024 * 1024 * 1024 /* 1GB max cache size */ 33 | #define RDB_CACHE_PAGE_SIZE 4096 /* 4KB page size */ 34 | #define RDB_CACHE_WRITE_BUFFER_SIZE 64 * 1024 /* 64KB write buffer */ 35 | 36 | /* Cache algorithm types */ 37 | typedef enum { 38 | RDB_CACHE_LRU = 1, /* Least Recently Used */ 39 | RDB_CACHE_LFU, /* Least Frequently Used */ 40 | RDB_CACHE_ARC, /* Adaptive Replacement Cache */ 41 | RDB_CACHE_W_TINY_LFU, /* W-TinyLFU (2025 best practice) */ 42 | RDB_CACHE_AURA /* AURA algorithm for stability */ 43 | } rdb_cache_algorithm_t; 44 | 45 | /* Cache level configuration */ 46 | typedef struct { 47 | size_t level; /* Cache level (0 = fastest, N = slowest) */ 48 | size_t max_size; /* Maximum size in bytes */ 49 | size_t max_entries; /* Maximum number of entries */ 50 | rdb_cache_algorithm_t algorithm; /* Eviction algorithm */ 51 | bool is_memory; /* true for memory, false for disk */ 52 | double hit_ratio_threshold; /* Hit ratio threshold for auto-tuning */ 53 | size_t write_buffer_size; /* Write buffer size for disk caches */ 54 | } rdb_cache_level_config_t; 55 | 56 | /* Cache entry structure */ 57 | typedef struct rdb_cache_entry { 58 | void *key; /* Cache key */ 59 | void *value; /* Cached value */ 60 | size_t key_size; /* Key size in bytes */ 61 | size_t value_size; /* Value size in bytes */ 62 | size_t level; /* Cache level where entry resides */ 63 | 64 | /* Access tracking for different algorithms */ 65 | time_t last_access_time; /* Last access time */ 66 | uint32_t access_count; /* Access frequency counter */ 67 | uint32_t access_frequency; /* Normalized access frequency */ 68 | double access_score; /* Composite access score */ 69 | 70 | /* Cache metadata */ 71 | bool is_dirty; /* Whether entry needs to be written to disk */ 72 | bool is_pinned; /* Whether entry is pinned (cannot be evicted) */ 73 | uint32_t reference_count; /* Reference counting */ 74 | 75 | /* Linked list pointers for LRU/LFU */ 76 | struct rdb_cache_entry *prev; 77 | struct rdb_cache_entry *next; 78 | 79 | /* Hash table collision chain */ 80 | struct rdb_cache_entry *hash_next; 81 | } rdb_cache_entry_t; 82 | 83 | /* Cache statistics */ 84 | typedef struct { 85 | uint64_t total_requests; /* Total cache requests */ 86 | uint64_t hits; /* Cache hits */ 87 | uint64_t misses; /* Cache misses */ 88 | uint64_t evictions; /* Number of evictions */ 89 | uint64_t writes; /* Number of writes */ 90 | uint64_t reads; /* Number of reads */ 91 | double hit_ratio; /* Current hit ratio */ 92 | size_t current_size; /* Current cache size in bytes */ 93 | size_t current_entries; /* Current number of entries */ 94 | time_t last_reset; /* Last statistics reset time */ 95 | } rdb_cache_stats_t; 96 | 97 | /* Cache level structure */ 98 | typedef struct { 99 | rdb_cache_level_config_t config; 100 | rdb_cache_stats_t stats; 101 | 102 | /* Storage structures */ 103 | fi_map *entries; /* Hash map of cache entries */ 104 | fi_array *entry_list; /* Ordered list for eviction algorithms */ 105 | 106 | /* Algorithm-specific structures */ 107 | union { 108 | struct { 109 | rdb_cache_entry_t *head; /* LRU head */ 110 | rdb_cache_entry_t *tail; /* LRU tail */ 111 | } lru; 112 | struct { 113 | fi_btree *frequency_tree; /* B-tree sorted by frequency */ 114 | } lfu; 115 | struct { 116 | fi_map *t1; /* T1: recently accessed items */ 117 | fi_map *t2; /* T2: frequently accessed items */ 118 | fi_map *b1; /* B1: recently evicted from T1 */ 119 | fi_map *b2; /* B2: recently evicted from T2 */ 120 | size_t p; /* Target size of T1 */ 121 | } arc; 122 | struct { 123 | fi_map *window_cache; /* Window cache for recent items */ 124 | fi_map *main_cache; /* Main cache with TinyLFU */ 125 | fi_btree *frequency_sketch; /* Count-min sketch for frequency */ 126 | } wtinylfu; 127 | struct { 128 | fi_map *stability_map; /* H-factor (stability) tracking */ 129 | fi_map *value_map; /* V-factor (value) tracking */ 130 | double alpha; /* Exploration vs exploitation balance */ 131 | } aura; 132 | } algorithm_data; 133 | 134 | /* Persistence */ 135 | char *disk_path; /* Disk storage path */ 136 | int disk_fd; /* File descriptor for disk storage */ 137 | void *mmap_ptr; /* Memory-mapped file pointer */ 138 | size_t mmap_size; /* Size of memory-mapped region */ 139 | 140 | /* Thread safety */ 141 | pthread_rwlock_t rwlock; /* Read-write lock for cache level */ 142 | pthread_mutex_t stats_mutex; /* Mutex for statistics */ 143 | 144 | /* Write buffer for disk caches */ 145 | void *write_buffer; 146 | size_t write_buffer_pos; 147 | pthread_mutex_t write_buffer_mutex; 148 | } rdb_cache_level_t; 149 | 150 | /* Multi-level cache system */ 151 | typedef struct { 152 | char *name; /* Cache system name */ 153 | size_t num_levels; /* Number of cache levels */ 154 | rdb_cache_level_t **levels; /* Array of cache level pointers */ 155 | 156 | /* Configuration */ 157 | bool auto_tune; /* Whether to auto-tune cache parameters */ 158 | double target_hit_ratio; /* Target hit ratio for auto-tuning */ 159 | size_t tune_interval; /* Auto-tuning interval in seconds */ 160 | 161 | /* Global statistics */ 162 | rdb_cache_stats_t global_stats; 163 | time_t last_tune_time; /* Last auto-tuning time */ 164 | 165 | /* Thread safety */ 166 | pthread_rwlock_t global_rwlock; /* Global read-write lock */ 167 | pthread_mutex_t tune_mutex; /* Mutex for auto-tuning */ 168 | 169 | /* Persistence configuration */ 170 | char *persistence_dir; /* Directory for persistent storage */ 171 | bool enable_persistence; /* Whether persistence is enabled */ 172 | size_t checkpoint_interval; /* Checkpoint interval in seconds */ 173 | time_t last_checkpoint; /* Last checkpoint time */ 174 | } rdb_cache_system_t; 175 | 176 | /* Cache system operations */ 177 | rdb_cache_system_t* rdb_cache_system_create(const char *name, size_t num_levels, 178 | const rdb_cache_level_config_t *configs); 179 | void rdb_cache_system_destroy(rdb_cache_system_t *cache_system); 180 | 181 | /* Cache level operations */ 182 | rdb_cache_level_t* rdb_cache_level_create(const rdb_cache_level_config_t *config); 183 | void rdb_cache_level_destroy(rdb_cache_level_t *level); 184 | 185 | /* Cache operations */ 186 | int rdb_cache_get(rdb_cache_system_t *cache_system, const void *key, size_t key_size, 187 | void **value, size_t *value_size); 188 | int rdb_cache_put(rdb_cache_system_t *cache_system, const void *key, size_t key_size, 189 | const void *value, size_t value_size, bool pin_entry); 190 | int rdb_cache_remove(rdb_cache_system_t *cache_system, const void *key, size_t key_size); 191 | int rdb_cache_clear(rdb_cache_system_t *cache_system); 192 | 193 | /* Cache management */ 194 | int rdb_cache_resize(rdb_cache_system_t *cache_system, size_t level, size_t new_size); 195 | int rdb_cache_tune(rdb_cache_system_t *cache_system); 196 | int rdb_cache_checkpoint(rdb_cache_system_t *cache_system); 197 | int rdb_cache_warmup(rdb_cache_system_t *cache_system, const char *warmup_data_path); 198 | 199 | /* Statistics and monitoring */ 200 | void rdb_cache_print_stats(rdb_cache_system_t *cache_system); 201 | void rdb_cache_print_level_stats(rdb_cache_system_t *cache_system, size_t level); 202 | rdb_cache_stats_t* rdb_cache_get_stats(rdb_cache_system_t *cache_system); 203 | rdb_cache_stats_t* rdb_cache_get_level_stats(rdb_cache_system_t *cache_system, size_t level); 204 | 205 | /* Configuration */ 206 | int rdb_cache_set_algorithm(rdb_cache_system_t *cache_system, size_t level, 207 | rdb_cache_algorithm_t algorithm); 208 | int rdb_cache_set_size(rdb_cache_system_t *cache_system, size_t level, size_t new_size); 209 | int rdb_cache_set_auto_tune(rdb_cache_system_t *cache_system, bool enable, double target_ratio); 210 | 211 | /* Persistence operations */ 212 | int rdb_cache_enable_persistence(rdb_cache_system_t *cache_system, const char *persistence_dir); 213 | int rdb_cache_save_to_disk(rdb_cache_system_t *cache_system); 214 | int rdb_cache_load_from_disk(rdb_cache_system_t *cache_system); 215 | 216 | /* Utility functions */ 217 | uint32_t rdb_cache_hash_key(const void *key, size_t key_size); 218 | int rdb_cache_compare_keys(const void *key1, const void *key2); 219 | void rdb_cache_entry_free(void *entry); 220 | void rdb_cache_key_free(void *key); 221 | void rdb_cache_value_free(void *value); 222 | 223 | /* Algorithm-specific functions */ 224 | int rdb_cache_lru_evict(rdb_cache_level_t *level); 225 | int rdb_cache_lfu_evict(rdb_cache_level_t *level); 226 | int rdb_cache_arc_evict(rdb_cache_level_t *level); 227 | int rdb_cache_wtiny_lfu_evict(rdb_cache_level_t *level); 228 | int rdb_cache_aura_evict(rdb_cache_level_t *level); 229 | 230 | /* Thread-safe operations */ 231 | int rdb_cache_get_thread_safe(rdb_cache_system_t *cache_system, const void *key, size_t key_size, 232 | void **value, size_t *value_size); 233 | int rdb_cache_put_thread_safe(rdb_cache_system_t *cache_system, const void *key, size_t key_size, 234 | const void *value, size_t value_size, bool pin_entry); 235 | int rdb_cache_remove_thread_safe(rdb_cache_system_t *cache_system, const void *key, size_t key_size); 236 | 237 | #endif //__CACHE_SYSTEM_H__ 238 | -------------------------------------------------------------------------------- /examples/rdb/rdb_demo.c: -------------------------------------------------------------------------------- 1 | #include "rdb.h" 2 | #include 3 | #include 4 | #include 5 | 6 | /* Demo functions */ 7 | void demo_basic_operations(void); 8 | void demo_table_operations(void); 9 | void demo_data_operations(void); 10 | void demo_index_operations(void); 11 | void demo_sql_parser(void); 12 | 13 | /* Helper functions */ 14 | rdb_column_t* create_column(const char *name, rdb_data_type_t type, bool primary_key, bool unique, bool nullable); 15 | void print_separator(const char *title); 16 | 17 | int main() { 18 | printf("=== FI Relational Database Demo ===\n\n"); 19 | 20 | demo_basic_operations(); 21 | demo_table_operations(); 22 | demo_data_operations(); 23 | demo_index_operations(); 24 | demo_sql_parser(); 25 | 26 | printf("\n=== Demo completed successfully! ===\n"); 27 | return 0; 28 | } 29 | 30 | void demo_basic_operations(void) { 31 | print_separator("Basic Database Operations"); 32 | 33 | /* Create database */ 34 | rdb_database_t *db = rdb_create_database("test_db"); 35 | if (!db) { 36 | printf("Error: Failed to create database\n"); 37 | return; 38 | } 39 | 40 | printf("Database '%s' created successfully\n", db->name); 41 | 42 | /* Open database */ 43 | if (rdb_open_database(db) == 0) { 44 | printf("Database opened successfully\n"); 45 | } 46 | 47 | /* Print database info */ 48 | rdb_print_database_info(db); 49 | 50 | /* Close and destroy database */ 51 | rdb_close_database(db); 52 | rdb_destroy_database(db); 53 | 54 | printf("Database operations completed\n"); 55 | } 56 | 57 | void demo_table_operations(void) { 58 | print_separator("Table Operations"); 59 | 60 | rdb_database_t *db = rdb_create_database("table_demo"); 61 | if (!db) return; 62 | 63 | rdb_open_database(db); 64 | 65 | /* Create columns for a student table */ 66 | fi_array *columns = fi_array_create(5, sizeof(rdb_column_t*)); 67 | 68 | rdb_column_t *col1 = create_column("id", RDB_TYPE_INT, true, true, false); 69 | rdb_column_t *col2 = create_column("name", RDB_TYPE_VARCHAR, false, false, false); 70 | rdb_column_t *col3 = create_column("age", RDB_TYPE_INT, false, false, true); 71 | rdb_column_t *col4 = create_column("gpa", RDB_TYPE_FLOAT, false, false, true); 72 | rdb_column_t *col5 = create_column("is_active", RDB_TYPE_BOOLEAN, false, false, false); 73 | 74 | fi_array_push(columns, &col1); 75 | fi_array_push(columns, &col2); 76 | fi_array_push(columns, &col3); 77 | fi_array_push(columns, &col4); 78 | fi_array_push(columns, &col5); 79 | 80 | /* Create table */ 81 | if (rdb_create_table(db, "students", columns) == 0) { 82 | printf("Table 'students' created successfully\n"); 83 | } 84 | 85 | /* Print table info */ 86 | rdb_print_table_info(db, "students"); 87 | 88 | /* Create another table */ 89 | fi_array *course_columns = fi_array_create(3, sizeof(rdb_column_t*)); 90 | rdb_column_t *c_col1 = create_column("course_id", RDB_TYPE_INT, true, true, false); 91 | rdb_column_t *c_col2 = create_column("title", RDB_TYPE_VARCHAR, false, false, false); 92 | rdb_column_t *c_col3 = create_column("credits", RDB_TYPE_INT, false, false, false); 93 | 94 | fi_array_push(course_columns, &c_col1); 95 | fi_array_push(course_columns, &c_col2); 96 | fi_array_push(course_columns, &c_col3); 97 | 98 | if (rdb_create_table(db, "courses", course_columns) == 0) { 99 | printf("Table 'courses' created successfully\n"); 100 | } 101 | 102 | /* Print database info */ 103 | rdb_print_database_info(db); 104 | 105 | /* Clean up */ 106 | fi_array_destroy(columns); 107 | fi_array_destroy(course_columns); 108 | rdb_destroy_database(db); 109 | 110 | printf("Table operations completed\n"); 111 | } 112 | 113 | void demo_data_operations(void) { 114 | print_separator("Data Operations"); 115 | 116 | rdb_database_t *db = rdb_create_database("data_demo"); 117 | if (!db) return; 118 | 119 | rdb_open_database(db); 120 | 121 | /* Create students table */ 122 | fi_array *columns = fi_array_create(5, sizeof(rdb_column_t*)); 123 | rdb_column_t *d_col1 = create_column("id", RDB_TYPE_INT, true, true, false); 124 | rdb_column_t *d_col2 = create_column("name", RDB_TYPE_VARCHAR, false, false, false); 125 | rdb_column_t *d_col3 = create_column("age", RDB_TYPE_INT, false, false, true); 126 | rdb_column_t *d_col4 = create_column("gpa", RDB_TYPE_FLOAT, false, false, true); 127 | rdb_column_t *d_col5 = create_column("is_active", RDB_TYPE_BOOLEAN, false, false, false); 128 | 129 | fi_array_push(columns, &d_col1); 130 | fi_array_push(columns, &d_col2); 131 | fi_array_push(columns, &d_col3); 132 | fi_array_push(columns, &d_col4); 133 | fi_array_push(columns, &d_col5); 134 | 135 | rdb_create_table(db, "students", columns); 136 | 137 | /* Insert sample data */ 138 | fi_array *values1 = fi_array_create(5, sizeof(rdb_value_t*)); 139 | rdb_value_t *val1_1 = rdb_create_int_value(1); 140 | rdb_value_t *val1_2 = rdb_create_string_value("Alice Johnson"); 141 | rdb_value_t *val1_3 = rdb_create_int_value(20); 142 | rdb_value_t *val1_4 = rdb_create_float_value(3.8); 143 | rdb_value_t *val1_5 = rdb_create_bool_value(true); 144 | 145 | fi_array_push(values1, &val1_1); 146 | fi_array_push(values1, &val1_2); 147 | fi_array_push(values1, &val1_3); 148 | fi_array_push(values1, &val1_4); 149 | fi_array_push(values1, &val1_5); 150 | 151 | rdb_insert_row(db, "students", values1); 152 | 153 | /* Insert more data */ 154 | fi_array *values2 = fi_array_create(5, sizeof(rdb_value_t*)); 155 | rdb_value_t *val2_1 = rdb_create_int_value(2); 156 | rdb_value_t *val2_2 = rdb_create_string_value("Bob Smith"); 157 | rdb_value_t *val2_3 = rdb_create_int_value(22); 158 | rdb_value_t *val2_4 = rdb_create_float_value(3.5); 159 | rdb_value_t *val2_5 = rdb_create_bool_value(true); 160 | 161 | fi_array_push(values2, &val2_1); 162 | fi_array_push(values2, &val2_2); 163 | fi_array_push(values2, &val2_3); 164 | fi_array_push(values2, &val2_4); 165 | fi_array_push(values2, &val2_5); 166 | 167 | rdb_insert_row(db, "students", values2); 168 | 169 | fi_array *values3 = fi_array_create(5, sizeof(rdb_value_t*)); 170 | rdb_value_t *val3_1 = rdb_create_int_value(3); 171 | rdb_value_t *val3_2 = rdb_create_string_value("Carol Davis"); 172 | rdb_value_t *val3_3 = rdb_create_int_value(19); 173 | rdb_value_t *val3_4 = rdb_create_float_value(3.9); 174 | rdb_value_t *val3_5 = rdb_create_bool_value(false); 175 | 176 | fi_array_push(values3, &val3_1); 177 | fi_array_push(values3, &val3_2); 178 | fi_array_push(values3, &val3_3); 179 | fi_array_push(values3, &val3_4); 180 | fi_array_push(values3, &val3_5); 181 | 182 | rdb_insert_row(db, "students", values3); 183 | 184 | /* Print table data */ 185 | printf("Sample data inserted:\n"); 186 | rdb_print_table_data(db, "students", 10); 187 | 188 | /* Clean up */ 189 | fi_array_destroy(columns); 190 | fi_array_destroy(values1); 191 | fi_array_destroy(values2); 192 | fi_array_destroy(values3); 193 | rdb_destroy_database(db); 194 | 195 | printf("Data operations completed\n"); 196 | } 197 | 198 | void demo_index_operations(void) { 199 | print_separator("Index Operations"); 200 | 201 | rdb_database_t *db = rdb_create_database("index_demo"); 202 | if (!db) return; 203 | 204 | rdb_open_database(db); 205 | 206 | /* Create table with some data */ 207 | fi_array *columns = fi_array_create(3, sizeof(rdb_column_t*)); 208 | rdb_column_t *i_col1 = create_column("id", RDB_TYPE_INT, true, true, false); 209 | rdb_column_t *i_col2 = create_column("name", RDB_TYPE_VARCHAR, false, false, false); 210 | rdb_column_t *i_col3 = create_column("score", RDB_TYPE_INT, false, false, false); 211 | 212 | fi_array_push(columns, &i_col1); 213 | fi_array_push(columns, &i_col2); 214 | fi_array_push(columns, &i_col3); 215 | 216 | rdb_create_table(db, "scores", columns); 217 | 218 | /* Insert sample data */ 219 | for (int i = 1; i <= 10; i++) { 220 | fi_array *values = fi_array_create(3, sizeof(rdb_value_t*)); 221 | rdb_value_t *v1 = rdb_create_int_value(i); 222 | 223 | char name[32]; 224 | snprintf(name, sizeof(name), "Student%d", i); 225 | rdb_value_t *v2 = rdb_create_string_value(name); 226 | 227 | rdb_value_t *v3 = rdb_create_int_value(rand() % 100); 228 | 229 | fi_array_push(values, &v1); 230 | fi_array_push(values, &v2); 231 | fi_array_push(values, &v3); 232 | 233 | rdb_insert_row(db, "scores", values); 234 | fi_array_destroy(values); 235 | } 236 | 237 | printf("Table 'scores' created with 10 rows\n"); 238 | 239 | /* Create index on name column */ 240 | if (rdb_create_index(db, "scores", "idx_name", "name") == 0) { 241 | printf("Index 'idx_name' created on 'name' column\n"); 242 | } 243 | 244 | /* Create index on score column */ 245 | if (rdb_create_index(db, "scores", "idx_score", "score") == 0) { 246 | printf("Index 'idx_score' created on 'score' column\n"); 247 | } 248 | 249 | /* Print table info to show indexes */ 250 | rdb_print_table_info(db, "scores"); 251 | 252 | /* Clean up */ 253 | fi_array_destroy(columns); 254 | rdb_destroy_database(db); 255 | 256 | printf("Index operations completed\n"); 257 | } 258 | 259 | void demo_sql_parser(void) { 260 | print_separator("SQL Parser Demo"); 261 | 262 | printf("SQL Parser functionality is currently under development.\n"); 263 | printf("This demo shows the basic database operations without SQL parsing.\n"); 264 | 265 | /* Simple demonstration of value creation and manipulation */ 266 | printf("\nDemonstrating value creation:\n"); 267 | 268 | rdb_value_t *int_val = rdb_create_int_value(42); 269 | rdb_value_t *float_val = rdb_create_float_value(3.14159); 270 | rdb_value_t *string_val = rdb_create_string_value("Hello World"); 271 | rdb_value_t *bool_val = rdb_create_bool_value(true); 272 | 273 | if (int_val) { 274 | char *str = rdb_value_to_string(int_val); 275 | printf("Integer value: %s\n", str); 276 | free(str); 277 | rdb_value_free(int_val); 278 | } 279 | 280 | if (float_val) { 281 | char *str = rdb_value_to_string(float_val); 282 | printf("Float value: %s\n", str); 283 | free(str); 284 | rdb_value_free(float_val); 285 | } 286 | 287 | if (string_val) { 288 | char *str = rdb_value_to_string(string_val); 289 | printf("String value: %s\n", str); 290 | free(str); 291 | rdb_value_free(string_val); 292 | } 293 | 294 | if (bool_val) { 295 | char *str = rdb_value_to_string(bool_val); 296 | printf("Boolean value: %s\n", str); 297 | free(str); 298 | rdb_value_free(bool_val); 299 | } 300 | 301 | printf("SQL parser demo completed\n"); 302 | } 303 | 304 | /* Helper functions */ 305 | rdb_column_t* create_column(const char *name, rdb_data_type_t type, bool primary_key, bool unique, bool nullable) { 306 | rdb_column_t *column = malloc(sizeof(rdb_column_t)); 307 | if (!column) return NULL; 308 | 309 | strncpy(column->name, name, sizeof(column->name) - 1); 310 | column->name[sizeof(column->name) - 1] = '\0'; 311 | column->type = type; 312 | column->max_length = (type == RDB_TYPE_VARCHAR) ? 255 : 0; 313 | column->primary_key = primary_key; 314 | column->unique = unique; 315 | column->nullable = nullable; 316 | column->default_value[0] = '\0'; 317 | 318 | return column; 319 | } 320 | 321 | void print_separator(const char *title) { 322 | printf("\n"); 323 | printf("========================================\n"); 324 | printf(" %s\n", title); 325 | printf("========================================\n"); 326 | } 327 | -------------------------------------------------------------------------------- /examples/rdb/transaction_demo.c: -------------------------------------------------------------------------------- 1 | #include "rdb.h" 2 | #include "sql_parser.h" 3 | #include 4 | #include 5 | #include 6 | 7 | /* Demo functions */ 8 | void demo_basic_transaction(void); 9 | void demo_transaction_rollback(void); 10 | void demo_transaction_isolation(void); 11 | void demo_sql_transaction_commands(void); 12 | 13 | /* Helper functions */ 14 | rdb_column_t* create_column(const char *name, rdb_data_type_t type, bool primary_key, bool unique, bool nullable); 15 | void print_separator(const char *title); 16 | void execute_sql(rdb_database_t *db, const char *sql); 17 | 18 | int main() { 19 | printf("=== FI Relational Database Transaction Demo ===\n\n"); 20 | 21 | demo_basic_transaction(); 22 | demo_transaction_rollback(); 23 | demo_transaction_isolation(); 24 | demo_sql_transaction_commands(); 25 | 26 | printf("\n=== Transaction demo completed successfully! ===\n"); 27 | return 0; 28 | } 29 | 30 | void demo_basic_transaction(void) { 31 | print_separator("Basic Transaction Demo"); 32 | 33 | rdb_database_t *db = rdb_create_database("transaction_demo"); 34 | if (!db) { 35 | printf("Error: Failed to create database\n"); 36 | return; 37 | } 38 | 39 | rdb_open_database(db); 40 | 41 | /* Create a simple table */ 42 | fi_array *columns = fi_array_create(3, sizeof(rdb_column_t*)); 43 | rdb_column_t *id = create_column("id", RDB_TYPE_INT, true, true, false); 44 | rdb_column_t *name = create_column("name", RDB_TYPE_VARCHAR, false, false, false); 45 | rdb_column_t *balance = create_column("balance", RDB_TYPE_FLOAT, false, false, false); 46 | 47 | fi_array_push(columns, &id); 48 | fi_array_push(columns, &name); 49 | fi_array_push(columns, &balance); 50 | 51 | if (rdb_create_table(db, "accounts", columns) == 0) { 52 | printf("Table 'accounts' created successfully\n"); 53 | } 54 | 55 | /* Show initial transaction status */ 56 | rdb_print_transaction_status(db); 57 | 58 | /* Begin transaction */ 59 | printf("\n--- Beginning transaction ---\n"); 60 | if (rdb_begin_transaction(db, RDB_ISOLATION_READ_COMMITTED) == 0) { 61 | rdb_print_transaction_status(db); 62 | 63 | /* Insert some data */ 64 | printf("\n--- Inserting data within transaction ---\n"); 65 | fi_array *values1 = fi_array_create(3, sizeof(rdb_value_t*)); 66 | rdb_value_t *v1_id = rdb_create_int_value(1); 67 | rdb_value_t *v1_name = rdb_create_string_value("Alice"); 68 | rdb_value_t *v1_balance = rdb_create_float_value(1000.0); 69 | 70 | fi_array_push(values1, &v1_id); 71 | fi_array_push(values1, &v1_name); 72 | fi_array_push(values1, &v1_balance); 73 | 74 | rdb_insert_row(db, "accounts", values1); 75 | 76 | fi_array *values2 = fi_array_create(3, sizeof(rdb_value_t*)); 77 | rdb_value_t *v2_id = rdb_create_int_value(2); 78 | rdb_value_t *v2_name = rdb_create_string_value("Bob"); 79 | rdb_value_t *v2_balance = rdb_create_float_value(500.0); 80 | 81 | fi_array_push(values2, &v2_id); 82 | fi_array_push(values2, &v2_name); 83 | fi_array_push(values2, &v2_balance); 84 | 85 | rdb_insert_row(db, "accounts", values2); 86 | 87 | /* Show data */ 88 | rdb_print_table_data(db, "accounts", 10); 89 | 90 | /* Commit transaction */ 91 | printf("\n--- Committing transaction ---\n"); 92 | rdb_commit_transaction(db); 93 | rdb_print_transaction_status(db); 94 | 95 | /* Show final data */ 96 | printf("\nFinal data after commit:\n"); 97 | rdb_print_table_data(db, "accounts", 10); 98 | 99 | /* Clean up */ 100 | fi_array_destroy(values1); 101 | fi_array_destroy(values2); 102 | } 103 | 104 | /* Clean up */ 105 | fi_array_destroy(columns); 106 | rdb_destroy_database(db); 107 | 108 | printf("Basic transaction demo completed\n"); 109 | } 110 | 111 | void demo_transaction_rollback(void) { 112 | print_separator("Transaction Rollback Demo"); 113 | 114 | rdb_database_t *db = rdb_create_database("rollback_demo"); 115 | if (!db) { 116 | printf("Error: Failed to create database\n"); 117 | return; 118 | } 119 | 120 | rdb_open_database(db); 121 | 122 | /* Create a simple table */ 123 | fi_array *columns = fi_array_create(2, sizeof(rdb_column_t*)); 124 | rdb_column_t *id = create_column("id", RDB_TYPE_INT, true, true, false); 125 | rdb_column_t *name = create_column("name", RDB_TYPE_VARCHAR, false, false, false); 126 | 127 | fi_array_push(columns, &id); 128 | fi_array_push(columns, &name); 129 | 130 | if (rdb_create_table(db, "users", columns) == 0) { 131 | printf("Table 'users' created successfully\n"); 132 | } 133 | 134 | /* Insert initial data */ 135 | fi_array *initial_values = fi_array_create(2, sizeof(rdb_value_t*)); 136 | rdb_value_t *iv_id = rdb_create_int_value(1); 137 | rdb_value_t *iv_name = rdb_create_string_value("Initial User"); 138 | 139 | fi_array_push(initial_values, &iv_id); 140 | fi_array_push(initial_values, &iv_name); 141 | 142 | rdb_insert_row(db, "users", initial_values); 143 | 144 | printf("Initial data:\n"); 145 | rdb_print_table_data(db, "users", 10); 146 | 147 | /* Begin transaction and make changes */ 148 | printf("\n--- Beginning transaction with changes ---\n"); 149 | if (rdb_begin_transaction(db, RDB_ISOLATION_READ_COMMITTED) == 0) { 150 | 151 | /* Insert new data */ 152 | fi_array *values = fi_array_create(2, sizeof(rdb_value_t*)); 153 | rdb_value_t *v_id = rdb_create_int_value(2); 154 | rdb_value_t *v_name = rdb_create_string_value("New User"); 155 | 156 | fi_array_push(values, &v_id); 157 | fi_array_push(values, &v_name); 158 | 159 | rdb_insert_row(db, "users", values); 160 | 161 | /* Update existing data */ 162 | fi_array *set_columns = fi_array_create(1, sizeof(char*)); 163 | fi_array *set_values = fi_array_create(1, sizeof(rdb_value_t*)); 164 | fi_array *where_conditions = fi_array_create(1, sizeof(char*)); 165 | 166 | const char *col_name = "name"; 167 | rdb_value_t *new_name = rdb_create_string_value("Updated User"); 168 | const char *where_cond = "id = 1"; 169 | 170 | fi_array_push(set_columns, &col_name); 171 | fi_array_push(set_values, &new_name); 172 | fi_array_push(where_conditions, &where_cond); 173 | 174 | rdb_update_rows(db, "users", set_columns, set_values, where_conditions); 175 | 176 | printf("Data within transaction (before rollback):\n"); 177 | rdb_print_table_data(db, "users", 10); 178 | 179 | /* Rollback transaction */ 180 | printf("\n--- Rolling back transaction ---\n"); 181 | rdb_rollback_transaction(db); 182 | 183 | printf("Data after rollback:\n"); 184 | rdb_print_table_data(db, "users", 10); 185 | 186 | /* Clean up */ 187 | fi_array_destroy(values); 188 | fi_array_destroy(set_columns); 189 | fi_array_destroy(set_values); 190 | fi_array_destroy(where_conditions); 191 | } 192 | 193 | /* Clean up */ 194 | fi_array_destroy(columns); 195 | fi_array_destroy(initial_values); 196 | rdb_destroy_database(db); 197 | 198 | printf("Transaction rollback demo completed\n"); 199 | } 200 | 201 | void demo_transaction_isolation(void) { 202 | print_separator("Transaction Isolation Levels Demo"); 203 | 204 | rdb_database_t *db = rdb_create_database("isolation_demo"); 205 | if (!db) { 206 | printf("Error: Failed to create database\n"); 207 | return; 208 | } 209 | 210 | rdb_open_database(db); 211 | 212 | /* Test different isolation levels */ 213 | printf("Testing different isolation levels:\n\n"); 214 | 215 | /* Set default isolation level */ 216 | rdb_set_isolation_level(db, RDB_ISOLATION_READ_COMMITTED); 217 | 218 | /* Begin transaction with specific isolation level */ 219 | printf("--- Beginning transaction with READ COMMITTED isolation ---\n"); 220 | if (rdb_begin_transaction(db, RDB_ISOLATION_READ_COMMITTED) == 0) { 221 | rdb_print_transaction_status(db); 222 | rdb_commit_transaction(db); 223 | } 224 | 225 | printf("\n--- Beginning transaction with SERIALIZABLE isolation ---\n"); 226 | if (rdb_begin_transaction(db, RDB_ISOLATION_SERIALIZABLE) == 0) { 227 | rdb_print_transaction_status(db); 228 | rdb_commit_transaction(db); 229 | } 230 | 231 | /* Test autocommit */ 232 | printf("\n--- Testing autocommit functionality ---\n"); 233 | rdb_set_autocommit(db, false); 234 | rdb_print_transaction_status(db); 235 | 236 | rdb_set_autocommit(db, true); 237 | rdb_print_transaction_status(db); 238 | 239 | /* Clean up */ 240 | rdb_destroy_database(db); 241 | 242 | printf("Transaction isolation demo completed\n"); 243 | } 244 | 245 | void demo_sql_transaction_commands(void) { 246 | print_separator("SQL Transaction Commands Demo"); 247 | 248 | rdb_database_t *db = rdb_create_database("sql_transaction_demo"); 249 | if (!db) { 250 | printf("Error: Failed to create database\n"); 251 | return; 252 | } 253 | 254 | rdb_open_database(db); 255 | 256 | /* Test SQL transaction commands */ 257 | printf("Testing SQL transaction commands:\n\n"); 258 | 259 | /* Test BEGIN TRANSACTION */ 260 | printf("--- Testing BEGIN TRANSACTION ---\n"); 261 | execute_sql(db, "BEGIN TRANSACTION"); 262 | rdb_print_transaction_status(db); 263 | 264 | /* Test COMMIT */ 265 | printf("\n--- Testing COMMIT ---\n"); 266 | execute_sql(db, "COMMIT"); 267 | rdb_print_transaction_status(db); 268 | 269 | /* Test ROLLBACK */ 270 | printf("\n--- Testing ROLLBACK ---\n"); 271 | execute_sql(db, "BEGIN"); 272 | execute_sql(db, "ROLLBACK"); 273 | rdb_print_transaction_status(db); 274 | 275 | /* Clean up */ 276 | rdb_destroy_database(db); 277 | 278 | printf("SQL transaction commands demo completed\n"); 279 | } 280 | 281 | /* Helper function implementations */ 282 | rdb_column_t* create_column(const char *name, rdb_data_type_t type, bool primary_key, bool unique, bool nullable) { 283 | rdb_column_t *column = malloc(sizeof(rdb_column_t)); 284 | if (!column) return NULL; 285 | 286 | strncpy(column->name, name, sizeof(column->name) - 1); 287 | column->name[sizeof(column->name) - 1] = '\0'; 288 | column->type = type; 289 | column->max_length = (type == RDB_TYPE_VARCHAR) ? 255 : 0; 290 | column->primary_key = primary_key; 291 | column->unique = unique; 292 | column->nullable = nullable; 293 | column->default_value[0] = '\0'; 294 | column->is_foreign_key = false; 295 | column->foreign_table[0] = '\0'; 296 | column->foreign_column[0] = '\0'; 297 | 298 | return column; 299 | } 300 | 301 | void print_separator(const char *title) { 302 | printf("\n"); 303 | printf("========================================\n"); 304 | printf(" %s\n", title); 305 | printf("========================================\n"); 306 | } 307 | 308 | void execute_sql(rdb_database_t *db, const char *sql) { 309 | if (!db || !sql) return; 310 | 311 | printf("Executing SQL: %s\n", sql); 312 | 313 | sql_parser_t *parser = sql_parser_create(sql); 314 | if (!parser) { 315 | printf("Error: Failed to create SQL parser\n"); 316 | return; 317 | } 318 | 319 | rdb_statement_t *stmt = sql_parse_statement(parser); 320 | if (!stmt) { 321 | printf("Error: Failed to parse SQL statement: %s\n", sql_parser_get_error(parser)); 322 | sql_parser_destroy(parser); 323 | return; 324 | } 325 | 326 | /* Execute the statement */ 327 | int result = sql_execute_statement(db, stmt); 328 | if (result != 0) { 329 | printf("Error: Failed to execute SQL statement\n"); 330 | } 331 | 332 | /* Clean up */ 333 | sql_statement_free(stmt); 334 | sql_parser_destroy(parser); 335 | } 336 | -------------------------------------------------------------------------------- /examples/rdb/persistence.h: -------------------------------------------------------------------------------- 1 | #ifndef __PERSISTENCE_H__ 2 | #define __PERSISTENCE_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | /* Use mutex for thread safety (rwlock compatibility) */ 16 | #define pthread_rwlock_t pthread_mutex_t 17 | #define pthread_rwlock_init(lock, attr) pthread_mutex_init(lock, attr) 18 | #define pthread_rwlock_destroy(lock) pthread_mutex_destroy(lock) 19 | #define pthread_rwlock_rdlock(lock) pthread_mutex_lock(lock) 20 | #define pthread_rwlock_wrlock(lock) pthread_mutex_lock(lock) 21 | #define pthread_rwlock_unlock(lock) pthread_mutex_unlock(lock) 22 | 23 | /* Include FI data structures */ 24 | #include "../../src/include/fi.h" 25 | #include "../../src/include/fi_map.h" 26 | #include "../../src/include/fi_btree.h" 27 | 28 | /* Include RDB structures */ 29 | #include "rdb.h" 30 | 31 | /* Persistence configuration */ 32 | #define RDB_PERSISTENCE_MAX_PATH_LEN 512 33 | #define RDB_PERSISTENCE_DEFAULT_DIR "./rdb_data" 34 | #define RDB_PERSISTENCE_CHECKPOINT_INTERVAL 3600 /* 1 hour */ 35 | #define RDB_PERSISTENCE_WAL_SIZE 16 * 1024 * 1024 /* 16MB WAL */ 36 | #define RDB_PERSISTENCE_PAGE_SIZE 4096 /* 4KB pages */ 37 | #define RDB_PERSISTENCE_MAX_PAGES 1024 * 1024 /* 4GB max database size */ 38 | 39 | /* Persistence modes */ 40 | typedef enum { 41 | RDB_PERSISTENCE_MEMORY_ONLY = 0, /* No persistence */ 42 | RDB_PERSISTENCE_WAL_ONLY, /* Write-ahead log only */ 43 | RDB_PERSISTENCE_CHECKPOINT_ONLY, /* Periodic checkpoints only */ 44 | RDB_PERSISTENCE_FULL /* WAL + checkpoints */ 45 | } rdb_persistence_mode_t; 46 | 47 | /* WAL entry types */ 48 | typedef enum { 49 | RDB_WAL_INSERT = 1, 50 | RDB_WAL_UPDATE, 51 | RDB_WAL_DELETE, 52 | RDB_WAL_CREATE_TABLE, 53 | RDB_WAL_DROP_TABLE, 54 | RDB_WAL_CREATE_INDEX, 55 | RDB_WAL_DROP_INDEX, 56 | RDB_WAL_CHECKPOINT, 57 | RDB_WAL_COMMIT, 58 | RDB_WAL_ROLLBACK 59 | } rdb_wal_entry_type_t; 60 | 61 | /* WAL entry structure */ 62 | typedef struct { 63 | uint64_t sequence_number; /* Monotonic sequence number */ 64 | uint64_t timestamp; /* Unix timestamp */ 65 | rdb_wal_entry_type_t type; /* Entry type */ 66 | uint32_t transaction_id; /* Transaction ID */ 67 | uint32_t data_size; /* Size of data payload */ 68 | char table_name[64]; /* Table name */ 69 | uint64_t row_id; /* Row ID (for row operations) */ 70 | char data[]; /* Variable length data payload */ 71 | } rdb_wal_entry_t; 72 | 73 | /* Page structure for persistent storage */ 74 | typedef struct { 75 | uint64_t page_id; /* Page identifier */ 76 | uint64_t checksum; /* Page checksum */ 77 | uint32_t version; /* Page version for MVCC */ 78 | uint32_t data_size; /* Size of data in page */ 79 | time_t last_modified; /* Last modification time */ 80 | bool is_dirty; /* Whether page needs to be written */ 81 | bool is_pinned; /* Whether page is pinned in memory */ 82 | uint32_t reference_count; /* Reference count */ 83 | char data[RDB_PERSISTENCE_PAGE_SIZE - 64]; /* Page data (64 bytes overhead) */ 84 | } rdb_persistent_page_t; 85 | 86 | /* Table metadata for persistence */ 87 | typedef struct { 88 | char table_name[64]; /* Table name */ 89 | uint64_t first_page_id; /* First page containing table data */ 90 | uint64_t last_page_id; /* Last page containing table data */ 91 | uint64_t row_count; /* Number of rows in table */ 92 | uint64_t total_pages; /* Total pages used by table */ 93 | time_t created_time; /* Table creation time */ 94 | time_t last_modified; /* Last modification time */ 95 | bool is_compressed; /* Whether table data is compressed */ 96 | uint32_t compression_type; /* Compression algorithm used */ 97 | } rdb_persistent_table_metadata_t; 98 | 99 | /* Database metadata for persistence */ 100 | typedef struct { 101 | char magic[16]; /* Magic number: "FI_RDB_PERSIST" */ 102 | uint32_t version; /* Database version */ 103 | uint64_t created_time; /* Database creation time */ 104 | uint64_t last_checkpoint; /* Last checkpoint time */ 105 | uint64_t next_page_id; /* Next available page ID */ 106 | uint64_t total_pages; /* Total pages in database */ 107 | uint64_t wal_sequence; /* Current WAL sequence number */ 108 | uint32_t table_count; /* Number of tables */ 109 | uint32_t checksum; /* Header checksum */ 110 | char reserved[448]; /* Reserved for future use */ 111 | } rdb_persistent_header_t; 112 | 113 | /* Write-ahead log structure */ 114 | typedef struct { 115 | char *wal_path; /* Path to WAL file */ 116 | int wal_fd; /* File descriptor for WAL */ 117 | uint64_t sequence_number; /* Current sequence number */ 118 | uint64_t current_offset; /* Current write offset */ 119 | uint64_t max_size; /* Maximum WAL size */ 120 | void *mmap_ptr; /* Memory-mapped WAL */ 121 | size_t mmap_size; /* Size of memory-mapped region */ 122 | pthread_mutex_t wal_mutex; /* Mutex for WAL operations */ 123 | bool is_compressed; /* Whether WAL is compressed */ 124 | } rdb_wal_t; 125 | 126 | /* Page cache structure */ 127 | typedef struct { 128 | fi_map *page_map; /* Map of page_id -> rdb_persistent_page_t* */ 129 | fi_array *page_list; /* LRU list of pages */ 130 | size_t max_pages; /* Maximum pages in cache */ 131 | size_t current_pages; /* Current pages in cache */ 132 | uint64_t hit_count; /* Cache hits */ 133 | uint64_t miss_count; /* Cache misses */ 134 | pthread_rwlock_t cache_rwlock; /* Read-write lock for page cache */ 135 | } rdb_page_cache_t; 136 | 137 | /* Persistence manager */ 138 | typedef struct { 139 | char *data_dir; /* Data directory path */ 140 | rdb_persistence_mode_t mode; /* Persistence mode */ 141 | 142 | /* File handles */ 143 | char *db_file_path; /* Path to main database file */ 144 | int db_fd; /* File descriptor for database */ 145 | void *db_mmap_ptr; /* Memory-mapped database */ 146 | size_t db_mmap_size; /* Size of memory-mapped region */ 147 | 148 | /* WAL */ 149 | rdb_wal_t *wal; /* Write-ahead log */ 150 | 151 | /* Page cache */ 152 | rdb_page_cache_t *page_cache; /* Page cache */ 153 | 154 | /* Metadata */ 155 | rdb_persistent_header_t header; /* Database header */ 156 | fi_map *table_metadata; /* Map of table_name -> rdb_persistent_table_metadata_t* */ 157 | 158 | /* Checkpointing */ 159 | time_t last_checkpoint; /* Last checkpoint time */ 160 | uint64_t checkpoint_interval; /* Checkpoint interval in seconds */ 161 | bool checkpoint_in_progress; /* Whether checkpoint is in progress */ 162 | pthread_mutex_t checkpoint_mutex; /* Mutex for checkpoint operations */ 163 | 164 | /* Thread safety */ 165 | pthread_rwlock_t persistence_rwlock; /* Read-write lock for persistence operations */ 166 | pthread_mutex_t metadata_mutex; /* Mutex for metadata operations */ 167 | 168 | /* Statistics */ 169 | uint64_t total_writes; /* Total write operations */ 170 | uint64_t total_reads; /* Total read operations */ 171 | uint64_t checkpoint_count; /* Number of checkpoints performed */ 172 | uint64_t wal_entries; /* Number of WAL entries written */ 173 | } rdb_persistence_manager_t; 174 | 175 | /* Persistence manager operations */ 176 | rdb_persistence_manager_t* rdb_persistence_create(const char *data_dir, 177 | rdb_persistence_mode_t mode); 178 | void rdb_persistence_destroy(rdb_persistence_manager_t *pm); 179 | int rdb_persistence_init(rdb_persistence_manager_t *pm); 180 | int rdb_persistence_shutdown(rdb_persistence_manager_t *pm); 181 | 182 | /* Database operations with persistence */ 183 | int rdb_persistence_open_database(rdb_persistence_manager_t *pm, rdb_database_t *db); 184 | int rdb_persistence_close_database(rdb_persistence_manager_t *pm, rdb_database_t *db); 185 | int rdb_persistence_save_database(rdb_persistence_manager_t *pm, rdb_database_t *db); 186 | int rdb_persistence_load_database(rdb_persistence_manager_t *pm, rdb_database_t *db); 187 | 188 | /* Timeout-protected database operations */ 189 | int rdb_persistence_save_database_timeout(rdb_persistence_manager_t *pm, rdb_database_t *db, int timeout_seconds); 190 | int rdb_persistence_close_database_timeout(rdb_persistence_manager_t *pm, rdb_database_t *db, int timeout_seconds); 191 | 192 | /* Table operations with persistence */ 193 | int rdb_persistence_create_table(rdb_persistence_manager_t *pm, rdb_database_t *db, 194 | const char *table_name, fi_array *columns); 195 | int rdb_persistence_drop_table(rdb_persistence_manager_t *pm, rdb_database_t *db, 196 | const char *table_name); 197 | 198 | /* Row operations with persistence */ 199 | int rdb_persistence_insert_row(rdb_persistence_manager_t *pm, rdb_database_t *db, 200 | const char *table_name, rdb_row_t *row); 201 | int rdb_persistence_update_row(rdb_persistence_manager_t *pm, rdb_database_t *db, 202 | const char *table_name, rdb_row_t *old_row, rdb_row_t *new_row); 203 | int rdb_persistence_delete_row(rdb_persistence_manager_t *pm, rdb_database_t *db, 204 | const char *table_name, uint64_t row_id); 205 | 206 | /* WAL operations */ 207 | int rdb_wal_create(rdb_wal_t **wal, const char *wal_path, uint64_t max_size); 208 | void rdb_wal_destroy(rdb_wal_t *wal); 209 | int rdb_wal_append(rdb_wal_t *wal, rdb_wal_entry_type_t type, uint32_t transaction_id, 210 | const char *table_name, uint64_t row_id, const void *data, uint32_t data_size); 211 | int rdb_wal_replay(rdb_wal_t *wal, rdb_database_t *db); 212 | int rdb_wal_truncate(rdb_wal_t *wal); 213 | 214 | /* Page operations */ 215 | int rdb_page_cache_create(rdb_page_cache_t **cache, size_t max_pages); 216 | void rdb_page_cache_destroy(rdb_page_cache_t *cache); 217 | int rdb_page_cache_get(rdb_page_cache_t *cache, uint64_t page_id, 218 | rdb_persistent_page_t **page); 219 | int rdb_page_cache_put(rdb_page_cache_t *cache, rdb_persistent_page_t *page); 220 | int rdb_page_cache_evict(rdb_page_cache_t *cache, uint64_t page_id); 221 | 222 | /* Checkpoint operations */ 223 | int rdb_persistence_checkpoint(rdb_persistence_manager_t *pm, rdb_database_t *db); 224 | int rdb_persistence_force_checkpoint(rdb_persistence_manager_t *pm, rdb_database_t *db); 225 | 226 | /* Utility functions */ 227 | int rdb_persistence_create_directory(const char *path); 228 | int rdb_persistence_file_exists(const char *path); 229 | uint32_t rdb_persistence_calculate_checksum(const void *data, size_t size); 230 | int rdb_persistence_verify_checksum(const void *data, size_t size, uint32_t checksum); 231 | 232 | /* Serialization functions */ 233 | int rdb_serialize_table(rdb_table_t *table, void **data, size_t *data_size); 234 | int rdb_deserialize_table(const void *data, size_t data_size, rdb_table_t **table); 235 | int rdb_serialize_row(rdb_row_t *row, void **data, size_t *data_size); 236 | int rdb_deserialize_row(const void *data, size_t data_size, rdb_row_t **row); 237 | 238 | /* Thread-safe operations */ 239 | int rdb_persistence_insert_row_thread_safe(rdb_persistence_manager_t *pm, rdb_database_t *db, 240 | const char *table_name, rdb_row_t *row); 241 | int rdb_persistence_update_row_thread_safe(rdb_persistence_manager_t *pm, rdb_database_t *db, 242 | const char *table_name, rdb_row_t *old_row, rdb_row_t *new_row); 243 | int rdb_persistence_delete_row_thread_safe(rdb_persistence_manager_t *pm, rdb_database_t *db, 244 | const char *table_name, uint64_t row_id); 245 | 246 | /* Statistics and monitoring */ 247 | void rdb_persistence_print_stats(rdb_persistence_manager_t *pm); 248 | int rdb_persistence_get_stats(rdb_persistence_manager_t *pm, uint64_t *writes, uint64_t *reads, 249 | uint64_t *checkpoints, uint64_t *wal_entries); 250 | 251 | /* Configuration */ 252 | int rdb_persistence_set_mode(rdb_persistence_manager_t *pm, rdb_persistence_mode_t mode); 253 | int rdb_persistence_set_checkpoint_interval(rdb_persistence_manager_t *pm, uint64_t interval); 254 | int rdb_persistence_set_wal_size(rdb_persistence_manager_t *pm, uint64_t max_size); 255 | 256 | #endif //__PERSISTENCE_H__ 257 | -------------------------------------------------------------------------------- /examples/rdb/cached_rdb_demo.c: -------------------------------------------------------------------------------- 1 | #include "cached_rdb.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* Helper function to create columns */ 9 | rdb_column_t* create_column(const char *name, rdb_data_type_t type, bool primary_key, bool unique, bool nullable) { 10 | rdb_column_t *column = malloc(sizeof(rdb_column_t)); 11 | if (!column) return NULL; 12 | 13 | strncpy(column->name, name, sizeof(column->name) - 1); 14 | column->name[sizeof(column->name) - 1] = '\0'; 15 | column->type = type; 16 | column->max_length = (type == RDB_TYPE_VARCHAR) ? 255 : 0; 17 | column->primary_key = primary_key; 18 | column->unique = unique; 19 | column->nullable = nullable; 20 | column->default_value[0] = '\0'; 21 | column->is_foreign_key = false; 22 | column->foreign_table[0] = '\0'; 23 | column->foreign_column[0] = '\0'; 24 | 25 | return column; 26 | } 27 | 28 | /* Helper function to create values */ 29 | fi_array* create_values(int64_t id, const char *name, int age, double salary) { 30 | fi_array *values = fi_array_create(4, sizeof(rdb_value_t*)); 31 | if (!values) return NULL; 32 | 33 | rdb_value_t *id_val = rdb_create_int_value(id); 34 | rdb_value_t *name_val = rdb_create_string_value(name); 35 | rdb_value_t *age_val = rdb_create_int_value(age); 36 | rdb_value_t *salary_val = rdb_create_float_value(salary); 37 | 38 | if (!id_val || !name_val || !age_val || !salary_val) { 39 | if (id_val) rdb_value_free(id_val); 40 | if (name_val) rdb_value_free(name_val); 41 | if (age_val) rdb_value_free(age_val); 42 | if (salary_val) rdb_value_free(salary_val); 43 | fi_array_destroy(values); 44 | return NULL; 45 | } 46 | 47 | fi_array_push(values, &id_val); 48 | fi_array_push(values, &name_val); 49 | fi_array_push(values, &age_val); 50 | fi_array_push(values, &salary_val); 51 | 52 | return values; 53 | } 54 | 55 | /* Performance test function */ 56 | void performance_test(cached_rdb_t *cached_rdb) { 57 | printf("\n=== Performance Test ===\n"); 58 | 59 | const int num_inserts = 1000; 60 | const int num_selects = 500; 61 | 62 | /* Test insert performance */ 63 | printf("Testing insert performance with %d records...\n", num_inserts); 64 | clock_t start_time = clock(); 65 | 66 | for (int i = 1; i <= num_inserts; i++) { 67 | fi_array *values = create_values(i, "Employee", 25 + (i % 50), 50000.0 + (i * 100)); 68 | if (values) { 69 | cached_rdb_insert_row(cached_rdb, "employees", values); 70 | /* Free individual values */ 71 | for (size_t j = 0; j < fi_array_count(values); j++) { 72 | rdb_value_t **val_ptr = (rdb_value_t**)fi_array_get(values, j); 73 | if (val_ptr && *val_ptr) { 74 | rdb_value_free(*val_ptr); 75 | } 76 | } 77 | fi_array_destroy(values); 78 | } 79 | } 80 | 81 | clock_t end_time = clock(); 82 | double insert_time = ((double)(end_time - start_time)) / CLOCKS_PER_SEC; 83 | printf("Inserted %d records in %.3f seconds (%.0f records/second)\n", 84 | num_inserts, insert_time, num_inserts / insert_time); 85 | 86 | /* Test select performance */ 87 | printf("\nTesting select performance with %d queries...\n", num_selects); 88 | start_time = clock(); 89 | 90 | for (int i = 0; i < num_selects; i++) { 91 | /* Create where conditions for random employee */ 92 | fi_array *where_conditions = fi_array_create(1, sizeof(char*)); 93 | char condition[256]; 94 | int random_id = (rand() % num_inserts) + 1; 95 | sprintf(condition, "id = %d", random_id); 96 | 97 | char *condition_ptr = malloc(strlen(condition) + 1); 98 | strcpy(condition_ptr, condition); 99 | fi_array_push(where_conditions, &condition_ptr); 100 | 101 | /* Select columns */ 102 | fi_array *columns = fi_array_create(1, sizeof(char*)); 103 | char *name_col = malloc(5); 104 | strcpy(name_col, "name"); 105 | fi_array_push(columns, &name_col); 106 | 107 | fi_array *result = cached_rdb_select_rows(cached_rdb, "employees", columns, where_conditions); 108 | 109 | /* Clean up */ 110 | if (result) fi_array_destroy(result); 111 | free(condition_ptr); 112 | free(name_col); 113 | fi_array_destroy(where_conditions); 114 | fi_array_destroy(columns); 115 | } 116 | 117 | end_time = clock(); 118 | double select_time = ((double)(end_time - start_time)) / CLOCKS_PER_SEC; 119 | printf("Executed %d select queries in %.3f seconds (%.0f queries/second)\n", 120 | num_selects, select_time, num_selects / select_time); 121 | } 122 | 123 | /* Cache hit ratio test */ 124 | void cache_hit_ratio_test(cached_rdb_t *cached_rdb) { 125 | printf("\n=== Cache Hit Ratio Test ===\n"); 126 | 127 | /* Insert some test data */ 128 | for (int i = 1; i <= 100; i++) { 129 | fi_array *values = create_values(i, "TestEmployee", 30, 60000.0); 130 | if (values) { 131 | cached_rdb_insert_row(cached_rdb, "employees", values); 132 | /* Free individual values */ 133 | for (size_t j = 0; j < fi_array_count(values); j++) { 134 | rdb_value_t **val_ptr = (rdb_value_t**)fi_array_get(values, j); 135 | if (val_ptr && *val_ptr) { 136 | rdb_value_free(*val_ptr); 137 | } 138 | } 139 | fi_array_destroy(values); 140 | } 141 | } 142 | 143 | /* First round - should be cache misses */ 144 | printf("First round - accessing data (should be cache misses):\n"); 145 | for (int i = 1; i <= 50; i++) { 146 | fi_array *where_conditions = fi_array_create(1, sizeof(char*)); 147 | char condition[256]; 148 | sprintf(condition, "id = %d", i); 149 | char *condition_ptr = malloc(strlen(condition) + 1); 150 | strcpy(condition_ptr, condition); 151 | fi_array_push(where_conditions, &condition_ptr); 152 | 153 | fi_array *columns = fi_array_create(1, sizeof(char*)); 154 | char *name_col = malloc(5); 155 | strcpy(name_col, "name"); 156 | fi_array_push(columns, &name_col); 157 | 158 | fi_array *result = cached_rdb_select_rows(cached_rdb, "employees", columns, where_conditions); 159 | 160 | if (result) fi_array_destroy(result); 161 | free(condition_ptr); 162 | free(name_col); 163 | fi_array_destroy(where_conditions); 164 | fi_array_destroy(columns); 165 | } 166 | 167 | /* Second round - should be cache hits */ 168 | printf("Second round - accessing same data (should be cache hits):\n"); 169 | for (int i = 1; i <= 50; i++) { 170 | fi_array *where_conditions = fi_array_create(1, sizeof(char*)); 171 | char condition[256]; 172 | sprintf(condition, "id = %d", i); 173 | char *condition_ptr = malloc(strlen(condition) + 1); 174 | strcpy(condition_ptr, condition); 175 | fi_array_push(where_conditions, &condition_ptr); 176 | 177 | fi_array *columns = fi_array_create(1, sizeof(char*)); 178 | char *name_col = malloc(5); 179 | strcpy(name_col, "name"); 180 | fi_array_push(columns, &name_col); 181 | 182 | fi_array *result = cached_rdb_select_rows(cached_rdb, "employees", columns, where_conditions); 183 | 184 | if (result) fi_array_destroy(result); 185 | free(condition_ptr); 186 | free(name_col); 187 | fi_array_destroy(where_conditions); 188 | fi_array_destroy(columns); 189 | } 190 | } 191 | 192 | /* Persistence test */ 193 | void persistence_test(cached_rdb_t *cached_rdb) { 194 | printf("\n=== Persistence Test ===\n"); 195 | 196 | /* Insert some data */ 197 | printf("Inserting test data...\n"); 198 | for (int i = 1; i <= 10; i++) { 199 | fi_array *values = create_values(i, "PersistentEmployee", 25 + i, 50000.0 + (i * 1000)); 200 | if (values) { 201 | cached_rdb_insert_row(cached_rdb, "employees", values); 202 | /* Free individual values */ 203 | for (size_t j = 0; j < fi_array_count(values); j++) { 204 | rdb_value_t **val_ptr = (rdb_value_t**)fi_array_get(values, j); 205 | if (val_ptr && *val_ptr) { 206 | rdb_value_free(*val_ptr); 207 | } 208 | } 209 | fi_array_destroy(values); 210 | } 211 | } 212 | 213 | /* Save to disk */ 214 | printf("Saving database to disk...\n"); 215 | if (cached_rdb_save(cached_rdb) == 0) { 216 | printf("Database saved successfully\n"); 217 | } else { 218 | printf("Failed to save database\n"); 219 | } 220 | 221 | /* Force checkpoint */ 222 | printf("Performing checkpoint...\n"); 223 | if (cached_rdb_checkpoint(cached_rdb) == 0) { 224 | printf("Checkpoint completed successfully\n"); 225 | } else { 226 | printf("Failed to perform checkpoint\n"); 227 | } 228 | } 229 | 230 | /* Configuration test */ 231 | void configuration_test(cached_rdb_t *cached_rdb) { 232 | printf("\n=== Configuration Test ===\n"); 233 | 234 | /* Test cache level configuration */ 235 | printf("Current cache configuration:\n"); 236 | cached_rdb_print_stats(cached_rdb); 237 | 238 | /* Test auto-tuning */ 239 | printf("\nTesting auto-tuning...\n"); 240 | if (cached_rdb_set_auto_tuning(cached_rdb, true, 0.9) == 0) { 241 | printf("Auto-tuning enabled with 90%% target hit ratio\n"); 242 | } else { 243 | printf("Failed to enable auto-tuning\n"); 244 | } 245 | 246 | /* Test cache algorithm change */ 247 | printf("\nTesting cache algorithm change...\n"); 248 | if (cached_rdb_set_cache_algorithm(cached_rdb, 0, RDB_CACHE_AURA) == 0) { 249 | printf("Cache algorithm changed to AURA for level 0\n"); 250 | } else { 251 | printf("Failed to change cache algorithm\n"); 252 | } 253 | } 254 | 255 | int main() { 256 | printf("=== Cached RDB Demo with N-Level Cache and Persistence ===\n\n"); 257 | 258 | /* Seed random number generator */ 259 | srand(time(NULL)); 260 | 261 | /* Get default configuration */ 262 | cached_rdb_config_t *config = cached_rdb_get_default_config(); 263 | if (!config) { 264 | printf("Error: Failed to create default configuration\n"); 265 | return 1; 266 | } 267 | 268 | /* Customize configuration */ 269 | config->cache_levels = 3; /* Use 3 cache levels */ 270 | config->enable_query_cache = true; 271 | config->enable_auto_tuning = true; 272 | config->target_hit_ratio = 0.85; 273 | 274 | /* Create cached RDB instance */ 275 | cached_rdb_t *cached_rdb = cached_rdb_create("demo_database", config); 276 | if (!cached_rdb) { 277 | printf("Error: Failed to create cached RDB instance\n"); 278 | cached_rdb_free_config(config); 279 | return 1; 280 | } 281 | 282 | /* Initialize cached RDB */ 283 | if (cached_rdb_init(cached_rdb) != 0) { 284 | printf("Error: Failed to initialize cached RDB\n"); 285 | cached_rdb_destroy(cached_rdb); 286 | cached_rdb_free_config(config); 287 | return 1; 288 | } 289 | 290 | /* Open cached RDB */ 291 | if (cached_rdb_open(cached_rdb) != 0) { 292 | printf("Error: Failed to open cached RDB\n"); 293 | cached_rdb_destroy(cached_rdb); 294 | cached_rdb_free_config(config); 295 | return 1; 296 | } 297 | 298 | printf("Cached RDB opened successfully\n"); 299 | 300 | /* Create employees table */ 301 | fi_array *emp_columns = fi_array_create(4, sizeof(rdb_column_t*)); 302 | rdb_column_t *emp_id = create_column("id", RDB_TYPE_INT, true, true, false); 303 | rdb_column_t *emp_name = create_column("name", RDB_TYPE_VARCHAR, false, false, false); 304 | rdb_column_t *emp_age = create_column("age", RDB_TYPE_INT, false, false, false); 305 | rdb_column_t *emp_salary = create_column("salary", RDB_TYPE_FLOAT, false, false, false); 306 | 307 | fi_array_push(emp_columns, &emp_id); 308 | fi_array_push(emp_columns, &emp_name); 309 | fi_array_push(emp_columns, &emp_age); 310 | fi_array_push(emp_columns, &emp_salary); 311 | 312 | if (cached_rdb_create_table(cached_rdb, "employees", emp_columns) == 0) { 313 | printf("Table 'employees' created successfully\n"); 314 | } else { 315 | printf("Error creating employees table\n"); 316 | cached_rdb_close(cached_rdb); 317 | cached_rdb_destroy(cached_rdb); 318 | cached_rdb_free_config(config); 319 | return 1; 320 | } 321 | 322 | /* Run tests */ 323 | printf("\n=== Running Tests ===\n"); 324 | 325 | /* Basic functionality test */ 326 | printf("1. Basic functionality test:\n"); 327 | fi_array *values = create_values(1, "Alice Johnson", 28, 75000.0); 328 | if (values) { 329 | if (cached_rdb_insert_row(cached_rdb, "employees", values) == 0) { 330 | printf(" Employee inserted successfully\n"); 331 | } else { 332 | printf(" Error inserting employee\n"); 333 | } 334 | /* Free individual values */ 335 | for (size_t i = 0; i < fi_array_count(values); i++) { 336 | rdb_value_t **val_ptr = (rdb_value_t**)fi_array_get(values, i); 337 | if (val_ptr && *val_ptr) { 338 | rdb_value_free(*val_ptr); 339 | } 340 | } 341 | fi_array_destroy(values); 342 | } 343 | 344 | /* Performance test */ 345 | performance_test(cached_rdb); 346 | 347 | /* Cache hit ratio test */ 348 | cache_hit_ratio_test(cached_rdb); 349 | 350 | /* Persistence test */ 351 | persistence_test(cached_rdb); 352 | 353 | /* Configuration test */ 354 | configuration_test(cached_rdb); 355 | 356 | /* Print final statistics */ 357 | printf("\n=== Final Statistics ===\n"); 358 | cached_rdb_print_stats(cached_rdb); 359 | 360 | /* Clean up */ 361 | printf("\n=== Cleanup ===\n"); 362 | cached_rdb_close(cached_rdb); 363 | cached_rdb_destroy(cached_rdb); 364 | cached_rdb_free_config(config); 365 | 366 | printf("Demo completed successfully!\n"); 367 | return 0; 368 | } 369 | -------------------------------------------------------------------------------- /examples/rdb/multi_table_functions.c: -------------------------------------------------------------------------------- 1 | /* Multi-table operations */ 2 | fi_array* rdb_select_join(rdb_database_t *db, const rdb_statement_t *stmt) { 3 | if (!db || !stmt || !stmt->from_tables || fi_array_count(stmt->from_tables) == 0) { 4 | return NULL; 5 | } 6 | 7 | /* Create result array */ 8 | fi_array *result = fi_array_create(100, sizeof(rdb_result_row_t*)); 9 | if (!result) return NULL; 10 | 11 | /* Get the first table */ 12 | const char *first_table = *(const char**)fi_array_get(stmt->from_tables, 0); 13 | rdb_table_t *table1 = rdb_get_table(db, first_table); 14 | if (!table1) { 15 | fi_array_destroy(result); 16 | return NULL; 17 | } 18 | 19 | /* If only one table, perform simple select */ 20 | if (fi_array_count(stmt->from_tables) == 1) { 21 | for (size_t i = 0; i < fi_array_count(table1->rows); i++) { 22 | rdb_row_t *row = *(rdb_row_t**)fi_array_get(table1->rows, i); 23 | if (!row) continue; 24 | 25 | /* Create result row */ 26 | fi_array *table_names = fi_array_create(1, sizeof(char*)); 27 | fi_map *values = fi_map_create(16, sizeof(char*), sizeof(rdb_value_t*), 28 | fi_map_hash_string, fi_map_compare_string); 29 | 30 | if (table_names && values) { 31 | fi_array_push(table_names, &first_table); 32 | 33 | /* Copy values with table.column keys */ 34 | for (size_t j = 0; j < fi_array_count(table1->columns); j++) { 35 | rdb_column_t *col = (rdb_column_t*)fi_array_get(table1->columns, j); 36 | rdb_value_t *val = *(rdb_value_t**)fi_array_get(row->values, j); 37 | 38 | if (col && val) { 39 | char key[128]; 40 | snprintf(key, sizeof(key), "%s.%s", first_table, col->name); 41 | 42 | /* Create a copy of the value */ 43 | rdb_value_t *val_copy = malloc(sizeof(rdb_value_t)); 44 | if (val_copy) { 45 | *val_copy = *val; 46 | if (val->type == RDB_TYPE_VARCHAR || val->type == RDB_TYPE_TEXT) { 47 | if (val->data.string_val) { 48 | val_copy->data.string_val = malloc(strlen(val->data.string_val) + 1); 49 | if (val_copy->data.string_val) { 50 | strcpy(val_copy->data.string_val, val->data.string_val); 51 | } 52 | } 53 | } 54 | fi_map_put(values, &key, &val_copy); 55 | } 56 | } 57 | } 58 | 59 | rdb_result_row_t *result_row = rdb_create_result_row(row->row_id, table_names, values); 60 | if (result_row) { 61 | fi_array_push(result, &result_row); 62 | } 63 | } 64 | 65 | if (table_names) fi_array_destroy(table_names); 66 | if (values) fi_map_destroy(values); 67 | } 68 | return result; 69 | } 70 | 71 | /* Multi-table JOIN - simplified implementation for two tables */ 72 | if (fi_array_count(stmt->from_tables) == 2) { 73 | const char *second_table = *(const char**)fi_array_get(stmt->from_tables, 1); 74 | rdb_table_t *table2 = rdb_get_table(db, second_table); 75 | if (!table2) { 76 | fi_array_destroy(result); 77 | return NULL; 78 | } 79 | 80 | /* Perform cartesian product with join conditions */ 81 | for (size_t i = 0; i < fi_array_count(table1->rows); i++) { 82 | rdb_row_t *row1 = *(rdb_row_t**)fi_array_get(table1->rows, i); 83 | if (!row1) continue; 84 | 85 | for (size_t j = 0; j < fi_array_count(table2->rows); j++) { 86 | rdb_row_t *row2 = *(rdb_row_t**)fi_array_get(table2->rows, j); 87 | if (!row2) continue; 88 | 89 | /* Check join conditions if any */ 90 | bool matches = true; 91 | if (stmt->join_conditions && fi_array_count(stmt->join_conditions) > 0) { 92 | matches = false; 93 | for (size_t k = 0; k < fi_array_count(stmt->join_conditions); k++) { 94 | rdb_join_condition_t *condition = *(rdb_join_condition_t**)fi_array_get(stmt->join_conditions, k); 95 | if (rdb_row_matches_join_condition(row1, row2, condition, table1, table2)) { 96 | matches = true; 97 | break; 98 | } 99 | } 100 | } 101 | 102 | if (matches) { 103 | /* Create result row */ 104 | fi_array *table_names = fi_array_create(2, sizeof(char*)); 105 | fi_map *values = fi_map_create(32, sizeof(char*), sizeof(rdb_value_t*), 106 | fi_map_hash_string, fi_map_compare_string); 107 | 108 | if (table_names && values) { 109 | fi_array_push(table_names, &first_table); 110 | fi_array_push(table_names, &second_table); 111 | 112 | /* Add values from first table */ 113 | for (size_t k = 0; k < fi_array_count(table1->columns); k++) { 114 | rdb_column_t *col = (rdb_column_t*)fi_array_get(table1->columns, k); 115 | rdb_value_t *val = *(rdb_value_t**)fi_array_get(row1->values, k); 116 | 117 | if (col && val) { 118 | char key[128]; 119 | snprintf(key, sizeof(key), "%s.%s", first_table, col->name); 120 | 121 | rdb_value_t *val_copy = malloc(sizeof(rdb_value_t)); 122 | if (val_copy) { 123 | *val_copy = *val; 124 | if (val->type == RDB_TYPE_VARCHAR || val->type == RDB_TYPE_TEXT) { 125 | if (val->data.string_val) { 126 | val_copy->data.string_val = malloc(strlen(val->data.string_val) + 1); 127 | if (val_copy->data.string_val) { 128 | strcpy(val_copy->data.string_val, val->data.string_val); 129 | } 130 | } 131 | } 132 | fi_map_put(values, &key, &val_copy); 133 | } 134 | } 135 | } 136 | 137 | /* Add values from second table */ 138 | for (size_t k = 0; k < fi_array_count(table2->columns); k++) { 139 | rdb_column_t *col = (rdb_column_t*)fi_array_get(table2->columns, k); 140 | rdb_value_t *val = *(rdb_value_t**)fi_array_get(row2->values, k); 141 | 142 | if (col && val) { 143 | char key[128]; 144 | snprintf(key, sizeof(key), "%s.%s", second_table, col->name); 145 | 146 | rdb_value_t *val_copy = malloc(sizeof(rdb_value_t)); 147 | if (val_copy) { 148 | *val_copy = *val; 149 | if (val->type == RDB_TYPE_VARCHAR || val->type == RDB_TYPE_TEXT) { 150 | if (val->data.string_val) { 151 | val_copy->data.string_val = malloc(strlen(val->data.string_val) + 1); 152 | if (val_copy->data.string_val) { 153 | strcpy(val_copy->data.string_val, val->data.string_val); 154 | } 155 | } 156 | } 157 | fi_map_put(values, &key, &val_copy); 158 | } 159 | } 160 | } 161 | 162 | size_t combined_row_id = (row1->row_id << 16) | row2->row_id; 163 | rdb_result_row_t *result_row = rdb_create_result_row(combined_row_id, table_names, values); 164 | if (result_row) { 165 | fi_array_push(result, &result_row); 166 | } 167 | } 168 | 169 | if (table_names) fi_array_destroy(table_names); 170 | if (values) fi_map_destroy(values); 171 | } 172 | } 173 | } 174 | } 175 | 176 | return result; 177 | } 178 | 179 | bool rdb_row_matches_join_condition(const rdb_row_t *left_row, const rdb_row_t *right_row, 180 | const rdb_join_condition_t *condition, 181 | const rdb_table_t *left_table, const rdb_table_t *right_table) { 182 | if (!left_row || !right_row || !condition || !left_table || !right_table) { 183 | return false; 184 | } 185 | 186 | /* Get column indices */ 187 | int left_col_idx = rdb_get_column_index((rdb_table_t*)left_table, condition->left_column); 188 | int right_col_idx = rdb_get_column_index((rdb_table_t*)right_table, condition->right_column); 189 | 190 | if (left_col_idx < 0 || right_col_idx < 0) { 191 | return false; 192 | } 193 | 194 | /* Get values */ 195 | rdb_value_t *left_val = *(rdb_value_t**)fi_array_get(left_row->values, left_col_idx); 196 | rdb_value_t *right_val = *(rdb_value_t**)fi_array_get(right_row->values, right_col_idx); 197 | 198 | if (!left_val || !right_val) { 199 | return false; 200 | } 201 | 202 | /* Compare values */ 203 | return rdb_value_compare(&left_val, &right_val) == 0; 204 | } 205 | 206 | int rdb_validate_foreign_key(rdb_database_t *db, const char *table_name, 207 | const char *column_name, const rdb_value_t *value) { 208 | if (!db || !table_name || !column_name || !value) return -1; 209 | 210 | /* Find foreign key constraints for this table/column */ 211 | fi_map_iterator iter = fi_map_iterator_create(db->foreign_keys); 212 | 213 | /* Handle first element if iterator is valid */ 214 | if (iter.is_valid) { 215 | rdb_foreign_key_t **fk_ptr = (rdb_foreign_key_t**)fi_map_iterator_value(&iter); 216 | if (fk_ptr && *fk_ptr) { 217 | rdb_foreign_key_t *fk = *fk_ptr; 218 | if (strcmp(fk->table_name, table_name) == 0 && 219 | strcmp(fk->column_name, column_name) == 0) { 220 | 221 | /* Check if referenced value exists */ 222 | rdb_table_t *ref_table = rdb_get_table(db, fk->ref_table_name); 223 | if (!ref_table) return -1; 224 | 225 | int ref_col_idx = rdb_get_column_index(ref_table, fk->ref_column_name); 226 | if (ref_col_idx < 0) return -1; 227 | 228 | /* Search for matching value in referenced table */ 229 | for (size_t i = 0; i < fi_array_count(ref_table->rows); i++) { 230 | rdb_row_t *row = *(rdb_row_t**)fi_array_get(ref_table->rows, i); 231 | if (!row || !row->values) continue; 232 | 233 | rdb_value_t *ref_val = *(rdb_value_t**)fi_array_get(row->values, ref_col_idx); 234 | if (ref_val && rdb_value_compare(&value, &ref_val) == 0) { 235 | return 0; /* Valid foreign key */ 236 | } 237 | } 238 | 239 | return -1; /* Foreign key violation */ 240 | } 241 | } 242 | } 243 | 244 | /* Handle remaining elements */ 245 | while (fi_map_iterator_next(&iter)) { 246 | rdb_foreign_key_t **fk_ptr = (rdb_foreign_key_t**)fi_map_iterator_value(&iter); 247 | if (fk_ptr && *fk_ptr) { 248 | rdb_foreign_key_t *fk = *fk_ptr; 249 | if (strcmp(fk->table_name, table_name) == 0 && 250 | strcmp(fk->column_name, column_name) == 0) { 251 | 252 | /* Check if referenced value exists */ 253 | rdb_table_t *ref_table = rdb_get_table(db, fk->ref_table_name); 254 | if (!ref_table) return -1; 255 | 256 | int ref_col_idx = rdb_get_column_index(ref_table, fk->ref_column_name); 257 | if (ref_col_idx < 0) return -1; 258 | 259 | /* Search for matching value in referenced table */ 260 | for (size_t i = 0; i < fi_array_count(ref_table->rows); i++) { 261 | rdb_row_t *row = *(rdb_row_t**)fi_array_get(ref_table->rows, i); 262 | if (!row || !row->values) continue; 263 | 264 | rdb_value_t *ref_val = *(rdb_value_t**)fi_array_get(row->values, ref_col_idx); 265 | if (ref_val && rdb_value_compare(&value, &ref_val) == 0) { 266 | return 0; /* Valid foreign key */ 267 | } 268 | } 269 | 270 | return -1; /* Foreign key violation */ 271 | } 272 | } 273 | } 274 | 275 | return 0; /* No foreign key constraint found */ 276 | } 277 | 278 | int rdb_enforce_foreign_key_constraints(rdb_database_t *db, const char *table_name, 279 | rdb_row_t *row) { 280 | if (!db || !table_name || !row) return -1; 281 | 282 | rdb_table_t *table = rdb_get_table(db, table_name); 283 | if (!table) return -1; 284 | 285 | /* Check each column for foreign key constraints */ 286 | for (size_t i = 0; i < fi_array_count(table->columns); i++) { 287 | rdb_column_t *col = (rdb_column_t*)fi_array_get(table->columns, i); 288 | if (!col) continue; 289 | 290 | rdb_value_t *val = *(rdb_value_t**)fi_array_get(row->values, i); 291 | if (!val) continue; 292 | 293 | if (rdb_validate_foreign_key(db, table_name, col->name, val) != 0) { 294 | printf("Error: Foreign key constraint violation on column '%s'\n", col->name); 295 | return -1; 296 | } 297 | } 298 | 299 | return 0; 300 | } 301 | --------------------------------------------------------------------------------