├── connector └── go │ ├── go.mod │ └── crossdb.go ├── SECURITY.md ├── examples ├── go │ ├── go.mod │ ├── bench.go │ └── example.go ├── c │ └── Makefile └── python │ └── example.py ├── winbuild.cmd ├── test ├── Makefile ├── xdb_smoke_ddl.c ├── xdb_smoke_func.c ├── xdb_smoke_test.c └── xdb_smoke_semantic.c ├── jni ├── CMakeLists.txt ├── include │ └── db_jni.h └── db_jni.cpp ├── src ├── lib │ ├── xdb_vector.c │ ├── xdb_time.c │ ├── xdb_dynbuf.h │ ├── xdb_dynbuf.c │ ├── xdb_vector.h │ ├── xdb_bmp.h │ ├── xdb_objm.h │ ├── xdb_thread.c │ ├── xdb_lib.c │ ├── xdb_objm.c │ ├── xdb_str.c │ ├── xdb_lib.h │ └── xdb_sock.c ├── core │ ├── xdb_sysdb.h │ ├── xdb_crud.h │ ├── xdb_trigger.h │ ├── xdb_sql.h │ ├── xdb_fkey.h │ ├── xdb_rbtree.h │ ├── xdb_hash.h │ ├── xdb_cfg.h │ ├── xdb_expr.h │ ├── xdb_conn.c │ ├── xdb_index.h │ ├── xdb_vdata.h │ ├── xdb_store.h │ ├── xdb_expr.c │ ├── xdb_conn.h │ ├── xdb_wal.h │ ├── xdb_table.h │ ├── xdb_fkey.c │ ├── xdb_db.h │ ├── xdb_trigger.c │ └── xdb_trans.h ├── admin │ ├── xdb_backup.h │ └── xdb_shell.h ├── server │ ├── xdb_server.h │ ├── xdb_pubsub.h │ └── xdb_client.c ├── parser │ ├── xdb_token.h │ ├── xdb_parser_trig.c │ ├── xdb_parser_svr.c │ ├── xdb_parser_idx.c │ ├── xdb_json.c │ └── xdb_parser_pubsub.c ├── 3rd │ ├── wyhash32.h │ └── crossline.h ├── xdb-cli.c └── crossdb.c ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── bench └── basic │ ├── Makefile │ ├── bench-crossdb.c │ ├── bench-stlmap.cpp │ ├── bench-sqlite.c │ └── bench-boostmidx.cpp ├── CONTRIBUTING.md ├── CMakeLists.txt ├── Makefile ├── README.md └── CODE_OF_CONDUCT.md /connector/go/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/crossdb-org/crossdb/connector/go 2 | 3 | go 1.14 4 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | Please submit CVE to https://github.com/crossdb-org/crossdb/security/advisories. 6 | -------------------------------------------------------------------------------- /examples/go/go.mod: -------------------------------------------------------------------------------- 1 | module example 2 | 3 | go 1.14 4 | 5 | replace github.com/crossdb-org/crossdb/connector/go => ../../connector/go 6 | 7 | require github.com/crossdb-org/crossdb/connector/go v0.8.0 8 | -------------------------------------------------------------------------------- /winbuild.cmd: -------------------------------------------------------------------------------- 1 | IF not exist build (mkdir build) 2 | gcc -o build/libcrossdb.dll src/crossdb.c -fPIC -shared -lws2_32 -lpthread -lregex -static -O2 -Wl,--out-implib,build/libcrossdb.lib 3 | gcc -o build/xdb-cli src/xdb-cli.c -lws2_32 -lpthread -lregex -static -O2 4 | copy /Y include\crossdb.h build\ 5 | xcopy /ehiqy build\* c:\crossdb\ -------------------------------------------------------------------------------- /examples/c/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | $(CC) -o example.bin example.c -lcrossdb -lpthread 3 | ./example.bin 4 | 5 | debug: 6 | $(CC) -o example.bin example.c ../../src/crossdb.c -lpthread -g -fsanitize=address 7 | ./example.bin 8 | 9 | gdb: 10 | $(CC) -o example.bin example.c ../../src/crossdb.c -lpthread -g -fsanitize=address 11 | gdb example.bin 12 | 13 | clean: 14 | rm -f a.out *.bin -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | $(CC) -o xdb_smoke_test.bin xdb_smoke_test.c -O2 -lcrossdb -lpthread 3 | ./xdb_smoke_test.bin 4 | 5 | debug: 6 | $(CC) -o xdb_smoke_test.bin xdb_smoke_test.c ../src/crossdb.c -g -fsanitize=address 7 | ./xdb_smoke_test.bin 8 | 9 | gdb: 10 | $(CC) -o xdb_smoke_test.bin xdb_smoke_test.c ../src/crossdb.c -g -fsanitize=address 11 | gdb xdb_smoke_test.bin 12 | 13 | clean: 14 | rm -rf *.bin testdb 15 | -------------------------------------------------------------------------------- /jni/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.3) 2 | project(crossdbjni) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | set(CMAKE_CXX_STANDARD_REQUIRED True) 6 | 7 | find_package(JNI REQUIRED) 8 | include_directories(${JNI_INCLUDE_DIRS}) 9 | include_directories(include) 10 | include_directories(../include) 11 | 12 | link_directories(./lib) 13 | 14 | 15 | add_library(crossdbjni-win64 SHARED db_jni.cpp) 16 | target_link_libraries(crossdbjni-win64 PRIVATE crossdb) -------------------------------------------------------------------------------- /src/lib/xdb_vector.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #include "xdb_vector.h" 13 | 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Desktop (please complete the following information):** 20 | - OS: [e.g. Win/Linux/MacOS/BSD/etc] 21 | - Version [e.g. 0.8.0] 22 | 23 | **Additional context** 24 | Add any other context about the problem here. 25 | -------------------------------------------------------------------------------- /examples/python/example.py: -------------------------------------------------------------------------------- 1 | import crossdb 2 | 3 | conn = crossdb.connect(database=":memory:") 4 | cursor = conn.cursor() 5 | cursor.execute("CREATE TABLE student (name CHAR(16), age INT, class CHAR(16))") 6 | cursor.execute("INSERT INTO student (name,age,class) VALUES ('jack',10,'3-1'), ('tom',11,'2-5')") 7 | print ("insert rows: ", cursor.affected_rows) 8 | cursor.execute("SELECT * from student") 9 | print ("select row count: ", cursor.rowcount) 10 | names = [description[0] for description in cursor.description] 11 | print ("columns: ", names) 12 | print ("rows:") 13 | for row in cursor: 14 | print (row) 15 | cursor.close() 16 | #cursor.execute("SHELL") 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /src/core/xdb_sysdb.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __XDB_SYSDB_H__ 13 | #define __XDB_SYSDB_H__ 14 | 15 | static xdb_conn_t *s_xdb_sysdb_pConn; 16 | 17 | #endif // __XDB_SYSDB_H__ 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | #*.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | # Misc 55 | *.bin 56 | build/* 57 | -------------------------------------------------------------------------------- /src/core/xdb_crud.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __XDB_CRUD_H__ 13 | #define __XDB_CRUD_H__ 14 | 15 | #define XDB_ROW_BUF_SIZE (1024*1024) 16 | 17 | #define XDB_HASH_COM_MUL 17 18 | 19 | #endif // __XDB_CRUD_H__ 20 | -------------------------------------------------------------------------------- /src/core/xdb_trigger.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __XDB_TRIGGER_H__ 13 | #define __XDB_TRIGGER_H__ 14 | 15 | int xdb_call_trigger (xdb_conn_t *pConn, xdb_tblm_t *pTblm, xdb_trig_e type, xdb_row_t *pNewRow, xdb_row_t *pOldRow); 16 | 17 | #endif // __XDB_TRIGGER_H__ 18 | -------------------------------------------------------------------------------- /src/core/xdb_sql.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __XDB_SQL_H__ 13 | #define __XDB_SQL_H__ 14 | 15 | #define XDB_ROW_BUF_SIZE (1024*1024) 16 | 17 | 18 | XDB_STATIC int 19 | xdb_exec_out (xdb_conn_t *pConn, const char *sql, int len); 20 | 21 | XDB_STATIC int 22 | xdb_str_escape (char *dest, const char *src, int len); 23 | 24 | #endif // __XDB_SQL_H__ 25 | -------------------------------------------------------------------------------- /src/admin/xdb_backup.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __CROSS_BACKUP_H__ 13 | #define __CROSS_BACKUP_H__ 14 | 15 | XDB_STATIC int 16 | xdb_source (xdb_conn_t* pConn, const char *file); 17 | 18 | XDB_STATIC int 19 | xdb_dump (xdb_conn_t* pConn, xdb_dbm_t *pDbm, const char *file, bool bNoDrop, bool bNoCreate, bool bNoData); 20 | 21 | #endif // __CROSS_BACKUP_H__ 22 | -------------------------------------------------------------------------------- /src/admin/xdb_shell.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __XDB_SHELL_H__ 13 | #define __XDB_SHELL_H__ 14 | 15 | XDB_STATIC int 16 | xdb_shell_loop (xdb_conn_t* pConn, const char *prompt, const char *db, bool bQuite); 17 | 18 | XDB_STATIC void 19 | xdb_output_table (xdb_conn_t *pConn, xdb_res_t *pRes); 20 | 21 | XDB_STATIC bool 22 | xdb_is_sql_complete (char *sql, bool split); 23 | 24 | #endif // __XDB_SHELL_H__ 25 | -------------------------------------------------------------------------------- /src/core/xdb_fkey.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __XDB_FKEY_H__ 13 | #define __XDB_FKEY_H__ 14 | 15 | typedef struct xdb_fkeym_t { 16 | xdb_obj_t obj; 17 | struct xdb_tblm_t *pTblm; 18 | struct xdb_tblm_t *pRefTblm; 19 | int fld_count; 20 | xdb_fkey_action on_del_act; 21 | xdb_fkey_action on_upd_act; 22 | //xdb_field_t *pFields[XDB_MAX_MATCH_COL]; 23 | xdb_singfilter_t filter; 24 | } xdb_fkeym_t; 25 | 26 | #endif // __XDB_FKEY_H__ 27 | -------------------------------------------------------------------------------- /src/server/xdb_server.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __XDB_SERVER_H__ 13 | #define __XDB_SERVER_H__ 14 | 15 | #define XDB_SVR_PORT 7777 16 | #ifndef _WIN32 17 | #define XDB_DATA_DIR "/var/xdb_data" 18 | #else 19 | #define XDB_DATA_DIR "c:/crossdb/xdb_data" 20 | #endif 21 | 22 | typedef struct xdb_server_t { 23 | xdb_obj_t obj; 24 | int svr_port; 25 | int sockfd; 26 | bool bDrop; 27 | xdb_thread_t tid; 28 | } xdb_server_t; 29 | 30 | #if (XDB_ENABLE_PUBSUB == 1) 31 | XDB_STATIC int 32 | xdb_pub_notify (xdb_tblm_t *pTblm, const char *sql, int len); 33 | #endif 34 | 35 | #endif // __XDB_SERVER_H__ 36 | -------------------------------------------------------------------------------- /src/core/xdb_rbtree.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __XDB_RBTREE_H__ 13 | #define __XDB_RBTREE_H__ 14 | 15 | typedef struct { 16 | xdb_rowid rb_left; 17 | xdb_rowid rb_right; 18 | xdb_rowid rb_parent; // in main is 0, in sib msb=1 XDB_RB_SIB_NODE 19 | xdb_rowid color_sibling; // in main list, it points to first, for sibing first, prev = 0, sibing = parent node 20 | } xdb_rbnode_t; 21 | 22 | typedef struct { 23 | xdb_stghdr_t blk_hdr; 24 | uint64_t query_times; 25 | xdb_rowid row_count; 26 | xdb_rowid node_count; 27 | xdb_rowid rb_root; 28 | xdb_rowid rsvd[3]; // 29 | xdb_rbnode_t rb_node[1]; // 0 is rsvd for NULL node 30 | } xdb_rbtree_t; 31 | 32 | #endif // __XDB_RBTREE_H__ 33 | -------------------------------------------------------------------------------- /src/lib/xdb_time.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | static inline uint64_t 13 | xdb_timestamp () 14 | { 15 | struct timeval time; 16 | gettimeofday(&time,NULL); 17 | return time.tv_sec; 18 | } 19 | 20 | static inline uint64_t 21 | xdb_timestamp_ms () 22 | { 23 | struct timeval time; 24 | gettimeofday(&time,NULL); 25 | return time.tv_sec * 1000LL + time.tv_usec/1000; 26 | } 27 | 28 | static inline uint64_t 29 | xdb_timestamp_us () 30 | { 31 | struct timeval time; 32 | gettimeofday(&time,NULL); 33 | return time.tv_sec * 1000000LL + time.tv_usec; 34 | } 35 | 36 | static inline uint64_t 37 | xdb_uptime () 38 | { 39 | struct timespec uptime; 40 | clock_gettime(CLOCK_MONOTONIC, &uptime); 41 | return uptime.tv_sec; 42 | } 43 | 44 | -------------------------------------------------------------------------------- /src/core/xdb_hash.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __XDB_HASH_H__ 13 | #define __XDB_HASH_H__ 14 | 15 | typedef struct xdb_hashNode_t { 16 | xdb_rowid next; 17 | xdb_rowid prev; 18 | xdb_rowid sibling; 19 | uint32_t hash_val; 20 | } xdb_hashNode_t; 21 | 22 | typedef struct xdb_hashHdr_t { 23 | xdb_stghdr_t blk_hdr; 24 | uint64_t query_times; 25 | xdb_rowid row_count; 26 | xdb_rowid node_count; 27 | xdb_rowid slot_count; 28 | xdb_rowid old_count; // if !=0 in rehashing 29 | xdb_rowid rehash_count; // rehash slot location 30 | uint32_t slot_mask; 31 | uint32_t rsvd[4]; 32 | xdb_hashNode_t hash_node[]; 33 | } xdb_hashHdr_t; 34 | 35 | typedef struct xdb_hashSlot_t { 36 | xdb_stghdr_t blk_hdr; 37 | xdb_rowid hash_slot[]; 38 | } xdb_hashSlot_t; 39 | 40 | #endif // __XDB_HASH_H__ 41 | -------------------------------------------------------------------------------- /src/lib/xdb_dynbuf.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __XDB_DYNBUF_H__ 13 | #define __XDB_DYNBUF_H__ 14 | 15 | #define XDB_BUF_DEF(ptr,size) char ptr##_buf[size], *ptr=ptr##_buf; int ptr##_size=sizeof(ptr##_buf) 16 | #define XDB_BUF_ALLOC(ptr,size) \ 17 | if(size > ptr##_size) { \ 18 | if (ptr==ptr##_buf) { ptr = NULL;} \ 19 | ptr = xdb_realloc(ptr, size); \ 20 | if (NULL != ptr) { ptr##_size = size; } \ 21 | } 22 | #define XDB_BUF_FREE(ptr) \ 23 | if ((NULL!=ptr) && (ptr!=ptr##_buf)) { \ 24 | free(ptr); \ 25 | ptr = ptr##_buf; \ 26 | ptr##_size = sizeof(ptr##_buf); \ 27 | } 28 | 29 | typedef struct { 30 | void *pBuf; 31 | void *pCur; 32 | uint64_t cap; 33 | uint64_t free; 34 | uint8_t buf[2048]; 35 | } xdb_dynbuf_t; 36 | 37 | XDB_STATIC void* 38 | xdb_dynbuf_resize (xdb_dynbuf_t *pDynBuf, uint64_t size); 39 | 40 | #endif // __XDB_DYNBUF_H__ 41 | -------------------------------------------------------------------------------- /src/core/xdb_cfg.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __CROSS_CFG_H__ 13 | #define __CROSS_CFG_H__ 14 | 15 | /****************************************************************************** 16 | CrossDB Config 17 | ******************************************************************************/ 18 | 19 | #define XDB_VERSION "0.14.0" 20 | 21 | #define XDB_MAX_DB 1024 // at most 4096 22 | #define XDB_MAX_TBL 4095 // per DB 23 | #define XDB_MAX_COLUMN 1024 // per table 24 | #define XDB_MAX_INDEX 16 // at most 64 per table 25 | #define XDB_MAX_FKEY 16 26 | #define XDB_NAME_LEN 64 27 | #define XDB_MAX_MATCH_COL 64 28 | #define XDB_MAX_ROWS ((1U<<31) - 1) 29 | #define XDB_MAX_SQL_BUF (1024*1024) 30 | #define XDB_MAX_JOIN 8 31 | 32 | #define XDB_PATH_LEN 512 33 | 34 | #ifndef XDB_ENABLE_SERVER 35 | #define XDB_ENABLE_SERVER 1 36 | #endif 37 | 38 | #ifndef XDB_ENABLE_PUBSUB 39 | #define XDB_ENABLE_PUBSUB 1 40 | #endif 41 | 42 | #endif // __CROSS_CFG_H__ 43 | -------------------------------------------------------------------------------- /src/server/xdb_pubsub.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __XDB_PUBSUB_H__ 13 | #define __XDB_PUBSUB_H__ 14 | 15 | typedef struct xdb_replica_t { 16 | xdb_obj_t obj; 17 | char svr_host[XDB_NAME_LEN*2 + 1]; 18 | char *dbs; 19 | char *tables; 20 | int svr_port; 21 | xdb_thread_t tid; 22 | } xdb_replica_t; 23 | 24 | typedef struct xdb_pub_t { 25 | xdb_obj_t obj; 26 | xdb_objm_t sub_list; 27 | int format; // SQL, JSON 28 | } xdb_pub_t; 29 | 30 | typedef struct xdb_subscribe_t { 31 | xdb_obj_t obj; 32 | xdb_conn_t *pConn; 33 | bool bReplica; 34 | char *dbs; 35 | char *tables; 36 | char sub_name[XDB_NAME_LEN + 1]; 37 | char client_id[XDB_NAME_LEN + 1]; 38 | xdb_pub_t *pPub; 39 | xdb_vec_t db_list; 40 | xdb_vec_t tbl_list; 41 | bool bSync; 42 | xdb_vec_t cud_cache; 43 | } xdb_subscribe_t; 44 | 45 | 46 | #if (XDB_ENABLE_PUBSUB == 1) 47 | XDB_STATIC int 48 | xdb_pub_notify (xdb_tblm_t *pTblm, const char *sql, int len); 49 | XDB_STATIC int 50 | xdb_initial_sync (xdb_subscribe_t *pSubscribe); 51 | #endif 52 | 53 | XDB_STATIC const void * 54 | xdb_poll_sql (xdb_conn_t *pConn, int *pLen, uint32_t timeout); 55 | 56 | #endif // __XDB_PUBSUB_H__ 57 | -------------------------------------------------------------------------------- /src/parser/xdb_token.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | typedef enum { 13 | XDB_TOK_ERR = -1, // make enum signed 14 | XDB_TOK_ID = 0, // normal char 15 | XDB_TOK_STR, // ' " ` 16 | XDB_TOK_HEX, // 17 | XDB_TOK_NUM, // 0-9, 0xA-Z,a-z 18 | XDB_TOK_ESC, /* \ */ 19 | XDB_TOK_SP, // \t \n \r sp 20 | XDB_TOK_EQ, // = 21 | XDB_TOK_LT, // < 22 | XDB_TOK_LE, // <= 23 | XDB_TOK_GT, // > 24 | XDB_TOK_GE, // >= 25 | XDB_TOK_LIKE, 26 | XDB_TOK_BTWN, 27 | XDB_TOK_REGEXP, 28 | XDB_TOK_IN, 29 | XDB_TOK_NE, // != <> 30 | XDB_TOK_EXTRACT, // -> 31 | XDB_TOK_ADD, // + 32 | XDB_TOK_SUB, // - 33 | XDB_TOK_MUL, // * 34 | XDB_TOK_DIV, // / 35 | XDB_TOK_MOD, // % 36 | XDB_TOK_BAND, // & 37 | XDB_TOK_BOR, // | 38 | XDB_TOK_BXOR, // ^ 39 | XDB_TOK_NOT, // ~ 40 | XDB_TOK_NEG, // ! 41 | XDB_TOK_DOT, // . 42 | XDB_TOK_COMMA, // , 43 | XDB_TOK_COLON, // : 44 | XDB_TOK_LP, // ( Parentheses 45 | XDB_TOK_RP, // ) 46 | XDB_TOK_LB, // { Braces 47 | XDB_TOK_RB, // } 48 | XDB_TOK_LBK, // [ Brackets 49 | XDB_TOK_RBK, // ] 50 | XDB_TOK_QM, // ? 51 | 52 | XDB_TOK_BIGINT, 53 | XDB_TOK_DOUBLE, 54 | 55 | // AGG function 56 | XDB_TOK_COUNT, 57 | XDB_TOK_SUM, 58 | XDB_TOK_MIN, 59 | XDB_TOK_MAX, 60 | XDB_TOK_AVG, 61 | 62 | XDB_TOK_AND, 63 | XDB_TOK_OR, 64 | 65 | XDB_TOK_NONE, 66 | 67 | XDB_TOK_INV, // invalid 68 | XDB_TOK_END, // ; Semi-colon 69 | XDB_TOK_EOF, // \0 70 | } xdb_token_type; 71 | -------------------------------------------------------------------------------- /src/core/xdb_expr.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #if 0 13 | typedef enum XDB_TOK_t { 14 | XDB_TOK_NONE, 15 | 16 | XDB_TOK_OR, 17 | XDB_TOK_AND, 18 | 19 | XDB_TOK_EQ = XDB_TOK_EQ, // = 6 20 | XDB_TOK_LT = XDB_TOK_LT, // < 21 | XDB_TOK_LE = XDB_TOK_LE, // <= 22 | XDB_TOK_GT = XDB_TOK_GT, // > 23 | XDB_TOK_GE = XDB_TOK_GE, // >= 24 | XDB_TOK_NE = XDB_TOK_NE, // != <> 25 | XDB_OPLOG_MAX = XDB_TOK_NE, 26 | 27 | XDB_TOK_ADD = XDB_TOK_ADD, 28 | XDB_TOK_SUB = XDB_TOK_SUB, 29 | XDB_TOK_MUL = XDB_TOK_MUL, 30 | XDB_TOK_DIV = XDB_TOK_DIV, 31 | 32 | XDB_TOK_BIGINT, 33 | XDB_TOK_DOUBLE, 34 | XDB_TOK_CHAR, 35 | 36 | XDB_TOK_COUNT, 37 | XDB_TOK_SUM, 38 | XDB_TOK_MIN, 39 | XDB_TOK_MAX, 40 | XDB_TOK_AVG, 41 | } XDB_TOK_t; 42 | #endif 43 | 44 | #define XDB_TYPE_FIELD 128 45 | 46 | typedef struct xdb_value_t { 47 | uint8_t val_type; // xdb_type_t int, uint, double, string, binary, field, function | =, !=, >, <, >=, <=, AND, OR, + - * / 48 | uint8_t fld_type; 49 | uint8_t sup_type; 50 | xdb_field_t *pField; 51 | char *pExtract; 52 | uint8_t reftbl_id; 53 | xdb_str_t val_str; // raw text value field 54 | xdb_str_t val_str2; // raw text value table 55 | union { 56 | double fval; 57 | uint64_t uval; 58 | int64_t ival; 59 | xdb_str_t str; // str/binary 60 | xdb_inet_t inet; 61 | xdb_mac_t mac; 62 | }; 63 | void *pExpr; 64 | } xdb_value_t; 65 | 66 | typedef struct xdb_expr_t { 67 | xdb_value_t val[XDB_MAX_MATCH_COL]; 68 | } xdb_expr_t; 69 | -------------------------------------------------------------------------------- /bench/basic/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | $(CC) -o bench-crossdb.bin bench-crossdb.c -O2 -lcrossdb -lpthread 3 | ./bench-crossdb.bin 4 | 5 | debug: 6 | $(CC) -o bench-crossdb.bin bench-crossdb.c ../../src/crossdb.c -lpthread -g -fsanitize=address 7 | 8 | gdb: debug 9 | gdb ./bench-crossdb.bin 10 | 11 | sqlite: 12 | $(CC) -o bench-sqlite.bin bench-sqlite.c -O2 -lsqlite3 -lpthread 13 | ./bench-sqlite.bin 14 | 15 | stlmap: 16 | $(CC) -o bench-stlmap.bin bench-stlmap.cpp -std=c++17 -O2 -lpthread -lstdc++ -lm 17 | ./bench-stlmap.bin 18 | 19 | boostmidx: 20 | $(CC) -o bench-boostmidx.bin bench-boostmidx.cpp -std=c++17 -O2 -lpthread -lstdc++ -lm 21 | ./bench-boostmidx.bin 22 | 23 | fast: 24 | $(CC) -o bench-crossdb.bin bench-crossdb.c ../../src/crossdb.c -O3 -march=native -lpthread 25 | ./bench-crossdb.bin 26 | 27 | build: 28 | $(CC) -o bench-crossdb.bin bench-crossdb.c -O2 -lcrossdb -lpthread 29 | $(CC) -o bench-sqlite.bin bench-sqlite.c -O2 -lsqlite3 -lpthread 30 | $(CC) -o bench-stlmap.bin bench-stlmap.cpp -std=c++17 -O2 -lpthread -lstdc++ -lm 31 | $(CC) -o bench-boostmidx.bin bench-boostmidx.cpp -std=c++17 -O2 -lpthread -lstdc++ -lm 32 | 33 | test: build 34 | @echo 35 | @echo "\n***************** CrossDB *****************\n" 36 | @./bench-crossdb.bin -q -j -r 5 -n 1000 37 | @./bench-crossdb.bin -q -j -r 5 -n 10000 38 | @./bench-crossdb.bin -q -j -r 5 -n 100000 39 | @./bench-crossdb.bin -q -j -r 5 -n 1000000 40 | @./bench-crossdb.bin -q -j -r 5 -n 10000000 41 | 42 | @echo 43 | @echo "\n***************** STL Map & HashMap *****************\n" 44 | @./bench-stlmap.bin -q -j -r 5 -n 1000 45 | @./bench-stlmap.bin -q -j -r 5 -n 10000 46 | @./bench-stlmap.bin -q -j -r 5 -n 100000 47 | @./bench-stlmap.bin -q -j -r 5 -n 1000000 48 | @./bench-stlmap.bin -q -j -r 5 -n 10000000 49 | 50 | @echo 51 | @echo "\n***************** SQLite *****************\n" 52 | @./bench-sqlite.bin -q -j -r 5 -n 1000 53 | @./bench-sqlite.bin -q -j -r 5 -n 10000 54 | @./bench-sqlite.bin -q -j -r 5 -n 100000 55 | @./bench-sqlite.bin -q -j -r 5 -n 1000000 56 | @./bench-sqlite.bin -q -j -r 5 -n 10000000 57 | 58 | clean: 59 | rm -f a.out *.bin *.txt 60 | -------------------------------------------------------------------------------- /src/3rd/wyhash32.h: -------------------------------------------------------------------------------- 1 | // Author: Wang Yi 2 | #include 3 | #include 4 | #ifndef WYHASH32_BIG_ENDIAN 5 | static inline unsigned _wyr32(const uint8_t *p) { unsigned v; memcpy(&v, p, 4); return v;} 6 | #elif defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) 7 | static inline unsigned _wyr32(const uint8_t *p) { unsigned v; memcpy(&v, p, 4); return __builtin_bswap32(v);} 8 | #elif defined(_MSC_VER) 9 | static inline unsigned _wyr32(const uint8_t *p) { unsigned v; memcpy(&v, p, 4); return _byteswap_ulong(v);} 10 | #endif 11 | static inline unsigned _wyr24(const uint8_t *p, unsigned k) { return (((unsigned)p[0])<<16)|(((unsigned)p[k>>1])<<8)|p[k-1];} 12 | static inline void _wymix32(unsigned *A, unsigned *B){ 13 | uint64_t c=*A^0x53c5ca59u; c*=*B^0x74743c1bu; 14 | *A=(unsigned)c; 15 | *B=(unsigned)(c>>32); 16 | } 17 | // This version is vulnerable when used with a few bad seeds, which should be skipped beforehand: 18 | // 0x429dacdd, 0xd637dbf3 19 | static inline unsigned wyhash32(const void *key, uint64_t len, unsigned seed) { 20 | const uint8_t *p=(const uint8_t *)key; uint64_t i=len; 21 | unsigned see1=(unsigned)len; seed^=(unsigned)(len>>32); _wymix32(&seed, &see1); 22 | for(;i>8;i-=8,p+=8){ seed^=_wyr32(p); see1^=_wyr32(p+4); _wymix32(&seed, &see1); } 23 | if(i>=4){ seed^=_wyr32(p); see1^=_wyr32(p+i-4); } else if (i) seed^=_wyr24(p,(unsigned)i); 24 | _wymix32(&seed, &see1); _wymix32(&seed, &see1); return seed^see1; 25 | } 26 | // duplicate definition in wyhash.h also 27 | #ifndef wyhash_final_version_3 28 | static inline uint64_t wyrand(uint64_t *seed){ 29 | *seed+=0xa0761d6478bd642full; 30 | uint64_t see1=*seed^0xe7037ed1a0b428dbull; 31 | see1*=(see1>>32)|(see1<<32); 32 | return (*seed*((*seed>>32)|(*seed<<32)))^((see1>>32)|(see1<<32)); 33 | } 34 | #endif 35 | static inline unsigned wy32x32(unsigned a, unsigned b) { _wymix32(&a,&b); _wymix32(&a,&b); return a^b; } 36 | static inline float wy2u01(unsigned r) { const float _wynorm=1.0f/(1ull<<23); return (r>>9)*_wynorm;} 37 | static inline float wy2gau(unsigned r) { const float _wynorm=1.0f/(1ull<<9); return ((r&0x3ff)+((r>>10)&0x3ff)+((r>>20)&0x3ff))*_wynorm-3.0f;} -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to CrossDB 2 | 3 | CrossDB OSS is free, open-source software. Its development is led by the CrossDB Team, but we welcome contributions from all community members and open-source developers. This document describes how you can contribute, no matter whether you're a user or a developer yourself. 4 | 5 | ## Bug reports 6 | 7 | All users can report bugs to us through the **[GitHub issue tracker](https://github.com/crossdb-org/crossdb/issues)**. To ensure that the development team can locate and resolve the issue that you experienced, please include the following in your bug report: 8 | 9 | - A detailed description of the issue, including the steps to reproduce it. 10 | - Any log files that may be relevant to the issue. 11 | 12 | ## Code contributions 13 | 14 | Developers are encouraged to submit patches to the project, and all contributions, from minor documentation changes to bug fixes, are appreciated by our team. To ensure that your code can be merged successfully and improve the experience for other community members, we ask that you go through the following procedure before submitting a pull request: 15 | 16 | 1. Read and accept the terms of the CrossDB Contributor License Agreement (CLA) located at [https://cla-assistant.io/crossdb-org/crossdb](https://cla-assistant.io/crossdb-org/crossdb). 17 | 18 | 2. For bug fixes, search the [GitHub issue tracker](https://github.com/crossdb-org/crossdb/issues) to check whether the bug has already been filed. 19 | - If the bug that you want to fix already exists in the issue tracker, review the previous discussion before submitting your patch. 20 | - If the bug that you want to fix does not exist in the issue tracker, click **New issue** and file a report. 21 | - Ensure that you note the issue number in your pull request when you submit your patch. 22 | 23 | 3. Fork our repository to your GitHub account and create a branch for your patch. 24 | 25 | 4. Create a pull request to merge your changes into the development branch, and our team members will review the request as soon as possible. 26 | 27 | If you encounter any difficulties or problems in contributing your code, you can join our [GitHub Discussions](https://github.com/crossdb-org/crossdb/discussions) and receive assistance from members of the CrossDB Team. 28 | -------------------------------------------------------------------------------- /src/lib/xdb_dynbuf.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #include "xdb_dynbuf.h" 13 | 14 | static inline void 15 | xdb_dynbuf_init (xdb_dynbuf_t *pDynBuf) 16 | { 17 | pDynBuf->pBuf = pDynBuf->buf; 18 | pDynBuf->pCur = pDynBuf->buf; 19 | pDynBuf->cap = sizeof(pDynBuf->buf); 20 | pDynBuf->free = sizeof(pDynBuf->buf); 21 | } 22 | 23 | static inline void* 24 | xdb_dynbuf_alloc (xdb_dynbuf_t *pDynBuf, uint32_t size) 25 | { 26 | if (pDynBuf->free < size) { 27 | void *ptr = xdb_dynbuf_resize (pDynBuf, pDynBuf->cap + size); 28 | if (NULL == ptr) { 29 | return NULL; 30 | } 31 | } 32 | void *ptr = pDynBuf->pCur; 33 | pDynBuf->free -= size; 34 | return ptr; 35 | } 36 | 37 | static inline void 38 | xdb_dynbuf_free (xdb_dynbuf_t *pDynBuf) 39 | { 40 | if (pDynBuf->pBuf != pDynBuf->buf) { 41 | xdb_free (pDynBuf->pBuf); 42 | xdb_dynbuf_init (pDynBuf); 43 | } 44 | } 45 | 46 | XDB_STATIC void* 47 | xdb_dynbuf_resize (xdb_dynbuf_t *pDynBuf, uint64_t size) 48 | { 49 | if (0 == pDynBuf->cap) { 50 | xdb_dynbuf_init (pDynBuf); 51 | } 52 | if (size <= pDynBuf->cap) { 53 | return pDynBuf->pBuf; 54 | } 55 | int new_cap = pDynBuf->cap; 56 | do { 57 | new_cap <<= 1; 58 | } while (new_cap < size); 59 | 60 | void *pBuf; 61 | if (pDynBuf->pBuf == pDynBuf->buf) { 62 | pBuf = xdb_malloc (size); 63 | if (NULL == pBuf) { 64 | return NULL; 65 | } 66 | memcpy (pBuf, pDynBuf->buf, sizeof(pDynBuf->buf)); 67 | } else { 68 | pBuf = xdb_realloc (pDynBuf->pBuf, pDynBuf->cap); 69 | if (NULL == pBuf) { 70 | return NULL; 71 | } 72 | } 73 | 74 | pDynBuf->pCur = pBuf + (pDynBuf->pCur - pDynBuf->pBuf); 75 | pDynBuf->pBuf = pBuf; 76 | pDynBuf->free += (new_cap - pDynBuf->cap); 77 | pDynBuf->cap = new_cap; 78 | 79 | return pBuf; 80 | } 81 | -------------------------------------------------------------------------------- /src/lib/xdb_vector.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __XDB_VECTOR_H__ 13 | #define __XDB_VECTOR_H__ 14 | 15 | typedef struct { 16 | union { 17 | void **pEle; 18 | uint8_t *pBuf; 19 | }; 20 | int cap; 21 | int count; 22 | } xdb_vec_t; 23 | 24 | static inline int 25 | xdb_vec_find (xdb_vec_t *pVec, void *pE) 26 | { 27 | for (int i = 0; i < pVec->count; ++i) { 28 | if (pVec->pEle[i] == pE) { 29 | return i; 30 | } 31 | } 32 | return -1; 33 | } 34 | 35 | static inline bool 36 | xdb_vec_add (xdb_vec_t *pVec, void *pE) 37 | { 38 | if (xdb_vec_find (pVec, pE) >= 0) { 39 | return true; 40 | } 41 | if (xdb_unlikely (pVec->count >= pVec->cap)) { 42 | void *pEle = xdb_realloc (pVec->pEle, pVec->cap + 64*sizeof(void*)); 43 | if (NULL == pEle) { 44 | return false; 45 | } 46 | pVec->pEle = pEle; 47 | pVec->cap += 64; 48 | } 49 | pVec->pEle[pVec->count++] = pE; 50 | return true; 51 | } 52 | 53 | static inline bool 54 | xdb_vec_addStr (xdb_vec_t *pVec, const void *pPtr, int len) 55 | { 56 | if (xdb_unlikely (pVec->count + len >= pVec->cap)) { 57 | int new_len = XDB_ALIGN1M(pVec->count + len + 1); 58 | void *pBuf = xdb_realloc (pVec->pEle, new_len); 59 | if (NULL == pBuf) { 60 | return false; 61 | } 62 | pVec->pBuf = pBuf; 63 | pVec->cap = new_len; 64 | } 65 | memcpy (&pVec->pBuf[pVec->count], pPtr, len); 66 | pVec->count += len; 67 | pVec->pBuf[pVec->count] = '\0'; 68 | return true; 69 | } 70 | 71 | static inline bool 72 | xdb_vec_del (xdb_vec_t *pVec, void *pE) 73 | { 74 | // TBD 75 | return true; 76 | } 77 | 78 | static inline void 79 | xdb_vec_free (xdb_vec_t *pVec) 80 | { 81 | xdb_free (pVec->pEle); 82 | memset (pVec, 0, sizeof (*pVec)); 83 | } 84 | 85 | #endif // __XDB_VECTOR_H__ 86 | -------------------------------------------------------------------------------- /src/lib/xdb_bmp.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __XDB_BMP_H__ 13 | #define __XDB_BMP_H__ 14 | 15 | typedef struct { 16 | uint64_t bmp4; // 64bit (bit>>6)&0x3f 17 | uint64_t bmp5[64]; // 64*64bit bit&0x3f 18 | uint16_t count; 19 | bool bMalloc; 20 | } xdb_lv2bmp_t; // 4K 21 | 22 | typedef struct { 23 | uint64_t bmp2; // 64bit (bit>>18)&7 24 | uint64_t bmp3[64]; // 64*54bit (bit>>12)&7 25 | xdb_lv2bmp_t *pLv2Bmp[64][64]; 26 | bool bMalloc; 27 | } xdb_lv1bmp_t; // 4K 28 | 29 | typedef struct { 30 | uint64_t bmp1[2]; // 2*8bit (bit>>30) (bit>>24)&0x3f 31 | xdb_lv1bmp_t *pLv1Bmp[2][64]; 32 | 33 | xdb_lv1bmp_t lv1_bmp[1]; // [0][0] 34 | xdb_lv2bmp_t lv2_bmp[128]; // 128*4K 512K rows 35 | uint16_t lv1_alloc; 36 | uint16_t lv2_alloc; 37 | } xdb_bmp_t; // 128 38 | 39 | typedef int (*xdb_bmp_cb) (uint32_t bit, void *pArg); 40 | 41 | XDB_STATIC void 42 | xdb_bmp_init (xdb_bmp_t *pBmp); 43 | 44 | XDB_STATIC void 45 | xdb_bmp_free (xdb_bmp_t *pBmp); 46 | 47 | XDB_STATIC void 48 | xdb_bmp_iterate (xdb_bmp_t *pBmp, xdb_bmp_cb pCb, void *pArg); 49 | 50 | XDB_STATIC int 51 | xdb_bmp_set (xdb_bmp_t *pBmp, uint32_t bit); 52 | 53 | XDB_STATIC void 54 | xdb_bmp_clr (xdb_bmp_t *pBmp, uint32_t bit); 55 | 56 | XDB_STATIC bool 57 | xdb_bmp_get (xdb_bmp_t *pBmp, uint32_t bit); 58 | 59 | XDB_STATIC void 60 | xdb_lv2bmp_iterate (xdb_lv2bmp_t *pLv2Bmp, xdb_bmp_cb pCb, void *pArg); 61 | XDB_STATIC void 62 | xdb_lv2bmp_init (xdb_lv2bmp_t *pLv2Bmp); 63 | XDB_STATIC void 64 | xdb_lv2bmp_set (xdb_lv2bmp_t *pLv2Bmp, uint32_t bit); 65 | XDB_STATIC void 66 | xdb_lv2bmp_clr (xdb_lv2bmp_t *pLv2Bmp, uint32_t bit); 67 | XDB_STATIC bool 68 | xdb_lv2bmp_get (xdb_lv2bmp_t *pLv2Bmp, uint32_t bit); 69 | 70 | #endif // __XDB_BMP_H__ 71 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(crossdb) 3 | add_compile_options(-O2) 4 | 5 | option(BUILD_SHARED_LIBS "Build using shared libraries" ON) 6 | option(ENABLE_CLI "Build CLI executable" ON) 7 | option(ENABLE_PACKAGE "Build package" ON) 8 | 9 | add_library(crossdb src/crossdb.c) 10 | IF (CMAKE_SYSTEM_NAME MATCHES "Windows") 11 | target_link_libraries(crossdb pthread ws2_32 regex) 12 | ENDIF () 13 | set_target_properties(crossdb PROPERTIES PUBLIC_HEADER "include/crossdb.h") 14 | 15 | #add_subdirectory(jni) 16 | include(GNUInstallDirs) 17 | INSTALL( 18 | TARGETS crossdb 19 | EXPORT crossdb-export 20 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 21 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 22 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 23 | INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 24 | PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 25 | ) 26 | 27 | INSTALL( 28 | EXPORT crossdb-export 29 | FILE crossdb-config.cmake 30 | NAMESPACE crossdb:: 31 | DESTINATION lib/cmake/crossdb 32 | ) 33 | 34 | if(ENABLE_CLI) 35 | add_executable(xdb-cli src/xdb-cli.c) 36 | IF (CMAKE_SYSTEM_NAME MATCHES "Windows") 37 | target_link_libraries(xdb-cli pthread ws2_32 regex) 38 | ELSE () 39 | target_link_libraries(xdb-cli pthread dl) 40 | ENDIF () 41 | INSTALL( 42 | TARGETS xdb-cli 43 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 44 | ) 45 | endif() 46 | 47 | add_custom_target("uninstall" COMMENT "Uninstall installed files") 48 | add_custom_command( 49 | TARGET "uninstall" 50 | POST_BUILD 51 | COMMENT "Uninstall files with install_manifest.txt" 52 | COMMAND xargs rm -vf < install_manifest.txt || echo Nothing in 53 | install_manifest.txt to be uninstalled! 54 | ) 55 | 56 | if(ENABLE_PACKAGE) 57 | set(CPACK_PACKAGE_NAME crossdb) 58 | set(CPACK_PACKAGE_VERSION_MAJOR 0) 59 | set(CPACK_PACKAGE_VERSION_MINOR 14) 60 | set(CPACK_PACKAGE_VERSION_PATCH 0) 61 | set(CPACK_PACKAGE_CONTACT "crossdb ") 62 | #set(CPACK_GENERATOR DEB NSIS RPM) 63 | IF (CMAKE_SYSTEM_NAME MATCHES "Linux") 64 | set(CPACK_GENERATOR DEB ZIP) 65 | ELSE () 66 | set(CPACK_GENERATOR ZIP) 67 | ENDIF () 68 | include(CPack) 69 | endif() 70 | -------------------------------------------------------------------------------- /src/lib/xdb_objm.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __XDB_OBJM_H__ 13 | #define __XDB_OBJM_H__ 14 | 15 | typedef struct xdb_obj_t { 16 | struct xdb_obj_t *pNxtObj; 17 | int xoid; 18 | uint32_t hashcode; 19 | uint8_t nm_len; 20 | char obj_name[XDB_NAME_LEN+1]; 21 | uint8_t obj_type; 22 | xdb_rwlock_t lock; 23 | } xdb_obj_t; 24 | 25 | #define XDB_OBJ_ID(pObj) ((pObj)->obj.xoid) 26 | #define XDB_OBJ_NAME(pObj) ((pObj)->obj.obj_name) 27 | #define XDB_OBJ_NMLEN(pObj) ((pObj)->obj.nm_len) 28 | 29 | #define XDB_OBJM_COUNT(objm) ((objm).obj_count) 30 | #define XDB_OBJM_MAX(objm) ((objm).obj_max) 31 | #define XDB_OBJM_GET(objm,id) (void*)((objm).ppObjPool[id]) 32 | 33 | #define XDB_OBJ_RDLOCK(pObj) xdb_rwlock_rdlock(&(pObj)->obj.lock) 34 | #define XDB_OBJ_RDUNLOCK(pObj) xdb_rwlock_rdunlock(&(pObj)->obj.lock) 35 | #define XDB_OBJ_WRLOCK(pObj) xdb_rwlock_wrlock(&(pObj)->obj.lock) 36 | #define XDB_OBJ_WRUNLOCK(pObj) xdb_rwlock_wrunlock(&(pObj)->obj.lock) 37 | 38 | typedef struct xdb_objm_t { 39 | xdb_obj_t **ppObjPool; 40 | int obj_cap; 41 | int obj_max; 42 | int obj_count; 43 | xdb_obj_t **ppObjHash; 44 | uint32_t hash_mask; 45 | } xdb_objm_t; 46 | 47 | static inline void* 48 | xdb_objm_getbyid (xdb_objm_t *pObjm, int xoid) 49 | { 50 | return xoid < pObjm->obj_max ? pObjm->ppObjPool[xoid] : NULL; 51 | } 52 | 53 | XDB_STATIC void* 54 | xdb_objm_get2 (xdb_objm_t *pObjm, const char *name, int len); 55 | 56 | static inline void* 57 | xdb_objm_get (xdb_objm_t *pObjm, const char *name) 58 | { 59 | return xdb_objm_get2 (pObjm, name, 0); 60 | } 61 | 62 | XDB_STATIC int 63 | xdb_objm_add (xdb_objm_t *pObjm, void *pObj); 64 | 65 | XDB_STATIC int 66 | xdb_objm_del (xdb_objm_t *pObjm, void *pObj); 67 | 68 | XDB_STATIC void 69 | xdb_objm_free (xdb_objm_t *pObjm); 70 | 71 | #endif // __XDB_OBJM_H__ 72 | -------------------------------------------------------------------------------- /src/core/xdb_conn.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | static uint32_t s_xdb_conn_count = 0; 13 | 14 | XDB_STATIC void 15 | xdb_conn_init (xdb_conn_t* pConn) 16 | { 17 | pConn->pConn = pConn; 18 | pConn->pSql = pConn->sql_buf; 19 | pConn->sqlbuf_len = sizeof (pConn->sql_buf); 20 | pConn->conn_res.len_type = (sizeof (xdb_res_t)) | (XDB_RET_REPLY<<28); 21 | xdb_rowset_init (&pConn->row_set); 22 | pConn->bInTrans = false; 23 | pConn->bAutoTrans = false; 24 | pConn->bAutoCommit = true; 25 | pConn->conn_stdout = stdout; 26 | pConn->ref_cnt = 1; 27 | xdb_lv2bmp_init (&pConn->dbTrans_bmp); 28 | xdb_atomic_inc (&s_xdb_conn_count); 29 | } 30 | 31 | xdb_conn_t* 32 | xdb_open (const char *dbpath) 33 | { 34 | xdb_conn_t* pConn = xdb_calloc (sizeof (xdb_conn_t)); 35 | if (NULL == pConn) { 36 | return NULL; 37 | } 38 | 39 | xdb_init (); 40 | xdb_sysdb_init (); 41 | 42 | xdb_conn_init (pConn); 43 | 44 | if (NULL != dbpath) { 45 | xdb_res_t *pRes = xdb_pexec (pConn, "CREATE DATABASE IF NOT EXISTS \'%s\'", dbpath); 46 | XDB_RESCHK (pRes); 47 | } 48 | 49 | return pConn; 50 | } 51 | 52 | void 53 | xdb_close (xdb_conn_t* pConn) 54 | { 55 | if (NULL == pConn) { 56 | return; 57 | } 58 | if (--pConn->ref_cnt > 0) { 59 | return; 60 | } 61 | 62 | xdb_rollback (pConn); 63 | 64 | if ((pConn->conn_stdout != stdout) && ((uintptr_t)pConn->conn_stdout != pConn->sockfd)) { 65 | fclose (pConn->conn_stdout); 66 | } else if (pConn->sockfd > 0) { 67 | xdb_sock_close (pConn->sockfd); 68 | } 69 | 70 | xdb_trans_free (pConn); 71 | xdb_rowset_free (&pConn->row_set); 72 | xdb_free (pConn->pQueryRes); 73 | memset (pConn, 0, sizeof (*pConn)); 74 | xdb_free (pConn); 75 | 76 | xdb_atomic_dec (&s_xdb_conn_count); 77 | 78 | if (1 == s_xdb_conn_count) { 79 | xdb_exit (); 80 | } 81 | } 82 | 83 | const char* 84 | xdb_curdb (xdb_conn_t* pConn) 85 | { 86 | return pConn->cur_db; 87 | } 88 | -------------------------------------------------------------------------------- /test/xdb_smoke_ddl.c: -------------------------------------------------------------------------------- 1 | UTEST_I(XdbTest, create_db, 2) 2 | { 3 | xdb_res_t *pRes; 4 | xdb_conn_t *pConn = utest_fixture->pConn; 5 | pRes = xdb_exec (pConn, "CREATE DATABASE xdb"); 6 | ASSERT_EQ (xdb_errcode(pRes), XDB_OK); 7 | pRes = xdb_exec (pConn, "CREATE DATABASE xdb"); 8 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 9 | pRes = xdb_exec (pConn, "CREATE DATABASE IF NOT EXISTS xdb"); 10 | ASSERT_EQ (xdb_errcode(pRes), XDB_OK); 11 | pRes = xdb_exec (pConn, "DROP DATABASE xdb"); 12 | ASSERT_EQ (xdb_errcode(pRes), XDB_OK); 13 | } 14 | 15 | UTEST_I(XdbTest, drop_db, 2) 16 | { 17 | xdb_res_t *pRes; 18 | xdb_conn_t *pConn = utest_fixture->pConn; 19 | pRes = xdb_exec (pConn, "DROP DATABASE xdb"); 20 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 21 | pRes = xdb_exec (pConn, "DROP DATABASE IF EXISTS xdb"); 22 | ASSERT_EQ (xdb_errcode(pRes), XDB_OK); 23 | pRes = xdb_exec (pConn, "CREATE DATABASE xdb"); 24 | ASSERT_EQ (xdb_errcode(pRes), XDB_OK); 25 | pRes = xdb_exec (pConn, "DROP DATABASE xdb"); 26 | ASSERT_EQ (xdb_errcode(pRes), XDB_OK); 27 | } 28 | 29 | UTEST_I(XdbTest, create_table, 2) 30 | { 31 | xdb_res_t *pRes; 32 | xdb_conn_t *pConn = utest_fixture->pConn; 33 | pRes = xdb_exec (pConn, "CREATE TABLE teacher (id INT PRIMARY KEY, name CHAR(16), age TINYINT)"); 34 | ASSERT_EQ (xdb_errcode(pRes), XDB_OK); 35 | pRes = xdb_exec (pConn, "CREATE TABLE teacher (id INT PRIMARY KEY, name CHAR(16), age TINYINT)"); 36 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 37 | pRes = xdb_exec (pConn, "CREATE TABLE IF NOT EXISTS teacher (id INT PRIMARY KEY, name CHAR(16), age TINYINT)"); 38 | ASSERT_EQ (xdb_errcode(pRes), XDB_OK); 39 | pRes = xdb_exec (pConn, "DROP TABLE teacher"); 40 | ASSERT_EQ (xdb_errcode(pRes), XDB_OK); 41 | } 42 | 43 | UTEST_I(XdbTest, drop_table, 2) 44 | { 45 | xdb_res_t *pRes; 46 | xdb_conn_t *pConn = utest_fixture->pConn; 47 | pRes = xdb_exec (pConn, "DROP TABLE teacher"); 48 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 49 | pRes = xdb_exec (pConn, "DROP TABLE IF EXISTS teacher"); 50 | ASSERT_EQ (xdb_errcode(pRes), XDB_OK); 51 | pRes = xdb_exec (pConn, "CREATE TABLE teacher (id INT PRIMARY KEY, name CHAR(16), age TINYINT)"); 52 | ASSERT_EQ (xdb_errcode(pRes), XDB_OK); 53 | pRes = xdb_exec (pConn, "DROP TABLE teacher"); 54 | ASSERT_EQ (xdb_errcode(pRes), XDB_OK); 55 | } 56 | 57 | UTEST_I(XdbTestRows, create_idx, 2) 58 | { 59 | } 60 | 61 | UTEST_I(XdbTestRows, drop_idx, 2) 62 | { 63 | } 64 | 65 | -------------------------------------------------------------------------------- /src/parser/xdb_parser_trig.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | XDB_STATIC xdb_stmt_t* 13 | xdb_parse_create_trigger (xdb_conn_t* pConn, xdb_token_t *pTkn) 14 | { 15 | xdb_token_type type = xdb_next_token (pTkn); 16 | xdb_stmt_trig_t *pStmt = &pConn->stmt_union.trig_stmt; 17 | pStmt->stmt_type = XDB_STMT_CREATE_TRIG; 18 | pStmt->pSql = NULL; 19 | 20 | XDB_EXPECT (XDB_TOK_ID==type, XDB_E_STMT, "Miss Trigger name"); 21 | 22 | pStmt->trig_name = pTkn->token; 23 | bool bAft = false; 24 | 25 | type = xdb_next_token (pTkn); 26 | XDB_EXPECT ((XDB_TOK_ID==type), XDB_E_STMT, "Expect { BEFORE | AFTER }"); 27 | if (!strcasecmp (pTkn->token, "AFTER")) { 28 | bAft = true; 29 | } else { 30 | XDB_EXPECT (!strcasecmp (pTkn->token, "BEFORE"), XDB_E_STMT, "Expect BEFORE or AFTER"); 31 | } 32 | type = xdb_next_token (pTkn); 33 | XDB_EXPECT ((XDB_TOK_ID==type), XDB_E_STMT, "Expect { INSERT | UPDATE | DELETE }"); 34 | if (!strcasecmp (pTkn->token, "INSERT")) { 35 | pStmt->trig_type = XDB_TRIG_BEF_INS + bAft; 36 | } else if (!strcasecmp (pTkn->token, "UPDATE")) { 37 | pStmt->trig_type = XDB_TRIG_BEF_UPD + bAft; 38 | } else if (!strcasecmp (pTkn->token, "DELETE")) { 39 | pStmt->trig_type = XDB_TRIG_BEF_DEL + bAft; 40 | } else { 41 | XDB_EXPECT (0, XDB_E_STMT, "Expect { INSERT | UPDATE | DELETE }"); 42 | } 43 | 44 | type = xdb_next_token (pTkn); 45 | XDB_EXPECT ((XDB_TOK_ID==type) && !strcasecmp (pTkn->token, "ON"), XDB_E_STMT, "Expect ON"); 46 | 47 | type = xdb_next_token (pTkn); 48 | XDB_EXPECT (XDB_TOK_STR>=type, XDB_E_STMT, "Miss table name"); 49 | XDB_PARSE_DBTBLNAME(); 50 | 51 | XDB_EXPECT ((XDB_TOK_ID==type) && !strcasecmp (pTkn->token, "CALL"), XDB_E_STMT, "Expect CALL"); 52 | 53 | type = xdb_next_token (pTkn); 54 | XDB_EXPECT (XDB_TOK_STR>=type, XDB_E_STMT, "Miss func name"); 55 | pStmt->func_name = pTkn->token; 56 | 57 | return (xdb_stmt_t*)pStmt; 58 | 59 | error: 60 | xdb_stmt_free ((xdb_stmt_t*)pStmt); 61 | return NULL; 62 | } 63 | -------------------------------------------------------------------------------- /src/lib/xdb_thread.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #define xdb_yield() sched_yield() 13 | 14 | static inline void 15 | xdb_rwlock_init(xdb_rwlock_t *rwl) 16 | { 17 | rwl->count = 0; 18 | } 19 | 20 | static inline void 21 | xdb_rwlock_rdlock(xdb_rwlock_t *rwl) 22 | { 23 | int32_t x; 24 | int success = 0; 25 | 26 | while (success == 0) { 27 | x = __atomic_load_n(&rwl->count, __ATOMIC_RELAXED); 28 | // write lock is held 29 | if (xdb_unlikely (x < 0)) { 30 | xdb_yield (); 31 | continue; 32 | } 33 | success = __atomic_compare_exchange_n(&rwl->count, &x, x + 1, 1, 34 | __ATOMIC_ACQUIRE, __ATOMIC_RELAXED); 35 | } 36 | } 37 | 38 | static inline void 39 | xdb_rwlock_wrlock(xdb_rwlock_t *rwl) 40 | { 41 | int32_t x; 42 | int success = 0; 43 | 44 | while (success == 0) { 45 | x = __atomic_load_n(&rwl->count, __ATOMIC_RELAXED); 46 | // a lock is held 47 | if (xdb_unlikely (x != 0)) { 48 | xdb_yield (); 49 | continue; 50 | } 51 | success = __atomic_compare_exchange_n(&rwl->count, &x, -1, 1, 52 | __ATOMIC_ACQUIRE, __ATOMIC_RELAXED); 53 | } 54 | } 55 | 56 | static inline void 57 | xdb_rwlock_rdunlock(xdb_rwlock_t *rwl) 58 | { 59 | __atomic_fetch_sub(&rwl->count, 1, __ATOMIC_RELEASE); 60 | } 61 | 62 | static inline void 63 | xdb_rwlock_wrunlock(xdb_rwlock_t *rwl) 64 | { 65 | __atomic_store_n(&rwl->count, 0, __ATOMIC_RELEASE); 66 | } 67 | 68 | typedef pthread_t xdb_thread_t; 69 | 70 | static inline int 71 | xdb_create_thread (xdb_thread_t *pThread, const void *pAttr, void *(*start_routine) (void *), void *pArg) 72 | { 73 | return pthread_create (pThread, pAttr, start_routine, pArg); 74 | } 75 | 76 | #if 0 77 | #ifdef _WIN32 78 | typedef DWORD xdb_thread_t; 79 | static inline int 80 | xdb_create_thread (xdb_thread_t *thread, const void *attr, void *(*start_routine) (void *), void *arg) 81 | { 82 | (void) attr; 83 | CreateThread (0, 0, (LPTHREAD_START_ROUTINE)start_routine, arg, 0, thread); 84 | return 0; 85 | } 86 | #endif 87 | #endif 88 | -------------------------------------------------------------------------------- /src/core/xdb_index.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __XDB_INDEX_H__ 13 | #define __XDB_INDEX_H__ 14 | 15 | typedef struct { 16 | int (*idx_add) (xdb_conn_t *pConn, struct xdb_idxm_t* pIdxm, xdb_rowid new_rid, void *pRow); 17 | int (*idx_rem) (struct xdb_idxm_t* pIdxm, xdb_rowid rid, void *pRow); 18 | xdb_rowid (*idx_query) (xdb_conn_t *pConn, xdb_idxfilter_t *pIdxFilter, xdb_rowset_t *pRowSet); 19 | xdb_rowid (*idx_query2) (xdb_conn_t *pConn, struct xdb_idxm_t* pIdxm, void *pRow); 20 | int (*idx_create) (struct xdb_idxm_t *pIdxm); 21 | int (*idx_close) (struct xdb_idxm_t* pIdxm); 22 | int (*idx_drop) (struct xdb_idxm_t* pIdxm); 23 | int (*idx_sync) (struct xdb_idxm_t* pIdxm); 24 | int (*idx_init) (struct xdb_idxm_t* pIdxm); 25 | } xdb_idx_ops; 26 | 27 | typedef struct xdb_idxm_t { 28 | xdb_obj_t obj; 29 | struct xdb_tblm_t *pTblm; 30 | xdb_idx_type idx_type; 31 | bool bPrimary; 32 | bool bUnique; 33 | int fld_count; 34 | xdb_field_t *pFields[XDB_MAX_MATCH_COL]; 35 | char *pExtract[XDB_MAX_MATCH_COL]; 36 | uint32_t slot_mask; 37 | xdb_hashHdr_t *pHashHdr; 38 | xdb_rbtree_t *pRbtrHdr; 39 | xdb_rowid *pHashSlot; 40 | xdb_rowid slot_cap; 41 | xdb_rowid node_cap; 42 | xdb_hashNode_t *pHashNode; 43 | xdb_stgmgr_t stg_mgr; 44 | xdb_stgmgr_t stg_mgr2; 45 | xdb_idx_ops *pIdxOps; 46 | } xdb_idxm_t; 47 | 48 | XDB_STATIC int 49 | xdb_idx_addRow (xdb_conn_t *pConn, xdb_tblm_t *pTblm, xdb_rowid rid, void *pRow); 50 | 51 | XDB_STATIC int 52 | xdb_idx_addRow_bmp (xdb_conn_t *pConn, xdb_tblm_t *pTblm, xdb_rowid rid, void *pRow, uint8_t *idx_del, int count); 53 | 54 | XDB_STATIC int 55 | xdb_idx_remRow (xdb_tblm_t *pTblm, xdb_rowid rid, void *pRow); 56 | 57 | XDB_STATIC int 58 | xdb_idx_remRow_bmp (xdb_tblm_t *pTblm, xdb_rowid rid, void *pRow, uint8_t *idx_del, int count); 59 | 60 | XDB_STATIC xdb_idxm_t* 61 | xdb_find_index (xdb_tblm_t *pTblm, const char *idx_name); 62 | 63 | XDB_STATIC const char* 64 | xdb_idx2str(xdb_idx_type tp); 65 | 66 | #endif // __XDB_INDEX_H__ 67 | -------------------------------------------------------------------------------- /test/xdb_smoke_func.c: -------------------------------------------------------------------------------- 1 | UTEST_I(XdbTestRows, agg_count, 2) 2 | { 3 | xdb_res_t *pRes; 4 | xdb_row_t *pRow; 5 | xdb_conn_t *pConn = utest_fixture->pConn; 6 | 7 | pRes = xdb_exec (pConn, "SELECT COUNT(*) FROM student"); 8 | CHECK_EXP(pRes, 1, ASSERT_EQ(xdb_column_int(pRes, pRow, 0), 7)); 9 | 10 | pRes = xdb_exec (pConn, "SELECT COUNT(id) FROM student"); 11 | CHECK_EXP(pRes, 1, ASSERT_EQ(xdb_column_int(pRes, pRow, 0), 7)); 12 | 13 | pRes = xdb_exec (pConn, "SELECT COUNT(*) FROM student WHERE id > 1003"); 14 | CHECK_EXP(pRes, 1, ASSERT_EQ(xdb_column_int(pRes, pRow, 0), 3)); 15 | 16 | pRes = xdb_exec (pConn, "SELECT COUNT(id) FROM student WHERE id > 1003"); 17 | CHECK_EXP(pRes, 1, ASSERT_EQ(xdb_column_int(pRes, pRow, 0), 3)); 18 | } 19 | 20 | UTEST_I(XdbTestRows, agg_minmax, 2) 21 | { 22 | xdb_res_t *pRes; 23 | xdb_row_t *pRow; 24 | xdb_conn_t *pConn = utest_fixture->pConn; 25 | 26 | pRes = xdb_exec (pConn, "SELECT MIN(id) FROM student"); 27 | CHECK_EXP(pRes, 1, ASSERT_EQ(xdb_column_int(pRes, pRow, 0), 1000)); 28 | 29 | pRes = xdb_exec (pConn, "SELECT MAX(id) FROM student"); 30 | CHECK_EXP(pRes, 1, ASSERT_EQ(xdb_column_int(pRes, pRow, 0), 1006)); 31 | 32 | pRes = xdb_exec (pConn, "SELECT MIN(id),MAX(id) FROM student"); 33 | CHECK_EXP(pRes, 1, ASSERT_EQ(xdb_column_int(pRes, pRow, 0), 1000); ASSERT_EQ(xdb_column_int(pRes, pRow, 1), 1006)); 34 | 35 | pRes = xdb_exec (pConn, "SELECT MIN(id),MAX(id) FROM student WHERE id > 1001"); 36 | CHECK_EXP(pRes, 1, ASSERT_EQ(xdb_column_int(pRes, pRow, 0), 1002); ASSERT_EQ(xdb_column_int(pRes, pRow, 1), 1006)); 37 | } 38 | 39 | UTEST_I(XdbTestRows, agg_sumavg, 2) 40 | { 41 | xdb_res_t *pRes; 42 | xdb_row_t *pRow; 43 | xdb_conn_t *pConn = utest_fixture->pConn; 44 | 45 | pRes = xdb_exec (pConn, "SELECT SUM(id) FROM student"); 46 | int sum = 1000+1001+1002+1003+1004+1005+1006; 47 | CHECK_EXP(pRes, 1, ASSERT_EQ(xdb_column_int(pRes, pRow, 0), sum)); 48 | 49 | pRes = xdb_exec (pConn, "SELECT AVG(id) FROM student"); 50 | CHECK_EXP(pRes, 1, ASSERT_NEAR(xdb_column_double(pRes, pRow, 0), (double)sum/7, 0.000001)); 51 | 52 | pRes = xdb_exec (pConn, "SELECT SUM(id),AVG(id) FROM student"); 53 | CHECK_EXP(pRes, 1, ASSERT_EQ(xdb_column_int(pRes, pRow, 0), sum); ASSERT_NEAR(xdb_column_double(pRes, pRow, 1), (double)sum/7, 0.000001)); 54 | 55 | pRes = xdb_exec (pConn, "SELECT SUM(id),AVG(id) FROM student WHERE id > 1001"); 56 | sum -= (1000 + 1001); 57 | CHECK_EXP(pRes, 1, ASSERT_EQ(xdb_column_int(pRes, pRow, 0), sum); ASSERT_NEAR(xdb_column_double(pRes, pRow, 1), (double)sum/5, 0.000001)); 58 | } 59 | 60 | -------------------------------------------------------------------------------- /src/parser/xdb_parser_svr.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | XDB_STATIC xdb_stmt_t* 13 | xdb_parse_create_server (xdb_conn_t* pConn, xdb_token_t *pTkn) 14 | { 15 | xdb_stmt_svr_t *pStmt = &pConn->stmt_union.svr_stmt; 16 | memset (pStmt, 0, sizeof (*pStmt)); 17 | pStmt->stmt_type = XDB_STMT_CREATE_SVR; 18 | 19 | xdb_token_type type = xdb_next_token (pTkn); 20 | XDB_EXPECT (XDB_TOK_STR>=type, XDB_E_STMT, "Miss server name"); 21 | 22 | if ((XDB_TOK_ID==type) && !strcasecmp (pTkn->token, "IF")) { 23 | type = xdb_next_token (pTkn); 24 | XDB_EXPECT ((XDB_TOK_ID==type) && !strcasecmp (pTkn->token, "NOT"), XDB_E_STMT, "Miss NOT"); 25 | type = xdb_next_token (pTkn); 26 | XDB_EXPECT ((XDB_TOK_ID==type) && !strcasecmp (pTkn->token, "EXISTS"), XDB_E_STMT, "Miss EXISTS"); 27 | type = xdb_next_token (pTkn); 28 | XDB_EXPECT (XDB_TOK_ID==type, XDB_E_STMT, "Miss server name"); 29 | pStmt->bIfExistOrNot = true; 30 | } 31 | pStmt->svr_name = pTkn->token; 32 | pStmt->svr_port = XDB_SVR_PORT; 33 | type = xdb_next_token (pTkn); 34 | if (XDB_TOK_ID == type) { 35 | if (0 == strcasecmp (pTkn->token, "PORT")) { 36 | type = xdb_next_token (pTkn); 37 | XDB_EXPECT (XDB_TOK_EQ==type, XDB_E_STMT, "Miss ="); 38 | type = xdb_next_token (pTkn); 39 | XDB_EXPECT (XDB_TOK_NUM==type, XDB_E_STMT, "Miss Port"); 40 | pStmt->svr_port = atoi (pTkn->token); 41 | XDB_EXPECT (pStmt->svr_port>0 && pStmt->svr_port<65536, XDB_E_STMT, "Wrong Port number should be in range (0, 65536)"); 42 | } 43 | } 44 | 45 | return (xdb_stmt_t*)pStmt; 46 | 47 | error: 48 | xdb_stmt_free ((xdb_stmt_t*)pStmt); 49 | return NULL; 50 | } 51 | 52 | XDB_STATIC xdb_stmt_t* 53 | xdb_parse_drop_server (xdb_conn_t* pConn, xdb_token_t *pTkn) 54 | { 55 | xdb_stmt_svr_t *pStmt = &pConn->stmt_union.svr_stmt; 56 | memset (pStmt, 0, sizeof (*pStmt)); 57 | pStmt->stmt_type = XDB_STMT_DROP_SVR; 58 | 59 | xdb_token_type type = xdb_next_token (pTkn); 60 | XDB_EXPECT (XDB_TOK_STR>=type, XDB_E_STMT, "Miss server name"); 61 | pStmt->svr_name = pTkn->token; 62 | 63 | return (xdb_stmt_t*)pStmt; 64 | 65 | error: 66 | xdb_stmt_free ((xdb_stmt_t*)pStmt); 67 | return NULL; 68 | } 69 | 70 | -------------------------------------------------------------------------------- /examples/go/bench.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "math/rand" 7 | "time" 8 | "github.com/crossdb-org/crossdb/connector/go" 9 | ) 10 | 11 | const ( 12 | numUsers = 1000000 13 | numBenchmarkRuns = 1000 14 | ) 15 | 16 | var firstNames = []string{"John", "Jane", "Michael", "Emily", "David", "Sarah", "Christopher", "Jessica", "Matthew", "Jennifer"} 17 | var lastNames = []string{"Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez"} 18 | 19 | func generateRandomName() string { 20 | return firstNames[rand.Intn(len(firstNames))] + " " + lastNames[rand.Intn(len(lastNames))] 21 | } 22 | 23 | func main() { 24 | rand.Seed(time.Now().UnixNano()) 25 | 26 | // Open a connection to CrossDB 27 | conn, err := crossdb.Open("./benchmark_db") 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | defer conn.Close() 32 | 33 | // Create the users table 34 | _, err = conn.Exec("DROP TABLE IF EXISTS users") 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | _, err = conn.Exec("CREATE TABLE users (id INT PRIMARY KEY, name CHAR(50), age INT)") 39 | if err != nil { 40 | log.Fatal(err) 41 | } 42 | 43 | // Insert 1,000,000 users 44 | fmt.Println("Inserting 1,000,000 users...") 45 | startTime := time.Now() 46 | for i := 1; i <= numUsers; i++ { 47 | name := generateRandomName() 48 | age := rand.Intn(50) + 20 // Random age between 20 and 69 49 | _, err := conn.Exec(fmt.Sprintf("INSERT INTO users (id, name, age) VALUES (%d, '%s', %d)", i, name, age)) 50 | if err != nil { 51 | log.Printf("Error inserting user %d: %v", i, err) 52 | } 53 | if i%100000 == 0 { 54 | fmt.Printf("%d users inserted...\n", i) 55 | } 56 | } 57 | fmt.Printf("Insertion completed in %v\n", time.Since(startTime)) 58 | 59 | // Benchmark random SELECT with LIKE queries 60 | fmt.Println("\nRunning benchmark...") 61 | totalDuration := time.Duration(0) 62 | for i := 0; i < numBenchmarkRuns; i++ { 63 | searchName := firstNames[rand.Intn(len(firstNames))] 64 | query := fmt.Sprintf("SELECT * FROM users WHERE name LIKE '%%%s%%'", searchName) 65 | 66 | startTime := time.Now() 67 | res, err := conn.Exec(query) 68 | if err != nil { 69 | log.Printf("Error executing query: %v", err) 70 | continue 71 | } 72 | duration := time.Since(startTime) 73 | totalDuration += duration 74 | 75 | // Count the results 76 | count := 0 77 | for res.FetchRow() != nil { 78 | count++ 79 | } 80 | 81 | fmt.Printf("Run %d: Query '%s' returned %d results in %v\n", i+1, query, count, duration) 82 | } 83 | 84 | averageDuration := totalDuration / numBenchmarkRuns 85 | fmt.Printf("\nBenchmark completed.\n") 86 | fmt.Printf("Average query time: %v\n", averageDuration) 87 | } 88 | -------------------------------------------------------------------------------- /src/core/xdb_vdata.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __XDB_VDATA_H__ 13 | #define __XDB_VDATA_H__ 14 | 15 | #define XDB_VTYPE_NONE 255 // NO vdata 16 | //#define XDB_VTYPE_VID 17 | #define XDB_VTYPE_DATA 254 // vdata is after row 18 | #define XDB_VTYPE_PTR 253 // vdata is in ptr array 19 | #define XDB_VTYPE_MAX 76 // vdata is in db 20 | 21 | #define XDB_VTYPE_OK(type) (typestg_mgr[type]; 59 | if (xdb_unlikely (NULL == pStgMgr->pStgHdr)) { 60 | xdb_vdata_create (pVdatm, type); 61 | } 62 | return XDB_IDPTR(pStgMgr, vid); 63 | } 64 | 65 | // MSB=1 is for use/free mark, next 3b is refcnt 66 | #define XDB_VDAT_LENBITS 28 67 | #define XDB_VDAT_LENMASK ((1<stg_mgr[type]; 82 | if (xdb_unlikely (NULL == pStgMgr->pStgHdr)) { 83 | xdb_vdata_create (pVdatm, type); 84 | } 85 | return xdb_stg_expand (&pVdatm->stg_mgr[type]); 86 | } 87 | 88 | #endif // __XDB_VDATA_H__ 89 | -------------------------------------------------------------------------------- /src/core/xdb_store.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __XDB_STORE_H__ 13 | #define __XDB_STORE_H__ 14 | 15 | #define XDB_IDPTR(pStgMgr, id) ((pStgMgr)->pBlkDat1 + id * (pStgMgr)->blk_size) 16 | 17 | typedef struct xdb_store_ops { 18 | int (*store_open) (const char *file, xdb_fd *pFd, xdb_size *pSize); 19 | int (*store_close) (xdb_fd *pFd, xdb_size size, void **ptr); 20 | int (*store_drop) (char *file); 21 | int (*store_map) (xdb_fd fd, xdb_size size, bool bTruncate, void **ptr); 22 | int (*store_remap) (xdb_fd fd, xdb_size old_size, xdb_size new_size, void **ptr); 23 | int (*store_sync) (xdb_fd fd, xdb_size size, void *ptr, bool bAsync); 24 | } xdb_store_ops; 25 | 26 | #define XDB_ROW_MASK (0x3) 27 | 28 | typedef enum { 29 | XDB_ROW_FREE = 0, // 00 ->dirty->trans->commit 30 | XDB_ROW_DIRTY = 1, // 01 31 | XDB_ROW_COMMIT = 2, // 10 32 | XDB_ROW_TRANS = 3, // 11 in trans, not commit 33 | } xdb_rowCtrl_e; 34 | 35 | #define XDB_ROW_CTRL(pStgHdr,pRow) *((uint8_t*)(pRow) + pStgHdr->ctl_off) 36 | 37 | #define XDB_STG_NOALLOC (1<<0) 38 | #define XDB_STG_CLEAR (1<<1) 39 | 40 | typedef struct xdb_stghdr_t { 41 | uint32_t stg_magic; 42 | uint32_t revision; 43 | uint32_t blk_size; 44 | uint32_t ctl_off; 45 | uint16_t blk_off; 46 | uint8_t blk_type; 47 | uint8_t blk_flags; 48 | xdb_rowid blk_head; 49 | xdb_rowid blk_tail; 50 | xdb_rowid blk_alloc; 51 | xdb_rowid blk_maxid; 52 | xdb_rowid blk_cap; 53 | xdb_rowid blk_limit; 54 | uint32_t rsvd2[5]; 55 | } xdb_stghdr_t; 56 | 57 | typedef struct xdb_stgmgr_t { 58 | xdb_stghdr_t *pStgHdr; 59 | xdb_fd stg_fd; 60 | xdb_store_ops *pOps; 61 | void *pBlkDat; 62 | void *pBlkDat1; 63 | uint32_t blk_size; 64 | char *file; 65 | } xdb_stgmgr_t; 66 | 67 | #define XDB_STG_CAP(pStgMgr) (pStgMgr)->pStgHdr->blk_cap 68 | #define XDB_STG_MAXID(pStgMgr) (pStgMgr)->pStgHdr->blk_maxid 69 | #define XDB_STG_ALLOC(pStgMgr) (pStgMgr)->pStgHdr->blk_alloc 70 | 71 | #define XDB_ROW_VALID(pTblm,pRow) ((XDB_ROW_CTRL (pTblm->stg_mgr.pStgHdr, pRow) & 0x3) >= XDB_ROW_COMMIT) 72 | 73 | XDB_STATIC xdb_rowid 74 | xdb_stg_alloc (xdb_stgmgr_t *pStgMgr, void **ppRow); 75 | 76 | XDB_STATIC int 77 | xdb_stg_expand (xdb_stgmgr_t *pStgMgr); 78 | 79 | XDB_STATIC void 80 | xdb_stg_free (xdb_stgmgr_t *pStgMgr, xdb_rowid rowid, void *pRow); 81 | 82 | #endif // __XDB_STORE_H__ 83 | -------------------------------------------------------------------------------- /jni/include/db_jni.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class com_open_crossdb_jni_core_CrossDBJNI */ 4 | 5 | #ifndef _Included_com_open_crossdb_jni_core_CrossDBJNI 6 | #define _Included_com_open_crossdb_jni_core_CrossDBJNI 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: com_open_crossdb_jni_core_CrossDBJNI 12 | * Method: jniOpenDB 13 | * Signature: (Ljava/lang/String;)J 14 | */ 15 | JNIEXPORT jlong JNICALL Java_com_open_crossdb_jni_core_CrossDBJNI_jniOpenDB 16 | (JNIEnv *, jclass, jstring); 17 | 18 | /* 19 | * Class: com_open_crossdb_jni_core_CrossDBJNI 20 | * Method: jniBegin 21 | * Signature: (J)I 22 | */ 23 | JNIEXPORT jint JNICALL Java_com_open_crossdb_jni_core_CrossDBJNI_jniBegin 24 | (JNIEnv *, jclass, jlong); 25 | 26 | /* 27 | * Class: com_open_crossdb_jni_core_CrossDBJNI 28 | * Method: jniCommit 29 | * Signature: (J)I 30 | */ 31 | JNIEXPORT jint JNICALL Java_com_open_crossdb_jni_core_CrossDBJNI_jniCommit 32 | (JNIEnv *, jclass, jlong); 33 | 34 | /* 35 | * Class: com_open_crossdb_jni_core_CrossDBJNI 36 | * Method: jniRollback 37 | * Signature: (J)I 38 | */ 39 | JNIEXPORT jint JNICALL Java_com_open_crossdb_jni_core_CrossDBJNI_jniRollback 40 | (JNIEnv *, jclass, jlong); 41 | 42 | /* 43 | * Class: com_open_crossdb_jni_core_CrossDBJNI 44 | * Method: jniXdbExec 45 | * Signature: (JLjava/lang/String;)J 46 | */ 47 | JNIEXPORT jlong JNICALL Java_com_open_crossdb_jni_core_CrossDBJNI_jniXdbExec 48 | (JNIEnv *, jclass, jlong, jstring); 49 | 50 | /* 51 | * Class: com_open_crossdb_jni_core_CrossDBJNI 52 | * Method: jniFetchRow 53 | * Signature: (J)J 54 | */ 55 | JNIEXPORT jlong JNICALL Java_com_open_crossdb_jni_core_CrossDBJNI_jniFetchRow 56 | (JNIEnv *, jclass, jlong); 57 | 58 | /* 59 | * Class: com_open_crossdb_jni_core_CrossDBJNI 60 | * Method: jniColumnInt 61 | * Signature: (JJI)I 62 | */ 63 | JNIEXPORT jint JNICALL Java_com_open_crossdb_jni_core_CrossDBJNI_jniColumnInt 64 | (JNIEnv *, jclass, jlong, jlong, jint); 65 | 66 | /* 67 | * Class: com_open_crossdb_jni_core_CrossDBJNI 68 | * Method: jniColumnStr 69 | * Signature: (JJI)Ljava/lang/String; 70 | */ 71 | JNIEXPORT jstring JNICALL Java_com_open_crossdb_jni_core_CrossDBJNI_jniColumnStr 72 | (JNIEnv *, jclass, jlong, jlong, jint); 73 | 74 | /* 75 | * Class: com_open_crossdb_jni_core_CrossDBJNI 76 | * Method: jniColumnFloat 77 | * Signature: (JJI)F 78 | */ 79 | JNIEXPORT jfloat JNICALL Java_com_open_crossdb_jni_core_CrossDBJNI_jniColumnFloat 80 | (JNIEnv *, jclass, jlong, jlong, jint); 81 | 82 | /* 83 | * Class: com_open_crossdb_jni_core_CrossDBJNI 84 | * Method: jniClose 85 | * Signature: (J)I 86 | */ 87 | JNIEXPORT jint JNICALL Java_com_open_crossdb_jni_core_CrossDBJNI_jniClose 88 | (JNIEnv *, jclass, jlong); 89 | 90 | #ifdef __cplusplus 91 | } 92 | #endif 93 | #endif 94 | -------------------------------------------------------------------------------- /src/core/xdb_expr.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | const uint8_t s_xdb_tok_pri[] = 13 | { 14 | // logical 15 | [XDB_TOK_OR] = 0, 16 | [XDB_TOK_AND] = 1, 17 | 18 | // comparison operator 19 | [XDB_TOK_EQ] = 2, 20 | [XDB_TOK_NE] = 2, 21 | [XDB_TOK_GE] = 2, 22 | [XDB_TOK_GT] = 2, 23 | [XDB_TOK_LE] = 2, 24 | [XDB_TOK_LT] = 2, 25 | [XDB_TOK_IN] = 2, 26 | [XDB_TOK_BTWN] = 2, 27 | 28 | // bit operator: | & ^ << >> 29 | 30 | // arithmetic operator 31 | [XDB_TOK_ADD] = 6, 32 | [XDB_TOK_SUB] = 6, 33 | [XDB_TOK_MUL] = 7, 34 | [XDB_TOK_DIV] = 7, 35 | [XDB_TOK_MOD] = 7, 36 | 37 | [XDB_TOK_LP] = 10, 38 | [XDB_TOK_RP] = 10, 39 | }; 40 | 41 | int xdb_expr_eval (xdb_value_t *pOut, xdb_value_t *pExpVal, int id, int count) 42 | { 43 | int nid; 44 | int pri = s_xdb_tok_pri[pExpVal[id+1].sup_type]; 45 | 46 | for (;id < count; id = nid) { 47 | xdb_value_t *pVal, val; 48 | if ((id+3 < count) && (s_xdb_tok_pri[pExpVal[id+3].sup_type] > pri)) { 49 | val.sup_type = XDB_TYPE_BIGINT; 50 | val.ival = 0; 51 | nid = xdb_expr_eval (&val, pExpVal, id + 1, count); 52 | pVal = &val; 53 | } else { 54 | nid = id + 3; 55 | pVal = &pExpVal[id+3]; 56 | } 57 | 58 | switch (pVal[id+1].sup_type) { 59 | case XDB_TOK_ADD: 60 | switch (pVal->sup_type) { 61 | case XDB_TOK_BIGINT: 62 | pOut->ival += pVal->ival; 63 | break; 64 | case XDB_TOK_DOUBLE: 65 | pOut->fval += pVal->fval; 66 | break; 67 | } 68 | break; 69 | case XDB_TOK_SUB: 70 | switch (pVal[id].sup_type) { 71 | case XDB_TOK_BIGINT: 72 | pOut->ival -= pVal->ival; 73 | break; 74 | case XDB_TOK_DOUBLE: 75 | pOut->fval -= pVal->fval; 76 | break; 77 | } 78 | case XDB_TOK_MUL: 79 | switch (pVal[id].sup_type) { 80 | case XDB_TOK_BIGINT: 81 | pOut->ival *= pVal->ival; 82 | break; 83 | case XDB_TOK_DOUBLE: 84 | pOut->fval *= pVal->fval; 85 | break; 86 | } 87 | } 88 | } 89 | return 0; 90 | } 91 | 92 | #if 0 93 | void expr_test () 94 | { 95 | xdb_value_t expr[] = { 96 | {.sup_type = XDB_TOK_BIGINT, .ival = 4}, 97 | {.sup_type = XDB_TOK_ADD}, 98 | {.sup_type = XDB_TOK_BIGINT, .ival = 3}, 99 | }; 100 | 101 | xdb_value_t val = {.sup_type = XDB_TYPE_BIGINT, .ival = 0}; 102 | xdb_expr_eval (&val, expr, 0, 3); 103 | printf ("%d\n", val.ival); 104 | } 105 | #endif 106 | -------------------------------------------------------------------------------- /src/core/xdb_conn.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __XDB_CONN_H__ 13 | #define __XDB_CONN_H__ 14 | 15 | typedef enum { 16 | XDB_FMT_TABLE, // shell 17 | XDB_FMT_VERTICAL, // shell 18 | XDB_FMT_JSON, // shell 19 | XDB_FMT_JSONLINE, // shell 20 | XDB_FMT_JSONMIME, // shell 21 | XDB_FMT_NDJSON, // shell 22 | 23 | XDB_FMT_NATIVELE = 32, 24 | XDB_FMT_NATIVEBE, 25 | } xdb_format_t; 26 | 27 | typedef struct xdb_queryRes_t { 28 | uint64_t buf_len; 29 | uint64_t buf_free; // if not -1 means but not freed yet 30 | xdb_stmt_select_t *pStmt; 31 | 32 | xdb_objm_t *pMetaHash; 33 | xdb_conn_t *pConn; // pConn must be before res 34 | xdb_res_t res; // must be last 35 | } xdb_queryRes_t; 36 | 37 | typedef struct xdb_conn_t { 38 | uint64_t session_id; 39 | int32_t ref_cnt; 40 | pid_t pid; 41 | char connect_time[16]; 42 | uint64_t stmt_times; 43 | 44 | struct xdb_dbm_t* pCurDbm; 45 | 46 | xdb_conn_t *pConn; // pConn must be before conn_res 47 | xdb_res_t conn_res; // store query result without meta and rows 48 | xdb_msg_t conn_msg; // conn_msg must be after conn_res 49 | 50 | int sockfd; 51 | FILE *conn_stdout; 52 | 53 | xdb_queryRes_t *pQueryRes; // store query result with meta and rows 54 | 55 | char cur_db[XDB_NAME_LEN+1]; // current default DB 56 | char host[128]; // server side to save peer addr 57 | uint16_t port; 58 | char user[XDB_NAME_LEN+1]; 59 | char passwd[XDB_NAME_LEN+1]; 60 | 61 | xdb_status_t status; 62 | xdb_stmt_union_t stmt_union; 63 | 64 | char *pSql; 65 | int sqlbuf_len; 66 | char *pNxtSql; 67 | char sql_buf[4096]; 68 | 69 | #if (XDB_ENABLE_SERVER == 1) 70 | xdb_server_t *pServer; 71 | #endif 72 | xdb_rowset_t row_set; 73 | 74 | bool bAutoTrans; 75 | bool bInTrans; 76 | bool bAutoCommit; 77 | xdb_lv2bmp_t dbTrans_bmp; 78 | 79 | xdb_dbTrans_t *pDbTrans[XDB_MAX_DB]; 80 | 81 | bool conn_client; 82 | xdb_format_t res_format; 83 | 84 | char *poll_buf; 85 | uint32_t poll_size; 86 | 87 | struct xdb_subscribe_t *pSubscribe; 88 | } xdb_conn_t; 89 | 90 | XDB_STATIC void 91 | xdb_conn_init (xdb_conn_t* pConn); 92 | 93 | static inline void 94 | xdb_init_rowlist (xdb_queryRes_t *pQueryRes) 95 | { 96 | pQueryRes->res.row_data = (uintptr_t)((void*)pQueryRes + sizeof (*pQueryRes) + pQueryRes->res.meta_len); 97 | } 98 | 99 | #endif // __XDB_CONN_H__ 100 | -------------------------------------------------------------------------------- /src/core/xdb_wal.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __XDB_WAL_H__ 13 | #define __XDB_WAL_H__ 14 | 15 | #if 0 16 | #define XDB_WAL_LENBITS 28 17 | #define XDB_WAL_LENMASK ((1<stg_mgr.pStgHdr)) 67 | 68 | 69 | #define XDB_WAL_MAX_SIZE (256*1024*1024) 70 | 71 | XDB_STATIC int 72 | xdb_trans_db_wal (uint32_t did, void *pArg); 73 | 74 | XDB_STATIC bool 75 | xdb_wal_switch (struct xdb_dbm_t *pDbm); 76 | 77 | XDB_STATIC xdb_ret 78 | __xdb_wal_flush (xdb_walm_t *pWalMgmt, bool bFlush); 79 | 80 | XDB_STATIC void 81 | xdb_wal_close (struct xdb_dbm_t *pDbm); 82 | 83 | XDB_STATIC void 84 | xdb_wal_drop (struct xdb_dbm_t *pDbm); 85 | 86 | XDB_STATIC int 87 | xdb_wal_create (xdb_conn_t *pConn, struct xdb_dbm_t *pDbm); 88 | 89 | XDB_STATIC void 90 | xdb_wal_dump (struct xdb_dbm_t *pDbm); 91 | 92 | XDB_STATIC xdb_ret 93 | xdb_wal_redo (struct xdb_dbm_t *pDbm); 94 | 95 | #endif // __XDB_WAL_H__ 96 | -------------------------------------------------------------------------------- /src/core/xdb_table.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __XDB_TBL_H__ 13 | #define __XDB_TBL_H__ 14 | 15 | typedef struct xdb_tblm_t { 16 | xdb_obj_t obj; 17 | uint16_t fld_count; 18 | uint16_t vfld_count; 19 | uint16_t null_bytes; 20 | uint32_t row_size; 21 | uint32_t vtype_off; 22 | uint32_t null_off; 23 | uint32_t blk_size; 24 | uint64_t cur_ts; 25 | 26 | bool bLog; 27 | bool bMemory; 28 | bool bPrimary; 29 | xdb_lockmode_e lock_mode; 30 | 31 | struct xdb_dbm_t *pDbm; 32 | 33 | struct xdb_field_t *pFields; 34 | struct xdb_field_t **ppFields; 35 | struct xdb_field_t **ppVFields; 36 | uint8_t *pNullBytes; 37 | 38 | xdb_objm_t fld_objm; 39 | 40 | xdb_objm_t trig_objm[XDB_TRIG_MAX]; 41 | 42 | uint32_t meta_size; 43 | xdb_meta_t *pMeta; 44 | 45 | xdb_vdatm_t *pVdatm; 46 | 47 | xdb_objm_t idx_objm; 48 | uint8_t idx_order[XDB_MAX_INDEX]; 49 | 50 | xdb_objm_t fkey_objm; 51 | xdb_objm_t fkeyref_objm; 52 | 53 | xdb_rowid row_cap; 54 | 55 | xdb_stgmgr_t stg_mgr; 56 | 57 | xdb_rwlock_t tbl_lock; 58 | xdb_rwlock_t stg_lock; 59 | 60 | xdb_vec_t sub_list; 61 | 62 | xdb_bmp_t *pAuditRows; 63 | 64 | xdb_field_t* pTtlFld; 65 | uint64_t ttl_expire; 66 | uint64_t ttl_last; 67 | xdb_time_unit ttl_unit; 68 | } xdb_tblm_t; 69 | 70 | typedef struct xdb_tbl_t { 71 | xdb_stghdr_t blk_hdr; 72 | xdb_rowid row_count; 73 | uint8_t tbl_dirty; 74 | uint8_t rsvd[3]; 75 | uint64_t lastchg_id; // last commit id 76 | uint64_t flush_id; // last flush id 77 | //statistics 78 | uint64_t rsvd2[13]; 79 | uint8_t pRowDat[]; 80 | } xdb_tbl_t; 81 | 82 | #define XDB_TBLPTR(pTblm) ((xdb_tbl_t*)pTblm->stg_mgr.pStgHdr) 83 | 84 | 85 | static inline xdb_tblm_t* 86 | xdb_find_table (xdb_dbm_t *pDbm, const char *tbl_name) 87 | { 88 | return xdb_objm_get (&pDbm->db_objm, tbl_name); 89 | } 90 | 91 | static inline xdb_field_t* 92 | xdb_find_field (xdb_tblm_t *pTblm, const char *fld_name, int len) 93 | { 94 | return xdb_objm_get2 (&pTblm->fld_objm, fld_name, len); 95 | } 96 | 97 | XDB_STATIC int 98 | __xdb_repair_table (xdb_tblm_t *pTblm, uint32_t flags); 99 | 100 | XDB_STATIC int 101 | xdb_close_table (xdb_tblm_t *pTblm); 102 | 103 | XDB_STATIC int 104 | xdb_drop_table (xdb_tblm_t *pTblm); 105 | 106 | XDB_STATIC int 107 | xdb_dump_create_table (xdb_tblm_t *pTblm, char buf[], xdb_size size, uint32_t flags); 108 | 109 | XDB_STATIC int 110 | xdb_flush_table (xdb_tblm_t *pTblm, uint32_t flags); 111 | 112 | #endif // __XDB_TBL_H__ 113 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | help: 2 | @echo "make build Build crossdb library and tool" 3 | @echo "make debug Build crossdb library and tool with debug" 4 | @echo "make run Run crossdb tool" 5 | @echo "make clean Clean build result" 6 | @echo "make install Install crossdb(lib&tool&header) to Linux/MacOS/FreeBSD" 7 | @echo "make uninstall Uninstall crossdb from Linux/MacOS/FreeBSD" 8 | @echo "make example Build and run example (need to install crossdb first)" 9 | @echo "make smoketest Build and run smoke test (need to install crossdb first)" 10 | @echo "make bench Build and run bench test (need to install crossdb first)" 11 | @echo "make bench-sqlite Build and run sqlite bench test (need to install sqlite3 first)" 12 | @echo "make bench-stlmap Build and run C++ STL Map and HashMap(unordered_map) bench test" 13 | @echo "make bench-boostmidx Build and run C++ Boost MultiIndex Order and Hash bench test" 14 | 15 | .PHONY: build 16 | build: 17 | ifeq ($(shell uname -s), Darwin) 18 | else 19 | $(CC) -o build/libcrossdb.so -fPIC -shared -lpthread -O2 src/crossdb.c 20 | endif 21 | $(CC) -o build/xdb-cli src/xdb-cli.c -O2 -lpthread 22 | cp include/crossdb.h build/ 23 | 24 | debug: 25 | $(CC) -o build/libcrossdb.so -fPIC -lpthread -shared -g -DXDB_DEBUG src/crossdb.c 26 | $(CC) -o build/xdb-cli src/xdb-cli.c -lpthread -g 27 | cp include/crossdb.h build/ 28 | 29 | smoketest: 30 | make -C test/ 31 | 32 | run: 33 | build/xdb-cli 34 | 35 | clean: 36 | rm -rf build/* 37 | make -C examples/c/ clean 38 | make -C bench/basic/ clean 39 | make -C test/ clean 40 | 41 | wall: 42 | $(CC) -o build/xdb-cli src/xdb-cli.c -lpthread -O2 -Wall 43 | 44 | gdb: 45 | $(CC) -o build/xdb-cli src/xdb-cli.c -lpthread -fsanitize=address -g 46 | gdb build/xdb-cli 47 | 48 | install: 49 | @mkdir -p /usr/local/lib/ 50 | @mkdir -p /usr/local/bin/ 51 | install -c build/xdb-cli /usr/local/bin/ 52 | ifeq ($(shell uname -s), Darwin) 53 | $(CC) -o /usr/local/lib/libcrossdb.dylib -dynamiclib -lpthread -O2 src/crossdb.c 54 | install -c build/crossdb.h $(shell xcrun --show-sdk-path)/usr/include 55 | else 56 | @mkdir -p /usr/local/include/ 57 | install -c build/crossdb.h /usr/local/include/ 58 | install -c build/libcrossdb.so /usr/local/lib/ 59 | ldconfig 60 | endif 61 | 62 | winpack: 63 | mkdir -p build/crossdb-win64/include 64 | mkdir -p build/crossdb-win64/bin 65 | mkdir -p build/crossdb-win64/lib 66 | cp build/crossdb.h build/crossdb-win64/include 67 | cp build/xdb-cli.exe build/crossdb-win64/bin 68 | cp build/libcrossdb.dll build/libcrossdb.lib build/crossdb-win64/lib 69 | 70 | uninstall: 71 | rm -rf /usr/local/bin/xdb-cli 72 | ifeq ($(shell uname -s), Darwin) 73 | rm -rf /usr/local/lib/libcrossdb.dylib 74 | rm -rf $(shell xcrun --show-sdk-path)/usr/include/crossdb.h 75 | else 76 | rm -rf /usr/local/lib/libcrossdb.so 77 | rm -rf /usr/local/include/crossdb.h 78 | endif 79 | 80 | example: 81 | make -C examples/c/ 82 | 83 | .PHONY: bench 84 | bench: 85 | make -C bench/basic/ 86 | 87 | bench-sqlite: 88 | make -C bench/basic/ sqlite 89 | 90 | bench-stlmap: 91 | make -C bench/basic/ stlmap 92 | 93 | bench-boostmidx: 94 | make -C bench/basic/ boostmidx 95 | -------------------------------------------------------------------------------- /jni/db_jni.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Administrator on 2024/9/14. 3 | // 4 | #include 5 | #include "crossdb.h" 6 | 7 | JNIEXPORT jlong JNICALL Java_com_open_crossdb_jni_core_CrossDBJNI_jniOpenDB 8 | (JNIEnv *env, jclass clazz, jstring path){ 9 | const char *c_tpath = env->GetStringUTFChars(path, NULL); 10 | xdb_conn_t *pConn = xdb_open (c_tpath); 11 | XDB_CHECK (NULL != pConn, printf ("failed to create DB\n"); return -1;) 12 | env->ReleaseStringUTFChars(path, c_tpath); 13 | return reinterpret_cast(pConn); 14 | } 15 | 16 | 17 | JNIEXPORT jint JNICALL Java_com_open_crossdb_jni_core_CrossDBJNI_jniBegin 18 | (JNIEnv *env, jclass clazz, jlong conn){ 19 | xdb_conn_t *pConn = reinterpret_cast(conn); 20 | xdb_begin(pConn); 21 | return 1; 22 | } 23 | 24 | 25 | JNIEXPORT jint JNICALL Java_com_open_crossdb_jni_core_CrossDBJNI_jniCommit 26 | (JNIEnv *env, jclass clazz, jlong conn){ 27 | xdb_conn_t *pConn = reinterpret_cast(conn); 28 | xdb_commit(pConn); 29 | return 1; 30 | } 31 | 32 | 33 | JNIEXPORT jint JNICALL Java_com_open_crossdb_jni_core_CrossDBJNI_jniRollback 34 | (JNIEnv *env, jclass clazz, jlong conn){ 35 | xdb_conn_t *pConn = reinterpret_cast(conn); 36 | xdb_rollback(pConn); 37 | return 1; 38 | } 39 | 40 | 41 | JNIEXPORT jlong JNICALL Java_com_open_crossdb_jni_core_CrossDBJNI_jniXdbExec 42 | (JNIEnv *env, jclass clazz, jlong conn, jstring sql){ 43 | const char *c_sql = env->GetStringUTFChars(sql, NULL); 44 | xdb_conn_t *pConn = reinterpret_cast(conn); 45 | xdb_res_t *pRes = xdb_exec (pConn, c_sql); 46 | env->ReleaseStringUTFChars(sql, c_sql); 47 | 48 | return reinterpret_cast(pRes); 49 | } 50 | 51 | 52 | JNIEXPORT jlong JNICALL Java_com_open_crossdb_jni_core_CrossDBJNI_jniFetchRow 53 | (JNIEnv *env, jclass clazz, jlong res){ 54 | xdb_res_t *pRes = reinterpret_cast(res); 55 | xdb_row_t *pRow = xdb_fetch_row (pRes); 56 | return reinterpret_cast(pRow); 57 | } 58 | 59 | 60 | JNIEXPORT jint JNICALL Java_com_open_crossdb_jni_core_CrossDBJNI_jniColumnInt 61 | (JNIEnv *env, jclass clazz, jlong res, jlong row, jint colNum){ 62 | xdb_res_t *pRes = reinterpret_cast(res); 63 | xdb_row_t *pRow = reinterpret_cast(row); 64 | return xdb_column_int (pRes->col_meta, pRow, colNum); 65 | } 66 | 67 | 68 | JNIEXPORT jstring JNICALL Java_com_open_crossdb_jni_core_CrossDBJNI_jniColumnStr 69 | (JNIEnv *env, jclass clazz, jlong res, jlong row, jint colNum){ 70 | xdb_res_t *pRes = reinterpret_cast(res); 71 | xdb_row_t *pRow = reinterpret_cast(row); 72 | return (env)->NewStringUTF(xdb_column_str(pRes->col_meta, pRow, colNum)); 73 | } 74 | 75 | 76 | JNIEXPORT jfloat JNICALL Java_com_open_crossdb_jni_core_CrossDBJNI_jniColumnFloat 77 | (JNIEnv *env, jclass clazz, jlong res, jlong row, jint colNum){ 78 | xdb_res_t *pRes = reinterpret_cast(res); 79 | xdb_row_t *pRow = reinterpret_cast(row); 80 | return xdb_column_float(pRes->col_meta, pRow, colNum); 81 | } 82 | 83 | 84 | JNIEXPORT jint JNICALL Java_com_open_crossdb_jni_core_CrossDBJNI_jniClose 85 | (JNIEnv *env, jclass clazz, jlong conn){ 86 | xdb_conn_t *pConn = reinterpret_cast(conn); 87 | xdb_close(pConn); 88 | return 1; 89 | } 90 | -------------------------------------------------------------------------------- /src/core/xdb_fkey.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #if XDB_LOG_FLAGS & XDB_LOG_IDX 13 | #define xdb_fkeylog(...) xdb_print(__VA_ARGS__) 14 | #else 15 | #define xdb_fkeylog(...) 16 | #endif 17 | 18 | XDB_STATIC int 19 | xdb_create_fkey (xdb_stmt_fkey_t *pStmt) 20 | { 21 | xdb_conn_t *pConn = pStmt->pConn; 22 | 23 | xdb_dbglog ("create fkeys %s on %s %d", pStmt->fkey_name, XDB_OBJ_NAME(pStmt->pTblm), pStmt->fld_count); 24 | 25 | int rc = -1; 26 | xdb_tblm_t *pTblm = pStmt->pTblm; 27 | xdb_fkeym_t *pFkeym = NULL; 28 | 29 | XDB_EXPECT (XDB_OBJM_COUNT (pTblm->fkey_objm) < XDB_MAX_FKEY, XDB_E_FULL, "Can create at most %d foreign keys", XDB_MAX_FKEY); 30 | 31 | pFkeym = xdb_calloc (sizeof(xdb_fkeym_t) * 2); 32 | if (NULL == pFkeym) { 33 | goto error; 34 | } 35 | 36 | pFkeym->pTblm = pStmt->pTblm; 37 | 38 | pFkeym->on_del_act = pStmt->on_del_act; 39 | pFkeym->on_upd_act = pStmt->on_upd_act; 40 | pFkeym->fld_count = pStmt->fld_count; 41 | 42 | uint8_t fld_bmp[XDB_MAX_COLUMN/8]; 43 | memset (fld_bmp, 0, (pStmt->pRefTblm->fld_count+7)>>8); 44 | for (int i = 0; i < pFkeym->fld_count; ++i) { 45 | //pFkeym->pFields[i] = xdb_find_field (pStmt->pTblm, pStmt->fkey_col[i], 0); 46 | xdb_field_t *pField = xdb_find_field (pStmt->pTblm, pStmt->fkey_col[i], 0); 47 | XDB_EXPECT (pField != NULL, XDB_E_STMT, "Can't find field '%s'", pStmt->fkey_col[i]); 48 | pFkeym->filter.pFilters[i]->val.pField = pField; 49 | pFkeym->filter.pFilters[i]->pField = pStmt->pRefFlds[i]; 50 | pFkeym->filter.pFilters[i]->cmp_op = XDB_TOK_EQ; 51 | } 52 | xdb_find_idx (pStmt->pRefTblm, &pFkeym->filter, fld_bmp); 53 | 54 | xdb_strcpy (XDB_OBJ_NAME(pFkeym), pStmt->fkey_name); 55 | XDB_OBJ_ID(pFkeym) = -1; 56 | xdb_objm_add (&pTblm->fkey_objm, pFkeym); 57 | 58 | xdb_fkeym_t *pRefFkeym = pFkeym + 1; 59 | *pRefFkeym = *pFkeym; 60 | if (strlen (pFkeym->obj.obj_name) < XDB_NAME_LEN - 16) { 61 | sprintf (pRefFkeym->obj.obj_name, "%s_%d", pFkeym->obj.obj_name, XDB_OBJ_ID(pTblm)); 62 | } 63 | XDB_OBJ_ID(pRefFkeym) = -1; 64 | xdb_objm_add (&pTblm->fkeyref_objm, pRefFkeym); 65 | 66 | return XDB_OK; 67 | 68 | error: 69 | xdb_free (pFkeym); 70 | return rc; 71 | } 72 | 73 | #if 0 74 | XDB_STATIC int 75 | xdb_fkey_insert_check (xdb_conn_t *pConn, xdb_tblm_t *pTblm, void *pRow) 76 | { 77 | for (int i = 0; i < XDB_OBJM_COUNT(pTblm->fkey_objm); ++i) { 78 | xdb_fkeym_t *pFkeym = XDB_OBJM_GET (pTblm->fkey_objm, i); 79 | for (int j = 0; j < pFkeym->fld_count; ++j) { 80 | xdb_value_t *pVal = &pFkeym->filter.pFilters[j]->val; 81 | xdb_row_getVal (pRow, pVal); 82 | } 83 | 84 | xdb_rowset_t row_set; 85 | xdb_rowset_init (&row_set); 86 | row_set.limit = 1; 87 | xdb_sql_query2 (pConn, pFkeym->pRefTblm, &row_set, &pFkeym->filter); 88 | XDB_EXPECT (row_set.count > 0, XDB_E_CONSTRAINT, "Foreign Key doesn't exist in '%s'", XDB_OBJ_NAME(pFkeym->pRefTblm)); 89 | } 90 | 91 | return XDB_OK; 92 | 93 | error: 94 | return -1; 95 | } 96 | #endif 97 | -------------------------------------------------------------------------------- /bench/basic/bench-crossdb.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define BENCH_DBNAME "CrossDB" 4 | 5 | int LKUP_COUNT = 10000000; 6 | 7 | #include "bench.h" 8 | 9 | void* bench_open (const char *db) 10 | { 11 | xdb_conn_t* pConn; 12 | if (!s_bench_svr) { 13 | pConn = xdb_open (db); 14 | } else { 15 | pConn = xdb_connect (NULL, NULL, NULL, NULL, 7777); 16 | xdb_bexec (pConn, "CREATE DATABASE school ENGINE=MEMORY"); 17 | xdb_bexec (pConn, "USE school"); 18 | LKUP_COUNT = 100000; 19 | } 20 | XDB_CHECK (NULL != pConn, printf ("Can't open connection:\n"); return NULL;); 21 | return pConn; 22 | } 23 | 24 | void bench_close (void *pConn) 25 | { 26 | xdb_close (pConn); 27 | } 28 | 29 | bool bench_sql (void *pConn, const char *sql) 30 | { 31 | xdb_res_t* pRes = xdb_exec (pConn, sql); 32 | XDB_RESCHK (pRes, printf ("Can't exec '%s'\n", sql); return false;); 33 | return true; 34 | } 35 | 36 | bool bench_sql_insert (void *pConn, const char *sql, int id, const char *name, int age, const char *cls, int score) 37 | { 38 | xdb_res_t *pRes = xdb_bexec (pConn, sql, id, name, age, cls, score); 39 | return 1 == xdb_affected_rows(pRes); 40 | } 41 | 42 | bool bench_sql_get_byid (void *pConn, const char *sql, int id, stu_callback callback, void *pArg) 43 | { 44 | xdb_res_t *pRes = xdb_bexec (pConn, sql, id); 45 | xdb_row_t *pRow = xdb_fetch_row (pRes); 46 | if (NULL != pRow) { 47 | callback (pArg, xdb_column_int(pRes, pRow, 0), xdb_column_str(pRes, pRow, 1), 48 | xdb_column_int(pRes, pRow, 2), xdb_column_str(pRes, pRow, 3), xdb_column_int(pRes, pRow, 4)); 49 | } 50 | xdb_free_result (pRes); 51 | return pRow != NULL; 52 | } 53 | 54 | bool bench_sql_updAge_byid (void *pConn, const char *sql, int id, int age) 55 | { 56 | xdb_res_t *pRes = xdb_bexec (pConn, sql, age, id); 57 | if (xdb_affected_rows(pRes) != 1) { 58 | printf ("wrong\n"); 59 | } 60 | return 1 == xdb_affected_rows(pRes); 61 | } 62 | 63 | bool bench_sql_del_byid (void *pConn, const char *sql, int id) 64 | { 65 | xdb_res_t *pRes = xdb_bexec (pConn, sql, id); 66 | return 1 == xdb_affected_rows(pRes); 67 | } 68 | 69 | 70 | void* bench_stmt_prepare (void *pConn, const char *sql) 71 | { 72 | return xdb_stmt_prepare (pConn, sql); 73 | } 74 | 75 | void bench_stmt_close (void *pStmt) 76 | { 77 | xdb_stmt_close (pStmt); 78 | } 79 | 80 | bool bench_stmt_insert (void *pStmt, int id, const char *name, int age, const char *cls, int score) 81 | { 82 | xdb_res_t *pRes = xdb_stmt_bexec (pStmt, id, name, age, cls, score); 83 | return 1 == xdb_affected_rows(pRes); 84 | } 85 | 86 | bool bench_stmt_get_byid (void *pStmt, int id, stu_callback callback, void *pArg) 87 | { 88 | xdb_res_t *pRes = xdb_stmt_bexec (pStmt, id); 89 | xdb_row_t *pRow = xdb_fetch_row (pRes); 90 | if (NULL != pRow) { 91 | callback (pArg, xdb_column_int(pRes, pRow, 0), xdb_column_str(pRes, pRow, 1), 92 | xdb_column_int(pRes, pRow, 2), xdb_column_str(pRes, pRow, 3), xdb_column_int(pRes, pRow, 4)); 93 | } 94 | xdb_free_result (pRes); 95 | return pRow != NULL; 96 | } 97 | 98 | bool bench_stmt_updAge_byid (void *pStmt, int id, int age) 99 | { 100 | xdb_res_t *pRes = xdb_stmt_bexec (pStmt, age, id); 101 | return 1 == xdb_affected_rows(pRes); 102 | } 103 | 104 | bool bench_stmt_del_byid (void *pStmt, int id) 105 | { 106 | xdb_res_t *pRes = xdb_stmt_bexec (pStmt, id); 107 | return 1 == xdb_affected_rows(pRes); 108 | } 109 | -------------------------------------------------------------------------------- /src/core/xdb_db.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __XDB_DB_H__ 13 | #define __XDB_DB_H__ 14 | 15 | typedef struct xdb_dbm_t { 16 | xdb_obj_t obj; 17 | char db_path[XDB_PATH_LEN+1]; 18 | xdb_objm_t db_objm; 19 | xdb_stgmgr_t stg_mgr; 20 | bool bMemory; 21 | bool bSysDb; 22 | bool bTtlTbl; 23 | xdb_lockmode_e lock_mode; 24 | xdb_syncmode_e sync_mode; 25 | 26 | xdb_walm_t wal_mgr[2]; 27 | xdb_walm_t *pWalm; 28 | xdb_walm_t *pWalmBak; 29 | 30 | xdb_rwlock_t wal_lock; 31 | xdb_rwlock_t db_lock; 32 | 33 | xdb_vec_t sub_list; 34 | } xdb_dbm_t; 35 | 36 | typedef struct xdb_dbobj_t { 37 | uint64_t val[8]; 38 | } xdb_dbobj_t; 39 | 40 | typedef struct xdb_db_t { 41 | xdb_stghdr_t blk_hdr; 42 | uint64_t flush_time; 43 | uint64_t lastchg_id; // last commit id 44 | uint64_t flush_id; // last flush id 45 | uint8_t lock_mode; // xdb_lockmode_e 46 | uint32_t sync_mode; // xdb_syncmode_e 47 | uint8_t rsvd[7]; 48 | xdb_dbobj_t dbobj[]; 49 | } xdb_db_t; 50 | 51 | #define XDB_DBPTR(pDbm) ((xdb_db_t*)pDbm->stg_mgr.pStgHdr) 52 | 53 | XDB_STATIC xdb_dbm_t* 54 | xdb_find_db (const char *name); 55 | 56 | XDB_STATIC xdb_ret 57 | xdb_repair_db (xdb_dbm_t *pDbm, int flags); 58 | 59 | XDB_STATIC int 60 | xdb_flush_db (xdb_dbm_t *pDbm, uint32_t flags); 61 | 62 | XDB_STATIC int 63 | xdb_ttl_db (xdb_dbm_t *pDbm, bool bNow, uint32_t flags); 64 | 65 | XDB_STATIC int 66 | xdb_dump_create_db (xdb_dbm_t *pDbm, char buf[], xdb_size size, uint32_t flags); 67 | 68 | static inline int 69 | xdb_wal_rdlock (struct xdb_dbm_t *pDbm) 70 | { 71 | if (xdb_likely (XDB_LOCK_THREAD == pDbm->lock_mode)) { 72 | xdb_rwlock_rdlock (&pDbm->wal_lock); 73 | } else if (XDB_LOCK_PROCESS == pDbm->lock_mode) { 74 | xdb_file_rdlock (pDbm->stg_mgr.stg_fd, 1, 1); 75 | } 76 | return XDB_OK; 77 | } 78 | 79 | static inline int 80 | xdb_wal_rdunlock (struct xdb_dbm_t *pDbm) 81 | { 82 | if (xdb_likely (XDB_LOCK_THREAD == pDbm->lock_mode)) { 83 | xdb_rwlock_rdunlock (&pDbm->wal_lock); 84 | } else if (XDB_LOCK_PROCESS == pDbm->lock_mode) { 85 | return xdb_file_unlock (pDbm->stg_mgr.stg_fd, 1, 1); 86 | } 87 | return XDB_OK; 88 | } 89 | 90 | static inline int 91 | xdb_wal_wrlock (struct xdb_dbm_t *pDbm) 92 | { 93 | if (xdb_likely (XDB_LOCK_THREAD == pDbm->lock_mode)) { 94 | xdb_rwlock_wrlock (&pDbm->wal_lock); 95 | } else if (XDB_LOCK_PROCESS == pDbm->lock_mode) { 96 | return xdb_file_wrlock (pDbm->stg_mgr.stg_fd, 1, 1); 97 | } 98 | return XDB_OK; 99 | } 100 | 101 | static inline int 102 | xdb_wal_wrunlock (struct xdb_dbm_t *pDbm) 103 | { 104 | if (xdb_likely (XDB_LOCK_THREAD == pDbm->lock_mode)) { 105 | xdb_rwlock_wrunlock (&pDbm->wal_lock); 106 | } else if (XDB_LOCK_PROCESS == pDbm->lock_mode) { 107 | return xdb_file_unlock (pDbm->stg_mgr.stg_fd, 1, 1); 108 | } 109 | return XDB_OK; 110 | } 111 | 112 | #endif // __XDB_DB_H__ 113 | -------------------------------------------------------------------------------- /examples/go/example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "github.com/crossdb-org/crossdb/connector/go" 7 | ) 8 | 9 | func main() { 10 | // Open a connection to CrossDB 11 | conn, err := crossdb.Open(":memory:") 12 | if err != nil { 13 | log.Fatal(err) 14 | } 15 | defer conn.Close() 16 | 17 | /* 18 | // Drop the table if it exists 19 | _, err = conn.Exec("DROP TABLE IF EXISTS users") 20 | if err != nil { 21 | log.Fatal(err) 22 | } 23 | */ 24 | 25 | // Create a table 26 | _, err = conn.Exec("CREATE TABLE users (id INT PRIMARY KEY, name CHAR(50), age INT)") 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // Insert some data 32 | _, err = conn.Exec("INSERT INTO users (id, name, age) VALUES (1, 'Alice', 30), (2, 'Bob', 25), (3, 'Charlie', 35)") 33 | if err != nil { 34 | log.Fatal(err) 35 | } 36 | 37 | // Query the data 38 | res, err := conn.Exec("SELECT * FROM users") 39 | if err != nil { 40 | log.Fatal(err) 41 | } 42 | 43 | // Print the results 44 | fmt.Println("All users:") 45 | for row := res.FetchRow(); row != nil; row = res.FetchRow() { 46 | fmt.Printf("ID: %d, Name: %s, Age: %d\n", row[0].(int), row[1].(string), row[2].(int)) 47 | } 48 | 49 | // Update a record 50 | _, err = conn.Exec("UPDATE users SET age = 31 WHERE id = 1") 51 | if err != nil { 52 | log.Fatal(err) 53 | } 54 | 55 | // Query a specific user 56 | res, err = conn.Exec("SELECT * FROM users WHERE id = 1") 57 | if err != nil { 58 | log.Fatal(err) 59 | } 60 | 61 | fmt.Println("\nUpdated user:") 62 | if row := res.FetchRow(); row != nil { 63 | fmt.Printf("ID: %d, Name: %s, Age: %d\n", row[0].(int), row[1].(string), row[2].(int)) 64 | } 65 | 66 | // Delete a record 67 | _, err = conn.Exec("DELETE FROM users WHERE id = 2") 68 | if err != nil { 69 | log.Fatal(err) 70 | } 71 | 72 | // Query all users again 73 | res, err = conn.Exec("SELECT * FROM users") 74 | if err != nil { 75 | log.Fatal(err) 76 | } 77 | 78 | fmt.Println("\nRemaining users:") 79 | for row := res.FetchRow(); row != nil; row = res.FetchRow() { 80 | fmt.Printf("ID: %d, Name: %s, Age: %d\n", row[0].(int), row[1].(string), row[2].(int)) 81 | } 82 | 83 | // Demonstrate transaction 84 | err = conn.Begin() 85 | if err != nil { 86 | log.Fatal(err) 87 | } 88 | 89 | _, err = conn.Exec("INSERT INTO users (id, name, age) VALUES (4, 'David', 40)") 90 | if err != nil { 91 | conn.Rollback() 92 | log.Fatal(err) 93 | } 94 | 95 | err = conn.Commit() 96 | if err != nil { 97 | log.Fatal(err) 98 | } 99 | 100 | // Verify the transaction 101 | res, err = conn.Exec("SELECT * FROM users WHERE id = 4") 102 | if err != nil { 103 | log.Fatal(err) 104 | } 105 | 106 | fmt.Println("\nUser added in transaction:") 107 | if row := res.FetchRow(); row != nil { 108 | fmt.Printf("ID: %d, Name: %s, Age: %d\n", row[0].(int), row[1].(string), row[2].(int)) 109 | } 110 | 111 | fmt.Printf("\nCrossDB Version: %s\n", crossdb.Version()) 112 | } 113 | -------------------------------------------------------------------------------- /src/xdb-cli.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #include "crossdb.c" 13 | 14 | int main (int argc, char **argv) 15 | { 16 | char *db = NULL; 17 | char ch; 18 | bool bServer = false, bNeedPasswd = false, bQuite = false; 19 | #if (XDB_ENABLE_SERVER == 1) 20 | char *datadir = NULL, *svrid = "1", *host = NULL, *user = NULL, *password = NULL; 21 | int port = 0; 22 | #endif 23 | char *esql = NULL; 24 | 25 | while ((ch = getopt(argc, argv, "h:u:p:e:P:D:R:I:Sq")) != -1) { 26 | switch (ch) { 27 | case '?': 28 | xdb_print ("Usage: xdb-cli [OPTIONS] [[path]/db_name]\n"); 29 | xdb_print (" -? Show this help\n"); 30 | #if (XDB_ENABLE_SERVER == 1) 31 | xdb_print (" -S Server: Start in server mode, default port %d\n", XDB_SVR_PORT); 32 | xdb_print (" -I Server ID: \n"); 33 | xdb_print (" -h IP address to bind to or connect to\n"); 34 | xdb_print (" -P Port to listen or connect\n"); 35 | xdb_print (" -D Server: Data directory to store databases, default '%s'\n", XDB_DATA_DIR); 36 | xdb_print (" -q Server: quite mode.\n"); 37 | xdb_print (" -u Client user\n"); 38 | xdb_print (" -p Client password\n"); 39 | #endif // XDB_ENABLE_SERVER 40 | xdb_print (" -e Client: Execute command and quit.\n"); 41 | return -1; 42 | #if (XDB_ENABLE_SERVER == 1) 43 | case 'S': 44 | bServer = true; 45 | if (0 == port) { 46 | port = XDB_SVR_PORT; 47 | } 48 | if (NULL == datadir) { 49 | datadir = XDB_DATA_DIR; 50 | } 51 | break; 52 | case 'I': 53 | svrid = optarg; 54 | break; 55 | case 'h': 56 | host = optarg; 57 | if (0 == port) { 58 | port = XDB_SVR_PORT; 59 | } 60 | break; 61 | case 'P': 62 | port = atoi (optarg); 63 | if (NULL == host) { 64 | host = "127.0.0.1"; 65 | } 66 | break; 67 | case 'D': 68 | datadir = optarg; 69 | break; 70 | case 'u': 71 | user = optarg; 72 | break; 73 | case 'p': 74 | bNeedPasswd = true; 75 | break; 76 | #endif // XDB_ENABLE_SERVER 77 | case 'e': 78 | esql = optarg; 79 | break; 80 | case 'q': 81 | bQuite = true; 82 | break; 83 | } 84 | } 85 | 86 | if (bServer) { 87 | #if (XDB_ENABLE_SERVER == 1) 88 | xdb_start_server (host, port, svrid, datadir, bQuite); 89 | #endif 90 | } else { 91 | s_xdb_cli = true; 92 | if (bNeedPasswd) { 93 | xdb_print ("Enter password:\n"); 94 | } 95 | if ((argc > 1) && (*argv[argc-1] != '-') && (argc > 2 ? (*argv[argc-2] != '-') : 1)) { 96 | db = argv[argc-1]; 97 | } 98 | #if (XDB_ENABLE_SERVER == 1) 99 | xdb_conn_t *pConn = xdb_connect (host, user, password, (host || db) ? db : ":memory:", port); 100 | #else 101 | xdb_conn_t *pConn = xdb_open (db ? db : ":memory:"); 102 | #endif 103 | if (NULL != pConn) { 104 | if (NULL != esql) { 105 | xdb_shell_process (pConn, esql); 106 | } else { 107 | xdb_shell_loop (pConn, NULL, NULL, bQuite); 108 | } 109 | xdb_close (pConn); 110 | } 111 | } 112 | 113 | return 0; 114 | } 115 | -------------------------------------------------------------------------------- /src/lib/xdb_lib.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #include "xdb_lib.h" 13 | #include "xdb_str.c" 14 | #include "xdb_file.c" 15 | #include "xdb_mmap.c" 16 | #include "xdb_thread.c" 17 | #include "xdb_objm.c" 18 | #include "xdb_bmp.c" 19 | #include "xdb_time.c" 20 | #include "xdb_dynbuf.c" 21 | #if (XDB_ENABLE_SERVER == 1) 22 | #include "xdb_sock.c" 23 | #endif 24 | #include "xdb_vector.c" 25 | 26 | static char s_xdb_hex_2_str[256][2]; 27 | 28 | static short s_xdb_str_2_hex[256]; 29 | 30 | XDB_STATIC void 31 | xdb_hex_init () 32 | { 33 | int i, hex; 34 | for (i = 0; i < 256; ++i) { 35 | hex = i >> 4; 36 | s_xdb_hex_2_str[i][0] = (hex)<10 ? hex+'0' : hex-10+'a'; 37 | hex = i & 0xF; 38 | s_xdb_hex_2_str[i][1] = (hex)<10 ? hex+'0' : hex-10+'a'; 39 | } 40 | memset (s_xdb_str_2_hex, 0xff, sizeof(s_xdb_str_2_hex)); 41 | for (i = 0; i < 9; ++i) { 42 | s_xdb_str_2_hex['0' + i] = i; 43 | } 44 | for (i = 0; i < 6; ++i) { 45 | s_xdb_str_2_hex['a' + i] = i + 10; 46 | s_xdb_str_2_hex['A' + i] = i + 10; 47 | } 48 | } 49 | 50 | void 51 | xdb_hexdump (const void *addr, int len) 52 | { 53 | int i, j; 54 | const uint8_t *ptr = addr; 55 | xdb_print ("hexdump addr %p len %d\n", addr, len); 56 | for (i = 0; i < len; i+=16, ptr+=16) { 57 | xdb_print ("%08x ", i); 58 | for (j = 0; j < 16; ++j) { 59 | if (i + j < len) { 60 | xdb_print ("%02x ", ptr[j]); 61 | } else { 62 | xdb_print (" "); 63 | } 64 | if (j == 7) xdb_print (" "); 65 | } 66 | xdb_print (" |"); 67 | for (j = 0; j < 16; ++j) { 68 | xdb_print ("%c", (i+j>=len)?' ':(isprint(ptr[j])?ptr[j]:'.')); 69 | if (j == 7) xdb_print (" "); 70 | } 71 | xdb_print ("|\n"); 72 | } 73 | } 74 | 75 | /* 76 | sigaction(SIGPIPE, &(struct sigaction){{SIG_IGN}}, NULL) 77 | signal(SIGPIPE,SIG_IGN); 78 | */ 79 | #ifndef _WIN32 80 | #if (XDB_ENABLE_SERVER == 1) 81 | XDB_STATIC int 82 | xdb_signal_block (int signum) 83 | { 84 | #ifndef _WIN32 85 | sigset_t signal_mask; 86 | sigemptyset(&signal_mask); 87 | sigaddset(&signal_mask, signum); 88 | if (pthread_sigmask(SIG_BLOCK, &signal_mask, NULL) == -1) { 89 | perror ("SIGPIPE"); 90 | return -1; 91 | } 92 | #endif 93 | return 0; 94 | } 95 | #endif 96 | #endif 97 | 98 | XDB_STATIC uint64_t 99 | xdb_strcasehash(const char *key, int len) 100 | { 101 | if (xdb_unlikely (len <= 0)) { 102 | return 0; 103 | } 104 | 105 | XDB_BUF_DEF(str, 2048); 106 | if (len > 2048) { 107 | XDB_BUF_ALLOC (str, len); 108 | if (NULL == str) { 109 | return -1; 110 | } 111 | } 112 | for (int i = 0; i < len; ++i) { 113 | str[i] = tolower(key[i]); 114 | } 115 | uint64_t hash = xdb_wyhash (str, len); 116 | 117 | XDB_BUF_FREE(str); 118 | 119 | return hash; 120 | } 121 | 122 | static int xdb_fprintf (FILE *pFile, const char *format, ...) 123 | { 124 | int len; 125 | va_list ap; 126 | va_start(ap, format); 127 | 128 | if ((uintptr_t)pFile > 0xffff) { 129 | len = vfprintf (pFile, format, ap); 130 | } else { 131 | len = xdb_sock_vprintf ((uintptr_t)pFile, format, ap); 132 | } 133 | 134 | va_end(ap); 135 | 136 | return len; 137 | } 138 | 139 | static int xdb_fflush (FILE *pFile) 140 | { 141 | if ((uintptr_t)pFile > 0xffff) { 142 | return fflush (pFile); 143 | } 144 | return 0; 145 | } 146 | 147 | -------------------------------------------------------------------------------- /connector/go/crossdb.go: -------------------------------------------------------------------------------- 1 | package crossdb 2 | 3 | // #cgo CFLAGS: -I../src 4 | // #cgo LDFLAGS: -L. -lcrossdb 5 | // #include 6 | // #include 7 | import "C" 8 | import ( 9 | "errors" 10 | "fmt" 11 | "unsafe" 12 | ) 13 | 14 | type Conn struct { 15 | conn *C.xdb_conn_t 16 | } 17 | 18 | type Result struct { 19 | res *C.xdb_res_t 20 | } 21 | 22 | // Open opens a new CrossDB connection 23 | func Open(dbPath string) (*Conn, error) { 24 | cPath := C.CString(dbPath) 25 | defer C.free(unsafe.Pointer(cPath)) 26 | 27 | conn := C.xdb_open(cPath) 28 | if conn == nil { 29 | return nil, errors.New("failed to open database") 30 | } 31 | 32 | return &Conn{conn: conn}, nil 33 | } 34 | 35 | // Close closes the CrossDB connection 36 | func (c *Conn) Close() { 37 | C.xdb_close(c.conn) 38 | } 39 | 40 | // Exec executes an SQL statement 41 | func (c *Conn) Exec(sql string) (*Result, error) { 42 | cSQL := C.CString(sql) 43 | defer C.free(unsafe.Pointer(cSQL)) 44 | 45 | res := C.xdb_exec(c.conn, cSQL) 46 | if res == nil { 47 | return nil, errors.New("failed to execute SQL") 48 | } 49 | 50 | if res.errcode != 0 { 51 | errMsg := C.GoString(C.xdb_errmsg(res)) 52 | defer C.xdb_free_result(res) 53 | return nil, fmt.Errorf("SQL error %d: %s", res.errcode, errMsg) 54 | } 55 | 56 | return &Result{res: res}, nil 57 | } 58 | 59 | // FetchRow fetches a row from the result set 60 | func (r *Result) FetchRow() []interface{} { 61 | row := C.xdb_fetch_row(r.res) 62 | if row == nil { 63 | return nil 64 | } 65 | 66 | colCount := int(r.res.col_count) 67 | result := make([]interface{}, colCount) 68 | 69 | for i := 0; i < colCount; i++ { 70 | col := C.xdb_column_meta(C.uint64_t(r.res.col_meta), C.uint16_t(i)) 71 | switch col.col_type { 72 | case C.XDB_TYPE_INT: 73 | result[i] = int(C.xdb_column_int(C.uint64_t(r.res.col_meta), row, C.uint16_t(i))) 74 | case C.XDB_TYPE_BIGINT: 75 | result[i] = int64(C.xdb_column_int64(C.uint64_t(r.res.col_meta), row, C.uint16_t(i))) 76 | case C.XDB_TYPE_FLOAT: 77 | result[i] = float32(C.xdb_column_float(C.uint64_t(r.res.col_meta), row, C.uint16_t(i))) 78 | case C.XDB_TYPE_DOUBLE: 79 | result[i] = float64(C.xdb_column_double(C.uint64_t(r.res.col_meta), row, C.uint16_t(i))) 80 | case C.XDB_TYPE_CHAR: 81 | result[i] = C.GoString(C.xdb_column_str(C.uint64_t(r.res.col_meta), row, C.uint16_t(i))) 82 | default: 83 | result[i] = nil 84 | } 85 | } 86 | 87 | return result 88 | } 89 | 90 | // FreeResult frees the result set 91 | func (r *Result) FreeResult() { 92 | C.xdb_free_result(r.res) 93 | } 94 | 95 | // Begin starts a new transaction 96 | func (c *Conn) Begin() error { 97 | ret := C.xdb_begin(c.conn) 98 | if ret != 0 { 99 | return errors.New("failed to begin transaction") 100 | } 101 | return nil 102 | } 103 | 104 | // Commit commits the current transaction 105 | func (c *Conn) Commit() error { 106 | ret := C.xdb_commit(c.conn) 107 | if ret != 0 { 108 | return errors.New("failed to commit transaction") 109 | } 110 | return nil 111 | } 112 | 113 | // Rollback rolls back the current transaction 114 | func (c *Conn) Rollback() error { 115 | ret := C.xdb_rollback(c.conn) 116 | if ret != 0 { 117 | return errors.New("failed to rollback transaction") 118 | } 119 | return nil 120 | } 121 | 122 | // Version returns the CrossDB version 123 | func Version() string { 124 | return C.GoString(C.xdb_version()) 125 | } -------------------------------------------------------------------------------- /src/core/xdb_trigger.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | typedef struct xdb_trigfunc_t { 13 | xdb_obj_t obj; 14 | char lang[16]; 15 | char func_name[XDB_NAME_LEN * 2]; 16 | xdb_trig_callback cb_func; 17 | void *pArg; 18 | } xdb_trigfunc_t; 19 | 20 | typedef struct xdb_trig_t { 21 | xdb_obj_t obj; 22 | xdb_tblm_t *pTblm; 23 | int priority; 24 | xdb_trigfunc_t *pTrigf; 25 | } xdb_trig_t; 26 | 27 | static xdb_objm_t s_xdb_trigfunc; 28 | 29 | XDB_STATIC xdb_trigfunc_t* 30 | xdb_find_trigfunc (const char *name) 31 | { 32 | return xdb_objm_get (&s_xdb_trigfunc, name); 33 | } 34 | 35 | xdb_ret 36 | xdb_create_func (const char *name, xdb_func_e type, const char *lang, void *cb_func, void *pArg) 37 | { 38 | xdb_trigfunc_t *pTrigf = xdb_find_trigfunc (name); 39 | 40 | if (type >= XDB_FUNC_MAX) { 41 | return -XDB_E_PARAM; 42 | } 43 | 44 | if (NULL != pTrigf) { 45 | return -XDB_E_EXISTS; 46 | } 47 | 48 | pTrigf = xdb_calloc (sizeof (xdb_trigfunc_t)); 49 | if (NULL == pTrigf) { 50 | return -XDB_E_MEMORY; 51 | } 52 | 53 | xdb_strcpy (XDB_OBJ_NAME(pTrigf), name); 54 | XDB_OBJ_ID(pTrigf) = -1; 55 | 56 | xdb_strcpy (pTrigf->lang, lang); 57 | pTrigf->cb_func = cb_func; 58 | pTrigf->pArg = pArg; 59 | 60 | xdb_objm_add (&s_xdb_trigfunc, pTrigf); 61 | return XDB_OK; 62 | } 63 | 64 | XDB_STATIC xdb_trig_t* 65 | xdb_find_trigger (xdb_tblm_t *pTblm, const char *name) 66 | { 67 | for (xdb_trig_e type = 0; type < XDB_TRIG_MAX; ++type) { 68 | xdb_trig_t *pTrig = xdb_objm_get (&pTblm->trig_objm[type], name); 69 | if (NULL != pTrig) { 70 | return pTrig; 71 | } 72 | } 73 | return NULL; 74 | } 75 | 76 | // when for newrow 77 | XDB_STATIC int 78 | xdb_create_triggerEx (xdb_conn_t *pConn, const char *name, xdb_trig_e type, xdb_tblm_t *pTblm, const char *func_name, 79 | const char *when, const char *upd_cols, int priority) 80 | { 81 | xdb_trigfunc_t *pTrigf = xdb_find_trigfunc (func_name); 82 | if (NULL == pTrigf) { 83 | xdb_errlog ("Can't find trig func %s\n", func_name); 84 | return -XDB_E_NOTFOUND; 85 | } 86 | 87 | #if 0 88 | char *tblname = strchr (tbl_name, '.'); 89 | if (NULL != tblname) { 90 | char db_name[XDB_NAME_LEN+1]; 91 | memcpy (db_name, tbl_name, tblname-tbl_name); 92 | db_name[tblname-tbl_name] = '\0'; 93 | pDbm = xdb_find_db (db_name); 94 | if (NULL == pDbm) { 95 | xdb_errlog ("Can't find db %s\n", db_name); 96 | return -XDB_E_NOTFOUND; 97 | } 98 | tbl_name = tblname + 1; 99 | } 100 | 101 | xdb_tblm_t *pTblm = xdb_find_table (pDbm, tbl_name); 102 | if (NULL == pTblm) { 103 | xdb_errlog ("Can't find tbl %s\n", tbl_name); 104 | return -1; 105 | } 106 | #endif 107 | 108 | xdb_trig_t *pTrig = xdb_find_trigger (pTblm, name); 109 | if (NULL != pTrig) { 110 | return -XDB_E_EXISTS; 111 | } 112 | 113 | pTrig = xdb_calloc (sizeof (xdb_trig_t)); 114 | if (NULL == pTrig) { 115 | return -XDB_E_MEMORY; 116 | } 117 | 118 | xdb_strcpy (XDB_OBJ_NAME(pTrig), name); 119 | pTrig->pTrigf = pTrigf; 120 | pTrig->priority = priority; 121 | xdb_objm_add (&pTblm->trig_objm[type], pTrig); 122 | 123 | return XDB_OK; 124 | } 125 | 126 | int 127 | xdb_create_trigger (xdb_stmt_trig_t *pStmt) 128 | { 129 | return xdb_create_triggerEx (pStmt->pConn, pStmt->trig_name, pStmt->trig_type, pStmt->pTblm, pStmt->func_name, NULL, NULL, 0); 130 | } 131 | 132 | int xdb_call_trigger (xdb_conn_t *pConn, xdb_tblm_t *pTblm, xdb_trig_e type, xdb_row_t *pNewRow, xdb_row_t *pOldRow) 133 | { 134 | int count = XDB_OBJM_COUNT(pTblm->trig_objm[type]); 135 | xdb_objm_t *pTrigObj = &pTblm->trig_objm[type]; 136 | xdb_res_t res; 137 | res.col_meta = (uintptr_t)pTblm->pMeta; 138 | res.row_data = (uintptr_t)pTblm; 139 | res.col_count = pTblm->pMeta->col_count; 140 | for (int i = 0; i < count; ++i) { 141 | xdb_trig_t *pTrig = XDB_OBJM_GET(*pTrigObj, i); 142 | pTrig->pTrigf->cb_func (pConn, &res, type, pNewRow, pOldRow, pTrig->pTrigf->pArg); 143 | } 144 | return XDB_OK; 145 | } 146 | 147 | -------------------------------------------------------------------------------- /src/lib/xdb_objm.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #include "xdb_objm.h" 13 | 14 | XDB_STATIC void* 15 | xdb_objm_get2 (xdb_objm_t *pObjm, const char *name, int len) 16 | { 17 | if (xdb_unlikely (0 == len)) { 18 | len = strlen (name); 19 | } 20 | if (xdb_unlikely (NULL == pObjm->ppObjHash)) { 21 | return NULL; 22 | } 23 | 24 | uint32_t hashcode = xdb_strcasehash (name, len); 25 | uint32_t slot = hashcode & pObjm->hash_mask; 26 | 27 | xdb_obj_t *pObj; 28 | for (pObj = pObjm->ppObjHash[slot]; pObj != NULL; pObj=pObj->pNxtObj) { 29 | if ((pObj->hashcode == hashcode) && !strcasecmp (pObj->obj_name, name)) { 30 | return pObj; 31 | } 32 | } 33 | return NULL; 34 | } 35 | 36 | XDB_STATIC int 37 | xdb_objm_hash_add (xdb_objm_t *pObjm, xdb_obj_t *pNewObj) 38 | { 39 | pNewObj->hashcode = xdb_strcasehash (pNewObj->obj_name, pNewObj->nm_len); 40 | 41 | uint32_t slot = pNewObj->hashcode & pObjm->hash_mask; 42 | 43 | xdb_obj_t *pObj, *pPrevObj = NULL; 44 | for (pObj = pObjm->ppObjHash[slot]; pObj != NULL; pObj=pObj->pNxtObj) { 45 | if ((pObj->hashcode == pNewObj->hashcode) && !strcasecmp (pObj->obj_name, pNewObj->obj_name)) { 46 | return -XDB_E_EXISTS; 47 | } 48 | pPrevObj = pObj; 49 | } 50 | if (NULL == pPrevObj) { 51 | pNewObj->pNxtObj = NULL; 52 | pObjm->ppObjHash[slot] = pNewObj; 53 | } else { 54 | pNewObj->pNxtObj = pPrevObj->pNxtObj; 55 | pPrevObj->pNxtObj = pNewObj; 56 | } 57 | return XDB_OK; 58 | } 59 | 60 | XDB_STATIC int 61 | xdb_objm_add (xdb_objm_t *pObjm, void *obj) 62 | { 63 | xdb_obj_t *pObj = obj; 64 | 65 | // xdb_dbglog ("add %s\n", pObj->obj_name); 66 | pObj->nm_len = strlen (pObj->obj_name); 67 | if (pObjm->obj_count == pObjm->obj_cap || (pObj->xoid > pObjm->obj_cap)) { 68 | int newcap = 1; 69 | if (pObjm->obj_cap > 0) { 70 | newcap = pObjm->obj_cap << 1; 71 | } 72 | while (pObj->xoid >= newcap) { 73 | newcap <<= 1; 74 | } 75 | xdb_obj_t **ppObjPool = xdb_realloc (pObjm->ppObjPool, newcap*3 * sizeof (void*)); 76 | if (NULL == ppObjPool) { 77 | return -XDB_E_MEMORY; 78 | } 79 | pObjm->ppObjPool = ppObjPool; 80 | memset (ppObjPool + pObjm->obj_cap, 0, (newcap*3 - pObjm->obj_cap) * sizeof (void*)); 81 | 82 | pObjm->ppObjHash = &ppObjPool[newcap]; 83 | pObjm->hash_mask = ((newcap<<1) - 1); 84 | for (int i = 0 ; i < pObjm->obj_cap; ++i) { 85 | if (NULL != ppObjPool[i]) { 86 | xdb_objm_hash_add (pObjm, ppObjPool[i]); 87 | } 88 | } 89 | pObjm->obj_cap = newcap; 90 | } 91 | 92 | if (pObj->xoid < 0) { 93 | for (int i = 0; i < pObjm->obj_cap; ++i) { 94 | if (NULL == pObjm->ppObjPool[i]) { 95 | pObj->xoid = i; 96 | break; 97 | } 98 | } 99 | } 100 | 101 | pObjm->ppObjPool[pObj->xoid] = pObj; 102 | xdb_objm_hash_add (pObjm, pObj); 103 | pObjm->obj_count++; 104 | if (pObj->xoid >= pObjm->obj_max) { 105 | pObjm->obj_max = pObj->xoid + 1; 106 | } 107 | 108 | return XDB_OK; 109 | } 110 | 111 | XDB_STATIC int 112 | xdb_objm_del (xdb_objm_t *pObjm, void *obj) 113 | { 114 | xdb_obj_t *pDelObj = obj; 115 | //uint64_t hash = xdb_wyhash (pDelObj->obj_name, pDelObj->nm_len); 116 | int slot = pDelObj->hashcode & pObjm->hash_mask; 117 | 118 | // remove from hash 119 | xdb_obj_t *pObj, *pPrevObj = NULL; 120 | for (pObj = pObjm->ppObjHash[slot]; pObj != NULL; pObj=pObj->pNxtObj) { 121 | if (pObj == pDelObj) { 122 | if (NULL == pPrevObj) { 123 | pObjm->ppObjHash[slot] = pDelObj->pNxtObj; 124 | } else { 125 | pPrevObj->pNxtObj = pDelObj->pNxtObj; 126 | } 127 | break; 128 | } 129 | pPrevObj = pObj; 130 | } 131 | 132 | pObjm->ppObjPool[pDelObj->xoid] = NULL; 133 | pObjm->obj_count--; 134 | 135 | if (pDelObj->xoid + 1 == pObjm->obj_max) { 136 | int id; 137 | for (id = pDelObj->xoid - 1; (id >= 0) && (NULL == pObjm->ppObjPool[id]); --id) 138 | ; 139 | pObjm->obj_max = id + 1; 140 | } 141 | 142 | return XDB_OK; 143 | } 144 | 145 | XDB_STATIC void 146 | xdb_objm_free (xdb_objm_t *pObjm) 147 | { 148 | xdb_free (pObjm->ppObjPool); 149 | memset (pObjm, 0, sizeof (*pObjm)); 150 | } 151 | -------------------------------------------------------------------------------- /src/lib/xdb_str.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #include 13 | 14 | typedef struct xdb_str_t { 15 | char *str; 16 | int len; 17 | } xdb_str_t; 18 | 19 | static inline int xdb_strcmp (xdb_str_t *pStr1, xdb_str_t *pStr2) 20 | { 21 | int cmp = memcmp (pStr1->str, pStr2->str, pStr1->len<=pStr2->len ? pStr1->len : pStr2->len); 22 | if (!cmp) { 23 | return pStr1->len - pStr2->len; 24 | } 25 | return cmp; 26 | } 27 | 28 | static inline bool xdb_streq (xdb_str_t *pStr1, xdb_str_t *pStr2) 29 | { 30 | if (pStr1->len != pStr2->len) { 31 | return false; 32 | } 33 | return 0 == memcmp (pStr1->str, pStr2->str, pStr1->len); 34 | } 35 | 36 | 37 | #define xdb_strcpy(dst,src) xdb_strncpy(dst, src, sizeof(dst)) 38 | 39 | #define xdb_sprintf(dst, ...) do { snprintf(dst, sizeof(dst), __VA_ARGS__); (dst)[sizeof(dst)-1] = '\0'; } while (0) 40 | 41 | #ifdef _WIN32 42 | #define strcasecmp _stricmp 43 | #define strncasecmp _strnicmp 44 | #endif // _WIN32 45 | 46 | static inline char* 47 | xdb_strncpy (char *dst, const char *src, int len) 48 | { 49 | strncpy (dst, src, len - 1); 50 | dst[len-1] = '\0'; 51 | return dst; 52 | } 53 | 54 | static inline char* 55 | xdb_strdup (const char *str, int len) 56 | { 57 | if (0 == len) { 58 | len = strlen (str); 59 | } 60 | len++; 61 | void *dst = xdb_malloc(len); 62 | if (NULL != dst) { 63 | memcpy(dst, str, len); 64 | } 65 | return dst; 66 | } 67 | 68 | static bool __xdb_str_like (const char *string, int strLen, const char *pattern, int patLen, bool bNoCase, bool *bSkipLongMat) 69 | { 70 | while (patLen && strLen) { 71 | switch (*pattern) { 72 | case '%': 73 | while (patLen && ('%' == pattern[1])) { 74 | ++pattern; 75 | --patLen; 76 | } 77 | if (1 == patLen) { 78 | return true; 79 | } 80 | while (strLen) { 81 | if (__xdb_str_like (string, strLen, pattern+1, patLen-1, bNoCase, bSkipLongMat)) { 82 | return true; 83 | } 84 | if (*bSkipLongMat) { 85 | return false; 86 | } 87 | ++string; 88 | --strLen; 89 | } 90 | *bSkipLongMat = 1; 91 | return false; 92 | case '_': 93 | ++string; 94 | --strLen; 95 | break; 96 | case '\\': 97 | if (patLen >= 2) { 98 | ++pattern; 99 | --patLen; 100 | } 101 | // fall through 102 | default: 103 | if (!bNoCase) { 104 | if (*pattern != *string) { 105 | return false; 106 | } 107 | } else if (tolower((int)*pattern) != tolower((int)*string)) { 108 | return false; 109 | } 110 | ++string; 111 | --strLen; 112 | break; 113 | } 114 | ++pattern; 115 | --patLen; 116 | if (0 == strLen) { 117 | while ('%' == *pattern) { 118 | ++pattern; 119 | --patLen; 120 | } 121 | break; 122 | } 123 | } 124 | if (0 == (patLen + strLen)) { 125 | return true; 126 | } 127 | return false; 128 | } 129 | 130 | int xdb_str_like2 (const char *string, int strLen, const char *pattern, int patLen, bool bNoCase) 131 | { 132 | bool bSkipLongMat = 0; 133 | return __xdb_str_like (string, strLen, pattern, patLen, bNoCase, &bSkipLongMat); 134 | } 135 | 136 | int xdb_str_like (const char *string, const char *pattern, int bNoCase) 137 | { 138 | return xdb_str_like2 (string, strlen(string), pattern, strlen(pattern), bNoCase); 139 | } 140 | 141 | void * xdb_str_regcomp (const char *pattern) 142 | { 143 | regex_t *pRegex = xdb_calloc (sizeof (regex_t)); 144 | if (NULL == pRegex) { 145 | return NULL; 146 | } 147 | if (0 != regcomp(pRegex, pattern, REG_EXTENDED|REG_NOSUB)) { 148 | xdb_errlog ("Can parse patter: '%s'\n", pattern); 149 | xdb_free (pRegex); 150 | pRegex = NULL; 151 | } 152 | return pRegex; 153 | } 154 | 155 | void xdb_str_regfree (void *pRegExp) 156 | { 157 | regfree (pRegExp); 158 | xdb_free (pRegExp); 159 | } 160 | 161 | int xdb_str_regexec (const char *string, int len, void *pRegExp) 162 | { 163 | //regmatch_t match = {.rm_so = 0, .rm_eo = len}; 164 | //return 0 == regexec (pRegExp, string, 0, &match, REG_STARTEND); 165 | return 0 == regexec (pRegExp, string, 0, NULL, 0); 166 | } 167 | 168 | -------------------------------------------------------------------------------- /bench/basic/bench-stlmap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define BENCH_DBNAME "STL" 7 | #define TEST_NAME(i) i?"HashMap":"Map" 8 | int LKUP_COUNT = 10000000; 9 | 10 | using namespace std; 11 | 12 | #include "bench.h" 13 | 14 | class student { 15 | public: 16 | int id; 17 | string name; 18 | int age; 19 | string cls; 20 | int score; 21 | 22 | student () { 23 | } 24 | student (int id, string &name, int age, string &cls, int score) { 25 | this->id = id; 26 | this->name = name; 27 | this->age = age; 28 | this->cls = cls; 29 | this->score = score; 30 | } 31 | }; 32 | 33 | // std::shared_mutex is not used because, in a single-threaded context, the compiler optimizes the code and omits the lock. 34 | static pthread_rwlock_t stu_tbl_lock = PTHREAD_RWLOCK_INITIALIZER; 35 | 36 | 37 | /************************************************ 38 | Map 39 | ************************************************/ 40 | 41 | static map stu_tbl_map; 42 | 43 | void* bench_open (const char *db) 44 | { 45 | return &stu_tbl_map; 46 | } 47 | 48 | void bench_close (void *pDb) 49 | { 50 | } 51 | 52 | bool bench_sql (void *pDb, const char *sql) 53 | { 54 | return true; 55 | } 56 | 57 | student* stl_map_get_byid (int id) 58 | { 59 | auto iter = stu_tbl_map.find (id); 60 | if (iter != stu_tbl_map.end()) { 61 | return &iter->second; 62 | } else { 63 | return NULL; 64 | } 65 | } 66 | 67 | bool bench_sql_insert (void *pDb, const char *sql, int id, string &name, int age, string &cls, int score) 68 | { 69 | pthread_rwlock_wrlock(&stu_tbl_lock); 70 | auto ok = stu_tbl_map.try_emplace (id, id, name, age, cls, score); 71 | pthread_rwlock_unlock(&stu_tbl_lock); 72 | return ok.second; 73 | } 74 | 75 | bool bench_sql_get_byid (void *pDb, const char *sql, int id, stu_callback callback, void *pArg) 76 | { 77 | student *pStu, stu; 78 | 79 | pthread_rwlock_rdlock(&stu_tbl_lock); 80 | if (NULL != (pStu = stl_map_get_byid (id))) { 81 | stu = *pStu; 82 | pStu = &stu; 83 | } 84 | pthread_rwlock_unlock(&stu_tbl_lock); 85 | 86 | if (pStu) { 87 | // handle row out of the lock to reduce lock time 88 | callback (pArg, pStu->id, pStu->name, pStu->age, pStu->cls, pStu->score); 89 | } 90 | return pStu != NULL; 91 | } 92 | 93 | bool bench_sql_updAge_byid (void *pDb, const char *sql, int id, int age) 94 | { 95 | student *pStu; 96 | 97 | pthread_rwlock_wrlock(&stu_tbl_lock); 98 | if (NULL != (pStu = stl_map_get_byid (id))) { 99 | pStu->age = age; 100 | } 101 | pthread_rwlock_unlock(&stu_tbl_lock); 102 | return pStu != NULL; 103 | } 104 | 105 | bool bench_sql_del_byid (void *pDb, const char *sql, int id) 106 | { 107 | pthread_rwlock_wrlock(&stu_tbl_lock); 108 | bool ok = stu_tbl_map.erase(id);; 109 | pthread_rwlock_unlock(&stu_tbl_lock); 110 | return ok; 111 | } 112 | 113 | 114 | /************************************************ 115 | HashMap (unordered_map) 116 | ************************************************/ 117 | 118 | static unordered_map stu_tbl_hmap; 119 | 120 | void* bench_stmt_prepare (void *pDb, const char *sql) 121 | { 122 | return pDb; 123 | } 124 | 125 | void bench_stmt_close (void *pStmt) 126 | { 127 | } 128 | 129 | student* stl_hmap_get_byid (int id) 130 | { 131 | auto iter = stu_tbl_hmap.find (id); 132 | if (iter != stu_tbl_hmap.end()) { 133 | return &iter->second; 134 | } else { 135 | return NULL; 136 | } 137 | } 138 | 139 | bool bench_stmt_insert (void *pStmt, int id, string &name, int age, string &cls, int score) 140 | { 141 | pthread_rwlock_wrlock(&stu_tbl_lock); 142 | auto ok = stu_tbl_hmap.try_emplace (id, id, name, age, cls, score); 143 | pthread_rwlock_unlock(&stu_tbl_lock); 144 | return ok.second; 145 | } 146 | 147 | bool bench_stmt_get_byid (void *pStmt, int id, stu_callback callback, void *pArg) 148 | { 149 | student *pStu, stu; 150 | 151 | pthread_rwlock_rdlock(&stu_tbl_lock); 152 | if (NULL != (pStu = stl_hmap_get_byid (id))) { 153 | stu = *pStu; 154 | pStu = &stu; 155 | } 156 | pthread_rwlock_unlock(&stu_tbl_lock); 157 | 158 | if (pStu) { 159 | // handle row out of the lock to reduce lock time 160 | callback (pArg, pStu->id, pStu->name, pStu->age, pStu->cls, pStu->score); 161 | } 162 | return pStu != NULL; 163 | } 164 | 165 | bool bench_stmt_updAge_byid (void *pStmt, int id, int age) 166 | { 167 | student *pStu; 168 | 169 | pthread_rwlock_wrlock(&stu_tbl_lock); 170 | if (NULL != (pStu = stl_hmap_get_byid (id))) { 171 | pStu->age = age; 172 | } 173 | pthread_rwlock_unlock(&stu_tbl_lock); 174 | return pStu != NULL; 175 | } 176 | 177 | bool bench_stmt_del_byid (void *pStmt, int id) 178 | { 179 | pthread_rwlock_wrlock(&stu_tbl_lock); 180 | bool ok = stu_tbl_hmap.erase(id); 181 | pthread_rwlock_unlock(&stu_tbl_lock); 182 | return ok; 183 | } 184 | -------------------------------------------------------------------------------- /src/lib/xdb_lib.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __XDB_LIB_H__ 13 | #define __XDB_LIB_H__ 14 | 15 | #include 16 | #include 17 | #include 18 | //#ifndef _WIN32 19 | #include 20 | //#endif 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | //#ifndef _WIN32 29 | #include 30 | //#endif 31 | #include 32 | #include 33 | #include 34 | //#ifndef _WIN32 35 | #include 36 | //#endif 37 | #include 38 | //#ifndef _WIN32 39 | #include 40 | //#endif 41 | #include 42 | #include 43 | #include 44 | 45 | #ifdef _WIN32 46 | #include 47 | #include 48 | #define localtime_r(timep, result) localtime_s(result, timep) 49 | typedef unsigned int in_addr_t; 50 | #endif 51 | 52 | #if defined(__BIG_ENDIAN__) || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) 53 | #define XDB_BIG_ENDIAN 1 54 | #endif 55 | 56 | #define XDB_ARY_LEN(a) (sizeof(a)/sizeof(a[0])) 57 | #define XDB_OFFSET(st,fld) offsetof(st,fld) 58 | 59 | #define XDB_ALIGN2(len) ((len + 2) & ~1) 60 | #define XDB_ALIGN4(len) ((len + 3) & ~3) 61 | #define XDB_ALIGN8(len) ((len + 7) & ~7) 62 | #define XDB_ALIGN4K(len) ((len + 4093) & ~4093) 63 | #define XDB_ALIGN1M(len) ((len + (1024*1024-1)) & ~(1024*1024-1)) 64 | 65 | #ifdef XDB_DEBUG 66 | #define xdb_assert(exp) assert(exp) 67 | #else 68 | #define xdb_assert(exp) 69 | #endif 70 | 71 | #define xdb_prefetch(addr) __builtin_prefetch (addr, 0, 3) 72 | 73 | // Log 74 | #ifdef XDB_DEBUG 75 | #define xdb_dbglog(...) printf("[XDB Debug] " __VA_ARGS__) 76 | #else 77 | #define xdb_dbglog(...) 78 | #endif 79 | 80 | #define xdb_errlog(...) fprintf (stderr, "[XDB Error] " __VA_ARGS__) 81 | 82 | #define xdb_print(...) printf (__VA_ARGS__) 83 | 84 | #define xdb_dbgprint(...) printf (__VA_ARGS__) 85 | 86 | 87 | // Memory 88 | #define xdb_malloc malloc 89 | #define xdb_calloc(size) calloc(1,size) 90 | #define xdb_free(ptr) do { if(ptr) { free(ptr); ptr=NULL; } } while (0) 91 | #define xdb_realloc realloc 92 | 93 | // Byte swap 94 | #define xdb_bswap64(val) __builtin_bswap64(val) 95 | #define xdb_bswap32(val) __builtin_bswap32(val) 96 | #define xdb_bswap16(val) __builtin_bswap16(val) 97 | 98 | // Atomic 99 | #define xdb_atomic_read(ptr,val) __atomic_load(ptr, val, __ATOMIC_SEQ_CST) 100 | #define xdb_atomic_inc(ptr) __sync_add_and_fetch(ptr, 1) 101 | #define xdb_atomic_dec(ptr) __sync_sub_and_fetch(ptr, 1) 102 | #define xdb_atomic_add(ptr, val) __sync_add_and_fetch(ptr, val) 103 | #define xdb_atomic_sub(ptr, val) __sync_sub_and_fetch(ptr, val) 104 | 105 | // Types 106 | typedef int64_t xdb_ssize; 107 | typedef uint64_t xdb_size; 108 | 109 | #ifndef _WIN32 110 | typedef int xdb_fd; 111 | #define XDB_INV_FD -1 112 | #define XDB_FDFMT "d" 113 | #else 114 | typedef HANDLE xdb_fd; 115 | #define XDB_INV_FD INVALID_HANDLE_VALUE 116 | #define WIN_INVALID_HANDLE(handle) ((INVALID_HANDLE_VALUE==handle) || (NULL==handle)) 117 | #define XDB_FDFMT "p" 118 | #endif 119 | 120 | #if (XDB_ENABLE_SERVER==0) 121 | #define xdb_sock_close(fd) 122 | #endif 123 | 124 | #include "../3rd/wyhash.h" 125 | static inline uint64_t 126 | xdb_wyhash(const void *key, size_t len) 127 | { 128 | static const uint64_t secret[] = {0xa0761d6478bd642fLL,0xe7037ed1a0b428dbLL,0x8ebc6af09c88c6e3LL,0x589965cc75374cc3LL}; 129 | return wyhash (key, len, 0x9E3779B97F4A7C16LL, secret); 130 | } 131 | 132 | XDB_STATIC uint64_t 133 | xdb_strcasehash(const char *key, int len); 134 | 135 | void xdb_hexdump (const void *addr, int len); 136 | 137 | #ifndef _WIN32 138 | #if (XDB_ENABLE_SERVER == 1) 139 | XDB_STATIC int 140 | xdb_signal_block (int signum); 141 | #endif 142 | #endif 143 | 144 | static int xdb_fprintf (FILE *pFile, const char *format, ...); 145 | static int xdb_fflush (FILE *pFile); 146 | #define xdb_fputc(c, pFile) xdb_fprintf(pFile, "%c", c) 147 | 148 | 149 | typedef struct { 150 | volatile int32_t count; // -1 when W lock held, > 0 when R locks held. 151 | } xdb_rwlock_t; 152 | 153 | #define XDB_RWLOCK_INIT(lock) lock.count = 0 154 | 155 | 156 | #include "xdb_objm.h" 157 | #include "xdb_bmp.h" 158 | 159 | #endif // __XDB_LIB_H__ 160 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 |

7 | Ultra High-performance Lightweight Embedded and Server OLTP RDBMS✨ 8 |

9 | 10 | ## Features 11 | 12 | | Name | **CrossDB** 13 | ---- | ---- 14 | Description | Ultra High-performance Lightweight Embedded and Server OLTP RDBMS✨ 15 | Primary database model | Relational DBMS 16 | Secondary database models | Document store(JSON)
Key-value store(TBD) 17 | Website | crossdb.org 18 | Technical documentation | crossdb.org/intro 19 | Initial release | 2023 20 | License | Open Source, MPL 21 | Cloud-based only | no 22 | Implementation language | C 23 | Server operating systems | Server-Less Mode
Embedded Server Mode
Standalone Server Mode
Linux/MacOS/Windows/FreeBSD
32-bit or 64-bit X86, ARM, PowerPC, MIPS, etc 24 | Data scheme | yes 25 | Typing | yes
BOOL
TINYINT, SMALLINT, INT, BIGINT
UNSIGNED TINYINT, SMALLINT, INT, BIGINT
TIMESTAMP
FLOAT, DOUBLE
CHAR, VARCHAR
BINARY, VARBINARY
INET(IPv4/IPv6 host and subnet), MAC address
JSON 26 | XML support | no 27 | Secondary indexes | yes
HASH, RBTREE(TBD) 28 | SQL | yes, many extensions from MySQL 29 | APIs and other access methods | Proprietary native APIs
Multi-statement APIs
Prepared statement APIs 30 | Supported programming languages | C, C++, Python, GO, Rust
More bindings(TBD) 31 | Server-side scripts | LUA(TBD) 32 | Triggers | yes, native languages 33 | Partitioning methods | none 34 | Replication methods | Source-replica replication
Multi-source replication
Logical Replication(TBD) 35 | Data Subscription | yes(TBD) 36 | MapReduce | no 37 | Consistency concepts | Immediate Consistency 38 | Foreign keys | yes(TBD) 39 | TTL | yes 40 | Transaction concepts | ACID 41 | Concurrency | yes
Table-level read-write locks
Row-level read-write locks(TBD)
Reader-Writer MVCC (write transaction doesn't block read transactions)
PostgreSQL-like MVCC(TBD) 42 | Durability | yes, WAL 43 | In-memory capabilities | yes 44 | User concepts | yes(TBD) 45 | Storage Model | Row-oriented On-Disk, In-Memory, RamDisk
Hybrid Storage (on a table-by-table basis, tables can be designated for in-memory or on-disk storage) 46 | Admin | Embedded shell
xdb-cli tool
telnet
WEB GUI(TBD) 47 | 48 | 49 | ## Build and Install 50 | 51 | ### Download code 52 | 53 | ```bash 54 | git clone https://github.com/crossdb-org/crossdb.git 55 | cd crossdb 56 | ``` 57 | 58 | ### Linux/MacOS/FreeBSD 59 | 60 | ```bash 61 | make build 62 | sudo make install 63 | ``` 64 | 65 | ### Windows 66 | 67 | You need to install [MINGW64](https://www.mingw-w64.org/) to build. 68 | Then set the `gcc` path to `system environment variables` `Path` and make sure `gcc` can run. 69 | 70 | ``` 71 | winbuild.bat 72 | ``` 73 | 74 | ### CMake 75 | ``` 76 | cd build 77 | cmake .. 78 | make 79 | sudo make install 80 | ``` 81 | 82 | **Windows** 83 | 84 | You need to install [MINGW64](https://www.mingw-w64.org/) [CMAKE](https://cmake.org/download/) and [make](https://gnuwin32.sourceforge.net/packages/make.htm) to build. 85 | 86 | ``` 87 | cd build 88 | cmake -G "MinGW Makefiles" -DCMAKE_C_COMPILER=gcc .. 89 | make 90 | ``` 91 | 92 | ## Contribution 93 | 94 | This project is still in its early stages and currently lacks stability. We welcome the following contributions: 95 | 96 | - **Language bindings**: `Python`, `Java`, `Go`, `CSharp`, `JavaScript`, `PHP`, etc. 97 | - **Bug Reporting**: Identify and report any issues or bugs you encounter. 98 | - **Testing**: Participate in testing to ensure the reliability and stability of the project. 99 | 100 | Your contributions will be greatly appreciated and will help us make this project more robust and reliable. 101 | 102 | 103 | ## Reference 104 | 105 | ### 1,000,000 Rows Random Access Benchmark vs. SQLite 106 | 107 |

108 | 109 | 110 | 111 |

112 | 113 | https://crossdb.org/blog/benchmark/crossdb-vs-sqlite3/ 114 | 115 | ### 1,000,000 Rows Random Access Benchmark vs. C++ STL Map and HashMap 116 | 117 |

118 | 119 | 120 | 121 |

122 | 123 | https://crossdb.org/blog/benchmark/crossdb-vs-stlmap/ 124 | 125 | ### SQL Statements 126 | 127 | https://crossdb.org/sql/statements/ 128 | 129 | ### APIs 130 | 131 | https://crossdb.org/client/api-c/ 132 | 133 | ### Tutorial 134 | 135 | https://crossdb.org/get-started/tutorial/ 136 | 137 | ### CrossDB Server 138 | 139 | https://crossdb.org/develop/server/ 140 | 141 | ### CrossDB Replication 142 | 143 | https://crossdb.org/develop/replication/ 144 | 145 | ### CrossDB JSON 146 | 147 | https://crossdb.org/sql/json/ 148 | -------------------------------------------------------------------------------- /src/core/xdb_trans.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef __XDB_TRANS_H__ 13 | #define __XDB_TRANS_H__ 14 | 15 | typedef struct { 16 | struct xdb_dbTrans_t *pDbTrans; 17 | xdb_tblm_t *pTblm; 18 | xdb_bmp_t new_rows; 19 | xdb_bmp_t del_rows; 20 | } xdb_tblTrans_t; 21 | 22 | typedef struct xdb_dbTrans_t { 23 | xdb_dbm_t *pDbm; 24 | xdb_lv2bmp_t tbl_wrlocks; 25 | xdb_lv2bmp_t tbl_rdlocks; 26 | xdb_lv2bmp_t tbl_rows; 27 | uint64_t commit_len; 28 | xdb_tblTrans_t *pTblTrans[]; 29 | } xdb_dbTrans_t; 30 | 31 | XDB_STATIC int 32 | xdb_trans_addrow (xdb_conn_t *pConn, xdb_tblm_t *pTblm, xdb_rowid rid, bool bNew); 33 | 34 | XDB_STATIC int 35 | xdb_trans_delrow (xdb_conn_t *pConn, xdb_tblm_t *pTblm, xdb_rowid rid); 36 | 37 | XDB_STATIC bool 38 | xdb_trans_getrow (xdb_conn_t *pConn, xdb_tblm_t *pTblm, xdb_rowid rid, bool bNew); 39 | 40 | //XDB_STATIC int 41 | //xdb_rdlock_table (xdb_conn_t *pConn, xdb_tblm_t *pTblm); 42 | 43 | XDB_STATIC int 44 | xdb_rdunlock_table (xdb_tblm_t *pTblm); 45 | 46 | XDB_STATIC int 47 | xdb_wrlock_table (xdb_conn_t *pConn, xdb_tblm_t *pTblm); 48 | 49 | XDB_STATIC int 50 | xdb_wrunlock_table (xdb_tblm_t *pTblm); 51 | 52 | static inline int 53 | xdb_rdlock_tblstg (xdb_tblm_t *pTblm) 54 | { 55 | if (xdb_likely (XDB_LOCK_THREAD == pTblm->lock_mode)) { 56 | xdb_rwlock_rdlock (&pTblm->stg_lock); 57 | } else if (XDB_LOCK_PROCESS == pTblm->lock_mode) { 58 | xdb_file_rdlock (pTblm->stg_mgr.stg_fd, 1, 1); 59 | } 60 | return XDB_OK; 61 | } 62 | 63 | static inline int 64 | xdb_rdunlock_tblstg (xdb_tblm_t *pTblm) 65 | { 66 | if (xdb_likely (XDB_LOCK_THREAD == pTblm->lock_mode)) { 67 | xdb_rwlock_rdunlock (&pTblm->stg_lock); 68 | } else if (XDB_LOCK_PROCESS == pTblm->lock_mode) { 69 | return xdb_file_unlock (pTblm->stg_mgr.stg_fd, 1, 1); 70 | } 71 | return XDB_OK; 72 | } 73 | 74 | static inline int 75 | xdb_wrlock_tblstg (xdb_tblm_t *pTblm) 76 | { 77 | if (xdb_likely (XDB_LOCK_THREAD == pTblm->lock_mode)) { 78 | xdb_rwlock_wrlock (&pTblm->stg_lock); 79 | } else if (XDB_LOCK_PROCESS == pTblm->lock_mode) { 80 | return xdb_file_wrlock (pTblm->stg_mgr.stg_fd, 1, 1); 81 | } 82 | return XDB_OK; 83 | } 84 | 85 | static inline int 86 | xdb_wrunlock_tblstg (xdb_tblm_t *pTblm) 87 | { 88 | if (xdb_likely (XDB_LOCK_THREAD == pTblm->lock_mode)) { 89 | xdb_rwlock_wrunlock (&pTblm->stg_lock); 90 | } else if (XDB_LOCK_PROCESS == pTblm->lock_mode) { 91 | return xdb_file_unlock (pTblm->stg_mgr.stg_fd, 1, 1); 92 | } 93 | return XDB_OK; 94 | } 95 | 96 | static inline int 97 | xdb_rdlock_db (xdb_dbm_t *pDbm) 98 | { 99 | if (XDB_LOCK_PROCESS == pDbm->lock_mode) { 100 | return xdb_file_rdlock (pDbm->stg_mgr.stg_fd, 0, 1); 101 | } else { 102 | xdb_rwlock_rdlock (&pDbm->db_lock); 103 | } 104 | return XDB_OK; 105 | } 106 | 107 | static inline int 108 | xdb_rdunlock_db (xdb_dbm_t *pDbm) 109 | { 110 | if (XDB_LOCK_PROCESS == pDbm->lock_mode) { 111 | return xdb_file_unlock (pDbm->stg_mgr.stg_fd, 0, 1); 112 | } else { 113 | xdb_rwlock_rdunlock (&pDbm->db_lock); 114 | } 115 | return XDB_OK; 116 | } 117 | 118 | static inline int 119 | xdb_wrlock_db (xdb_dbm_t *pDbm) 120 | { 121 | if (XDB_LOCK_PROCESS == pDbm->lock_mode) { 122 | return xdb_file_wrlock (pDbm->stg_mgr.stg_fd, 0, 1); 123 | } else { 124 | xdb_rwlock_wrlock (&pDbm->db_lock); 125 | } 126 | return XDB_OK; 127 | } 128 | 129 | static inline int 130 | xdb_wrunlock_db (xdb_dbm_t *pDbm) 131 | { 132 | if (XDB_LOCK_PROCESS == pDbm->lock_mode) { 133 | return xdb_file_unlock (pDbm->stg_mgr.stg_fd, 0, 1); 134 | } else { 135 | xdb_rwlock_wrunlock (&pDbm->db_lock); 136 | } 137 | return XDB_OK; 138 | } 139 | 140 | XDB_STATIC void 141 | xdb_tbltrans_init (xdb_tblTrans_t *pTblRows); 142 | 143 | XDB_STATIC int 144 | xdb_begin2 (xdb_conn_t *pConn, bool bAutoCommit); 145 | 146 | static inline bool 147 | xdb_row_valid (xdb_conn_t *pConn, xdb_tblm_t *pTblm, void *pRow, xdb_rowid rid) 148 | { 149 | bool valid; 150 | uint8_t ctrl = XDB_ROW_CTRL (pTblm->stg_mgr.pStgHdr, pRow) & XDB_ROW_MASK; 151 | if (ctrl < XDB_ROW_COMMIT) { 152 | return false; 153 | } 154 | if (XDB_ROW_COMMIT == ctrl) { 155 | // not delete by this conn? 156 | valid = ! xdb_trans_getrow (pConn, pTblm, rid, false); 157 | } else { 158 | // insert by this conn? 159 | valid = xdb_trans_getrow (pConn, pTblm, rid, true); 160 | } 161 | if (valid) { 162 | if (xdb_unlikely (pTblm->pTtlFld != NULL)) { 163 | uint64_t ttl_ts = *(uint64_t*)(pRow + pTblm->pTtlFld->fld_off); 164 | return pTblm->cur_ts < ttl_ts; 165 | } 166 | } 167 | return valid; 168 | } 169 | 170 | #endif // __XDB_TRANS_H__ 171 | -------------------------------------------------------------------------------- /src/parser/xdb_parser_idx.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | XDB_STATIC int 13 | xdb_parse_create_idx_def (xdb_conn_t* pConn, xdb_token_t *pTkn, xdb_stmt_idx_t *pStmt) 14 | { 15 | int type = pTkn->tk_type; 16 | 17 | if ((XDB_TOK_ID==type) && !strcasecmp (pTkn->token, "USING")) { 18 | type = xdb_next_token (pTkn); 19 | if ((XDB_TOK_ID==type) && (!strcasecmp (pTkn->token, "BTREE") || !strcasecmp (pTkn->token, "RBTREE"))) { 20 | pStmt->idx_type = XDB_IDX_RBTREE; 21 | } else { 22 | XDB_EXPECT ((XDB_TOK_ID==type) && !strcasecmp (pTkn->token, "HASH"), XDB_E_STMT, "Expect HASH"); 23 | } 24 | type = xdb_next_token (pTkn); 25 | } 26 | 27 | XDB_EXPECT (XDB_TOK_LP==type, XDB_E_STMT, "Index Miss ("); 28 | 29 | // col list 30 | pStmt->fld_count = 0; 31 | do { 32 | type = xdb_next_token (pTkn); 33 | if (XDB_TOK_ID == type) { 34 | pStmt->idx_col[pStmt->fld_count] = pTkn->token; 35 | } else { 36 | break; 37 | } 38 | type = xdb_next_token (pTkn); 39 | pStmt->idx_extract[pStmt->fld_count] = NULL; 40 | if (XDB_TOK_EXTRACT == type) { 41 | type = xdb_next_token (pTkn); 42 | XDB_EXPECT (XDB_TOK_STR>=type, XDB_E_STMT, "Index field"); 43 | pStmt->idx_extract[pStmt->fld_count] = pTkn->token; 44 | type = xdb_next_token (pTkn); 45 | } 46 | pStmt->fld_count++; 47 | } while (XDB_TOK_COMMA == type); 48 | 49 | XDB_EXPECT (XDB_TOK_RP==type, XDB_E_STMT, "Index Miss )"); 50 | 51 | type = xdb_next_token (pTkn); 52 | if (XDB_TOK_ID==type && !strcasecmp (pTkn->token, "XOID")) { 53 | type = xdb_next_token (pTkn); 54 | XDB_EXPECT (XDB_TOK_EQ==type, XDB_E_STMT, "Index option Expect EQ"); 55 | type = xdb_next_token (pTkn); 56 | XDB_EXPECT (XDB_TOK_NUM >= type, XDB_E_STMT, "Index option Expect STRING"); 57 | pStmt->xoid = atoi (pTkn->token); 58 | type = xdb_next_token (pTkn); 59 | //xdb_dbglog ("index xoid = %d\n", pStmt->xoid); 60 | } 61 | 62 | return type; 63 | 64 | error: 65 | return -XDB_E_STMT; 66 | } 67 | 68 | XDB_STATIC xdb_stmt_t* 69 | xdb_parse_create_index (xdb_conn_t* pConn, xdb_token_t *pTkn, bool bUnique) 70 | { 71 | xdb_token_type type = xdb_next_token (pTkn); 72 | xdb_stmt_idx_t *pStmt = &pConn->stmt_union.idx_stmt;; 73 | pStmt->stmt_type = XDB_STMT_CREATE_IDX; 74 | pStmt->pSql = NULL; 75 | 76 | XDB_EXPECT (XDB_TOK_ID==type, XDB_E_STMT, "Miss Index name"); 77 | 78 | pStmt->idx_name = pTkn->token; 79 | pStmt->idx_type = XDB_IDX_HASH; 80 | pStmt->xoid = -1; 81 | pStmt->bUnique = bUnique; 82 | 83 | type = xdb_next_token (pTkn); 84 | XDB_EXPECT ((XDB_TOK_ID==type) && !strcasecmp (pTkn->token, "ON"), XDB_E_STMT, "Expect ON"); 85 | 86 | type = xdb_next_token (pTkn); 87 | 88 | XDB_EXPECT (XDB_TOK_STR>=type, XDB_E_STMT, "Miss table name"); 89 | 90 | XDB_PARSE_DBTBLNAME(); 91 | 92 | xdb_idxm_t *pIdxm = xdb_find_index (pStmt->pTblm, pStmt->idx_name); 93 | XDB_EXPECT (NULL == pIdxm, XDB_E_EXISTS, "Index '%s' already exists", pStmt->idx_name); 94 | 95 | #ifndef XDB_DEBUG 96 | if (pStmt->pTblm->pDbm->bSysDb) { 97 | XDB_EXPECT (pConn == s_xdb_sysdb_pConn, XDB_E_CONSTRAINT, "Can't create index '%s' on table '%s' in system database", pStmt->idx_name, XDB_OBJ_NAME(pStmt->pTblm)); 98 | } 99 | #endif 100 | 101 | int rc = xdb_parse_create_idx_def (pConn, pTkn, pStmt); 102 | XDB_EXPECT2 (rc >= XDB_OK); 103 | 104 | return (xdb_stmt_t*)pStmt; 105 | 106 | error: 107 | xdb_stmt_free ((xdb_stmt_t*)pStmt); 108 | return NULL; 109 | } 110 | 111 | XDB_STATIC xdb_stmt_t* 112 | xdb_parse_drop_index (xdb_conn_t* pConn, xdb_token_t *pTkn) 113 | { 114 | xdb_stmt_idx_t *pStmt = &pConn->stmt_union.idx_stmt; 115 | pStmt->stmt_type = XDB_STMT_DROP_IDX; 116 | pStmt->pSql = NULL; 117 | 118 | xdb_token_type type = xdb_next_token (pTkn); 119 | XDB_EXPECT (type == XDB_TOK_ID, XDB_E_STMT, "Miss INDEX name: "XDB_SQL_DROP_IDX_STMT); 120 | 121 | pStmt->idx_name = pTkn->token; 122 | 123 | type = xdb_next_token (pTkn); 124 | XDB_EXPECT ((XDB_TOK_ID == type) && !strcasecmp (pTkn->token, "ON"), XDB_E_STMT, "Expect ON: "XDB_SQL_DROP_IDX_STMT); 125 | 126 | type = xdb_next_token (pTkn); 127 | XDB_EXPECT (type <= XDB_TOK_STR, XDB_E_STMT, "Miss table name: "XDB_SQL_DROP_IDX_STMT); 128 | 129 | XDB_PARSE_DBTBLNAME(); 130 | 131 | pStmt->pIdxm = xdb_find_index (pStmt->pTblm, pStmt->idx_name); 132 | XDB_EXPECT (NULL!=pStmt->pIdxm, XDB_E_NOTFOUND, "Index '%s' doesn't exist", pStmt->idx_name); 133 | 134 | if ((NULL != pStmt->pIdxm) && pStmt->pIdxm->pTblm->pDbm->bSysDb) { 135 | XDB_EXPECT (pConn == s_xdb_sysdb_pConn, XDB_E_CONSTRAINT, "Can't drop table '%s' index '%s' in system database", XDB_OBJ_NAME(pStmt->pIdxm->pTblm), XDB_OBJ_NAME(pStmt->pIdxm)); 136 | } 137 | 138 | return (xdb_stmt_t*)pStmt; 139 | error: 140 | xdb_stmt_free ((xdb_stmt_t*)pStmt); 141 | return NULL; 142 | } 143 | -------------------------------------------------------------------------------- /src/parser/xdb_json.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | static bool xdb_json_match_ary (xdb_token_t *pTkn, const char *path, int len, xdb_value_t *pVal); 13 | 14 | static bool xdb_json_match_obj (xdb_token_t *pTkn, const char *path, int len, xdb_value_t *pVal) 15 | { 16 | bool bFound; 17 | 18 | while (1) { 19 | xdb_token_type type = xdb_next_token_mark (pTkn, false); 20 | XDB_EXPECT3 (type == XDB_TOK_STR, "expect field, but got %s\n", xdb_tok2str(type)); 21 | bFound = (path && (pTkn->tk_len == len) && !memcmp (pTkn->token, path, len)); 22 | 23 | type = xdb_next_token_mark (pTkn, false); 24 | XDB_EXPECT3 (type == XDB_TOK_COLON, "expect :, but got %s\n", xdb_tok2str(type)); 25 | type = xdb_next_token_mark (pTkn, false); 26 | 27 | switch (type) { 28 | case XDB_TOK_LB: 29 | bFound = xdb_json_match_obj (pTkn, NULL, 0, NULL); 30 | if (bFound) { 31 | return true; 32 | } 33 | break; 34 | case XDB_TOK_LBK: 35 | bFound = xdb_json_match_ary (pTkn, NULL, 0, NULL); 36 | if (bFound) { 37 | return true; 38 | } 39 | break; 40 | case XDB_TOK_RB: 41 | return false; 42 | case XDB_TOK_NUM: 43 | if (bFound) { 44 | char buf[128]; 45 | XDB_EXPECT3 (pTkn->tk_len < sizeof(buf), "too big num"); 46 | memcpy (buf, pTkn->token, pTkn->tk_len); 47 | pVal->val_str.str = pTkn->token; 48 | pVal->val_str.len = pTkn->tk_len; 49 | buf[pTkn->tk_len] = '\0'; 50 | if (!pTkn->bFloat) { 51 | pVal->ival = atoll (pTkn->token); 52 | pVal->val_type = XDB_TYPE_BIGINT; 53 | } else { 54 | pVal->fval = atof (pTkn->token); 55 | pVal->val_type = XDB_TYPE_DOUBLE; 56 | } 57 | return true; 58 | } 59 | break; 60 | case XDB_TOK_STR: 61 | if (bFound) { 62 | pVal->val_type = XDB_TYPE_CHAR; 63 | pVal->str.str = pTkn->token; 64 | pVal->str.len = pTkn->tk_len; 65 | pVal->val_str.str = pTkn->token; 66 | pVal->val_str.len = pTkn->tk_len; 67 | return true; 68 | } 69 | break; 70 | default: 71 | XDB_EXPECT3 (0, "wrong json format %s\n", xdb_tok2str(type)); 72 | } 73 | 74 | type = xdb_next_token_mark (pTkn, false); 75 | if (XDB_TOK_RB == type) { 76 | return false; 77 | } 78 | XDB_EXPECT3 (XDB_TOK_COMMA == type, "expect , but got %s\n", xdb_tok2str(type)); 79 | } 80 | 81 | error: 82 | pVal->val_type = XDB_TYPE_NULL; 83 | return false; 84 | } 85 | 86 | static bool xdb_json_match_ary (xdb_token_t *pTkn, const char *path, int len, xdb_value_t *pVal) 87 | { 88 | bool bFound; 89 | while (1) { 90 | xdb_token_type type = xdb_next_token_mark (pTkn, false); 91 | switch (type) { 92 | case XDB_TOK_LB: 93 | bFound = xdb_json_match_obj (pTkn, NULL, 0, NULL); 94 | if (bFound) { 95 | return true; 96 | } 97 | break; 98 | case XDB_TOK_LBK: 99 | bFound = xdb_json_match_ary (pTkn, NULL, 0, NULL); 100 | if (bFound) { 101 | return true; 102 | } 103 | continue; 104 | case XDB_TOK_RBK: 105 | return false; 106 | case XDB_TOK_NUM: 107 | break; 108 | case XDB_TOK_STR: 109 | break; 110 | default: 111 | XDB_EXPECT3 (0, "wrong json format %s\n", xdb_tok2str(type)); 112 | } 113 | 114 | type = xdb_next_token_mark (pTkn, false); 115 | if (XDB_TOK_RBK == type) { 116 | return false; 117 | } 118 | XDB_EXPECT3 (XDB_TOK_COMMA == type, "expect , but got %s\n", xdb_tok2str(type)); 119 | } 120 | 121 | error: 122 | return false; 123 | } 124 | 125 | static bool xdb_json_extract (const char* json, const char *path, xdb_value_t *pVal) 126 | { 127 | xdb_token_type type; 128 | int len = strlen(path); 129 | 130 | xdb_token_t token = XDB_TOK_INIT((char*)json); 131 | 132 | type = xdb_next_token_mark (&token, false); 133 | 134 | XDB_EXPECT3 (type == XDB_TOK_LB, "Expect {\n"); 135 | 136 | bool bFound = xdb_json_match_obj (&token, path, len, pVal); 137 | if (bFound) { 138 | return true; 139 | } 140 | 141 | error: 142 | pVal->val_type = XDB_TYPE_NULL; 143 | return false; 144 | } 145 | 146 | static int xdb_json_replace (char* dst, xdb_str_t *pJson, const char *path, xdb_value_t *pVal) 147 | { 148 | xdb_value_t val; 149 | int len = pJson->len; 150 | bool bOk = xdb_json_extract (pJson->str, path, &val); 151 | if (bOk) { 152 | int offset = val.val_str.str - pJson->str; 153 | if (dst != pJson->str) { 154 | memcpy (dst, pJson->str, offset); 155 | memcpy (dst + offset, pVal->val_str.str, pVal->val_str.len); 156 | memcpy (dst + offset + pVal->val_str.len, val.val_str.str + val.val_str.len, pJson->len - offset - val.val_str.len); 157 | } else { 158 | memmove (dst + offset + pVal->val_str.len, val.val_str.str + val.val_str.len, pJson->len - offset - val.val_str.len); 159 | memcpy (dst + offset, pVal->val_str.str, pVal->val_str.len); 160 | } 161 | 162 | len += pVal->val_str.len - val.val_str.len; 163 | dst[len] = '\0'; 164 | } 165 | return len; 166 | } 167 | 168 | -------------------------------------------------------------------------------- /bench/basic/bench-sqlite.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | 4 | #define BENCH_DBNAME "SQLite" 5 | int LKUP_COUNT = 1000000; 6 | 7 | #include "bench.h" 8 | 9 | static inline void error_check (int status) 10 | { 11 | if (status != SQLITE_OK) { 12 | printf("sqlite3 error: status = %d\n", status); 13 | exit(1); 14 | } 15 | } 16 | 17 | static inline void exec_error_check (int status, char *err_msg) 18 | { 19 | if (status != SQLITE_OK) { 20 | printf ("SQL error: %s\n", err_msg); 21 | sqlite3_free (err_msg); 22 | exit(1); 23 | } 24 | } 25 | 26 | static inline void step_error_check (int status) 27 | { 28 | if (status != SQLITE_DONE) { 29 | printf ("SQL step error: status = %d\n", status); 30 | exit(1); 31 | } 32 | } 33 | 34 | void* bench_open (const char *db) 35 | { 36 | int status; 37 | char* err_msg = NULL; 38 | sqlite3* pDb = NULL; 39 | 40 | status = sqlite3_open (db, &pDb); 41 | error_check (status); 42 | 43 | char * sqlite3_cfg[] = { 44 | "PRAGMA synchronous = OFF", 45 | "PRAGMA journal_mode = OFF", 46 | "PRAGMA temp_store = memory", 47 | "PRAGMA optimize", 48 | NULL 49 | }; 50 | for (int i = 0; NULL != sqlite3_cfg[i]; ++i) { 51 | status = sqlite3_exec (pDb, sqlite3_cfg[i], NULL, NULL, &err_msg); 52 | exec_error_check (status, err_msg); 53 | } 54 | 55 | return pDb; 56 | 57 | error: 58 | return NULL; 59 | } 60 | 61 | void bench_close (void *pDb) 62 | { 63 | int status = sqlite3_close (pDb); 64 | error_check (status); 65 | } 66 | 67 | bool bench_sql (void *pDb, const char *sql) 68 | { 69 | char* err_msg = NULL; 70 | int status = sqlite3_exec (pDb, sql, NULL, NULL, &err_msg); 71 | exec_error_check (status, err_msg); 72 | return true; 73 | } 74 | 75 | bool bench_sql_insert (void *pDb, const char *sql, int id, const char *name, int age, const char *cls, int score) 76 | { 77 | sqlite3_stmt *pStmt = bench_stmt_prepare (pDb, sql); 78 | bool ok = bench_stmt_insert (pStmt, id, name, age, cls, score); 79 | bench_stmt_close (pStmt); 80 | return ok; 81 | } 82 | 83 | bool bench_sql_get_byid (void *pDb, const char *sql, int id, stu_callback callback, void *pArg) 84 | { 85 | sqlite3_stmt *pStmt = bench_stmt_prepare (pDb, sql); 86 | bool ok = bench_stmt_get_byid (pStmt, id, callback, pArg); 87 | bench_stmt_close (pStmt); 88 | return ok; 89 | } 90 | 91 | bool bench_sql_updAge_byid (void *pDb, const char *sql, int id, int age) 92 | { 93 | sqlite3_stmt *pStmt = bench_stmt_prepare (pDb, sql); 94 | bool ok = bench_stmt_updAge_byid (pStmt, id, age); 95 | bench_stmt_close (pStmt); 96 | return ok; 97 | } 98 | 99 | bool bench_sql_del_byid (void *pDb, const char *sql, int id) 100 | { 101 | sqlite3_stmt *pStmt = bench_stmt_prepare (pDb, sql); 102 | bool ok = bench_stmt_del_byid (pStmt, id); 103 | bench_stmt_close (pStmt); 104 | return ok; 105 | } 106 | 107 | void* bench_stmt_prepare (void *pDb, const char *sql) 108 | { 109 | sqlite3_stmt *pStmt; 110 | int status = sqlite3_prepare_v2 (pDb, sql, -1, &pStmt, NULL); 111 | error_check (status); 112 | return pStmt; 113 | } 114 | 115 | void bench_stmt_close (void *pStmt) 116 | { 117 | sqlite3_finalize (pStmt); 118 | } 119 | 120 | bool bench_stmt_insert (void *pStmt, int id, const char *name, int age, const char *cls, int score) 121 | { 122 | sqlite3_bind_int (pStmt, 1, id); 123 | sqlite3_bind_text (pStmt, 2, name, strlen(name), SQLITE_STATIC); 124 | sqlite3_bind_int (pStmt, 3, age); 125 | sqlite3_bind_text (pStmt, 4, cls, strlen(cls), SQLITE_STATIC); 126 | sqlite3_bind_int (pStmt, 5, score); 127 | 128 | int status = sqlite3_step (pStmt); 129 | step_error_check(status); 130 | 131 | // Reset SQLite statement for next call 132 | sqlite3_clear_bindings (pStmt); 133 | sqlite3_reset (pStmt); 134 | return true; 135 | } 136 | 137 | bool bench_stmt_get_byid (void *pStmt, int id, stu_callback callback, void *pArg) 138 | { 139 | int ok = false; 140 | int status; 141 | 142 | sqlite3_bind_int (pStmt, 1, id); 143 | // Execute query_stmt 144 | if ((status = sqlite3_step (pStmt)) == SQLITE_ROW) { 145 | int id = sqlite3_column_int (pStmt, 0); 146 | const char *name = (const char *)sqlite3_column_text (pStmt, 1); 147 | int age = sqlite3_column_int (pStmt, 2); 148 | const char *cls = (const char *)sqlite3_column_text (pStmt, 3); 149 | int score = sqlite3_column_int (pStmt, 4); 150 | callback (pArg, id, name, age, cls, score); 151 | ok = true; 152 | } else { 153 | bench_print ("sqlite3 error: status = %d, no row found\n", status); 154 | } 155 | 156 | // Reset SQLite statement for next call 157 | sqlite3_clear_bindings (pStmt); 158 | sqlite3_reset (pStmt); 159 | return ok; 160 | } 161 | 162 | bool bench_stmt_updAge_byid (void *pStmt, int id, int age) 163 | { 164 | sqlite3_bind_int (pStmt, 1, age); 165 | sqlite3_bind_int (pStmt, 2, id); 166 | int status = sqlite3_step (pStmt); 167 | step_error_check(status); 168 | 169 | // Reset SQLite statement for next call 170 | sqlite3_clear_bindings (pStmt); 171 | sqlite3_reset (pStmt); 172 | return true; 173 | } 174 | 175 | bool bench_stmt_del_byid (void *pStmt, int id) 176 | { 177 | sqlite3_bind_int (pStmt, 1, id); 178 | int status = sqlite3_step (pStmt); 179 | step_error_check(status); 180 | 181 | // Reset SQLite statement for next call 182 | sqlite3_clear_bindings (pStmt); 183 | sqlite3_reset (pStmt); 184 | return true; 185 | } 186 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | support@crossdb.org. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /bench/basic/bench-boostmidx.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define BENCH_DBNAME "Boost" 10 | #define TEST_NAME(i) i?"Hash":"Order" 11 | int LKUP_COUNT = 10000000; 12 | 13 | using namespace std; 14 | 15 | #include "bench.h" 16 | 17 | class student { 18 | public: 19 | int id; 20 | string name; 21 | int age; 22 | string cls; 23 | int score; 24 | 25 | student () { 26 | } 27 | student (int id, string &name, int age, string &cls, int score) { 28 | this->id = id; 29 | this->name = name; 30 | this->age = age; 31 | this->cls = cls; 32 | this->score = score; 33 | } 34 | }; 35 | 36 | // std::shared_mutex is not used because, in a single-threaded context, the compiler optimizes the code and omits the lock. 37 | static pthread_rwlock_t stu_tbl_lock = PTHREAD_RWLOCK_INITIALIZER; 38 | 39 | 40 | /************************************************ 41 | Ordered Index 42 | ************************************************/ 43 | 44 | struct _id {}; 45 | 46 | using student_table_map = 47 | boost::multi_index::multi_index_container< 48 | student, 49 | boost::multi_index::indexed_by< 50 | boost::multi_index::ordered_unique, BOOST_MULTI_INDEX_MEMBER(student, int, id)> 51 | > 52 | >; 53 | 54 | student_table_map stu_tbl_map; 55 | 56 | student_table_map::index<_id>::type& find_id_map = stu_tbl_map.get<_id>(); 57 | 58 | void* bench_open (const char *db) 59 | { 60 | return &stu_tbl_map; 61 | } 62 | 63 | void bench_close (void *pDb) 64 | { 65 | } 66 | 67 | bool bench_sql (void *pDb, const char *sql) 68 | { 69 | return true; 70 | } 71 | 72 | bool bench_sql_insert (void *pDb, const char *sql, int id, string &name, int age, string &cls, int score) 73 | { 74 | pthread_rwlock_wrlock(&stu_tbl_lock); 75 | auto ok = stu_tbl_map.emplace (id, name, age, cls, score); 76 | pthread_rwlock_unlock(&stu_tbl_lock); 77 | return ok.second; 78 | } 79 | 80 | bool bench_sql_get_byid (void *pDb, const char *sql, int id, stu_callback callback, void *pArg) 81 | { 82 | student stu, *pStu = NULL; 83 | 84 | pthread_rwlock_rdlock(&stu_tbl_lock); 85 | student_table_map::index<_id>::type::iterator iter_id = find_id_map.find(id); 86 | if (iter_id != find_id_map.end()) { 87 | stu = *iter_id; 88 | pStu = &stu; 89 | } 90 | pthread_rwlock_unlock(&stu_tbl_lock); 91 | 92 | if (pStu) { 93 | // handle row out of the lock to reduce lock time 94 | callback (pArg, pStu->id, pStu->name, pStu->age, pStu->cls, pStu->score); 95 | } 96 | return pStu != NULL; 97 | } 98 | 99 | bool bench_sql_updAge_byid (void *pDb, const char *sql, int id, int age) 100 | { 101 | bool ok = false; 102 | student stu; 103 | 104 | pthread_rwlock_wrlock(&stu_tbl_lock); 105 | student_table_map::index<_id>::type::iterator iter_id = find_id_map.find(id); 106 | if (iter_id != find_id_map.end()) { 107 | stu = *iter_id; 108 | stu.age += age; 109 | ok = find_id_map.replace(iter_id, stu); 110 | } 111 | pthread_rwlock_unlock(&stu_tbl_lock); 112 | return ok; 113 | } 114 | 115 | bool bench_sql_del_byid (void *pDb, const char *sql, int id) 116 | { 117 | pthread_rwlock_wrlock(&stu_tbl_lock); 118 | bool ok = find_id_map.erase(id); 119 | pthread_rwlock_unlock(&stu_tbl_lock); 120 | return ok; 121 | } 122 | 123 | 124 | /************************************************ 125 | Hashed Index 126 | ************************************************/ 127 | 128 | using student_table_hmap = 129 | boost::multi_index::multi_index_container< 130 | student, 131 | boost::multi_index::indexed_by< 132 | boost::multi_index::hashed_unique, BOOST_MULTI_INDEX_MEMBER(student, int, id)> 133 | > 134 | >; 135 | 136 | student_table_hmap stu_tbl_hmap; 137 | 138 | student_table_hmap::index<_id>::type& find_id_hmap = stu_tbl_hmap.get<_id>(); 139 | 140 | void* bench_stmt_prepare (void *pDb, const char *sql) 141 | { 142 | return pDb; 143 | } 144 | 145 | void bench_stmt_close (void *pStmt) 146 | { 147 | } 148 | 149 | bool bench_stmt_insert (void *pStmt, int id, string &name, int age, string &cls, int score) 150 | { 151 | pthread_rwlock_wrlock(&stu_tbl_lock); 152 | auto ok = stu_tbl_hmap.emplace (id, name, age, cls, score); 153 | pthread_rwlock_unlock(&stu_tbl_lock); 154 | return ok.second; 155 | } 156 | 157 | bool bench_stmt_get_byid (void *pStmt, int id, stu_callback callback, void *pArg) 158 | { 159 | student stu, *pStu = NULL; 160 | 161 | pthread_rwlock_rdlock(&stu_tbl_lock); 162 | student_table_hmap::index<_id>::type::iterator iter_id = find_id_hmap.find(id); 163 | if (iter_id != find_id_hmap.end()) { 164 | stu = *iter_id; 165 | pStu = &stu; 166 | } 167 | pthread_rwlock_unlock(&stu_tbl_lock); 168 | 169 | if (pStu) { 170 | // handle row out of the lock to reduce lock time 171 | callback (pArg, stu.id, stu.name, stu.age, stu.cls, stu.score); 172 | } 173 | return pStu != NULL; 174 | } 175 | 176 | bool bench_stmt_updAge_byid (void *pStmt, int id, int age) 177 | { 178 | bool ok = false; 179 | student stu; 180 | 181 | pthread_rwlock_wrlock(&stu_tbl_lock); 182 | 183 | student_table_hmap::index<_id>::type::iterator iter_id = find_id_hmap.find(id); 184 | if (iter_id != find_id_hmap.end()) { 185 | stu = *iter_id; 186 | stu.age += age; 187 | ok = find_id_hmap.replace(iter_id, stu); 188 | } 189 | 190 | pthread_rwlock_unlock(&stu_tbl_lock); 191 | return ok; 192 | } 193 | 194 | bool bench_stmt_del_byid (void *pStmt, int id) 195 | { 196 | pthread_rwlock_wrlock(&stu_tbl_lock); 197 | bool ok = find_id_hmap.erase(id); 198 | pthread_rwlock_unlock(&stu_tbl_lock); 199 | return ok; 200 | } 201 | -------------------------------------------------------------------------------- /src/parser/xdb_parser_pubsub.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | XDB_STATIC xdb_stmt_t* 13 | xdb_parse_create_pub (xdb_conn_t* pConn, xdb_token_t *pTkn) 14 | { 15 | xdb_stmt_pub_t *pStmt = &pConn->stmt_union.pub_stmt; 16 | memset (pStmt, 0, sizeof (*pStmt)); 17 | pStmt->stmt_type = XDB_STMT_CREATE_PUB; 18 | 19 | xdb_token_type type = xdb_next_token (pTkn); 20 | XDB_EXPECT (XDB_TOK_STR>=type, XDB_E_STMT, "Miss publication name"); 21 | 22 | if ((XDB_TOK_ID==type) && !strcasecmp (pTkn->token, "IF")) { 23 | type = xdb_next_token (pTkn); 24 | XDB_EXPECT ((XDB_TOK_ID==type) && !strcasecmp (pTkn->token, "NOT"), XDB_E_STMT, "Miss NOT"); 25 | type = xdb_next_token (pTkn); 26 | XDB_EXPECT ((XDB_TOK_ID==type) && !strcasecmp (pTkn->token, "EXISTS"), XDB_E_STMT, "Miss EXISTS"); 27 | type = xdb_next_token (pTkn); 28 | XDB_EXPECT (XDB_TOK_ID==type, XDB_E_STMT, "Miss publication name"); 29 | pStmt->bIfExistOrNot = true; 30 | } 31 | pStmt->pub_name = pTkn->token; 32 | 33 | return (xdb_stmt_t*)pStmt; 34 | 35 | error: 36 | xdb_stmt_free ((xdb_stmt_t*)pStmt); 37 | return NULL; 38 | } 39 | 40 | XDB_STATIC xdb_stmt_t* 41 | xdb_parse_create_replica (xdb_conn_t* pConn, xdb_token_t *pTkn) 42 | { 43 | xdb_stmt_replica_t *pStmt = &pConn->stmt_union.replica_stmt; 44 | memset (pStmt, 0, sizeof (*pStmt)); 45 | pStmt->stmt_type = XDB_STMT_CREATE_REPLICA; 46 | 47 | xdb_token_type type = xdb_next_token (pTkn); 48 | XDB_EXPECT (XDB_TOK_STR>=type, XDB_E_STMT, "Miss replica name"); 49 | 50 | if ((XDB_TOK_ID==type) && !strcasecmp (pTkn->token, "IF")) { 51 | type = xdb_next_token (pTkn); 52 | XDB_EXPECT ((XDB_TOK_ID==type) && !strcasecmp (pTkn->token, "NOT"), XDB_E_STMT, "Miss NOT"); 53 | type = xdb_next_token (pTkn); 54 | XDB_EXPECT ((XDB_TOK_ID==type) && !strcasecmp (pTkn->token, "EXISTS"), XDB_E_STMT, "Miss EXISTS"); 55 | type = xdb_next_token (pTkn); 56 | XDB_EXPECT (XDB_TOK_ID==type, XDB_E_STMT, "Miss repica name"); 57 | pStmt->bIfExistOrNot = true; 58 | } 59 | pStmt->rep_name = pTkn->token; 60 | pStmt->svr_port = XDB_SVR_PORT; 61 | 62 | do { 63 | type = xdb_next_token (pTkn); 64 | if (XDB_TOK_END <= type) { 65 | break; 66 | } 67 | XDB_EXPECT (XDB_TOK_ID==type, XDB_E_STMT, "Expect ID"); 68 | 69 | const char *var = pTkn->token; 70 | type = xdb_next_token (pTkn); 71 | XDB_EXPECT (XDB_TOK_EQ==type, XDB_E_STMT, "Expect EQ"); 72 | type = xdb_next_token (pTkn); 73 | 74 | if (!strcasecmp (var, "DO_DB")) { 75 | XDB_EXPECT (XDB_TOK_LP==type, XDB_E_STMT, "Expect ("); 76 | pStmt->dbs = pTkn->token + 1; 77 | type = xdb_next_token2 (pTkn, ')'); 78 | } else if (!strcasecmp (var, "DO_TABLE")) { 79 | XDB_EXPECT (XDB_TOK_LP==type, XDB_E_STMT, "Expect ("); 80 | pStmt->tables = pTkn->token + 1; 81 | type = xdb_next_token2 (pTkn, ')'); 82 | } else if (!strcasecmp (var, "HOST")) { 83 | XDB_EXPECT (XDB_TOK_STR == type, XDB_E_STMT, "Expect HOST"); 84 | pStmt->svr_host = pTkn->token; 85 | } else if (!strcasecmp (var, "PORT")) { 86 | XDB_EXPECT (XDB_TOK_NUM == type, XDB_E_STMT, "Expect number"); 87 | pStmt->svr_port = atoi (pTkn->token); 88 | } 89 | type = xdb_next_token (pTkn); 90 | } while (XDB_TOK_COMMA == type); 91 | 92 | XDB_EXPECT (pStmt->svr_host != NULL, XDB_E_STMT, "Expect Server HOST"); 93 | 94 | return (xdb_stmt_t*)pStmt; 95 | 96 | error: 97 | xdb_stmt_free ((xdb_stmt_t*)pStmt); 98 | return NULL; 99 | } 100 | 101 | #if 0 102 | XDB_STATIC xdb_stmt_t* 103 | xdb_parse_drop_pub (xdb_conn_t* pConn, xdb_token_t *pTkn) 104 | { 105 | xdb_stmt_svr_t *pStmt = &pConn->stmt_union.svr_stmt; 106 | memset (pStmt, 0, sizeof (*pStmt)); 107 | pStmt->stmt_type = XDB_STMT_DROP_SVR; 108 | 109 | xdb_token_type type = xdb_next_token (pTkn); 110 | XDB_EXPECT (XDB_TOK_STR>=type, XDB_E_STMT, "Miss server name"); 111 | pStmt->svr_name = pTkn->token; 112 | 113 | return (xdb_stmt_t*)pStmt; 114 | 115 | error: 116 | xdb_stmt_free ((xdb_stmt_t*)pStmt); 117 | return NULL; 118 | } 119 | #endif 120 | 121 | XDB_STATIC xdb_stmt_t* 122 | xdb_parse_subscribe (xdb_conn_t* pConn, xdb_token_t *pTkn) 123 | { 124 | xdb_stmt_subscribe_t *pStmt = &pConn->stmt_union.subscribe_stmt; 125 | memset (pStmt, 0, sizeof (*pStmt)); 126 | pStmt->stmt_type = XDB_STMT_SUBSCRIBE; 127 | 128 | xdb_token_type type = xdb_next_token (pTkn); 129 | XDB_EXPECT (XDB_TOK_STR>=type, XDB_E_STMT, "Miss publication name"); 130 | 131 | pStmt->sub_name = pTkn->token; 132 | 133 | do { 134 | type = xdb_next_token (pTkn); 135 | if (XDB_TOK_END <= type) { 136 | break; 137 | } 138 | XDB_EXPECT (XDB_TOK_ID==type, XDB_E_STMT, "Expect ID"); 139 | const char *var = pTkn->token; 140 | type = xdb_next_token (pTkn); 141 | XDB_EXPECT (XDB_TOK_EQ==type, XDB_E_STMT, "Expect EQ"); 142 | type = xdb_next_token (pTkn); 143 | 144 | if (0 == strcasecmp (var, "DB")) { 145 | pStmt->dbs = pTkn->token; 146 | } else if (0 == strcasecmp (var, "TABLE")) { 147 | pStmt->tables = pTkn->token; 148 | } else if (0 == strcasecmp (var, "REPLICA")) { 149 | pStmt->bReplica = atoi (pTkn->token); 150 | } else if (0 == strcasecmp (var, "CLIENT_ID")) { 151 | pStmt->client_id = pTkn->token; 152 | } 153 | type = xdb_next_token (pTkn); 154 | } while (XDB_TOK_COMMA == type); 155 | 156 | return (xdb_stmt_t*)pStmt; 157 | 158 | error: 159 | xdb_stmt_free ((xdb_stmt_t*)pStmt); 160 | return NULL; 161 | } 162 | 163 | -------------------------------------------------------------------------------- /src/lib/xdb_sock.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #if !defined(_WIN32) 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #define xdb_sock_open(domain,type,protocol) socket(domain, type, protocol) 21 | #define xdb_sock_read(sockfd, buf, len) read(sockfd, buf, len) 22 | #define xdb_sock_write(sockfd, buf, len) write(sockfd, buf, len) 23 | #define xdb_sock_close(sockfd) close(sockfd) 24 | #define xdb_sock_connect(sockfd,addr,addrlen) connect(sockfd,addr,addrlen) 25 | #define xdb_sock_init() xdb_signal_block(SIGPIPE) 26 | #define xdb_sock_exit() 27 | 28 | #else 29 | 30 | static int __xdb_winError2Errno(int err) { 31 | switch (err) { 32 | case WSAEWOULDBLOCK: 33 | return EWOULDBLOCK; 34 | case WSAEINPROGRESS: 35 | return EINPROGRESS; 36 | case WSAEALREADY: 37 | return EALREADY; 38 | case WSAENOTSOCK: 39 | return ENOTSOCK; 40 | case WSAEDESTADDRREQ: 41 | return EDESTADDRREQ; 42 | case WSAEMSGSIZE: 43 | return EMSGSIZE; 44 | case WSAEPROTOTYPE: 45 | return EPROTOTYPE; 46 | case WSAENOPROTOOPT: 47 | return ENOPROTOOPT; 48 | case WSAEPROTONOSUPPORT: 49 | return EPROTONOSUPPORT; 50 | case WSAEOPNOTSUPP: 51 | return EOPNOTSUPP; 52 | case WSAEAFNOSUPPORT: 53 | return EAFNOSUPPORT; 54 | case WSAEADDRINUSE: 55 | return EADDRINUSE; 56 | case WSAEADDRNOTAVAIL: 57 | return EADDRNOTAVAIL; 58 | case WSAENETDOWN: 59 | return ENETDOWN; 60 | case WSAENETUNREACH: 61 | return ENETUNREACH; 62 | case WSAENETRESET: 63 | return ENETRESET; 64 | case WSAECONNABORTED: 65 | return ECONNABORTED; 66 | case WSAECONNRESET: 67 | return ECONNRESET; 68 | case WSAENOBUFS: 69 | return ENOBUFS; 70 | case WSAEISCONN: 71 | return EISCONN; 72 | case WSAENOTCONN: 73 | return ENOTCONN; 74 | case WSAETIMEDOUT: 75 | return ETIMEDOUT; 76 | case WSAECONNREFUSED: 77 | return ECONNREFUSED; 78 | case WSAELOOP: 79 | return ELOOP; 80 | case WSAENAMETOOLONG: 81 | return ENAMETOOLONG; 82 | case WSAEHOSTUNREACH: 83 | return EHOSTUNREACH; 84 | case WSAENOTEMPTY: 85 | return ENOTEMPTY; 86 | default: 87 | return EIO; 88 | } 89 | } 90 | 91 | #define xdb_sock_open(domain,type,protocol) socket(domain, type, protocol) 92 | #define xdb_sock_close(sock) closesocket(sock) 93 | 94 | XDB_STATIC ssize_t 95 | xdb_sock_read(SOCKET sockfd, void *buf, size_t len) 96 | { 97 | int ret = recv(sockfd, buf, len, 0); 98 | errno = (SOCKET_ERROR != ret) ? 0 : __xdb_winError2Errno(WSAGetLastError()); 99 | return ret != SOCKET_ERROR ? ret : 0; 100 | } 101 | 102 | XDB_STATIC ssize_t 103 | xdb_sock_write(SOCKET sockfd, const void *buf, size_t len) 104 | { 105 | int ret = send(sockfd, buf, len, 0); 106 | errno = (SOCKET_ERROR != ret) ? 0 : __xdb_winError2Errno(WSAGetLastError()); 107 | return ret != SOCKET_ERROR ? ret : 0; 108 | } 109 | 110 | XDB_STATIC int 111 | xdb_sock_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen) 112 | { 113 | int ret = connect(sockfd,addr,addrlen); 114 | errno = (SOCKET_ERROR != ret) ? 0 : __xdb_winError2Errno(WSAGetLastError()); 115 | return ret != SOCKET_ERROR ? ret : 0; 116 | } 117 | 118 | XDB_STATIC void 119 | xdb_sock_init() 120 | { 121 | WORD versionWanted = MAKEWORD(1, 1); 122 | WSADATA wsaData; 123 | WSAStartup(versionWanted, &wsaData); 124 | } 125 | #define socket_exit() WSACleanup(); 126 | #endif 127 | 128 | static inline int xdb_sock_SetTcpNoDelay (int fd, int val) 129 | { 130 | #ifndef _WIN32 131 | if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) < 0) { 132 | return -1; 133 | } 134 | #endif 135 | return 0; 136 | } 137 | 138 | XDB_STATIC void xdb_sockaddr_init (struct sockaddr_in *addr, int port, const char *host) 139 | { 140 | memset (addr, 0, sizeof(*addr)); 141 | addr->sin_family = AF_INET; 142 | addr->sin_port = htons(port); 143 | in_addr_t hadd = inet_addr (host); 144 | memcpy (&addr->sin_addr, &hadd, sizeof (addr->sin_addr)); 145 | } 146 | 147 | static int xdb_sock_vprintf (int sockfd, const char *format, va_list ap) 148 | { 149 | char buf[32*1024], *pBuf = buf; 150 | va_list dupArgs; 151 | 152 | va_copy(dupArgs, ap); 153 | 154 | int len = vsnprintf(pBuf, sizeof(buf), format, ap); 155 | if (len >= sizeof(buf)) { 156 | pBuf = (char*) xdb_malloc(len + 1 ); 157 | if (NULL != pBuf) { 158 | vsnprintf (pBuf, len+1, format, dupArgs); 159 | pBuf[len] = '\0'; 160 | } 161 | } 162 | 163 | va_end(dupArgs); 164 | 165 | if (NULL != pBuf) { 166 | len = xdb_sock_write (sockfd, pBuf, len); 167 | } 168 | if (pBuf != buf) { 169 | xdb_free (pBuf); 170 | } 171 | 172 | return len; 173 | } 174 | 175 | #if 0 176 | static int xdb_sock_printf (int sockfd, const char *format, ...) 177 | { 178 | va_list ap; 179 | 180 | va_start(ap, format); 181 | int len = xdb_sock_vprintf (sockfd, format, ap); 182 | va_end(ap); 183 | 184 | return len; 185 | } 186 | 187 | static int cdb_sock_puts (int sockfd, const char *str) 188 | { 189 | int len = strlen(str); 190 | len = xdb_sock_write (sockfd, str, strlen(str)); 191 | return len; 192 | } 193 | 194 | static int cdb_sock_putc (int sockfd, const char ch) 195 | { 196 | return xdb_sock_write (sockfd, &ch, 1); 197 | } 198 | #endif 199 | -------------------------------------------------------------------------------- /src/crossdb.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #undef XDB_STATIC 13 | #define XDB_STATIC static 14 | 15 | #include "../include/crossdb.h" 16 | #include "core/xdb_cfg.h" 17 | #include "lib/xdb_lib.c" 18 | #include "core/xdb_common.h" 19 | #include "parser/xdb_stmt.h" 20 | #include "core/xdb_store.h" 21 | #include "core/xdb_wal.h" 22 | #include "core/xdb_db.h" 23 | #include "core/xdb_crud.h" 24 | #include "core/xdb_hash.h" 25 | #include "core/xdb_rbtree.h" 26 | #include "core/xdb_sql.h" 27 | #include "core/xdb_sysdb.h" 28 | #include "core/xdb_vdata.h" 29 | #include "core/xdb_fkey.h" 30 | #include "core/xdb_table.h" 31 | #include "core/xdb_index.h" 32 | #include "core/xdb_trans.h" 33 | #include "core/xdb_trigger.h" 34 | #if (XDB_ENABLE_SERVER == 1) 35 | #include "server/xdb_server.h" 36 | #endif 37 | #if (XDB_ENABLE_PUBSUB == 1) 38 | #include "server/xdb_pubsub.h" 39 | #endif 40 | #include "core/xdb_conn.h" 41 | #include "admin/xdb_shell.h" 42 | #include "admin/xdb_backup.h" 43 | #include "core/xdb_wal.h" 44 | 45 | 46 | static bool s_xdb_vdat[XDB_TYPE_MAX] = { 47 | [XDB_TYPE_VBINARY ] = true, 48 | [XDB_TYPE_VCHAR ] = true, 49 | [XDB_TYPE_JSON ] = true 50 | }; 51 | 52 | const char* xdb_type2str(xdb_type_t tp) 53 | { 54 | static const char *id2str[] = { 55 | [XDB_TYPE_NULL ] = "NULL", 56 | [XDB_TYPE_BOOL ] = "BOOL", 57 | [XDB_TYPE_TINYINT ] = "TINYINT", 58 | [XDB_TYPE_SMALLINT ] = "SMALLINT", 59 | [XDB_TYPE_INT ] = "INT", 60 | [XDB_TYPE_BIGINT ] = "BIGINT", 61 | [XDB_TYPE_UTINYINT ] = "TINYINT UNSIGNED", 62 | [XDB_TYPE_USMALLINT] = "SMALLINT UNSIGNED", 63 | [XDB_TYPE_UINT ] = "INT UNSIGNED", 64 | [XDB_TYPE_UBIGINT ] = "BIGINT UNSIGNED", 65 | [XDB_TYPE_FLOAT ] = "FLOAT", 66 | [XDB_TYPE_DOUBLE ] = "DOUBLE", 67 | [XDB_TYPE_TIMESTAMP] = "TIMESTAMP", 68 | [XDB_TYPE_CHAR ] = "CHAR", 69 | [XDB_TYPE_VCHAR ] = "VARCHAR", 70 | [XDB_TYPE_BINARY ] = "BINARY", 71 | [XDB_TYPE_VBINARY ] = "VARBINARY", 72 | [XDB_TYPE_INET ] = "INET", 73 | [XDB_TYPE_MAC ] = "MAC", 74 | [XDB_TYPE_JSON ] = "JSON", 75 | }; 76 | return tp <= XDB_ARY_LEN(id2str) ? id2str[tp] : "Unknown"; 77 | } 78 | 79 | static xdb_type_t s_xdb_prompt_type[] = { 80 | [XDB_TYPE_BOOL ] = XDB_TYPE_BIGINT, 81 | [XDB_TYPE_TINYINT ] = XDB_TYPE_BIGINT, 82 | [XDB_TYPE_SMALLINT ] = XDB_TYPE_BIGINT, 83 | [XDB_TYPE_INT ] = XDB_TYPE_BIGINT, 84 | [XDB_TYPE_BIGINT ] = XDB_TYPE_BIGINT, 85 | [XDB_TYPE_UTINYINT ] = XDB_TYPE_UBIGINT, 86 | [XDB_TYPE_USMALLINT ] = XDB_TYPE_UBIGINT, 87 | [XDB_TYPE_UINT ] = XDB_TYPE_UBIGINT, 88 | [XDB_TYPE_UBIGINT ] = XDB_TYPE_UBIGINT, 89 | [XDB_TYPE_FLOAT ] = XDB_TYPE_DOUBLE, 90 | [XDB_TYPE_DOUBLE ] = XDB_TYPE_DOUBLE, 91 | [XDB_TYPE_CHAR ] = XDB_TYPE_CHAR, 92 | [XDB_TYPE_VCHAR ] = XDB_TYPE_VCHAR, 93 | [XDB_TYPE_BINARY ] = XDB_TYPE_BINARY, 94 | [XDB_TYPE_VBINARY ] = XDB_TYPE_VBINARY, 95 | [XDB_TYPE_TIMESTAMP] = XDB_TYPE_BIGINT, 96 | [XDB_TYPE_INET] = XDB_TYPE_INET, 97 | [XDB_TYPE_MAC] = XDB_TYPE_MAC, 98 | [XDB_TYPE_JSON ] = XDB_TYPE_JSON, 99 | }; 100 | 101 | static uint8_t s_xdb_type_len[] = { 102 | [XDB_TYPE_BOOL ] = 1, 103 | [XDB_TYPE_TINYINT ] = 1, 104 | [XDB_TYPE_SMALLINT ] = 2, 105 | [XDB_TYPE_INT ] = 4, 106 | [XDB_TYPE_BIGINT ] = 8, 107 | [XDB_TYPE_UTINYINT ] = 1, 108 | [XDB_TYPE_USMALLINT ] = 2, 109 | [XDB_TYPE_UINT ] = 4, 110 | [XDB_TYPE_UBIGINT ] = 8, 111 | [XDB_TYPE_FLOAT ] = 4, 112 | [XDB_TYPE_DOUBLE ] = 8, 113 | [XDB_TYPE_CHAR ] = 2, 114 | [XDB_TYPE_VCHAR ] = 4, 115 | [XDB_TYPE_BINARY ] = 2, 116 | [XDB_TYPE_VBINARY ] = 4, 117 | [XDB_TYPE_TIMESTAMP] = 8, 118 | [XDB_TYPE_INET] = sizeof (xdb_inet_t), 119 | [XDB_TYPE_MAC] = sizeof (xdb_mac_t), 120 | [XDB_TYPE_JSON ] = 4, 121 | }; 122 | 123 | 124 | #include "parser/xdb_parser.c" 125 | #include "parser/xdb_json.c" 126 | #include "core/xdb_expr.c" 127 | #include "core/xdb_store.c" 128 | #include "core/xdb_sysdb.c" 129 | #include "core/xdb_db.c" 130 | #include "core/xdb_crud.c" 131 | #include "core/xdb_fkey.c" 132 | #include "core/xdb_index.c" 133 | #include "core/xdb_hash.c" 134 | #include "core/xdb_rbtree.c" 135 | #include "core/xdb_vdata.c" 136 | #include "core/xdb_table.c" 137 | #include "core/xdb_trans.c" 138 | #include "core/xdb_trigger.c" 139 | #include "core/xdb_conn.c" 140 | #if (XDB_ENABLE_SERVER == 1) 141 | #include "server/xdb_client.c" 142 | #include "server/xdb_server.c" 143 | #endif 144 | #include "admin/xdb_backup.c" 145 | #if (XDB_ENABLE_PUBSUB == 1) 146 | #include "server/xdb_pubsub.c" 147 | #endif 148 | #include "core/xdb_sql.c" 149 | #include "core/xdb_wal.c" 150 | #if (XDB_ENABLE_JNI == 1) 151 | #include "jni/xdb_jni.c" 152 | #endif 153 | #include "admin/xdb_shell.c" 154 | 155 | 156 | static volatile bool s_xdb_bInit = false; 157 | static bool s_xdb_cli = false; 158 | 159 | int 160 | xdb_init () 161 | { 162 | if (!s_xdb_bInit) { 163 | #if (XDB_ENABLE_SERVER == 1) 164 | xdb_sock_init (); 165 | #endif 166 | s_xdb_bInit = true; 167 | } 168 | xdb_vdat_init (); 169 | xdb_hex_init (); 170 | xdb_bgtask_init (); 171 | 172 | return XDB_OK; 173 | } 174 | 175 | int 176 | xdb_exit () 177 | { 178 | // Close all opened DBs 179 | s_xdb_bInit = false; 180 | while (s_xdb_bg_run) { 181 | xdb_yield (); 182 | } 183 | xdb_close_all_db (NULL); 184 | xdb_sysdb_exit (); 185 | 186 | return XDB_OK; 187 | } 188 | 189 | xdb_errno_e 190 | xdb_errcode (xdb_res_t *pRes) 191 | { 192 | return pRes->errcode; 193 | } 194 | 195 | const char* xdb_errmsg (xdb_res_t *pRes) 196 | { 197 | if (NULL == pRes) { 198 | return ""; 199 | } 200 | return pRes->row_data ? (const char*)pRes->row_data : "OK"; 201 | } 202 | 203 | xdb_msg_e 204 | xdb_msg_type (xdb_res_t *pRes) 205 | { 206 | if (xdb_unlikely (NULL != pRes)) { 207 | return pRes->len_type >= 28; 208 | } 209 | return 0; 210 | } 211 | 212 | const char* xdb_version () 213 | { 214 | return XDB_VERSION; 215 | } 216 | -------------------------------------------------------------------------------- /test/xdb_smoke_test.c: -------------------------------------------------------------------------------- 1 | #include "utest.h" 2 | #include 3 | 4 | typedef struct { 5 | int id; 6 | char *name; 7 | char age; 8 | float height; 9 | double weight; 10 | char *cls; 11 | short score; 12 | } student_t; 13 | 14 | #define STU_1000 1000, "jack", 11, 1.50, 41.2, "6-1", 93 15 | #define STU_1001 1001, "rose", 11, 1.55, 39.5, "6-4", 92 16 | #define STU_1002 1002, "tom", 12, 1.55, 42.9, "6-1", 95 17 | #define STU_1003 1003, "wendy",11, 1.55, 41.7, "6-3", 94 18 | #define STU_1004 1004, "jack", 10, 1.45, 40.2, "6-2", 92 19 | #define STU_1005 1005, "tom", 11, 1.55, 41.9, "6-1", 94 20 | #define STU_1006 1006, "jack", 11, 1.55, 40.2, "6-2", 92 21 | #define STU_1007 1007, "tom", 12, 1.55, 40.5, "6-1", 93 22 | 23 | #define STU2_1000 "1000, 'jack', 11, 1.50, 41.2, '6-1', 93" 24 | #define STU2_1001 "1001, 'rose', 11, 1.55, 39.5, '6-4', 92" 25 | #define STU2_1002 "1002, 'tom', 12, 1.55, 42.9, '6-1', 95" 26 | #define STU2_1003 "1003, 'wendy',11, 1.55, 41.7, '6-3', 94" 27 | #define STU2_1004 "1004, 'jack', 10, 1.45, 40.2, '6-2', 92" 28 | #define STU2_1005 "1005, 'tom', 11, 1.55, 41.9, '6-1', 94" 29 | #define STU2_1006 "1006, 'jack', 11, 1.55, 40.2, '6-2', 92" 30 | #define STU2_1007 "1007, 'tom', 12, 1.55, 40.5, '6-1', 93" 31 | 32 | #define STU2_1000_DUP "1000, 'jack', 12, 1.55, 40.2, '6-2', 92" 33 | 34 | student_t stu_info[] = { 35 | {STU_1000}, 36 | {STU_1001}, 37 | {STU_1002}, 38 | {STU_1003}, 39 | {STU_1004}, 40 | {STU_1005}, 41 | {STU_1006}, 42 | {STU_1007}, 43 | }; 44 | 45 | struct XdbTest { 46 | xdb_conn_t *pConn; 47 | }; 48 | 49 | struct XdbTestRows { 50 | xdb_conn_t *pConn; 51 | }; 52 | 53 | UTEST_I_SETUP(XdbTest) 54 | { 55 | xdb_conn_t *pConn = xdb_open (utest_index ? "testdb" : ":memory:"); 56 | ASSERT_TRUE (pConn!=NULL); 57 | utest_fixture->pConn = pConn; 58 | xdb_res_t *pRes = xdb_exec (pConn, "CREATE TABLE student (id INT PRIMARY KEY, name VARCHAR(32), age TINYINT, height FLOAT, weight DOUBLE, class CHAR(16), score SMALLINT)"); 59 | ASSERT_EQ_MSG (xdb_errcode(pRes), XDB_OK, xdb_errmsg(pRes)); 60 | } 61 | UTEST_I_TEARDOWN(XdbTest) 62 | { 63 | xdb_conn_t *pConn = utest_fixture->pConn; 64 | xdb_res_t *pRes = xdb_exec (pConn, "DROP TABLE student"); 65 | ASSERT_EQ_MSG (xdb_errcode(pRes), XDB_OK, xdb_errmsg(pRes)); 66 | xdb_close (pConn); 67 | } 68 | 69 | UTEST_F_SETUP(XdbTest) 70 | { 71 | xdb_conn_t *pConn = xdb_open (":memory:"); 72 | ASSERT_TRUE (pConn!=NULL); 73 | utest_fixture->pConn = pConn; 74 | xdb_res_t *pRes = xdb_exec (pConn, "CREATE TABLE student (id INT PRIMARY KEY, name VARCHAR(32), age TINYINT, height FLOAT, weight DOUBLE, class CHAR(16), score SMALLINT)"); 75 | ASSERT_EQ_MSG (xdb_errcode(pRes), XDB_OK, xdb_errmsg(pRes)); 76 | } 77 | UTEST_F_TEARDOWN(XdbTest) 78 | { 79 | xdb_conn_t *pConn = utest_fixture->pConn; 80 | xdb_res_t *pRes = xdb_exec (pConn, "DROP TABLE student"); 81 | ASSERT_EQ_MSG (xdb_errcode(pRes), XDB_OK, xdb_errmsg(pRes)); 82 | xdb_close (pConn); 83 | } 84 | 85 | UTEST_I_SETUP(XdbTestRows) 86 | { 87 | xdb_conn_t *pConn = xdb_open (utest_index ? "testdb" : ":memory:"); 88 | ASSERT_TRUE (pConn!=NULL); 89 | utest_fixture->pConn = pConn; 90 | xdb_res_t *pRes = xdb_exec (pConn, "CREATE TABLE student (id INT PRIMARY KEY, name VARCHAR(32), age TINYINT, height FLOAT, weight DOUBLE, class CHAR(16), score SMALLINT)"); 91 | ASSERT_EQ_MSG (xdb_errcode(pRes), XDB_OK, xdb_errmsg(pRes)); 92 | 93 | pRes = xdb_bexec (pConn, "INSERT INTO student (id,name,age,height,weight,class,score) VALUES (?,?,?,?,?,?,?),(?,?,?,?,?,?,?),(?,?,?,?,?,?,?),(?,?,?,?,?,?,?),(?,?,?,?,?,?,?),(?,?,?,?,?,?,?),(?,?,?,?,?,?,?)", 94 | STU_1000, STU_1001, STU_1002, STU_1003, STU_1004, STU_1005, STU_1006); 95 | ASSERT_EQ_MSG (xdb_errcode(pRes), XDB_OK, xdb_errmsg(pRes)); 96 | ASSERT_EQ (xdb_affected_rows(pRes), 7); 97 | } 98 | UTEST_I_TEARDOWN(XdbTestRows) 99 | { 100 | xdb_conn_t *pConn = utest_fixture->pConn; 101 | xdb_res_t *pRes = xdb_exec (pConn, "DROP TABLE student"); 102 | ASSERT_EQ_MSG (xdb_errcode(pRes), XDB_OK, xdb_errmsg(pRes)); 103 | xdb_close (pConn); 104 | } 105 | 106 | #define STU_ID(pRow) xdb_column_int (pRes, pRow, 0) 107 | #define STU_NAME(pRow) xdb_column_str (pRes, pRow, 1) 108 | #define STU_AGE(pRow) xdb_column_int (pRes, pRow, 2) 109 | 110 | #define CHECK_STUDENT(pRes, pRow, stu) \ 111 | ASSERT_EQ (xdb_column_int (pRes, pRow, 0), stu.id); \ 112 | ASSERT_STREQ (xdb_column_str (pRes, pRow, 1), stu.name); \ 113 | ASSERT_STREQ (xdb_column_str2 (pRes, pRow, 1, &len), stu.name); \ 114 | ASSERT_EQ (len, strlen(stu.name)); \ 115 | ASSERT_EQ (xdb_column_int (pRes, pRow, 2), stu.age); \ 116 | ASSERT_NEAR (xdb_column_float (pRes, pRow, 3), stu.height, 0.000001); \ 117 | ASSERT_NEAR (xdb_column_double (pRes, pRow, 4), stu.weight, 0.000001); \ 118 | ASSERT_STREQ (xdb_column_str2 (pRes, pRow, 5, &len), stu.cls); \ 119 | ASSERT_EQ (len, strlen(stu.cls)); \ 120 | ASSERT_EQ (xdb_column_int (pRes, pRow, 6), stu.score); 121 | 122 | #define CHECK_QUERY(pRes, num, action...) \ 123 | do { \ 124 | int count; \ 125 | bool id_mark[8] = {0}; \ 126 | ASSERT_EQ_MSG (xdb_errcode(pRes), XDB_OK, xdb_errmsg(pRes)); \ 127 | ASSERT_EQ (xdb_row_count(pRes), num); \ 128 | for (count = 0; (pRow = xdb_fetch_row (pRes)); count++) { \ 129 | int len; \ 130 | ASSERT_EQ(id_mark[STU_ID(pRow) - 1000], false); \ 131 | id_mark[STU_ID(pRow) - 1000] = true; \ 132 | student_t stu = stu_info[STU_ID(pRow) - 1000]; \ 133 | action; \ 134 | CHECK_STUDENT (pRes, pRow, stu); \ 135 | } \ 136 | ASSERT_EQ (xdb_row_count(pRes), count); \ 137 | xdb_free_result (pRes); \ 138 | } while (0) 139 | 140 | #define CHECK_EXP(pRes, num, action...) \ 141 | do { \ 142 | int count; \ 143 | ASSERT_EQ_MSG (xdb_errcode(pRes), XDB_OK, xdb_errmsg(pRes)); \ 144 | ASSERT_EQ (xdb_row_count(pRes), num); \ 145 | for (count = 0; (pRow = xdb_fetch_row (pRes)); count++) { \ 146 | action; \ 147 | } \ 148 | ASSERT_EQ (xdb_row_count(pRes), count); \ 149 | xdb_free_result (pRes); \ 150 | } while (0) 151 | 152 | #define CHECK_QUERY_ONE(pRes, stu, action...) \ 153 | do { \ 154 | int count; \ 155 | ASSERT_EQ_MSG (xdb_errcode(pRes), XDB_OK, xdb_errmsg(pRes)); \ 156 | ASSERT_EQ (xdb_row_count(pRes), 1); \ 157 | for (count = 0; (pRow = xdb_fetch_row (pRes)); count++) { \ 158 | int len; \ 159 | action; \ 160 | CHECK_STUDENT (pRes, pRow, stu); \ 161 | } \ 162 | ASSERT_EQ (xdb_row_count(pRes), count); \ 163 | xdb_free_result (pRes); \ 164 | } while (0) 165 | 166 | #define CHECK_AFFECT(pRes, num, action...) \ 167 | ASSERT_EQ_MSG (xdb_errcode(pRes), XDB_OK, xdb_errmsg(pRes)); \ 168 | ASSERT_EQ (xdb_affected_rows(pRes), num); \ 169 | action; 170 | 171 | #include "xdb_smoke_ddl.c" 172 | #include "xdb_smoke_dml.c" 173 | #include "xdb_smoke_func.c" 174 | #include "xdb_smoke_trans.c" 175 | #include "xdb_smoke_semantic.c" 176 | 177 | UTEST_I(XdbTestRows, sysdb_check, 2) 178 | { 179 | } 180 | 181 | UTEST_MAIN() 182 | -------------------------------------------------------------------------------- /src/server/xdb_client.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2024-present JC Wang. All rights reserved 3 | * 4 | * https://crossdb.org 5 | * https://github.com/crossdb-org/crossdb 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | ******************************************************************************/ 11 | 12 | #ifndef xdb_svrlog 13 | #if XDB_LOG_FLAGS & XDB_LOG_SVR 14 | #define xdb_svrlog(...) xdb_print(__VA_ARGS__) 15 | #else 16 | #define xdb_svrlog(...) 17 | #endif 18 | #endif 19 | 20 | XDB_STATIC int 21 | xdb_reconnect (xdb_conn_t *pConn) 22 | { 23 | int ret = XDB_E_SOCK; 24 | 25 | pConn->sockfd = xdb_sock_open (AF_INET, SOCK_STREAM, 0); 26 | if (pConn->sockfd < 0) { 27 | xdb_errlog ("Can't create socket\n"); 28 | goto error; 29 | } 30 | struct sockaddr_in serverin; 31 | xdb_sockaddr_init (&serverin, pConn->port, pConn->host); 32 | 33 | ret = xdb_sock_connect (pConn->sockfd, (struct sockaddr*)&serverin, sizeof(serverin)); 34 | if (0 != ret) { 35 | xdb_errlog ("Can't connect %s:%d, %s\n", pConn->host, pConn->port, strerror(errno)); 36 | goto error; 37 | } 38 | 39 | xdb_sock_SetTcpNoDelay (pConn->sockfd, 1); 40 | 41 | xdb_res_t *pRes = xdb_exec (pConn, "SET FORMAT=NATIVELE"); 42 | XDB_RESCHK(pRes, goto error); 43 | if (*pConn->cur_db != '\0') { 44 | pRes = xdb_pexec (pConn, "USE %s", pConn->cur_db); 45 | XDB_RESCHK(pRes, goto error); 46 | } 47 | return XDB_OK; 48 | 49 | error: 50 | if (pConn->sockfd >= 0) { 51 | xdb_sock_close (pConn->sockfd); 52 | pConn->sockfd = -1; 53 | } 54 | return ret; 55 | } 56 | 57 | xdb_conn_t* 58 | xdb_connect (const char *host, const char *user, const char *pass, const char *db, uint16_t port) 59 | { 60 | xdb_init (); 61 | 62 | if ((NULL == host) || ('\0' == *host)) { 63 | host = "127.0.0.1"; 64 | } else if (0 == port) { 65 | port = XDB_SVR_PORT; 66 | } 67 | 68 | if (0 == port) { 69 | return xdb_open (db); 70 | } 71 | 72 | xdb_conn_t* pConn = xdb_calloc (sizeof (xdb_conn_t)); 73 | if (NULL == pConn) { 74 | return NULL; 75 | } 76 | 77 | xdb_conn_init (pConn); 78 | 79 | if (NULL != db) { 80 | xdb_strcpy (pConn->cur_db, db); 81 | } 82 | xdb_strcpy (pConn->host, host); 83 | pConn->port = port; 84 | pConn->conn_client = true; 85 | 86 | int ret = xdb_reconnect (pConn); 87 | if (XDB_OK == ret) { 88 | xdb_atomic_inc (&s_xdb_conn_count); 89 | return pConn; 90 | } 91 | 92 | xdb_free (pConn); 93 | return NULL; 94 | } 95 | 96 | XDB_STATIC xdb_res_t* 97 | xdb_fetch_res_sock (xdb_conn_t *pConn) 98 | { 99 | xdb_res_t *pRes = &pConn->conn_res; 100 | 101 | // read result 102 | uint32_t len = xdb_sock_read (pConn->sockfd, pRes, sizeof(*pRes)); 103 | XDB_EXPECT (len >= sizeof(*pRes), XDB_E_SOCK, "Featch wrong header"); 104 | 105 | // read 106 | int reslen = sizeof (xdb_queryRes_t) + pRes->data_len; 107 | if (0 == pRes->meta_len) { 108 | if (pRes->data_len > 0) { 109 | len = xdb_sock_read (pConn->sockfd, &pConn->conn_msg, pRes->data_len); 110 | XDB_EXPECT (len >= pRes->data_len, XDB_E_SOCK, "Featch wrong header"); 111 | pRes->row_data = (uintptr_t)pConn->conn_msg.msg; 112 | } 113 | } else { 114 | int buf_len = reslen + pRes->meta_len + pRes->col_count * 8 + 7; 115 | pRes = xdb_queryres_alloc (pConn, buf_len); 116 | if (NULL != pRes) { 117 | return pRes; 118 | } 119 | xdb_queryRes_t *pQueryRes = pConn->pQueryRes; 120 | 121 | pRes = &pQueryRes->res; 122 | pQueryRes->res = pConn->conn_res; 123 | pQueryRes->pConn = pConn; 124 | pQueryRes->pStmt = NULL; 125 | 126 | int64_t rdlen = sizeof (xdb_queryRes_t); 127 | while (rdlen < reslen) { 128 | len = xdb_sock_read (pConn->sockfd, (void*)pQueryRes+rdlen, reslen-rdlen); 129 | if (len > 0) { 130 | rdlen += len; 131 | } else { 132 | return NULL; 133 | } 134 | } 135 | xdb_svrlog ("get response %d from server\n", sizeof(*pRes) + pRes->data_len); 136 | #if XDB_LOG_FLAGS & XDB_LOG_SVR 137 | //xdb_hexdump (pRes, sizeof(*pRes) + pRes->data_len); 138 | #endif 139 | 140 | xdb_meta_t *pMeta = (xdb_meta_t*)(pRes + 1); 141 | pRes->col_meta = (uintptr_t)pMeta; 142 | pMeta->col_list = ((uintptr_t)pQueryRes + sizeof (xdb_queryRes_t) + pRes->data_len + 7) & (~7LL); 143 | uint64_t *pColList = (uint64_t*)pMeta->col_list; 144 | xdb_col_t *pCol = (xdb_col_t*)((void*)pMeta + pMeta->cols_off); 145 | int col_cnt = 0; 146 | //xdb_hexdump (pMeta, pRes->meta_len); 147 | while (pCol->col_len) { 148 | *pColList++ = (uintptr_t)pCol; 149 | if (++col_cnt >= pMeta->col_count) { 150 | break; 151 | } 152 | pCol = (void*)pCol + pCol->col_len; 153 | if ((uintptr_t)pCol - (uintptr_t)pRes > buf_len) { 154 | xdb_free_result (pRes); 155 | pRes = &pConn->conn_res; 156 | XDB_EXPECT (0, XDB_E_SOCK, "Featch wrong meta"); 157 | } 158 | } 159 | xdb_init_rowlist (pQueryRes); 160 | pConn->ref_cnt++; 161 | } 162 | 163 | error: 164 | return pRes; 165 | } 166 | 167 | XDB_STATIC xdb_res_t* 168 | xdb_exec_client (xdb_conn_t *pConn, const char *sql, int len) 169 | { 170 | xdb_res_t *pRes = &pConn->conn_res; 171 | if (xdb_unlikely (pConn->sockfd < 0)) { 172 | int ret = xdb_reconnect (pConn); 173 | XDB_EXPECT(ret == XDB_OK, XDB_E_SOCK, "Can't connect server"); 174 | } 175 | xdb_svrlog ("send: '%s'\n", sql); 176 | // send socket 177 | char buf[64]; 178 | int nn = sprintf (buf, "$%d\n", len); 179 | int wlen = xdb_sock_write (pConn->sockfd, buf, nn); 180 | XDB_EXPECT_SOCK(wlen == nn, XDB_E_SOCK, "Socket Error write %d of %d", wlen, nn); 181 | wlen = xdb_sock_write (pConn->sockfd, sql, len); 182 | XDB_EXPECT_SOCK(wlen == len, XDB_E_SOCK, "Socket Error write %d of %d", wlen, len); 183 | pRes = xdb_fetch_res_sock (pConn); 184 | if ((XDB_STMT_USE_DB == pRes->stmt_type) && (0 == pRes->errcode) && pRes->row_data) { 185 | xdb_strcpy (pConn->cur_db, (char*)pRes->row_data); 186 | } 187 | 188 | error: 189 | return pRes; 190 | } 191 | 192 | // source | dump | shell | help 193 | XDB_STATIC bool 194 | xdb_is_local_stmt (const char *sql) 195 | { 196 | switch (sql[0]) { 197 | case 's': 198 | case 'S': 199 | switch (sql[1]) { 200 | case 'o': 201 | case 'O': 202 | if (!strncasecmp (sql, "SOURCE", 6)) { 203 | return true; 204 | } 205 | break; 206 | case 'h': 207 | case 'H': 208 | if (!strncasecmp (sql, "SHELL", 5)) { 209 | return true; 210 | } 211 | break; 212 | } 213 | break; 214 | case 'd': 215 | case 'D': 216 | if (!strncasecmp (sql, "DUMP", 4)) { 217 | return true; 218 | } 219 | break; 220 | case 'h': 221 | case 'H': 222 | if (!strncasecmp (sql, "HELP", 4)) { 223 | return true; 224 | } 225 | break; 226 | } 227 | return false; 228 | } 229 | 230 | -------------------------------------------------------------------------------- /test/xdb_smoke_semantic.c: -------------------------------------------------------------------------------- 1 | UTEST_F(XdbTest, create_db_wrong_semantic) 2 | { 3 | xdb_res_t *pRes; 4 | xdb_conn_t *pConn = utest_fixture->pConn; 5 | 6 | pRes = xdb_bexec (pConn, "CREATE2 DATABASE"); 7 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 8 | pRes = xdb_bexec (pConn, "CREATE2 DATABASE xdb"); 9 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 10 | pRes = xdb_bexec (pConn, "CREATE DATABASE2 xdb"); 11 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 12 | pRes = xdb_bexec (pConn, "CREATE DATABASE IF2 NOT EXISTS xdb"); 13 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 14 | pRes = xdb_bexec (pConn, "CREATE DATABASE IF NOT2 EXISTS xdb"); 15 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 16 | pRes = xdb_bexec (pConn, "CREATE DATABASE IF NOT2 EXISTS2 xdb"); 17 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 18 | } 19 | 20 | UTEST_F(XdbTest, drop_db_wrong_semantic) 21 | { 22 | xdb_res_t *pRes; 23 | xdb_conn_t *pConn = utest_fixture->pConn; 24 | pRes = xdb_bexec (pConn, "DROP DATABASE xdb"); 25 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 26 | } 27 | 28 | UTEST_F(XdbTest, create_table_wrong_semantic) 29 | { 30 | } 31 | 32 | UTEST_F(XdbTest, drop_table_wrong_semantic) 33 | { 34 | } 35 | 36 | UTEST_F(XdbTest, create_idx_semantic) 37 | { 38 | } 39 | 40 | UTEST_F(XdbTest, drop_idx_semantic) 41 | { 42 | } 43 | 44 | UTEST_F(XdbTest, insert_wrong_semantic) 45 | { 46 | int len; 47 | xdb_res_t *pRes; 48 | xdb_row_t *pRow; 49 | xdb_conn_t *pConn = utest_fixture->pConn; 50 | 51 | pRes = xdb_bexec (pConn, "INSERT2 INTO student (id,name,age,height,weight,class,score) VALUES ("STU2_1000")"); 52 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 53 | 54 | pRes = xdb_bexec (pConn, "INSERT INTO2 student (id,name,age,height,weight,class,score) VALUES ("STU2_1000")"); 55 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 56 | 57 | pRes = xdb_bexec (pConn, "INSERT2 INTO student2 (id,name,age,height,weight,class,score) VALUES ("STU2_1000")"); 58 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 59 | 60 | pRes = xdb_bexec (pConn, "INSERT2 INTO student (id2,name,age,height,weight,class,score) VALUES ("STU2_1000")"); 61 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 62 | 63 | pRes = xdb_bexec (pConn, "INSERT2 INTO student (id,name,age,height,weight,class,score) VALUES ("STU2_1000")"); 64 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 65 | 66 | pRes = xdb_bexec (pConn, "INSERT2 INTO student (id,name,age,height,weight,class,score) VALUES2 ("STU2_1000")"); 67 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 68 | } 69 | 70 | UTEST_F(XdbTest, query_wrong_semantic) 71 | { 72 | int len; 73 | xdb_res_t *pRes; 74 | xdb_row_t *pRow; 75 | xdb_conn_t *pConn = utest_fixture->pConn; 76 | 77 | pRes = xdb_pexec (pConn, "SELECT2 * FROM student WHERE id=%d", 1100); 78 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 79 | 80 | pRes = xdb_pexec (pConn, "SELECT name,age2 FROM student WHERE id=%d", 1100); 81 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 82 | 83 | pRes = xdb_pexec (pConn, "SELECT age2+5 FROM student WHERE id=%d", 1100); 84 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 85 | 86 | pRes = xdb_pexec (pConn, "SELECT COUNT2(*) FROM student WHERE id=%d", 1100); 87 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 88 | 89 | pRes = xdb_pexec (pConn, "SELECT MAX(*) FROM student WHERE id=%d", 1100); 90 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 91 | 92 | pRes = xdb_pexec (pConn, "SELECT MAX2(id) FROM student WHERE id=%d", 1100); 93 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 94 | 95 | pRes = xdb_pexec (pConn, "SELECT * FROM2 student WHERE id=%d", 1100); 96 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 97 | 98 | pRes = xdb_bexec (pConn, "SELECT * FROM student2 WHERE id=?", 1100); 99 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 100 | 101 | pRes = xdb_bexec (pConn, "SELECT * FROM student WHERE2 id=?", 1100); 102 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 103 | 104 | pRes = xdb_bexec (pConn, "SELECT * FROM student WHERE id2=?", 1100); 105 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 106 | } 107 | 108 | UTEST_F(XdbTest, update_wrong_semantic) 109 | { 110 | int len; 111 | xdb_res_t *pRes; 112 | xdb_row_t *pRow; 113 | xdb_conn_t *pConn = utest_fixture->pConn; 114 | 115 | pRes = xdb_exec (pConn, "UPDATE2 student SET age=age+1 WHERE id=1101"); 116 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 117 | 118 | pRes = xdb_exec (pConn, "UPDATE student2 SET age=age+1 WHERE id=1101"); 119 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 120 | 121 | pRes = xdb_exec (pConn, "UPDATE student2 SET2 age=age+1 WHERE id=1101"); 122 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 123 | 124 | pRes = xdb_exec (pConn, "UPDATE student SET age2=age+1 WHERE id=1101"); 125 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 126 | 127 | pRes = xdb_exec (pConn, "UPDATE student SET age=age2+1 WHERE id=1101"); 128 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 129 | 130 | pRes = xdb_exec (pConn, "UPDATE student SET age=age+1 WHERE2 id=1101"); 131 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 132 | 133 | pRes = xdb_exec (pConn, "UPDATE student SET age=age+1 WHERE2 id2=1101"); 134 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 135 | } 136 | 137 | UTEST_F(XdbTest, delete_wrong_semantic) 138 | { 139 | int len; 140 | xdb_res_t *pRes; 141 | xdb_row_t *pRow; 142 | xdb_conn_t *pConn = utest_fixture->pConn; 143 | 144 | pRes = xdb_pexec (pConn, "DELETE2 FROM student WHERE id=%d", 1101); 145 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 146 | 147 | pRes = xdb_pexec (pConn, "DELETE FROM2 student WHERE id=%d", 1101); 148 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 149 | 150 | pRes = xdb_pexec (pConn, "DELETE FROM student2 WHERE id=%d", 1101); 151 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 152 | 153 | pRes = xdb_pexec (pConn, "DELETE FROM student WHERE2 id=%d", 1101); 154 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 155 | 156 | pRes = xdb_pexec (pConn, "DELETE FROM student WHERE id2=%d", 1101); 157 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 158 | } 159 | 160 | UTEST_F(XdbTest, agg_wrong_semantic) 161 | { 162 | int len; 163 | xdb_res_t *pRes; 164 | xdb_row_t *pRow; 165 | xdb_conn_t *pConn = utest_fixture->pConn; 166 | 167 | pRes = xdb_pexec (pConn, "SELECT COUNT2(*) FROM student WHERE id=%d", 1100); 168 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 169 | 170 | pRes = xdb_pexec (pConn, "SELECT COUNT(id2) FROM student WHERE id=%d", 1100); 171 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 172 | 173 | pRes = xdb_pexec (pConn, "SELECT MIN2(*) FROM student WHERE id=%d", 1100); 174 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 175 | 176 | pRes = xdb_pexec (pConn, "SELECT MIN(id2) FROM student WHERE id=%d", 1100); 177 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 178 | 179 | pRes = xdb_pexec (pConn, "SELECT MAX2(*) FROM student WHERE id=%d", 1100); 180 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 181 | 182 | pRes = xdb_pexec (pConn, "SELECT MAX(id2) FROM student WHERE id=%d", 1100); 183 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 184 | 185 | pRes = xdb_pexec (pConn, "SELECT AVG2(*) FROM student WHERE id=%d", 1100); 186 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 187 | 188 | pRes = xdb_pexec (pConn, "SELECT AVG(id2) FROM student WHERE id=%d", 1100); 189 | ASSERT_NE (xdb_errcode(pRes), XDB_OK); 190 | } 191 | -------------------------------------------------------------------------------- /src/3rd/crossline.h: -------------------------------------------------------------------------------- 1 | /* crossline.h -- Version 1.0 2 | * 3 | * Crossline is a small, self-contained, zero-config, MIT licensed, 4 | * cross-platform, readline and libedit replacement. 5 | * 6 | * You can find the latest source code and description at: 7 | * https://github.com/jcwangxp/crossline 8 | * 9 | * ------------------------------------------------------------------------ 10 | * 11 | * MIT License 12 | * 13 | * Copyright (c) 2019, JC Wang (wang_junchuan@163.com) 14 | * 15 | * Permission is hereby granted, free of charge, to any person obtaining a copy 16 | * of this software and associated documentation files (the "Software"), to deal 17 | * in the Software without restriction, including without limitation the rights 18 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | * copies of the Software, and to permit persons to whom the Software is 20 | * furnished to do so, subject to the following conditions: 21 | * 22 | * The above copyright notice and this permission notice shall be included in all 23 | * copies or substantial portions of the Software. 24 | * 25 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | * SOFTWARE. 32 | * ------------------------------------------------------------------------ 33 | */ 34 | 35 | #ifndef __CROSSLINE_H 36 | #define __CROSSLINE_H 37 | 38 | #ifdef __cplusplus 39 | extern "C" { 40 | #endif 41 | 42 | #ifndef CLN_EXPORT 43 | #define CLN_EXPORT extern 44 | #endif 45 | 46 | typedef enum { 47 | CROSSLINE_FGCOLOR_DEFAULT = 0x00, 48 | CROSSLINE_FGCOLOR_BLACK = 0x01, 49 | CROSSLINE_FGCOLOR_RED = 0x02, 50 | CROSSLINE_FGCOLOR_GREEN = 0x03, 51 | CROSSLINE_FGCOLOR_YELLOW = 0x04, 52 | CROSSLINE_FGCOLOR_BLUE = 0x05, 53 | CROSSLINE_FGCOLOR_MAGENTA = 0x06, 54 | CROSSLINE_FGCOLOR_CYAN = 0x07, 55 | CROSSLINE_FGCOLOR_WHITE = 0x08, 56 | CROSSLINE_FGCOLOR_BRIGHT = 0x80, 57 | CROSSLINE_FGCOLOR_MASK = 0x7F, 58 | 59 | CROSSLINE_BGCOLOR_DEFAULT = 0x0000, 60 | CROSSLINE_BGCOLOR_BLACK = 0x0100, 61 | CROSSLINE_BGCOLOR_RED = 0x0200, 62 | CROSSLINE_BGCOLOR_GREEN = 0x0300, 63 | CROSSLINE_BGCOLOR_YELLOW = 0x0400, 64 | CROSSLINE_BGCOLOR_BLUE = 0x0500, 65 | CROSSLINE_BGCOLOR_MAGENTA = 0x0600, 66 | CROSSLINE_BGCOLOR_CYAN = 0x0700, 67 | CROSSLINE_BGCOLOR_WHITE = 0x0800, 68 | CROSSLINE_BGCOLOR_BRIGHT = 0x8000, 69 | CROSSLINE_BGCOLOR_MASK = 0x7F00, 70 | 71 | CROSSLINE_UNDERLINE = 0x10000, 72 | 73 | CROSSLINE_COLOR_DEFAULT = CROSSLINE_FGCOLOR_DEFAULT | CROSSLINE_BGCOLOR_DEFAULT 74 | } crossline_color_e; 75 | 76 | // Main API to read a line, return buf if get line, return NULL if EOF. 77 | CLN_EXPORT char* crossline_readline (const char *prompt, char *buf, int size); 78 | 79 | // Same with crossline_readline except buf holding initial input for editing. 80 | //CLN_EXPORT char* crossline_readline2 (const char *prompt, char *buf, int size); 81 | 82 | // Set move/cut word delimiter, default is all not digital and alphabetic characters. 83 | //CLN_EXPORT void crossline_delimiter_set (const char *delim); 84 | 85 | // Read a character from terminal without echo 86 | CLN_EXPORT int crossline_getch (void); 87 | 88 | 89 | /* 90 | * History APIs 91 | */ 92 | 93 | // Save history to file 94 | //CLN_EXPORT int crossline_history_save (const char *filename); 95 | 96 | // Load history from file 97 | //CLN_EXPORT int crossline_history_load (const char *filename); 98 | 99 | // Show history in buffer 100 | CLN_EXPORT void crossline_history_show (void); 101 | 102 | // Clear history 103 | CLN_EXPORT void crossline_history_clear (void); 104 | 105 | 106 | /* 107 | * Completion APIs 108 | */ 109 | 110 | typedef struct crossline_completions_t crossline_completions_t; 111 | typedef void (*crossline_completion_callback) (const char *buf, crossline_completions_t *pCompletions); 112 | typedef void (*crossline_completion_callback2) (const char *buf, crossline_completions_t *pCompletions, void *pArg); 113 | 114 | // Register completion callback 115 | #define crossline_completion_register(pCbFunc) crossline_completion_register2((crossline_completion_callback2)pCbFunc, NULL) 116 | CLN_EXPORT void crossline_completion_register2 (crossline_completion_callback2 pCbFunc, void *pArg); 117 | 118 | // Add completion in callback. Word is must, help for word is optional. 119 | CLN_EXPORT void crossline_completion_add (crossline_completions_t *pCompletions, const char *word, const char *help); 120 | 121 | // Add completion with color. 122 | CLN_EXPORT void crossline_completion_add_color (crossline_completions_t *pCompletions, const char *word, 123 | crossline_color_e wcolor, const char *help, crossline_color_e hcolor); 124 | 125 | // Set syntax hints in callback 126 | //CLN_EXPORT void crossline_hints_set (crossline_completions_t *pCompletions, const char *hints); 127 | 128 | // Set syntax hints with color 129 | CLN_EXPORT void crossline_hints_set_color (crossline_completions_t *pCompletions, const char *hints, crossline_color_e color); 130 | 131 | 132 | /* 133 | * Paging APIs 134 | */ 135 | 136 | // Enable/Disble paging control 137 | CLN_EXPORT int crossline_paging_set (int enable); 138 | 139 | // Check paging after print a line, return 1 means quit, 0 means continue 140 | // if you know only one line is printed, just give line_len = 1 141 | CLN_EXPORT int crossline_paging_check (int line_len); 142 | 143 | 144 | /* 145 | * Cursor APIs 146 | */ 147 | 148 | // Get screen rows and columns 149 | CLN_EXPORT void crossline_screen_get (int *pRows, int *pCols); 150 | 151 | // Clear current screen 152 | CLN_EXPORT void crossline_screen_clear (void); 153 | 154 | // Get cursor postion (0 based) 155 | //CLN_EXPORT int crossline_cursor_get (int *pRow, int *pCol); 156 | 157 | // Set cursor postion (0 based) 158 | //CLN_EXPORT void crossline_cursor_set (int row, int col); 159 | 160 | // Move cursor with row and column offset, row_off>0 move up row_off lines, <0 move down abs(row_off) lines 161 | // =0 no move for row, similar with col_off 162 | CLN_EXPORT void crossline_cursor_move (int row_off, int col_off); 163 | 164 | // Hide or show cursor 165 | //CLN_EXPORT void crossline_cursor_hide (int bHide); 166 | 167 | 168 | /* 169 | * Color APIs 170 | */ 171 | 172 | // Set text color, CROSSLINE_COLOR_DEFAULT will revert to default setting 173 | // `\t` is not supported in Linux terminal, same below. Don't use `\n` in Linux terminal, same below. 174 | CLN_EXPORT void crossline_color_set (crossline_color_e color); 175 | 176 | // Set default prompt color 177 | CLN_EXPORT void crossline_prompt_color_set (crossline_color_e color); 178 | 179 | #ifdef __cplusplus 180 | } 181 | #endif 182 | 183 | #endif /* __CROSSLINE_H */ --------------------------------------------------------------------------------